在Linux中,wait 和 waitpid 是用于进程控制的系统调用,它们主要用来让父进程等待子进程的终止,并获取子进程的退出状态。下面详细介绍它们的用法和区别。
wait() 函数wait() 会阻塞调用进程,直到一个子进程终止。它的典型用法如下:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);status: 这是一个指向整数的指针,用来存储子进程的退出状态。如果不关心退出状态,可以传递 NULL。-1 并设置 errno。int status;
pid_t pid = wait(&status);
if (pid == -1) {
    perror("wait");
} else {
    if (WIFEXITED(status)) {
        printf("Child exited with status %d\n", WEXITSTATUS(status));
    }
}status 是由子进程退出状态生成的,可以通过以下宏来检查子进程的退出原因:
WIFEXITED(status): 子进程是否正常退出。WEXITSTATUS(status): 获取子进程的退出状态(当 WIFEXITED(status) 为真时使用)。WIFSIGNALED(status): 子进程是否因为信号终止。WTERMSIG(status): 获取导致子进程终止的信号。WIFSTOPPED(status): 子进程是否处于暂停状态。WSTOPSIG(status): 获取导致子进程暂停的信号。wait() 只会等待任意一个终止的子进程,并返回其 PID。wait() 可能随机返回任意一个已终止的子进程,而不确定是哪一个。wait() 在没有子进程时返回 -1 并设置 errno 为 ECHILD。waitpid() 函数waitpid() 提供了更强的灵活性,可以指定等待特定的子进程,也可以选择非阻塞模式。
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);pid: 指定要等待的子进程的 PID,具体值可以为以下几种:      -1: 等待任意一个子进程(等价于 wait() 的行为)。> 0: 等待特定的子进程,PID 为 pid 的子进程。0: 等待与调用进程在同一个进程组中的任意子进程。< -1: 等待进程组 ID 为 |pid| 的任意子进程。status: 和 wait() 类似,用来存储子进程的状态信息。options: 控制行为的选项,常见的取值有:      0: 阻塞等待,直到子进程终止。WNOHANG: 非阻塞模式,如果没有子进程终止,waitpid() 返回 0。WUNTRACED: 还可以返回停止的子进程(收到 SIGSTOP、SIGTSTP 等信号)。WNOHANG 且没有终止的子进程,返回 0。-1 并设置 errno。int status;
pid_t pid = waitpid(-1, &status, 0); // 等待任意子进程
if (pid == -1) {
    perror("waitpid");
} else if (pid == 0) {
    // 当使用 WNOHANG 时,可以处理没有子进程退出的情况
    printf("No child has exited yet.\n");
} else {
    if (WIFEXITED(status)) {
        printf("Child exited with status %d\n", WEXITSTATUS(status));
    }
}waitpid() 比 wait() 更灵活,可以指定等待特定的子进程。WNOHANG 选项来避免阻塞。waitpid() 支持等待子进程暂停的状态。wait() 和 waitpid() 的区别| 功能 | wait() | waitpid() | 
|---|---|---|
| 等待特定子进程 | 不可以 | 可以(通过 pid 参数) | 
| 阻塞/非阻塞模式 | 只能阻塞 | 支持非阻塞(通过 WNOHANG 选项) | 
| 等待任意子进程 | 可以(默认行为) | 可以(通过 pid = -1) | 
| 支持停止的子进程 | 不支持 | 支持(通过 WUNTRACED) | 
| 等待同一进程组中的进程 | 不支持 | 可以(通过 pid = 0 或 pid = -<pgid>) | 
wait() 用于简单的父子进程同步,父进程等待任意一个子进程的退出。waitpid() 提供更多控制,可以指定等待特定的子进程,并支持非阻塞模式和等待停止的子进程。1:是什么

2:为什么也就是必要性
之前说过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法 杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对, 或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

wait回收僵尸进程(父进程等待是必须的)(wait成功返回的是回收子进程的PI,失败返回-1)


运行后子进程的状态是S+,然后5秒后就会变成Z+(僵尸状态),然后再过5秒,父进程就会用wait给回收掉(wait是只能等待任意一个子进程)

变成Z

回收了

如果有多个子进程呢,wait等待哪一个呢
首先创建多个子进程,此代码的意思是,父进程进入for循环后,经过fork创建了一个子进程进入了RunChild,然后执行完RunChild后,exit(0),就退出子进程了,然后父进程再次进入循环,再次创建一个子进。。。。。。以此类推

一个wait等待任意一个,10个子进程要用循环了,wait回收僵尸进程(父进程等待是必须的,意思是需要等子进程结束后父进程再进行回收)(wait返回的是回收子进程的PID)

如果子进程一直不死,父进程就会一直等待也不返回,只要你退出了我才返回,这叫做阻塞等待
waitpid(可以保证最后结束的一定是父进程)
waitpid的单个进程总代码



这个waitpid(代码例子是单个子进程的)与wait(单个的进程用法一样)

status的使用,waitpid的第一个参数是自己的子进程,不能等待别人的子进程

子进程,一共有几种退出的场景


而不用全局变量,先在子进程改完,再从父进程中拿到,这是不可以的,因为进程具有独立性,所以要用wait等系统调用

查看子进程错误信息码,和exit返回的值 退出信号(signal)


另一种写法(这里的进程出异常是子进程出异常了,wait failed是父进程调取出异常了)


代码例子是多个子进程的waitpid用法,从父进程中获取exit(i)中多个i的值

-1的情况总代码(没写)


非阻塞轮询(总代码没写)



这个第三个参数为0时就是默认是阻塞方式
举个例子:小张是操作系统,我是用户,我约着小张去学习,小张让我等他,我打电话催他就是系统调用的过程,打电话的本质就是问小张好了没,本质就是检查状态,小张说他好了或者没好,或者说还得等一会,这叫检查不成功,然后我把电话挂掉,这叫做系统调用立马返回,在这个过程中,我在不断不断的打电话询问,然后直接返回结果,立马挂断,叫做非阻塞,基于非阻塞然后一直给小张打电话(打电话不会被卡住),问他好了没有,这个过程就叫做轮询,所以非阻塞+循环就叫做非阻塞轮询。
然后第二天又约小张,因为有上次的教训,我就告诉小张,你不要挂电话,等你好的时候再说你好了,再挂电话,然后我一直抱着电话听他状态,这种就叫做纯阻塞式调用。(最常用够简单,父进程什么都做不了只能等待)
接着第三天, 又约他,变聪明了,一边打电话催,催完了,再自己玩自己的,在等他的过程中,也在做自己的事情,这就是非阻塞轮询+做自己的事情(这样就可以让父进程即在等待,又可以做自己的事情)
三种返回值结果:

这个第三个参数是非阻塞等待,是0就是阻塞等待

返回值有三种

记得加循环


如果把第三个参数改成0就没有这种情况了,父进程就不可以做自己的事情了

用到了函数指针
ret==0的情况,父进程做自己的事代码(单进程版本,多进程的waitpid的第一个参数 改成-1就好):makefile中加上-std=c99,j就可以用int i=0;在for循环中了



