一,基础知识
1,进程的各种状态
S:浅度睡眠,可以中断,可以杀死,暂停,修改等;
D:深度睡眠,不可中断,永远不可变;
2,僵死状态(Zombies)一个比较特殊的状态。 指子进程结束运行(exit(0))后,系统将会保留子进程的描述符信息(没有被回收,其他进程不可以重用该描述符),当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
在产生僵尸进程后,父进程必须调用系统调用函数wait()或waitpid()对僵死的子进程进行等待,对子进程的残留信息进行清理(进程等待的原因)**。
进程等待的方式:阻塞式等待和非阻塞式等待。
3,wait()和waitpid()的区别
<1>wait 函数:用来等待任何一个子进程退出,由父进程调用。
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
返回值:成功返回被等待子进程的pid,失败返回-1。
status:输出型参数,拿回子进程的退出信息。
wait:阻塞式调用,等待的子进程不退出时,父进程一直不退出。
目的:回收子进程,系统回收子进程的空间。
如果参数status不为空,则进程终止状态被保存于其中。
依据传统,返回的整形状态字是由实现定义的,其中有些位表示退出状态(正常返回),其他位表示信号编号(异常返回),有一位表示是否产生了一个core文件等。
终止状态是定义在 sys/wait.h中的各个宏,有四可互斥的宏可以用来取得进程终止的原因。
WIFEXITED:正常返回时为真,可以执行宏函数 WEXITSTATUS获取子进程传送给exit、_exit或_Exit的参数的低8位。
WIFSIGNALED :异常返回时为真,可以执行宏函数WTERMSIG取得子进程终止的信号编号,另外,对于一些实现,定义有宏WCOREDUMP宏,若以经昌盛终止进程的core文件,则为真。
WIFSTOPPED :若为当前暂停子进程的返回的状态,则为真,可执行WSTOPSIG取得使子进程暂停的信号编号。
WIFCONTINUED:若在作业控制暂停后已经继续的子进程返回了状态,则为真,仅用于waitpid。
<2>waitpid:
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int* status,int options);
1>参数:
pid参数
从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。
pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
status参数与wait()函数的基本相同,此处不再赘述。
options参数
当options参数为0时,与wait功能相同,仍是阻塞式等待,不提供额外功能,如果为下列常量按位或则提供更多功能:
WCONTINUED:若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但状态尚未报告,则返回状态
WNOHANG:若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,即此时以非阻塞方式(轮询式访问的必要条件)等待子进程,并且返回0。
WUNTRACED:若实现支持作业控制,而pid指定的任一子进程已经暂停,且其状态尚未报告,则返回其状态
2>返回值:
当正常返回的时候,waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD;
3>waitpid提供了三个wait所没有的功能:
1. waitpid可等待一个特定的进程
2. waitpid提供了一个wait的非阻塞版本
3. waitpid支持作业控制
二,验证子进程退出时会给父进程发送SIGCHLD信号
验证代码:
1 /**************************************
2 *文件说明:wait.c
3 *作者:段晓雪
4 *创建时间:2017年06月10日 星期六 23时50分23秒
5 *开发环境:Kali Linux/g++ v6.3.0
6 ****************************************/
7
8 #include<stdio.h>
9 #include<stdlib.h>
10 #include<signal.h>
11 #include<unistd.h>
12 #include<sys/types.h>
13
14 void handler(int sig)
15 {
16 printf("father is catching,child is quit\n");
17 }
18 int main()
19 {
20 signal(SIGCHLD,handler);
21 pid_t pid = fork();
22 if(pid == 0)//child
23 {
24 printf("child:my pid is %d\n",getpid());
25 sleep(2);
26 exit(-1);//子进程异常终止
27 }
28 while(1)
29 {
30 printf("father is doing something...\n");
31 sleep(1);
32 }
33 return 0;
34 }
运行结果:
三,子进程的异步等待方式
1,异步回收僵尸进程:
fork()之后,非阻塞(异步)等待子进程(回收僵尸)。
fork()之后,子进程和父进程分叉执行,僵尸进程的产生是因为父进程没有给子进程“收尸”造成的,又可以根据危害程度分为下述两类:
总体来说:当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程。
(1)当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程,父进程结束后僵尸被init进程回收。
(2)如果子进程结束了,但是父进程始终没有结束,那么这个僵尸将一直存在,而且随着exec,僵尸越来越多。
2,实现代码
1 /**************************************
2 *文件说明:asunwait.c
3 *作者:段晓雪
4 *创建时间:2017年06月11日 星期日 00时30分50秒
5 *开发环境:Kali Linux/g++ v6.3.0
6 ****************************************/
7
8 #include<stdio.h>
9 #include<stdlib.h>
10 #include<signal.h>
11 #include<unistd.h>
12 #include<sys/types.h>
13 #include<sys/wait.h>
14
15 void handler(int sig)
16 {
17 printf("father is catching,child is quit\n");
18
19 //以非阻塞方式等待所有异常退出的子进程
20 pid_t id;
21 while((id = waitpid(-1,NULL,WNOHANG)) > 0)
22 {
23 printf("wait child success:%d\n",id);
24 }
25 }
26
27 int main()
28 {
29 signal(SIGCHLD,handler);
30 pid_t pid1 = fork();
31 if(pid1 == 0)//child1
32 {
33 printf("child1:my pid is %d\n",getpid());
34 exit(-1);//子进程1异常终止
35 }
36
37 pid_t pid2 = fork();//child2
38 if(pid2 == 0)
39 {
40 printf("child2:my pid is %d\n",getpid());
41 exit(-1);//子进程2异常退出
42 }
43
44 pid_t pid3 = fork();//child3
45 if(pid3 == 0)
46 {
47 printf("child3:my pid is %d\n",getpid());//子进程3正常运行
48 }
49
50 while(1)
51 {
52 printf("father is doing something...\n");
53 sleep(1);
54 }
55
56 return 0;
57 }
运行结果: