【Linux系统编程】27.sigprocmask、sigpending、signel、sigaction

本文详细介绍了Linux系统中的信号处理函数sigprocmask、sigpending以及如何使用它们来屏蔽、查看信号和实现信号捕捉。通过示例代码展示了如何操作信号集,以及信号捕捉的特性,包括信号延迟执行和子进程回收等。
摘要由CSDN通过智能技术生成

目录

sigprocmask

参数how

参数set

参数oldset

返回值

sigpending

参数set

测试代码1

测试结果

signel

测试代码2

测试结果

sigaction

参数act

参数oldact

返回值

测试代码3

测试结果

信号捕捉特性

测试代码4

测试结果

测试代码5

测试结果

内核实现信号捕捉过程

测试代码6

测试结果

测试代码7

测试结果

慢速系统调用

sigprocmask

用来屏蔽信号、解除屏蔽也使用该函数。其本质,在PCB中读取或修改进程的信号屏蔽字。

注意:屏蔽信号只是将信号处理延后执行(延至解除屏蔽),而忽略表示将信号丢处理。

man 2 sigprocmask

参数how

  • SIG_BLOCK:设置阻塞

  • SIG_UNBLOCK:取消阻塞

  • SIG_SETMASK:用自定义的set替换mask

参数set

自定义set

参数oldset

保存旧的信号屏蔽集。

返回值

成功:0

失败:-1,设置errno

sigpending

查看未决信号集。

man 2 sigpending

参数set

传出的未决信号。

测试代码1

屏蔽CTRL+C、CTRL+\、7)、9)信号。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

int main(int argc, char *argv[])
{
    sigset_t XinHaoJi;      //信号集
    sigset_t Old_XinHaoJi;  //旧的信号集
    sigset_t WeiJue_XinHao; //未决信号
    int flag, i;
    sigemptyset(&XinHaoJi);        //清空信号集
    sigaddset(&XinHaoJi, SIGINT);  //屏蔽Ctrl+C信号
    sigaddset(&XinHaoJi, SIGQUIT); //屏蔽Ctrl+\信号
    sigaddset(&XinHaoJi, SIGBUS);  //屏蔽7)信号
    sigaddset(&XinHaoJi, SIGKILL); //屏蔽9)信号

    flag = sigprocmask(SIG_BLOCK, &XinHaoJi, &Old_XinHaoJi); //设置阻塞屏蔽信号
    if (flag == -1)
    {
        perror("设置阻塞屏蔽信号错误");
        exit(1);
    }

    while (1)
    {
        flag = sigpending(&WeiJue_XinHao); //获取未决信号
        if (flag == -1)
        {
            perror("获取未决信号错误");
            exit(1);
        }
        printf("未决信号集:");
        for (i = 1; i < 32; i++)
        {
            printf("%d", sigismember(&WeiJue_XinHao, i)); //判断一个信号是否在集合中
        }
        printf("\n");
        sleep(2);
    }
    
    return 0;
}

测试结果

只有9)信号不能被屏蔽,能够杀死进程。

signel

注册一个信号捕捉函数。

man 2 signel

测试代码2

捕捉CTRL+C信号,捕捉后输出内容。

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

void ChuLi_HanShu()
{
    static int i = 1;
    printf("成功捕捉到%d次。\n", i++);
}

int main(int argc, char *argv[])
{
    signal(SIGINT, ChuLi_HanShu); //注册信号捕捉函数,捕捉Ctrl+C信号
    printf("开始捕捉Ctrl+C信号!\n");
    while (1)
        ;
    return 0;
}

测试结果

sigaction

注册一个信号捕捉函数。

man 2 sigaction

参数act

传入参数,新的处理方式。

参数oldact

传出参数,旧的处理方式。

返回值

成功:0

失败:-1

测试代码3

用sigaction捕捉Ctrl+C、Ctrl+\信号,捕捉后输出内容。

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

void ChuLi_HanShu(int XinHao)
{
    static int i = 1;
    printf("成功捕捉到%d次,捕捉到的信号是%d。\n", i++, XinHao);
}

int main(int argc, char *argv[])
{
    int flag;
    struct sigaction New_ChuLi_FangShi;                               //新的处理方式
    struct sigaction Old_ChuLi_FangShi;                               //旧的处理方式
    New_ChuLi_FangShi.sa_handler = ChuLi_HanShu;                      //设置处理函数
    sigemptyset(&New_ChuLi_FangShi.sa_mask);                          //清空信号集
    New_ChuLi_FangShi.sa_flags = 0;                                   //默认值处理
    flag = sigaction(SIGINT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
    if (flag == -1)
    {
        perror("注册捕捉信号函数失败");
    }
    flag = sigaction(SIGQUIT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
    if (flag == -1)
    {
        perror("注册捕捉信号函数失败");
    }
    printf("开始捕捉Ctrl+C、Ctrl+\\信号!\n");
    while (1)
        ;
    return 0;
}

测试结果

信号捕捉特性

  1. 捕捉函数执行期间,信号屏蔽字由mask变成sa_mask,捕捉函数执行结束。再由sa_mask恢复回mask。

  2. 捕捉函数执行期间,本信号自动被屏蔽,sa_flags=0。

  3. 捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次。

测试代码4

按照Ctrl+\、Ctrl+C、Ctrl+\、Ctrl+C的顺序发送信号,查看执行结果。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void ChuLi_HanShu(int XinHao)
{
    static int i = 1;
    if (XinHao == SIGINT)
    {
        printf("\n成功捕捉到%d次,捕捉到的信号是%d,Ctrl+C开始睡10s。\n", i++, XinHao);
        sleep(10);
        printf("\nCtrl+C睡醒了。\n");
    }
    else if (XinHao == SIGQUIT)
    {
        printf("\n成功捕捉到%d次,捕捉到的信号是%d,Ctrl+\\开始睡10s。\n", i++, XinHao);
        sleep(10);
        printf("\nCtrl+\\睡醒了。\n");
    }
}

int main(int argc, char *argv[])
{
    int flag;
    struct sigaction New_ChuLi_FangShi;                               //新的处理方式
    struct sigaction Old_ChuLi_FangShi;                               //旧的处理方式
    New_ChuLi_FangShi.sa_handler = ChuLi_HanShu;                      //设置处理函数
    sigemptyset(&New_ChuLi_FangShi.sa_mask);                          //清空信号集
    New_ChuLi_FangShi.sa_flags = 0;                                   //默认值处理
    flag = sigaction(SIGINT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
    if (flag == -1)
    {
        perror("注册捕捉信号函数失败");
    }
    flag = sigaction(SIGQUIT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
    if (flag == -1)
    {
        perror("注册捕捉信号函数失败");
    }
    printf("开始捕捉Ctrl+C、Ctrl+\\信号!\n");
    while (1)
        ;
    return 0;
}

测试结果

推测

第1次捕捉到3)信号,未决信号3)置1,处理函数开始执行,未决信号3)清零。

第2次捕捉到2)信号,未决信号2)置1,中断第1次的处理函数,第2次处理函数开始执行,未决信号2)清零。

3)信号、2)信号按顺序到达,未决信号3)置1,未决信号2)置1。

第2次处理函数执行结束,检测未决信号2),发现还是1,继续执行信号2)第3次的处理函数,未决信号2)清零。

第3次的处理函数执行完成后,继续执行第1次的处理函数。

第1次处理函数执行完成后,检测未决信号3),发现还是1,继续执行信号3)第4次的处理函数。

总结:每执行完对应的信号处理函数,都再次检测未决信号,如果仍有未决信号,则继续执行处理函数;如果没有未决信号,则继续执行中断后的处理函数。

测试代码5

捕捉到1个信号后,在执行中,再捕捉到下一个信号,下一个信号延迟执行。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void ChuLi_HanShu(int XinHao)
{
    static int i = 1;
    if (XinHao == SIGINT)
    {
        printf("\n成功捕捉到%d次,捕捉到的信号是%d,Ctrl+C开始睡10s。\n", i++, XinHao);
        sleep(10);
        printf("\nCtrl+C睡醒了。\n");
    }
}

int main(int argc, char *argv[])
{
    int flag;
    struct sigaction New_ChuLi_FangShi;                               //新的处理方式
    struct sigaction Old_ChuLi_FangShi;                               //旧的处理方式
    New_ChuLi_FangShi.sa_handler = ChuLi_HanShu;                      //设置处理函数
    sigemptyset(&New_ChuLi_FangShi.sa_mask);                          //清空信号集
    sigaddset(&New_ChuLi_FangShi.sa_mask, SIGQUIT);                   //延迟执行的信号
    New_ChuLi_FangShi.sa_flags = 0;                                   //默认值处理
    flag = sigaction(SIGINT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
    if (flag == -1)
    {
        perror("注册捕捉信号函数失败");
    }
    printf("开始捕捉Ctrl+C、Ctrl+\\信号!\n");
    while (1)
        ;
    return 0;
}

测试结果

内核实现信号捕捉过程

 

测试代码6

借助信号捕捉回收子进程,全部回收后,父进程再继续执行。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>

void ChuLi_HanShu(int XinHao)
{
    int flag;
    pid_t JinCheng_ID;                                  //进程ID
    while ((JinCheng_ID = waitpid(-1, &flag, 0)) != -1) //循环回收子进程,防止僵尸进程出现
    {
        if (WIFEXITED(flag))
        {
            printf("这是处理函数,回收的子进程ID是%d。\n", JinCheng_ID);
        }
    }
}

int main(int argc, char *argv[])
{
    pid_t JinCheng_ID; //进程ID
    int flag, i;
    struct sigaction New_ChuLi_FangShi; //新的处理方式

    //创建子进程
    for (i = 0; i < 5; i++)
    {
        JinCheng_ID = fork();
        if (JinCheng_ID == 0)
        {
            break;
        }
    }

    if (i == 5) //父进程
    {
        New_ChuLi_FangShi.sa_handler = ChuLi_HanShu;         //设置处理函数
        sigemptyset(&New_ChuLi_FangShi.sa_mask);             //清空信号集
        New_ChuLi_FangShi.sa_flags = 0;                      //默认值处理
        flag = sigaction(SIGCHLD, &New_ChuLi_FangShi, NULL); //注册信号捕捉函数
        if (flag == -1)
        {
            perror("注册捕捉信号函数失败");
        }
        printf("这是父进程,进程ID是%d。\n", getpid());
        while (1)
        {
            printf("你好,世界!\n");
            sleep(1);
        }
    }
    else //子进程
    {
        sleep(i);
        printf("这是子进程,进程ID是%d,已死亡。\n", getpid());
        return i + 1;
    }
    return 0;
}

测试结果

测试代码7

借助信号捕捉回收子进程,死一个回收一个,同时执行。

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>

void ChuLi_HanShu(int XinHao)
{
    int flag;
    pid_t JinCheng_ID; //进程ID
    JinCheng_ID = waitpid(-1, &flag, 0);
    if (WIFEXITED(flag))
    {
        printf("这是处理函数,回收的子进程ID是%d。\n", JinCheng_ID);
    }
}

int main(int argc, char *argv[])
{
    pid_t JinCheng_ID; //进程ID
    int flag, i;
    struct sigaction New_ChuLi_FangShi; //新的处理方式

    //创建子进程
    for (i = 0; i < 5; i++)
    {
        JinCheng_ID = fork();
        if (JinCheng_ID == 0)
        {
            break;
        }
    }

    if (i == 5) //父进程
    {
        New_ChuLi_FangShi.sa_handler = ChuLi_HanShu;         //设置处理函数
        sigemptyset(&New_ChuLi_FangShi.sa_mask);             //清空信号集
        New_ChuLi_FangShi.sa_flags = 0;                      //默认值处理
        flag = sigaction(SIGCHLD, &New_ChuLi_FangShi, NULL); //注册信号捕捉函数
        if (flag == -1)
        {
            perror("注册捕捉信号函数失败");
        }
        printf("这是父进程,进程ID是%d。\n", getpid());
        while (1)
        {
            printf("你好,世界!\n");
            sleep(1);
        }
    }
    else //子进程
    {
        sleep(i);
        printf("这是子进程,进程ID是%d,已死亡。\n", getpid());
        return i + 1;
    }
    return 0;
}

测试结果

慢速系统调用

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

可修改sa_flags参数来设置被信号中断后系统调用是否重启:

  1. SA_INTERRURT:不重启。

  2. SA_RESTART:重启。

        捕捉到信号后,在执行捕捉函数期间,不希望自动阻塞该信号,可将sa_flags设置为SA_NODEFER,除非sa_mask中包含该信号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

因心,三人水

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

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

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

打赏作者

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

抵扣说明:

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

余额充值