当一个进程正常或异常终止时,内核就向父进程发送SIGCHLD信号,因为子进程终止是个异步事件,可以在父进程运行的任何时候发生,所以这种信号也是内核向父进程发的异步通知,父进程可以选择忽略该信号,或者提供一个该信号发生时即被调用执行的函数(即信号处理程序),对于这种信号,系统的默认动作是忽略他。
二、wait
pid_t wait (int * status);
定义于 sys/wait.h头文件中,若成功则返回进程ID,若出错返回-1。
如果参数status不为空,则进程终止状态被保存于其中。
依据传统,返回的整形状态字是由实现定义的,其中有些位表示退出状态(正常返回),其他位表示信号编号(异常返回),有一位表示是否产生了一个core文件等。
终止状态是定义在 sys/wait.h中的各个宏,有四可互斥的宏可以用来取得进程终止的原因。
-
WIFEXITED 正常返回时为真,可以执行宏函数WEXITSTATUS获取子进程传送给exit、_exit或_Exit的参数的低8位
-
WIFSIGNALED 异常返回时为真,可以执行宏函数WTERMSIG取得子进程终止的信号编号,另外,对于一些实现,定义有宏WCOREDUMP宏,若以经昌盛终止进程的core文件,则为真
-
WIFSTOPPED 若为当前暂停子进程的返回的状态,则为真,可执行WSTOPSIG取得使子进程暂停的信号编号
-
WIFCONTINUED 若在作业控制暂停后已经继续的子进程返回了状态,则为真,仅用于waitpid
7 #include "../apue.2e/include/apue.h"
8 #include <sys/wait.h>
9
10 void pr_exit (int status)
11 {
12 if (WIFEXITED(status))
13 printf ("normal termination, exit status = %d\n", WEXITSTATUS(status));
14 else if (WIFSIGNALED(status))
15 printf ("abnormal termination, signal number = %d%s\n", WTERMSIG(status),
16 #ifdef WCOREDUMP
17 WCOREDUMP(status)?"(core file generated)":"");
18 #else
19 "");
20 #endif
21 else if (WIFSTOPPED(status))
22 printf ("child stopped, signal number = %d\n", WSTOPSIG(status));
23 }
24
25 int main ()
26 {
27 pid_t pid;
28 int status;
29
30 if ((pid = fork())<0)
31 err_sys ("fork error");
32 else if (pid == 0)
33 exit (7);
34 if (wait(&status) != pid)
35 err_sys ("wait error");
36 pr_exit (status);
37
38 if ((pid = fork())<0)
39 err_sys ("fork error");
40 else if (pid == 0)
41 abort();
42 if (wait(&status) != pid)
43 err_sys ("wait error");
44 pr_exit (status);
45
46 if ((pid = fork())<0)
47 err_sys ("fork error");
48 else if (pid == 0)
49 status /= 0;
50 if (wait(&status) != pid)
51 err_sys ("wait error");
52 pr_exit (status);
53
54 exit (0);
55 }
运行结果如下:
normaltermination, exit status = 7
abnormaltermination, signal number = 6
abnormaltermination, signal number = 8
但是,6和8究竟指的是什么,并没有一种可移植的方法去说明,我们需要去查看signal.h头文件才能知道对应的宏的含义
三、waitpid
pid_t waitpid (pid_t pid, int * status, int options);
定义于 sys/wait.h头文件中,若成功则返回进程ID,0,若出错返回-1。
waitpid提供了wait更多的功能,基本用法于返回值与wait相同
1、pid参数
对于有多个子进程的父进程,只要有一个子进程终止,父进程中的wait函数就会返回,如果要等待特定的子进程,那就要不断的调用wait函数等待,POSIX定义了waitpid提供了等待特定子进程的功能及其他功能:waitpid的pid参数的作用:
-
pid== -1 等待任一子进程,wait的功能与此相同
-
pid> 0 等待进程ID为pid的子进程
-
pid== 0 等待组ID等于调用进程组ID的任一子进程
-
pid< -1 等待组ID等于-pid的任一子进程
waitpid函数返回终止子进程的进程ID,并将该子进程的终止状态存放在由status指针指向的空间中,如果指定的进程或进程组不存在,或者pid指定的进程不是调用进程的子进程,则调用失败返回-1。
2、options参数
当options参数为0时,与wait功能相同,不提供额外功能,如果为下列常量按位或则提供更多功能:
-
WCONTINUED 若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但状态尚未报告,则返回状态
-
WNOHANG 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时返回0
-
WUNTRACED 若实现支持作业控制,而pid指定的任一子进程已经暂停,且其状态尚未报告,则返回其状态
3、waitpid提供了三个wait所没有的功能
-
waitpid可以等待一个特定的进程,而wait则返回任一终止子进程的状态
-
waitpid可以使父进程不进入阻塞状态
-
waitpid支持作业控制
四、waitid
waitid 函数类似于 waitpid,但是提供了更多的灵活性
int waitid (idtype_t idtype, id_t id, siginfo_t *infop, int options);
定义于 <sys/wait.h> 头文件中,调用成功返回0,否则返回-1
1、idtype 常量
-
P_PID 等待一个特定的进程:id参数包含要等待子进程的进程ID
-
P_PGID 等待一个特定进程组中的任一子进程:id参数包含要等待子进程的进程组ID
-
P_ALL 等待任一子进程,忽略id参数
2、options 常量
-
WCONTINUED 等待一个进程,他以前曾被暂停,此后又已继续,但其状态尚未报告
-
WEXITED 等待已退出的进程
-
WNOHANG 如果没有可用的子进程退出状态,立即返回而不是阻塞
-
WNOWAIT 不破坏子进程退出状态,该子进程退出状态可由后续的 wait、waitid 或 waitpid 调用取得
-
WSTOPPED 等待一个进程,他已经被暂停,但是其状态尚未报告
infop 参数是指向 siginfo 结构的指针,该结构包含了有关引起子进程状态改变的生成信号的详细信息
需要注意的是,UNIX 系统中只有 solaris 支持该函数
===============================================
wait及waitpid一个易混淆区别在于:
wait()只能处理一个子进程的SIGCHLD,如果多个并发子进程同事关闭,则会出现僵尸进程,而用waitpid()则会避免这种情况:如下
void sig_chld(int signo)
{
pid_t pid;
int stat;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) //非阻塞
printf("child %d terminated\n", pid);
return;
}
这段代码体现在当有多个子进程结束并几乎同时提交信号时,waitpid可使所有被提交的信号得到处理,而不会留下zombie。