在Linux中,wait
和 waitpid
是用于进程控制的系统调用,它们主要用来让父进程等待子进程的终止,并获取子进程的退出状态。下面详细介绍它们的用法和区别。
1. wait()
函数
wait()
会阻塞调用进程,直到一个子进程终止。它的典型用法如下:
函数原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
- 参数:
status
: 这是一个指向整数的指针,用来存储子进程的退出状态。如果不关心退出状态,可以传递NULL
。
- 返回值:
- 成功时,返回终止的子进程的 PID。
- 失败时,返回
-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
。
2. 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
等信号)。
-
返回值:
- 成功时,返回子进程的 PID。
- 如果使用
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()
支持等待子进程暂停的状态。
3. wait()
和 waitpid()
的区别
功能 | wait() | waitpid() |
---|---|---|
等待特定子进程 | 不可以 | 可以(通过 pid 参数) |
阻塞/非阻塞模式 | 只能阻塞 | 支持非阻塞(通过 WNOHANG 选项) |
等待任意子进程 | 可以(默认行为) | 可以(通过 pid = -1 ) |
支持停止的子进程 | 不支持 | 支持(通过 WUNTRACED ) |
等待同一进程组中的进程 | 不支持 | 可以(通过 pid = 0 或 pid = -<pgid> ) |
4. 总结
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循环中了