【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号

文章目录

    • 2.25 sigprocmask函数使用
    • 2.26 sigaction信号捕捉函数
      • 内核实现信号捕捉的过程
      • 信号捕捉特性
    • 2.27SIGCHILD信号


2.25 sigprocmask函数使用

【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号_第1张图片阻塞信号集有时称作信号掩码。
联想:fcntl函数可以修改fd属性。

./sigprocmask &  //将程序设置为后台运行,输入ls可以同步有输出
fg              //将程序恢复到前台运行

#include 
#include 
#include 
#include 

int main() {

    // 设置2、3号信号阻塞
    sigset_t set;
    sigemptyset(&set);
    // 将2号和3号信号添加到信号集中
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGQUIT);

    // 修改内核中的阻塞信号集
    sigprocmask(SIG_BLOCK, &set, NULL);

    int num = 0;

    while(1) {
        num++;
        // 获取当前的未决信号集的数据
        sigset_t pendingset;
        sigemptyset(&pendingset);
        sigpending(&pendingset);

        // 遍历前32位
        for(int i = 1; i <= 31; i++) {
            if(sigismember(&pendingset, i) == 1) {
                printf("1");//未决
            }else if(sigismember(&pendingset, i) == 0) {
                printf("0");//非未决
            }else {
                perror("sigismember");
                exit(0);
            }
        }

        printf("\n");
        sleep(1);
        if(num == 10) {
            // 解除阻塞,SIGINT默认动作是终止进程
            sigprocmask(SIG_UNBLOCK, &set, NULL);
        }

    }


    return 0;
}



ctrl + C 2号信号:SIGINT
ctrl + \ 3号信号:SIGQUIT
ctrl + Z 19号信号:SIGTSTP

./out & 就是在后台运行
此时再输入ls之类的指令是可以的
fg:调到前台来运行,此时就无法输入其他指令,只能再开一个终端
【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号_第2张图片

2.26 sigaction信号捕捉函数

相较于signal函数,优先使用sigaction信号捕捉函数。(标准的原因)
在这里插入图片描述
【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号_第3张图片

#include 
#include 
#include 
#include 

void myalarm(int num) {
    printf("捕捉到了信号的编号是:%d\n", num);
    printf("xxxxxxx\n");
}

// 过3秒以后,每隔2秒钟定时一次
int main() {

    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = myalarm;
    sigemptyset(&act.sa_mask);  // 清空临时阻塞信号集
   
    // 注册信号捕捉
    sigaction(SIGALRM, &act, NULL);

    struct itimerval new_value;

    // 设置间隔的时间
    new_value.it_interval.tv_sec = 2;
    new_value.it_interval.tv_usec = 0;

    // 设置延迟的时间,3秒之后开始第一次定时
    new_value.it_value.tv_sec = 3;
    new_value.it_value.tv_usec = 0;

    int ret = setitimer(ITIMER_REAL, &new_value, NULL); // 非阻塞的
    printf("定时器开始了...\n");

    if(ret == -1) {
        perror("setitimer");
        exit(0);
    }

    // getchar();//等待键盘录入
    while(1);

    return 0;
}

显示结果:

内核实现信号捕捉的过程

【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号_第4张图片

信号捕捉特性

1、假设信号捕捉到,信号捕捉代码仍在执行,又发送一个信号,该信号会被执行嘛?
这个信号不会被处理,阻塞在那!
未决信号集中SIGALRM信号为1,若处理myalarm函数,则未决信号集中SIGALRM信号为0,如果一直在处理,又产生一个SIGALRM信号,则它的未决信号标志位变成1,阻塞在那,不会执行回调函数。当上一次的回调函数执行完,第二个信号的回调函数才会被执行,它的未决信号标志位再次变成0。
在myalarm回调函数使用的过程当中,SIGALARM信号自动被屏蔽,不许我们设置,为系统行为,当回调函数执行结束,SIGALARM信号不被屏蔽。
2、执行回调函数时使用临时阻塞信号集,当回调结束使用内核当中的信号集。(可能在执行回调函数的时候不希望被某些信号打断)
3、常规信号阻塞的时候不支持排队。不支持记录有多少个信号被阻塞未决。其他34-64,30个实时信号支持排队。

2.27SIGCHILD信号

【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号_第5张图片使用SIGCHLD信号解决僵尸进程的问题。
子进程结束时父进程有责任回收子进程的资源,一般不断循环调用wait(阻塞)或waitpid函数。实际过程中,父进程需要执行一些其他的操作,不可能一直阻塞等待回收子进程的资源。解决方案:捕捉SIGCHILD信号再调用wait(阻塞)或waitpid函数。
wait或waitpid函数一次只能回收一个子进程的资源。

#include 
#include 
#include 
#include 
#include 
#include 

void myFun(int num) {
    printf("捕捉到的信号 :%d\n", num);
    // 回收子进程PCB的资源
    //不使用while循环时,子进程回收不完全,未决信号集一次只能记录一个状态,产生的其余信号都被舍弃掉
    //死循环时,父进程一直被阻塞,不推荐
    // while(1) {
    //     wait(NULL); 
    /*
        17号信号进入未决信号集合,17位置1
        进入回调函数,17位置0
        但是如果有多个子进程同时结束发送信号
        未决信号集是不能累计的
        也就是说它只能处理一个信号
        那么其他同时发送的信号就被忽略了
        这种处理方式僵尸进程处理不干净
			 这个地方直接加while是没有意义的,因为一旦程序进入while就会一直阻塞在wait函数。
			 此时进程结束的信号初步处理都没有意义了
			 因为父进程一直卡在这里等待所有子进程结束
    */
    // }
    while(1) {
       int ret = waitpid(-1, NULL, WNOHANG);
       //所以在这里设置非阻塞 只要有信号,就会进来处理已经死亡的进程,而不管是哪一个。
       if(ret > 0) {
           printf("child die , pid = %d\n", ret); //继续while
       } else if(ret == 0) {
           // 说明还有子进程活着,没就结束这个回调函数,继续等下一个子进程死亡 给出SIGCHLD信号
           break;
       } else if(ret == -1) {
           // 说明没有子进程了,直接break除去
           break;
       }
    }
}

int main() {

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

    // 创建一些子进程20个
    pid_t pid;
    for(int i = 0; i < 20; i++) {
        pid = fork();
        if(pid == 0) {//阻止孙进程的创建
            break;
        }
    }

    if(pid > 0) {
        // 父进程

        // 捕捉子进程死亡时发送的SIGCHLD信号
        struct sigaction act;
        act.sa_flags = 0;
        act.sa_handler = myFun;
        sigemptyset(&act.sa_mask);
        //信号捕捉还没有注册成功,子进程就已经结束?就会产生问题,不能回收子进程
        //需要提前设置好阻塞信号集,阻塞SIGCHLD
        sigaction(SIGCHLD, &act, NULL);

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

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

    return 0;
}

你可能感兴趣的:(c++)