1. 多任务与并发概述

在单核或多核处理器上,让操作系统具备同时处理多个任务的能力称为多任务(Multitasking)

并发(Concurrency):微观上单核 CPU 采用时间片轮询算法在多个任务之间快速切换,宏观上表现为多个任务“同时”执行。

并行(Parallelism):在多核 CPU 或多处理器系统中,不同的任务同时在不同的物理核心上真实地独立运行。

实现多任务的常见方式包括:多进程 与 多线程

2. 深入理解 Linux 进程

2.1 进程与程序的区别

对比维度

程序(Program)

进程(Process)

概念定义

静态的数据指令集合

正在执行的程序

存储位置

外存/硬盘(如 a.out)

内存 + CPU 寄存器

生命周期

永久存在

动态的,从启动到消亡

资源消耗

仅占用磁盘空间

消耗内存空间与 CPU 计算资源

核心结论进程是操作系统资源分配的最小单位

2.2 进程的 0-4G 内存布局

当一个进程被创建时,操作系统会为其分配一个 4G(32 位系统) 的虚拟地址空间,分为:

0 - 3G:用户空间(各个进程相互独立)

3G - 4G:内核空间(所有进程共享)

3. 进程的调度与状态转换

3.1 CPU 调度算法

操作系统内核通过调度器(Scheduler)分配 CPU 资源,常见的调度策略包括:

1.时间片轮询算法(Round-Robin)

2.先来先服务(FCFS) / 短作业优先(SJF)

3.高优先级优先算法(Priority Scheduling)

3.2 Linux 进程状态(七态模型)

在 Linux 中,进程在生命周期内会经历如下状态变迁:

                

状态代码

状态名称

详细说明

R

Running / Runnable

运行态/就绪态:正在 CPU 上运行或等待 CPU 调度

S

Interruptible Sleep

可中断睡眠态:阻塞等待资源(如等待键盘输入),可被信号唤醒

D

Uninterruptible Sleep

不可中断睡眠态:进程不响应信号打断(通常发生在驱动 I/O)

T

Stopped

暂停态:收到 SIGSTOP 信号后暂停执行

Z

Zombie

僵尸态:进程已终止,但其资源尚未被父进程回收

X

Dead

消亡态:进程资源被彻底释放回收

4. 进程管理常用命令

在 Linux 终端中,可以使用以下命令查看与控制进程:

1.ps -aux:查看系统中所有进程的详细参数(PID、CPU/内存占用率、进程状态等)。
ps -aux | grep a.out

2.ps -ef:查看进程的树形父子关系(PID 与 PPID)。

3.top:动态实时查看进程状态与系统 CPU、内存占用率。

4.pstree -sp <PID>:查看指定 PID 进程的族谱/创建关系。

5.kill:给指定进程发送控制信号。

    • kill -l:查看系统支持的所有信号。
    • kill -9 <PID>:强制结束指定 PID 的进程。

6.jobs / fg:查看后台进程与将后台进程切换至前台。

5. 进程控制 API 实战

5.1 创建子进程 fork()

操作系统通过 fork() 创建新进程。fork() 调用完成后,系统会将父进程 0-3G 的用户空间完整拷贝一份给子进程(写时复制 Copy-On-Write 机制)。

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

返回值

在父进程中:返回创建成功的 子进程 PID(pid > 0)。

在子进程中:固定返回 0(pid == 0)。

失败:返回 -1。

注意

1.父子进程共享 3G-4G 内核空间,但各自 0-3G 用户空间相互独立,不能直接通过共享全局变量进行通信。

2.获取进程 PID API:getpid() 获取当前进程 PID,getppid() 获取父进程 PID。

C语言代码实战:fork() 使用示例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
    int num = 100;
    
    printf("Before fork, PID = %d\n", getpid());

    pid_t pid = fork();

    if (pid < 0) {
        perror("fork error");
        return -1;
    }
    else if (pid == 0) {
        // 子进程逻辑
        num += 50;
        printf("[Child]  PID: %d, PPID: %d, num = %d\n", getpid(), getppid(), num);
    }
    else {
        // 父进程逻辑
        sleep(1); // 让子进程先运行
        printf("[Parent] PID: %d, Child PID: %d, num = %d\n", getpid(), pid, num);
    }

    return 0;
}

5.2 进程的退出 exit()

进程退出的常见方式:

  1. main 函数中使用 return 退出。
  2. 调用标准 C 库函数 exit() 或系统调用 _exit() / _Exit()。
  3. 外部发送信号强行终止(如 kill -9 PID)。

#include <stdlib.h>

void exit(int status);

参数 status:0 表示正常退出;非 0 表示因异常或特定错误退出。

5.3 孤儿进程与僵尸进程处理

(1) 孤儿进程(Orphan Process)

定义:父进程先退出,子进程仍在运行。子进程成为孤儿进程

处理机制:孤儿进程会被系统顶级进程(init 或 systemd,PID=1)收养,子进程结束后由系统回收,不会造成系统资源泄露

(2) 僵尸进程(Zombie Process)

定义:子进程已经退出,但父进程没有调用 wait() / waitpid() 回收其内核空间资源(PCB)。

危害:过多僵尸进程会导致 PID 号被耗尽,系统无法创建新进程。

(3) 解决僵尸进程:wait() 与 waitpid()

父进程可以调用 wait() 或 waitpid() 阻塞/非阻塞等待子进程结束并回收其资源:

#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *wstatus);

C语言代码实战:wait() 回收子进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork error");
        return -1;
    }
    else if (pid == 0) {
        // 子进程逻辑
        printf("Child process running... PID: %d\n", getpid());
        sleep(2);
        exit(10); // 子进程带状态码 10 退出
    }
    else {
        // 父进程逻辑
        int status = 0;
        printf("Parent process waiting for child...\n");
        
        pid_t z_pid = wait(&status); // 阻塞等待并回收子进程

        if (WIFEXITED(status)) {
            printf("Child (PID: %d) exited with code: %d\n",
                   z_pid, WEXITSTATUS(status));
        }
    }

    return 0;
}

6. 总结

1.进程 是 Linux 系统分配资源的基本单位,拥有独立 0-3G 用户空间,安全稳定。

2.使用 fork() 可以轻松创建多进程,父子进程通过返回值区分逻辑。

3.必须注意避免产生 僵尸进程,通过 wait() / waitpid() 显式回收子进程资源。

4.由于进程间用户内存空间隔离,实现多进程协同需进一步学习 IPC(进程间通信:管道、信号、共享内存、消息队列、信号量)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐