wait函数和僵尸进程(APUE)
wait和waitpid函数原型:
1 #include <sys/wait.h>
2
3 pid_t wait(int *status);
4 pid_t waitpid(pid_t pid, int *status, int options);
当进程正常或者异常终止时,内核就像其父进程发送SIGHLD信号,因为子进程终止是个异步信号,所以这种信号也是内核向父进程发的异步通知。对于这种信号,系统默认动作是忽略它,此时进程状态一直保存在内存中,直到父进程使用wait函数收集状态信息,才清空。用wait来等待一个子进程终止运行叫回收进程,如果一个子进程终止后,父进程没有调用wait函数收集信息,子进程就会变成僵尸进程,僵尸进程不占有内存资源,仅仅在进程表中保留位置,占用进程ID。当子进程变成僵尸进程后,我们可以选择杀死父进程,这样init进程就会处理该僵尸进程。
下面看看僵尸进程的形成:
1 #include "apue.h"
2 #include <sys/wait.h>
3
4 int
5 main(void)
6 {
7 pid_t pid;
8 if((pid = fork()) < 0)
9 err_sys("fork error");
10 else if(pid == 0)
11 printf("This is child process with pid of %d\n", getpid());
12 else{
13 sleep(20);
14 printf("This is parent process with pid of %d\n", getpid());
15 }
16 exit(0);
17 }
编译程序,运行。
1 $ gcc test_zombie.c
2 $ ./a.out
同时打开另外一个终端:
1 $ ps -C a.out -o ppid,pid,stat,cmd
2 PPID PID STAT CMD
3 3649 3887 S+ ./a.out
4 3887 3888 Z+ [a.out] <defunct>
可以看到,子进程PID=3888退出(exit(0))后成为僵尸进程(stat为Z)。
调价wait() 调用。
1 #include "apue.h"
2 #include <sys/wait.h>
3
4 int
5 main(void)
6 {
7 pid_t pid;
8 if((pid = fork()) < 0)
9 err_sys("fork error");
10 else if(pid == 0)
11 printf("This is child process with pid of %d\n", getpid());
12 else{
13 printf("I catched a child process with pid of %d\n", wait(NULL));
14 sleep(20);
15 printf("This is parent process with pid of %d\n", getpid());
16 }
17 exit(0);
18 }
输出结果:
1 $ ps -C a.out -o ppid,pid,stat,cmd
2 PPID PID STAT CMD
3 3649 4100 S+ ./a.out
1 $ ./a.out
2 This is child process with pid of 4101
3 I catched a child process with pid of 4101
4 This is parent process with pid of 4100
waitpid函数(摘自网络):
参数pid的值有以下几种类型:
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去.
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样.
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬.
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值.
参数options的值有以下几种类型:
如果使用了WNOHANG参数,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去.
如果使用了WUNTRACED参数,则子进程进入暂停则马上返回,但结束状态不予以理会.
Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:ret=waitpid(-1,NULL,0);
waitpid的返回值比wait稍微复杂一些,一共有3种情况:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD.