//获取退出的那个子进程的状态     int quit_pid = wait(&status);     printf("sub process %d quit, exit status %d\n", quit_pid, status);     //新创建一个子进程     pid_t pid = fork();     if( 0 == pid )          ChildCycle(); void ParentCycle()     printf("Parent process %d\n", getpid());     signal(SIGCHLD, sub_quit_signal_handle);     while(1)         pause(); void ChildCycle()     printf("create sub process id: %d, parent id: %d\n", getpid(), getppid());     while(1)         pause();

Linux: 关于 SIGCHLD 的更多细节

何为僵尸进程?

一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid
获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程

成为僵尸进程的因素

  1. 子进程 先于 父进程退出;
  2. 子进程的状态信息,没有被父进程回收;

那么问题来了,子进程退出了,父进程怎么知道呢?

对该机制有稍微了解的话,不难得知一个关键因素: SIGCHLD 。正是这个 SIGCHLD 起到了通知的作用,所以后面的处理也是基于它而实现。

僵尸进程处理方案

  1. 父进程捕获 SIGCHLD 信号,则显示调用 wait waitpid
  2. 父进程直接忽略该信号。 signal(SIGCHLD, SIG_IGN) ,这样子进程直接会退出。

    需要注意的是,虽然进程对于 `SIGCHLD`的默认动作是忽略,但是还是显示写出来,才能有效;
  3. 把父进程杀了,子进程直接过继给 init ,由 init 伺候着。
    不用担心 init 会挂着一堆僵尸, init 本身的设计就有专门回收的处理,所以有多少回收多少;

SIGCHLD 还能干嘛

刚才我们在处理到父子进程相关的问题时,多多少少接触到 SIGCHLD , 那么,只有在回收子进程的时候才需要用到么?感觉好浪费不是么?

别担心 ~ 这个作用肯定是不止这样的!

其实对于 SIGCHLD ,我们一般的理解是,子进程退出发送的信号,但其实不是的,这个信号代表的含义是:

子进程状态变更了,例如停止、继续、退出等,都会发送这个信号通知父进程。而父进程就能通过 wait/waitpid 来获悉这些状态了。

看起来有点意思,我们仿佛能借此做些有趣的事情了。

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 时才会比较常见,那么它们之间的关系就真的是只是这样么?

其实 wait 是对 waitpid 的封装,专门用来回收子进程退出的信息,同样的,它简单粗暴的设置成了堵塞方式,如果没有任何子进程退出,那么就堵塞住。

waitpid 功能非常强大, pid options 都提供了非常灵活的用法:

< -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 命令将其转换成 前台进程时,才能继续工作

正是有这样的一套机制,所以我们也能做出很多比较实在的东西了~

https://segmentfault.com/a/1190000015060304?utm_source=tag-newest

https://blog.csdn.net/small_qch/article/details/21233977

LINUX C:创建与监控多个子进程#include &lt;unistd.h&gt;#include &lt;sys/types.h&gt;#include &lt;stdlib.h&gt;#include &lt;signal.h&gt;#include &lt;stdio.h&gt;//子进程个数#define SUB_PRO_COUNT 10//处理子进程的退出信号void sub_quit_signal_handle(int sig);//父进程的事件循环vo...
三年来一直从事服务器程序开发,一直都是忙忙碌碌,不久前结束了职业生涯的第一份工作,有了一个礼拜的休息时间,终于可以写写总结了。于是把以前的开源代码做了整理和优化,这就是FFLIB。虽然这边总结看起来像日记,有很多废话,但是此文仍然是有很大针对性的。针对服务器开发中常见的问题,如多线程并发、消息转发、异步、性能优化、单元测试,提出自己的见解。从事开发工程中,遇到过不少问题,很多时候由于时间紧迫,没有使用优雅的方案。在跟业内的一些朋友交流过程中,我也意识到有些问题是大家都存在的。简单列举如下:现在是多核时代,并发才能实现更高的吞吐量、更快的响应,但也是把双刃剑。总结如下几个用法:这是两种最常见的多
文章目录一、进程的概念二、进程的编号1、查看进程2、getpid库函数三、多进程 fork()四、课后作业 一、进程的概念 什么是进程进程这个概念是针对系统而不是针对程序员的,对程序员来说,我们面对的概念是程序,当输入指令执行一个程序的时候,对系统而言,它将启动一个进程进程就是正在内存中运行中的程序,Linux下一个进程在内存里有三部分的数据,就是“代码段”、“堆栈段”和“数据段”。 “代码段”,顾名思义,就是存放了程序代码。 “堆栈段”存放的就是程序的返回地址、程序的参数以及程序的局部变量。 进程进程是一个正在执行的程序,是向CPU申请资源的,进程之间数据相互独立,一个进程至少有一个线程。 线程:线程是进程中的单一的顺序控制流程也可以叫做最小控制单元,线程是进程中执行单元,开启一个线程比开启一个进程更加节省资源。 多线程:多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。...