0
点赞
收藏
分享

微信扫一扫

2022-1-25 牛客C++项目 —— SIGCHID 信号

芥子书屋 2022-01-27 阅读 60

在这里插入图片描述
SIGCHLD 可以用来解决僵尸进程的问题。
虽然父进程可以采用 wait()来回收子进程的资源,但是 wait ()函数是阻塞的,父进程也有自己的业务逻辑,不能老是为等待子进程而停留。父进程通过捕捉到 SIGCHLD ,并且对其进行信号的处理,便可以回收子进程的资源。

/*
SIGCHLD 信号产生的3个条件
1、子进程结束了
2、子进程暂停了
3、子进程继续运行
都会给父进程发送该信号,父进程会默认忽略该信号
使用 SIGCHLD 信号解决僵尸进程的问题。

*/
#include<stdio.h>
#include<wait.h>
#include<signal.h>
#include<stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include<wait.h>
void myFun(int num){
    //能够捕捉到信号,但是只靠这样的一句话真的能够回收资源吗?不能
    printf("捕捉到的信号 : %d.\n",num);
    //子进程结束后需要回收 PCB 的资源
    wait(NULL);
}


int main(void){
    //创建一些子进程
    pid_t pid;
    for(int i = 0;i < 20;i++){
        pid = fork();
        if(pid == 0){
            break;
        }
    }
    if(pid > 0){
        //捕捉子进程挂掉的时候发出的信号,这样父进程还能够做自己的事情
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = myFun;
        sigemptyset(&act.sa_mask);

        sigaction(SIGCHLD,&act,NULL);

        while (1)
        {
            printf("parent process : %d.\n",getpid());
            sleep(2);
        }
        
    }
    else if(pid == 0){
        printf("child process : %d.\n",getpid());
    }
    return 0;
}

但是根据上面这个进程的运行的结果,还是有部分子进程没有被回收。

在这里插入图片描述
原因是父进程在回收某一个子进程的信号的时候,另外的子进程产生了信号,但是未决信号集只能保存一个未决信号,其他重复的相同的信号会被丢弃。

一个不好的解决方法,写一个while 死循环来回收子进程,这样做会导致 父进程回收了一个被阻塞。

void myFun(int num){
    //能够捕捉到信号,但是只靠这样的一句话真的能够回收资源吗?不能
    printf("捕捉到的信号 : %d.\n",num);
    //子进程结束后需要回收 PCB 的资源
    while(1){
    wait(NULL);
    }
}

我个人想法是计数,来一个 SIGCHLD 的信号就计数一次,这个计数会被当作 wait () 函数执行的次数。

😔哎真的要复习了,使用 waitpid循环回收子进程

void myFun(int num){
    //能够捕捉到信号,但是只靠这样的一句话真的能够回收资源吗?不能
    printf("捕捉到的信号 : %d.\n",num);
while (1)
{
    int ret = waitpid(-1,NULL,WNOHANG);
    if(ret == -1){
        break;
        //说明没有子进程了,那就更不用管了
    }else if(ret > 0){
        printf("child die, pid = %d.\n",ret);
    }else if(ret == 0){
        break;
        //就是break,说明还有其他的子进程,但是先不用管,父进程先做好自己的事情
    }

}

}

但是这样依然可能会出现段错误。
如果信号发生的时候在未决信号集当中注册,在这个程序中子进程众多,父进程还没有来得及改变未决信号集里面的状态,所有的子进程就已经运行完了。
改进方式,在子进程尚未运行完成时,对 SIGCHLD 信号阻塞,运行完成之后统一设置成不阻塞。由父进程统一对释放的子进程进行处理。

在这里插入图片描述

解决方法

#include<stdio.h>
#include<wait.h>
#include<signal.h>
#include<stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include<wait.h>
void myFun(int num){
    //能够捕捉到信号,但是只靠这样的一句话真的能够回收资源吗?不能
    printf("捕捉到的信号 : %d.\n",num);
    //子进程结束后需要回收 PCB 的资源
    //1、不好,信号不能排队,会导致无法将所有的子进程回收完
   // wait(NULL);
   //2、不好,循环调用能够回所有的子进程,但是父进程会被阻塞
//    while(1){
//        wait(NULL);
//    }
//3、使用waitpid()
while (1)
{
    int ret = waitpid(-1,NULL,WNOHANG);
    // If wstatus is not NULL, wait() and waitpid() store status 
    //information in the int to which it points.  This integer can 
    //be inspected with  the following macros (which take the integer 
    //itself as an argument, not a pointer to it, as is done in wait() 
    //and waitpid()!):
    //第2个参数是用来存放子进程被回收的时候的状态来着。
    if(ret == -1){
        // perror("waitpid");
        // exit(0);
        break;
        //说明没有子进程了,那就更不用管了
    }else if(ret > 0){
        printf("child die, pid = %d.\n",ret);
    }else if(ret == 0){
        ///break;不能这样,说明还有其他的子进程
        break;

        //就是break,说明还有其他的子进程,但是先不用管,父进程先做好自己的事情
    }

}

}


int main(void){
    //提前设置好阻塞信号集,因为有可能进程很快结束,父进程还没有注册完信号捕捉。
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set,SIGCHLD);
    sigprocmask(SIG_BLOCK,&set,NULL);

    //创建一些子进程
    pid_t pid;
    for(int i = 0;i < 20;i++){
        pid = fork();
        if(pid == 0){
            break;
        }
    }
    if(pid > 0){
        //捕捉子进程挂掉的时候发出的信号,这样父进程还能够做自己的事情
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = myFun;
        sigemptyset(&act.sa_mask);

        sigaction(SIGCHLD,&act,NULL);

        //注册完信号捕捉以后解除阻塞
        sigprocmask(SIG_UNBLOCK,&set,NULL);
        

        while (1)
        {
            printf("parent process : %d.\n",getpid());
            sleep(2);
        }
        
    }
    else if(pid == 0){
        printf("child process : %d.\n",getpid());
    }
    return 0;
}
举报

相关推荐

0 条评论