
Linux内核学习笔记
进程调用 exit() 退出执行后,被设置为僵死状态,这时父进程可以通过
wait4()
系统调用查询子进程是否终结,之后再进行最后的操作,彻底删除进程所占用的内存资源。
wait4() 系统调用由 linux 内核实现,linux 系统通常提供了
wait()、waitpid()、wait3()、wait4()
这四个函数,四个函数的参数不同,语义也有细微的差别,但是都返回关于终止进程的状态信息。
Linux中wait的用法:
1、wait() 函数:
系统中的僵尸进程都要由wait系统调用来回收。
wait() 函数的原型是:
函数原型#include <sys/types.h>
#include <sys/types.h> // 提供类型 pid_t 的定义
#include <sys/wait.h>
pid_t wait(int *status);
#include <sys/wait.h>
当进程调用 wait() 时,会暂停目前进程的执行(即阻塞),由 wait()
来自动分析是否当前进程的某个子进程已经退出,如果找到了这样一个已经变成僵尸进程的子进程,wait
就会收集这个子进程的信息,并将其彻底销毁后返回;如果没有找到这样一个子进程,wait
就会一直阻塞在这里,直到出现僵尸进程。
pid_t wait(int *status);
参数 status 保存着子进程退出时的一些状态(包括
task_struct、thread_info及内核栈等)它是一个指向 int
类型的指针;如果不在意子进程的结束状态值,只想把这个僵尸进程消灭掉(实际上,大多数时候都是这样做的),则可以将这个参数设为
NULL,即:
进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经推出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
pid = wait(NULL); // 不管子进程的结束状态,直接杀死进程
参数status用来保存被收集进程退出是的一些状态,他是一个指向int类型的指针。但如果我们对这个子进程是如何死掉并不在意,只想把这个僵尸进程消灭掉,我们可以设定这个参数为NULL,
如果 wait()
调用成功,则会返回被收集子进程的进程ID;如果被调用进程没有子进程,则调用失败,返回
-1
pid=wait(NULL);
接下来用一段代码来演示一下 wait() 的用法:
如果收回成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被设置为ECHILD。
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6
7 void main(){
8 pid_t fpid,rpid;
9 fpid = fork();
10 if(fpid < 0){
11 perror("error on forking!n");
12 }
13 else if(fpid == 0){
14 printf("this is a child process! the pid is %dn",getpid());
15 sleep(3);
16 }
17 else{
18 rpid = wait(NULL); // 如果 wait()调用成功,则返回子进程的PID;如果调用失败,则返回 -1
19 printf("Catch the child process with pid of %dn",rpid);
20 }
21 exit(0);
22 }
如果参数status的值不是NULL,wait就会把子程序退出时的状态取出并存入其中,这是一个
输出结果如下:
整形值(int),指出了子进程是正常退出还是被非正常结束的,以及正常结束时的返回值,或被哪个信号结束的等信息。由于这些信息被存放在一个整数的不同二进制位中,所以用常规的方法读取会变得非常麻烦,人们就设计了专门的宏(macro)来完成这项工作,下面是其中常用的两个:
1,WIFEXITED(status)这个宏用来指出子进程是否为正常退出的,如果是,它会返回一个非零值。(此处的status是指status指针所指向的整数)
关于 status
参数,比较复杂,暂时不做讨论,可以参考这里:
大奖888网页版登陆,2,WEXITSTATUS(status)当这个宏返回非零值时,我们可以用这个宏来提取子进程的返回值,
如果子进程调用exit(5)退出,WEXITSTATUS就会返回5;如果进程不是正常退出,也就是说
2、waitpid() 函数:
返回0,这个值就毫无意义。
函数原型:
对于waitpid()函数来说,多出了两个可以由用户控制的参数pid和options。
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid,int *status,int options);
#include <sys/types.h> /* 提供类型pid_t的定义 */
waitpid() 函数的功能与 wait() 的功能类似,不过,它比 wait()
函数多了两个参数:
#include <sys/wait.h>
1)参数 pid 为欲等待的子进程的识别码:
pid_t waitpid(pid_t pid,int *status,int options)
pid < -1 ;等待进程组 ID 为 pid 绝对值的进程组中的任何子进程;
pid>0时,只等待进程ID等于pid的子进程,不管其他已经有多少子进程运行结束退出了,只要
pid = -1 ;等待任何子进程,此时 waitpid() 相当于
wait()。实际上,wait()就是 pid = -1、options = 0
的waitpid(), 且有:
指定的子进程还没有结束,waitpid就会一直等下去。
static inline pid_t wait(*status){
return waitpid(-1,*status,0);
}
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid = 0 ;等待进程组 ID
与当前进程相同的任何子进程(也就是等待同一个进程组中的任何子进程);
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入别的进程组,waitpid不
pid > 0 ;等待任何子进程 ID 为 pid
的子进程,只要指定的子进程还没有结束,waitpid() 就会一直等下去。
会对它做任何理睬。
2)参数 options 提供一些额外的选项来控制 waitpid():
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
WNOHANG;如果没有任何已经结束了的子进程,则马上返回,不等待;
options:
WUNTRACED;如果子进程进入暂停执行的情况,则马上返回,但结束状态不予理会;
如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不像wait
也可以将这两个选项组合起来使用,使用 OR
操作。如果不想使用这两个选项,也可以直接把 options 设为0 ,如下:
那样永远等下去。
waitpid(-1,NULL,WNOHANG | WUNTRACED); // 没有任何已结束了的子进程或子进程进入暂停执行的状态,则马上返回不等待
waitpid(-1,NULL,0); // options 设为0,则 waitpid() 会一直等待,直到有进程退出
waitpid返回值:
3)waitpid() 的返回值,有三种:
当正常返回的时候,waitpid返回收集到的子进程的进程ID;
a)正常返回时,waitpid() 返回收集到的子进程的PID;
如果设置了选项WNOHANG,而调用waitpid发现没有已经退出的子进程可收集,则返回0;
b)如果设置了 WNOHANG,而调用 waitpid()
时,没有发现已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误的所在;当pid所
c)如果调用出错,则返回 -1,这时erron
会被设置为相应的值以指示错误所在。(当 pid
所指示的子进程不错在,或此进程存在,但不是调用进程的子进程, waitpid()
就会返回出错,这时 erron 被设置为 ECHILD)
指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时
errno被设置成ECHILD。
1 #include <sys/types.h>
2 #include <sys/wait.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 void main(){
8 pid_t fpid,rpid; // fpid为fork()的返回值,rpid为waitpid()的返回值
9 fpid = fork();
10 if(fpid < 0){
11 printf("error on forking");
12 }
13 else if(fpid == 0){ // 子进程中 fork() 返回值为0
14 printf("this is a child process,pid is %dn",getpid());
15 sleep(10); // 睡眠10s,10s 后子进程退出
16 exit(0);
17 }
18 do{ // 父进程中,fork()返回新创建子进程的 PID
19 rpid = waitpid(fpid,NULL,WNOHANG); // 等待 PID = fpid 的进程(即子进程)退出,设置了WNOHANG选项,表明当没有发现已退出的子进程时不用等待直接返回,返回值为0;
20 if(rpid == 0){ // rpid = 0,说明没有发现已退出的子进程
21 printf("No child exitedn");
22 sleep(1);
23 }
24 }while(rpid == 0);
25 if(fpid == rpid) // 成功收集了退出的子进程,返回值为被收集子进程的PID
26 printf("successfully get child process %dn",rpid);
27 else
28 printf("error!n");
29 }
结果如下:
从结果中可以看到,在子进程休眠的10s时间里,waitpid()
并没有一直等待,而是直接返回0,然后做自己的事情(睡眠1s),如此重复了10次;当子进程退出时,waitpid()
收集到退出的子进程,并返回所收集子进程的PID。
3、wait3()、wait4() 函数:
函数原型:
#include <sys/tpyes.h>
#include <sys/wait.h>
pid_t wait3(int *status,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);
wait3() 和 wait4()
函数除了可以获得子进程状态信息外,还可以获得子进程的资源使用信息,这些信息是通过参数
rusage 得到的。而 wait3() 与 wait4() 之间的区别是,wait3()
等待所有进程,而 wait4() 可以根据 pid 的值选择要等待的子进程,参数 pid
的意义与 waitpid() 函数的一样。
本文主要参考: