【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

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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

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;
}

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

  • 子进程继承了父进程的信号屏蔽字和信号处理动作,但子进程没有继承未决信号集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中包含该信号,等等。


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


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SIGCHLD信号是在一个子进程结束运行后,向其父进程发送的一个信号。这个信号可以让父进程知道子进程已经结束,从而可以回收进程的资源。 当一个子进程结束时,操作系统会向其父进程发送SIGCHLD信号。父进程可以通过调用wait()或waitpid()函数来回收进程的资源。在父进程中,可以通过设置一个信号处理函数来处理SIGCHLD信号,以便在子进程结束时自动回收资源。 下面是一个使用waitpid()函数来回收进程资源的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t pid = fork(); if (pid == 0) { // 子进程 printf("child process\n"); exit(0); } else if (pid > 0) { // 父进程 printf("parent process\n"); int status; waitpid(pid, &status, 0); // 回收进程资源 if (WIFEXITED(status)) { printf("child process exited with code %d\n", WEXITSTATUS(status)); } } else { // fork失败 perror("fork"); exit(1); } return 0; } ``` 在上面的代码中,父进程调用waitpid()函数来等待子进程结束,并回收进程的资源。如果子进程正常结束,父进程就可以通过WIFEXITED(status)函数判断子进程是否正常结束,并通过WEXITSTATUS(status)函数获取子进程的退出码。 需要注意的是,如果父进程没有调用wait()或waitpid()函数来回收进程的资源,那么子进程就会成为一个僵尸进程,占用系统资源。因此,在父进程中一定要及时回收进程的资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mindtechnist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值