Linux 进程等待机制详解:wait 与 waitpid


在 Linux 系统编程中,父进程需要通过某种机制等待子进程的结束,以便清理资源、防止僵尸进程的产生。wait 和 waitpid 是我们最常用的两种方式。虽然它们都用于等待子进程的状态变化,但在功能和使用灵活性方面,waitpid 更加强大。本篇文章将深入讲解它们的使用方式和异同点,并结合代码实例帮助理解。

一、wait

1、函数原型

pid_t wait(int *status);

2、参数说明

  • int *status:这是一个输出参数,用于接收子进程的退出状态信息。

    • 如果你不关心退出状态,可以传 NULL
    wait(NULL);
    

    wait(NULL) 表示: 父进程会阻塞,直到任意一个子进程退出; 但不会获取子进程的退出状态,因为你没有传入 status 参数的地址。适用于父进程只想等子进程结束,不关心其结果。

    代码举例:

    #include <iostream>
    #include <sys/wait.h>
    #include <unistd.h>
    
    int main() {
        pid_t pid = fork();
        if (pid == 0) {
            std::cout << "子进程:执行任务并退出...\n";
            return 123;
        }
    
        // 父进程不关心退出状态,只等子进程结束
        wait(NULL);
    
        std::cout << "父进程:子进程已退出,但我不关心退出码\n";
        return 0;
    }
    
  • 如果传入的是一个指针,在子进程退出时,它会被填入状态码(不是直接的退出值,需要解析)。

3、返回值

成功:返回已终止子进程的 PID

失败:返回 -1,并设置 errno

4、如何解析status

在这里举两个例子
示例1:正常退出(WIFEXITED + WEXITSTATUS

#include <iostream>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        std::cout << "Child: 正常退出\n";
        return 5;  // 子进程返回5
    }

    int status;
    wait(&status);  // 等待任意子进程退出

    if (WIFEXITED(status)) {
        std::cout << "父进程:子进程正常退出,退出码 = "
                  << WEXITSTATUS(status) << "\n";  // 应输出5
    }

    return 0;
}

示例2:被信号杀死(WIFSIGNALED + WTERMSIG

#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        std::cout << "Child: 无限循环中,等待被杀...\n";
        while (true) {}  // 模拟卡死
    }

    sleep(1);  // 等待子进程运行
    kill(pid, SIGKILL);  // 杀死子进程(信号9)

    int status;
    wait(&status);

    if (WIFSIGNALED(status)) {
        std::cout << "父进程:子进程被信号杀死,信号编号 = "
                  << WTERMSIG(status) << "\n";  // 应输出9
    }

    return 0;
}

二、waitpid

waitpid 是 Linux/Unix 系统编程中用于等待子进程状态变化的系统调用,它比 wait 更灵活。

1、waitpid函数原型

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

2、 参数说明:

  • pid
    • -1:等待任意一个子进程(相当于 wait)。
    • > 0:等待进程号为 pid 的子进程。
    • 0:等待和当前进程同一进程组的任意子进程。
    • < -1:等待进程组ID为 |pid| 的任何子进程。
  • status:用于接收子进程的状态信息(可以通过宏解析)。
  • options
    • 0:阻塞等待。
    • WNOHANG:非阻塞等待,如果没有子进程退出,立即返回。
    • WUNTRACED:当子进程停止(如收到 SIGSTOP)也返回。

3、返回值

成功:返回子进程的 PID。

如果使用了 WNOHANG 且没有子进程退出:返回 0。

失败:返回 -1,并设置 errno。

4、示例代码:

#include <iostream>
#include <sys/wait.h>
#include <unistd.h>

pid_t pid = fork();
if (pid == 0) {
    // 子进程代码
    exit(0);
} else {
    int status;
    pid_t ret = waitpid(pid, &status, 0);  // 阻塞等待特定子进程结束
    if (WIFEXITED(status)) {
        printf("子进程正常退出,退出码:%d\n", WEXITSTATUS(status));
    }
}

读到这里,我们可以看出wait等价于:waitpid(-1, status, 0),也就是说它只能阻塞等待任意一个子进程

三、总结

特性waitwaitpid
是否阻塞可选(通过 options 控制)
是否支持指定子进程否(任意子进程)是(可指定特定 PID)
能否等待停止的子进程是(带 WUNTRACED
返回值子进程 PID 或 -1(无子进程)子进程 PID 或 0(非阻塞无变化)

在日常开发中,如果只是简单地等待任意一个子进程结束,wait 是足够的;但当你需要对特定子进程进行控制、或者实现非阻塞进程管理时,waitpid 就是更合适的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值