一个进程使用fork建立子进程,若是子进程退出,而父进程并无调用 wait 或 waitpid
获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程
那么问题来了,子进程退出了,父进程怎么知道呢?segmentfault
对该机制有稍微了解的话,不可贵知一个关键因素:SIGCHLD
。正是这个SIGCHLD
起到了通知的做用,因此后面的处理也是基于它而实现。spa
SIGCHLD
信号,则显示调用 wait
或 waitpid
;父进程直接忽略该信号。signal(SIGCHLD, SIG_IGN)
,这样子进程直接会退出。操作系统
须要注意的是,虽然进程对于 `SIGCHLD`的默认动做是忽略,可是仍是显示写出来,才能有效;
init
,由 init
伺候着。init
会挂着一堆僵尸, init
自己的设计就有专门回收的处理,因此有多少回收多少;刚才咱们在处理到父子进程相关的问题时,多多少少接触到SIGCHLD
, 那么,只有在回收子进程的时候才须要用到么?感受好浪费不是么?设计
别担忧 ~ 这个做用确定是不止这样的!code
其实对于SIGCHLD
,咱们通常的理解是,子进程退出发送的信号,但其实不是的,这个信号表明的含义是:blog
子进程状态变动了,例如中止、继续、退出等,都会发送这个信号通知父进程。而父进程就能经过 wait/waitpid 来获悉这些状态了。
看起来有点意思,咱们仿佛能借此作些有趣的事情了。进程
#include <sys/wait.h> pid_t wait(int * statloc); pid_t waitpid(pid_t pid,int *statloc,int options);
wait
相对来讲会经常使用点,由于不须要指定 pid
,而waitpid
就在一些须要指定特定pid
时才会比较常见,那么它们之间的关系就真的是只是这样么?ip
其实wait
是对waitpid
的封装,专门用来回收子进程退出的信息,一样的,它简单粗暴的设置成了堵塞方式,若是没有任何子进程退出,那么就堵塞住。get
而waitpid
功能很是强大,pid
和options
都提供了很是灵活的用法:string
pid: < -1: 取该 pid 的绝对值,若是任何子进程的进程组ID等于该值,则该进程组的任一子进程中的进程状态发生变化,都会触发`waitpid`的回调; == -1: 监听范围扩大到任意子进程,也就是 wait(status); == 0: 监听进程组ID和父进程同样的子进程; > 0: 监听该pid的子进程; options: WNOHANG: 调用时,指定的 pid 仍未结束运行,则 wait 当即返回 0; WUNTRACED: 当子进程被暂停时,则当即返回子进程的 pid; WCONTINUED: 当被暂停的子进程,又被信号恢复时,则当即返回子进程的pid;
而下面这些宏,将搭配status
一块儿使用:
WIFEXITED(status): 当子进程调用 exit、_exit或者正常从 main 返回等正常结束时,返回 true --> WEXITSTATUS(status): 获取上面的 exit_code WIFSIGNALED(status): 当子进程被信号杀死时,返回 true; --> WTERMSIG(status): 获取信号的值(int) WIFSTOPPED(status): 当本身弄成被信号暂停执行时,返回 true; --> WSTOPSIG(status): 获取该信号的值 WIFCONTINUED(status): 子进程接收到SIGCONT信号继续执行时,返回 true
咱们来个最小的 demo
来讲明上面的怎么用:
#include<stdio.h> #include<string.h> #include<signal.h> #include<stdlib.h> #include<sys/wait.h> #include<unistd.h> int main(){ int pid; if((pid = fork()) == 0){ while(1){ printf("Child: %d\n", getpid()); sleep(1); } } else{ int status; pid_t w; while(1){ // 但愿堵塞,因此没用 WNOHANG w = waitpid(pid, &status,WCONTINUED | WUNTRACED); if(WIFEXITED(status)){ printf("子进程正常退出,状态码: %d\n", WEXITSTATUS(status)); exit(0); } else if(WIFSIGNALED(status)){ printf("子进程被信号杀死了! 信号值: %d\n", WTERMSIG(status)); exit(0); } else if(WIFSTOPPED(status)){ printf("子进程被信号暂停了! 信号值: %d\n", WSTOPSIG(status)); } else if(WIFCONTINUED(status)){ printf("子进程又恢复继续运行了\n"); } } } }
终端输出:
Child: 10848 Child: 10848 # 子进程的 pid 子进程被信号暂停了!信号值:21 # kill -SIGTTIN 10848 子进程又恢复继续运行了 # kill -SIGTTIN 10848 ... 子进程被信号暂停了! 信号值: 19 # kill -SIGSTOP 10848 子进程又恢复继续运行了 # kill -SIGTTIN 10848 ... 子进程被信号杀死了! 信号值: 15 # kill -SIGTERM 10848 若是本身在子进程上面加个退出,就会打印:正常退出了
在上面的实验中,咱们已经发现经过SIGCHLD
除了用来回收子进程,还能获悉子进程的状态!
在操做系统上,也有不少利用这个在工做的,例如:后台进程,若是向标准输入读取内容时,是会被暂停的
为何呢?
由于后台进程,是和终端断开链接的,当它从标准输入读取内容时,终端的驱动程序会发现这个操做,会发一个 SIGTTIN 给后台进程,让其暂停,而且通知用户,只有用户经过 fg 命令将其转换成 前台进程时,才能继续工做
正是有这样的一套机制,因此咱们也能作出不少比较实在的东西了~
欢迎各位大神指点交流, QQ讨论群: 258498217
转载请注明来源: https://segmentfault.com/a/11...