在进程退出时,内核会释放掉该进程的所有资源,包括打开的文件和占用的内存等。但是仍然为该进程保留一些信息,主要指进程控制块的信息(包括进程号、退出状态、运行时间等)。所以为了回收这些资源,进程退出时会向他的父进程发送一个 SIGCHLD 信号,通知父进程来获取子进程结束时的状态,同时回收资源。
wait() 和 waitpid() 的区别
其实这两个函数功能差不多,只是 wait() 会阻塞,而 waitpid() 可以设置不阻塞,还可以指定等待哪个子进程结束。 他们的具体区别及参数返回值的使用可以通过执行 man 2 wait 去查看。这里简单描述。
pid_t wait(int* status);
status:用来存储子进程退出时的状态信息的指针。可设为 NULL,如果不是 NULL,我们需要用以下方式取出子进程的退出信息。
如果 WIFEXITED(status) != 0:
子进程退出是的状态信息为 WEXITSTATUS(status)
如果执行成功,则返回已退出的子进程的pid,否则返回 -1。
pid_t waitpid(pid_t pid, int* status, int options);
status 的用法与 wait() 一致。至于参数 pid,有一下几种类型:
pid > 0 : 等待进程 ID 为 pid 的子进程退出;
pid = 0 : 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
pid = -1 : 等待任一子进程,此时 waitpid 和 wait 作用一样。
pid < -1 : 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。
至于 options, 常用以下两个选项,可以设置阻塞或不阻塞:
0:同 wait(),阻塞父进程,等待子进程退出。
WNOHANG:没有任何结束的子进程,就会立即返回。
返回值:
当正常返回的时候,waitpid() 返回收集到的已经子进程的进程号;
如果设置了选项 WNOHANG,而调用中 waitpid() 发现没有已退出的子进程可等待,则返回 0;
如果调用中出错,则返回 -1,这时 errno 会被设置成相应的值以指示错误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD。
代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
pid = fork();
if( pid < 0 )
{
perror("fork");
return -1;
}
/* 子进程 */
if( pid == 0 )
{
int i = 6;
while(i--)
{
printf("我是子进程打印的\n");
sleep(1);
}
/* 子进程退出,数字 2 为子进程退出的状态 */
_exit(6); //
}
/* 父进程 */
else if( pid > 0)
{
int status = 0;
/* 1. 等待子进程结束,回收子进程的资源
* 此函数会阻塞
* status 的某个字段保存子进程调用退出是 _exit(6) 的 6,可以用宏定义取出
*/
//wait(&status);
/* 2. 和 wait() 没区别,0:阻塞 */
//waitpid(-1, &status, 0);
/* 3. 指定等待进程号为 pid 的子进程, 0 阻塞 */
//waitpid(pid, &status, 0);
/* 4. WNOHANG:不阻塞 */
waitpid(pid, &status, WNOHANG);
if(WIFEXITED(status) != 0){ // 子进程是否正常终止
printf("子进程退出时的状态为 %d\n", WEXITSTATUS(status));
}
printf("我是父进程打印的\n");
}
return 0;
}
当使用前三种方式等待子进程退出时,上述程序执行结果如下,可见父进程执行到 wait 或 waitpid 均是阻塞的,直到子进程退出后才继续执行下去。
[lingyun@manjaro study]$ gcc study.c
[lingyun@manjaro study]$ ./a.out
我是子进程打印的
我是子进程打印的
我是子进程打印的
我是子进程打印的
我是子进程打印的
我是子进程打印的
子进程退出时的状态为 6
我是父进程打印的
[lingyun@manjaro study]$
当使用最后一种方式时,上述程序执行结果如下,可见父进程执行到 wait 或 waitpid 没有阻塞,而是直接执行下去了。父进程执行完毕后,子进程仍继续执行。
[lingyun@manjaro study]$ gcc study.c
[lingyun@manjaro study]$ ./a.out
子进程退出时的状态为 0 /* 此时子进程还未执行 _exit(6); */
我是父进程打印的
我是子进程打印的
[lingyun@manjaro study]$ 我是子进程打印的
我是子进程打印的
我是子进程打印的
我是子进程打印的
我是子进程打印的