• 【Linux信号专题】五、SIGCHLD信号详解


    在这里插入图片描述

    欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起探讨和分享Linux C/C++/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。



    专栏传送门 :《Linux从小白到大神》 | 系统学习Linux开发、VIM/GCC/GDB/Make工具、Linux文件IO、进程管理、进程通信、多线程等,请关注专栏免费学习。


    1. SIGCHLD产生的条件

    实际上,在子进程结束的时候,会产生一个SIGCHLD信号,信号描述如下,根据man手册可以知道,子进程结束运行,其父进程会收到SIGCHLD信号,该信号的默认处理动作是忽略。

     SIGCHLD   20,17,18    Ign     Child stopped or terminated
    
    • 1

    SIGCHLD信号产生的条件主要有以下几个:

    • 子进程终止时;

    • 子进程接收到SIGSTOP信号停止时;

    • 子进程处在停止态,接受到SIGCONT后唤醒时;

    既然子进程在退出或暂停的时候会发送SIGCHLD信号,那么我们就可以利用该信号,捕捉该信号,并在捕捉函数中完成子进程状态的回收,这样就不用使用wait函数去等待了。

    2. 使用SIGCHLD信号完成子进程回收

    /************************************************************
      >File Name  : sigchld_test.c
      >Author     : Mindtechnist
      >Company    : Mindtechnist
      >Create Time: 2022年05月23日 星期一 14时20分42秒
    ************************************************************/
    #include 
    #include 
    #include 
    #include 
    #include 
    
    void mcatch(int signo)
    {
        printf("catch signal: %d\n", signo);
        pid_t pid = waitpid(-1, NULL, WNOHANG);
        if(pid > 0)
        {
            printf("recycle process: %d\n", pid);
        }
    }
    
    void mcatch2(int signo)
    {
        printf("catch signal: %d\n", signo);
        pid_t pid;
        while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
        {
            /*如果多个子进程同时结束(主控制中没有睡眠sleep),也能保证回收*/
            printf("recycle process: %d\n", pid);
        }
    }
    
    int main(void)
    {
        int i = 0;
        pid_t pid;
        /*先屏蔽SIGCHLD信号,否则的话,假如在注册信号捕捉函数之前子进程就已经结束的话,
        信号捕捉函数就什么也捕捉不到了,会产生僵尸进程*/
        sigset_t mset, old;
        sigemptyset(&mset);
        sigaddset(&mset, SIGCHLD);
        sigprocmask(SIG_BLOCK, &mset, &old);
        for(i = 0; i < 10; i++)
        {
            pid = fork();
            if(pid == 0)
            {
                break;
            }
        }
        if(i == 10) /*父进程*/
        {
            struct sigaction mact;
            mact.sa_flags = 0;
            sigemptyset(&mact.sa_mask);
            mact.sa_handler = mcatch;
            sigaction(SIGCHLD, &mact, NULL);
            /*恢复原来的屏蔽设置*/
            sigprocmask(SIG_SETMASK, &old, NULL);
            while(1)
            {
                sleep(1);
            }
        }
        else /*子进程*/
        {
            printf("child: %d\n", getpid());
            sleep(i); /*如果没有睡眠,可能多个子进程同时结束
            			这样使用mcatch的时候会不稳定,可能
            			产生僵尸进程,使用mcatch2会更好*/
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    根据这个例子,我们可以得到下面几点注意事项

    • 子进程继承了父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集spending;

    • 应该在fork之前,阻塞SIGCHLD信号,注册完捕捉函数后解除阻塞。这样做的目的是,假如在注册信号捕捉函数之前子进程就已经结束的话,信号捕捉函数就什么也捕捉不到了,会产生僵尸进程;

    3. 中断系统调用

    系统调用可分为两类:慢速系统调用和其他系统调用。

    • 慢速系统调用:可能会使进程永远阻塞的一类系统调用。如果在阻塞期间收到一个信号,该系统调用就被中断,不再继续执行(早期),也可以设定系统调用是否重启。比如,read、write、pause、wait等。

    • 其他系统调用:getpid、getppid、fork等

    通过pause系统调用,分析慢速系统调用,慢速系统调用被中断的相关行为,实际上就是pause的行为,比 如read

    ​ 慢速系统调用被中断的相关行为,实际上就是pause的行为,比 如read

    • 想中断pause,首先信号不能被屏蔽;

    • 信号的处理方式必须是捕捉 (默认动作、忽略都不可以);

    • 中断后返回-1, 设置errno为EINTR,表示被信号中断;

    可以通过修改sa_flags参数来设置被信号中断后系统调用是否重启:SA_INTERRURT不重启, SA_RESTART重启。sa_flags还有很多可选参数,适用于不同情况,比如:捕捉到信号后,在执行捕捉函数期间,不希望自动阻塞该信号,可将sa_flags设置为SA_NODEFER,除非sa_mask中包含该信号,等等。


    在这里插入图片描述
    在这里插入图片描述


  • 相关阅读:
    BootStrap响应式项目实战之世界杯网页设计
    如何使用nuScenes数据集格式的单帧数据推理(以DETR3D为例)
    MongoDB聚合运算符:$sinh
    java毕业设计在线测评系统2021Mybatis+系统+数据库+调试部署
    为什么软考通过率低,还有这么多人报考?
    Spring(一)
    95、Spring Data Redis 之使用RedisTemplate 实现自定义查询 及 Spring Data Redis 的样本查询
    [OS]11.9.2023 中断
    STC - 同时外挂扩展RAM和12864时, C库函数失效的问题
    【hadoop3.x】一 搭建集群调优
  • 原文地址:https://blog.csdn.net/qq_43471489/article/details/126772550