一、进程的基本概念

1.1 程序 vs 进程

程序 进程
定义 存放在外存中的代码集合 程序动态执行的过程
本质 静态的(文件) 动态的(运行中)
生命周期 持久(直到被删除) 创建→调度→消亡

程序是写在硬盘上的代码文件,进程是程序被加载到内存中运行起来的实例。一个程序可以同时运行多个进程(比如开多个终端窗口)。

1.2 进程相关命令

命令 作用
ps -ef 查看所有进程信息
ps -aux 查看进程运行状态
pstree 查看进程树(父子关系)
top 动态查看进程CPU/内存占用
kill -9 PID 强制杀死进程
killall -9 进程名 按名字杀死进程
jobs 查看后台任务
fg 编号 将后台任务调到前台

后台运行程序

bash

./a.out &          # 后台运行
jobs               # 查看后台任务
fg 1               # 将任务1调到前台

二、进程ID(PID)

每个进程都有一个唯一的整数编号——PID。可以通过 getpid() 获取当前进程的PID,getppid() 获取父进程的PID。

c

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    printf("PID: %d\n", getpid());
    printf("PPID: %d\n", getppid());
    return 0;
}

运行结果:

text

PID: 12345
PPID: 23456   // 父进程通常是shell(终端)

三、进程的创建 —— fork()

fork() 是创建新进程的核心函数。调用一次,返回两次:

c

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    pid_t pid = fork();
    
    if (pid == -1) {
        perror("fork");
        return -1;
    }
    
    if (pid == 0) {
        printf("子进程:PID=%d, PPID=%d\n", getpid(), getppid());
    } else {
        printf("父进程:PID=%d, 子进程PID=%d\n", getpid(), pid);
    }
    
    return 0;
}

fork() 的返回值

返回值 含义
-1 创建失败
0 当前在子进程中
>0 当前在父进程中,返回值是子进程的PID

四、进程的退出 —— exit()

进程结束时要释放资源,exit() 负责这个事。

c

#include <stdlib.h>
void exit(int status);
  • status 是退出状态码,父进程可以通过 wait() 获取

  • 0 通常表示正常退出,非0表示异常

  • exit() 会自动刷新缓冲区、关闭打开的文件

c

exit(0);   // 正常退出
exit(1);   // 异常退出

注意:return 只结束当前函数,exit() 结束整个进程。

五、等待子进程 —— wait()

父进程创建子进程后,通常需要等待子进程结束,获取它的退出状态。如果不调用 wait(),子进程结束后会变成僵尸进程

c

#include <sys/wait.h>
pid_t wait(int *status);

c

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

int main(void)
{
    pid_t pid = fork();
    
    if (pid == 0) {
        printf("子进程开始\n");
        sleep(2);
        printf("子进程结束\n");
        exit(10);
    } else {
        int status;
        pid_t ret = wait(&status);
        printf("父进程:子进程 %d 退出,状态码 %d\n", ret, WEXITSTATUS(status));
    }
    
    return 0;
}

wait() 的返回值:成功返回结束的子进程PID,失败返回 -1。

解析退出状态

作用
WEXITSTATUS(status) 获取子进程的退出码
WIFEXITED(status) 判断子进程是否正常退出

c

if (WIFEXITED(status)) {
    printf("正常退出,退出码:%d\n", WEXITSTATUS(status));
}

六、孤儿进程与僵尸进程

孤儿进程:父进程先于子进程结束,子进程变成孤儿,被 init 进程(PID=1)收养。

僵尸进程:子进程已结束,但父进程没有调用 wait() 回收资源,导致子进程的进程表项一直占用。

僵尸进程不占用内存,但占用进程表资源。如果大量僵尸进程积累,可能导致系统无法创建新进程。

七、今日总结

今天学了进程,核心要点如下:

  1. 程序 vs 进程:程序是静态文件,进程是动态执行实例

  2. PIDgetpid() 获取当前进程ID,getppid() 获取父进程ID

  3. fork():创建子进程,返回0表示子进程,>0表示父进程

  4. exit():结束进程,刷新缓冲区、关闭文件

  5. wait():父进程等待子进程结束,回收资源,防止僵尸进程

  6. 孤儿进程:父进程先结束,子进程被 init 收养

  7. 僵尸进程:子进程先结束,父进程未回收,占用进程表资源

今日感悟

今天第一次感受到"一个程序可以分成多个进程同时执行"。fork() 调用一次返回两次的特性,让父子进程能各自独立运行。另外,一定要记得使用 wait() ,否则子进程结束没人“收尸”,就成了僵尸进程。

Logo

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

更多推荐