Linux 进程等候

[复制链接]
发表于 2026-10-3 22:52:36 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

僵尸进程是 Linux 里很讨厌的一类问题:不及时回收,僵尸会越堆越多,占着 PID 和内核数据结构。更麻烦的是,进程一旦变成僵尸态就"刀枪不入",连 kill -9 也无能为力——谁也没办法杀掉一个已经死掉的进程。解决办法只有一个:让父进程等它、把它回收掉。

一、为什么会僵

子进程运行结束后,父进程没有等待并接收它的退出码和退出状态,操作系统就无法释放对应的内核数据结构与代码数据,于是子进程变成僵尸。

为了杜绝这种情况,父进程可以用函数等待子进程结束,此时父进程处于阻塞状态等待。

注意两点:

  • 进程的退出状态是必要的(要不要回收就得看它)。
  • 进程的执行结果(算得对不对)是父进程非必要的——结果准不准得自己判断,但父进程必须对子进程负责,别让它连累操作系统。

二、wait()

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

pid_t wait(int *status);
  • 返回值:成功返回被等待进程的 pid,失败返回 -1。
  • 参数:输出型参数,用来获取子进程退出状态;不关心的话可以写成 NULL。

示例:父进程先 fork 一个子进程跑 5 秒,自己 sleep 6 秒后调用 wait(NULL) 回收,最后父进程退出。

注意:wait 等待的是任意一个进程,不是特指某一个子进程。

三、waitpid()

pid_t waitpid(pid_t pid, int *status, int options);

1. 参数 pid

  • pid = -1:等待任一个子进程,与 wait 等效。
  • pid > 0:等待进程 ID 与 pid 相等的那个子进程。
  • pid = 0:等待与调用进程同进程组的任意子进程。
  • pid < -1:等待进程组 ID 等于 pid 绝对值的任意子进程。

2. 返回值

  • 正常返回:返回收集到的子进程的进程 ID。
  • 设置了 WNOHANG 而调用时并没有已退出的子进程可收集:返回 0。
  • 出错:返回 -1,errno 会指示错误原因。

四、怎么取出退出码

(status >> 8) & 0xFF 取退出码,(status & 0x7F) 取终止信号,这两串位运算很难记,系统提供了两个宏来简化:

  • WIFEXITED(status):判断进程退出状态,宏为真表示进程正常退出。
  • WEXITSTATUS(status):相当于 (status >> 8) & 0xFF,直接拿到退出码。
int status = 0;
pid_t rid = waitpid(id, &status, 0);
if (rid > 0) {
    if (WIFEXITED(status))
        printf("child exit code: %d
", WEXITSTATUS(status));
    else
        printf("child quit unusual!
");
}

五、阻塞等待与非阻塞等待

1. 阻塞等待

上面写的都是阻塞等待:子进程没退出,父进程就一直卡在 wait/waitpid 调用处。wait 函数本身只能阻塞等待。

2. 非阻塞等待

非阻塞等待的意思是:父进程执行到 waitpid 时,如果子进程还在跑,父进程不等,直接往下继续执行,通过 while 循环轮询检查。这样一来等待期间父进程还能干自己的事。

只有 waitpid 能通过 options 参数实现非阻塞等待,传 WNOHANG:

waitpid(id, &status, WNOHANG);

返回值含义:

  • ret > 0:等待成功,子进程已退出并被回收。
  • ret = 0:检测成功,但子进程还没退出,需要下一轮再等。
  • ret < 0:等待失败(比如进程已经不存在)。
while (1) {
    ret = waitpid(id, &status, WNOHANG);
    if (ret == -1) {
        printf("wait failed, process not exist
");
        break;
    } else if (ret == 0) {
        printf("child still running, do other work
");
        sleep(3);
    } else {
        printf("wait success, child recycled
");
        if (WIFEXITED(status))
            printf("exit code: %d
", WEXITSTATUS(status));
        break;
    }
}

六、实践建议

  • 服务型程序里,子进程退出后父进程要能回收,否则长期运行必然堆积僵尸。
  • 不确定子进程编号时,用 wait(-1, ...) 或 waitpid(-1, ...) 兜底回收。
  • 既要不阻塞主流程又要回收,就用 WNOHANG 轮询,别用 sleep 硬等。
  • 发现僵尸已堆积,先查父进程是不是还活着;父进程挂了子进程会被 init 收养并由 init 回收,这之后僵尸通常会自动消失。

qidao123.com企服评测-应用市场 编辑提示

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表