linux 信号集 信号捕捉函数

信号集:

  许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t

  在 PCB 中有两个非常重要的信号集。 一个称之为:“阻塞信号集”, 另一个称之为 “未决信号集”。这两个信号集都是内核使用 位图机制(类似 O_AAA | O_BBB)来实现的。 但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信号集进行修改。

  信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间

  信号的 “阻塞” 是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生

  信号的 阻塞 就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。

阻塞信号集和未决信号集:

  PCB在内核中,PCB内有 pid、ppid、未决信号集、阻塞信号集等等。

 

复制代码
1.用户通过键盘 Ctrl + C, 产生2号信号 SIGINT(信号被创建)
2.信号产生但是没有被处理(未决)
    - 在内核中将所有的没有被处理的信号存储在一个集合中 - 未决信号集
    - SIGINT信号状态被存储在第二个标志位上,
        - 这个标志位的值为0, 说明信号不是未决状态
        - 这个标志位的值为1, 说明信号处理未决状态
3.这个未决状态的信号,需要被处理,处理之前需要和另一个信号集(阻塞信号集), 进行比较
    - 阻塞信号集默认不阻塞任何的信号
    - 如果想要阻塞某些信号需要用户调用系统的API
4.在处理的时候和阻塞信号集中的标志位进行查询,看是不是对该信号设置阻塞了
    - 如果没有阻塞,这个信号就被处理
    - 如果阻塞了, 这个信号就继续处理未决状态,直到阻塞解除,这个信号就被处理
例: 2号未决信号集是1, 2号阻塞信号集是0 表示不阻塞 则未决信号2号则发送给进程,且2号未决信号集变为0
        2号阻塞信号集是1 表示阻塞  则未决信号2号被阻塞,不被处理, 当阻塞信号2号变为0之后,未决信号2号才发送
复制代码

 案例:

复制代码
 1 /*
 2     以下信号集相关的函数都是对  自定义  的信号集进行操作
 3     #include <signal.h>
 4     int sigemptyset(sigset_t* set);
 5         - 功能:清空信号集中的数据, 将信号集中的左右标志位置为0
 6         - 参数: set, 传出参数,需要操作的信号集
 7         - 返回值:成功返回 0,失败返回 -1
 8 
 9     int sigfillset(sigset_t* set);
10         - 功能:将信号集中的所有的标志位置为 1
11         - 参数: set, 传出参数,需要操作的信号集
12         - 返回值:成功返回 0,失败返回 -1
13 
14     int sigaddset(sigset_t* set, int signum);
15         - 功能:设置信号集中的某一个信号对应标志位为1,表示阻塞这个信号.
16         - 参数: 
17             - set, 传出参数,需要操作的信号集
18             - signum,需要设置阻塞的信号
19         - 返回值:成功返回 0,失败返回 -1
20     int sigdelset(sigset_t* set, int signum);
21         - 功能:设置信号集中的某一个信号对应标志位为0,表示不阻塞这个信号.
22         - 参数: 
23             - set, 传出参数,需要操作的信号集
24             - signum,需要设置不阻塞的信号
25         - 返回值:成功返回 0,失败返回 -1
26     int sigismember(const sigset_t* set, int signum);
27         - 功能:判断某个信号是否阻塞
28         - 参数:
29             - set,需要操作的信号集
30             - signum,需要判断的信号
31         - 返回值:
32             1: signum被阻塞
33             0: signum不阻塞            
34             -1: 调用失败
35 */
36 #include <stdio.h>
37 #include <signal.h>
38 int main()
39 {
40     //创建一个信号集
41     sigset_t set;//某些位可能为1
42     //清空信号集的内容
43     sigemptyset(&set);//传入信号集的地址
44     //判断 SIGINT 是否在信号集 set里
45     int ret = sigismember(&set, SIGINT);
46     if(ret == 0)
47     {
48         printf("SIGINT 不阻塞\n");//不阻塞
49     }
50     else if(ret == 1)
51     {
52         printf("SIGINT 阻塞\n");
53     }
54     //添加几个信号到信号集中
55     sigaddset(&set, SIGINT);
56     sigaddset(&set, SIGQUIT);
57     //判断SIGINT是否在信号集中
58     ret = sigismember(&set, SIGQUIT);
59     if(ret == 0)
60     {
61         printf("SIGQUIT 不阻塞\n");
62     }
63     else if(ret == 1)
64     {
65         printf("SIGQUIT 阻塞\n");
66     }
67     //从信号集删除一个信号
68     sigdelset(&set, SIGQUIT);
69     //查询SIGQUIT是否在信号集中
70     ret = sigismember(&set, SIGQUIT);
71     if(ret == 0)
72     {
73         printf("SIGQUIT 不阻塞\n");
74     }
75     else if(ret == 1)
76     {
77         printf("SIGQUIT 阻塞\n");
78     }
79     return 0;
80 }
复制代码

标红函数,都是对自定义信号集进行操作,且改变的是自定义的信号集,之后调用 sigprocmask函数  将自定义的信号集设置到内核当中。未决信号集和阻塞信号集都是通过 位图机制 实现的。

sigprocmask:

复制代码
 1 /*
 2     man 2 sigprocmask
 3     int sigprocmask(int how, const sigset_t* set, sigset_t* oldset);
 4         - 功能: 将自定义信号集中的数据设置到内核中(设置阻塞、接触阻塞、替换)
 5         - 参数: 
 6             - how:如何对内核阻塞信号集进行处理
 7                 SIG_BLOCK: 将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变.
 8                     或操作  假设内核中默认的阻塞信号集是 mask, mask | set
 9                 SIG_UNBLOCK: 根据用户设置的数据,对内核中的数据进行解除阻塞
10                     与操作  mask &= ~set
11                 SIG_SETMASK: 覆盖内核中原来的值
12             - set:已经初始化好的用户自定义的信号集
13             - oldset:保存设置之前的内核中的阻塞信号集的状态,可以是 NULL
14         - 返回值:
15             成功: 0
16             失败: -1
17                 设置错误号:EFAULT、EINVAL
18     int sigpending(sigset_t* set);
19         - 功能:获取内核中的未决信号集
20         - 参数:set,传出参数,保存的是内核中的未决信号集中的信息.
21 */
22 //编写一个程序, 把所有的常规信号,(1-31) 未决状态打印到屏幕
23 //设置某些信号是阻塞的, 通过键盘产生这些信号
24 #include <stdio.h>
25 #include <signal.h>
26 #include <unistd.h>
27 int main()
28 {
29     //设置2号、3号信号阻塞
30     sigset_t set;
31     sigemptyset(&set);
32     //将2号3号信号添加到信号集中
33     sigaddset(&set, SIGINT);//Ctrl + C      2号信号 仅将信号传递到后台 需要用 kill -9 进程号杀死
34     sigaddset(&set, SIGQUIT);//Ctrl + \     3号信号
35     //  ./可执行程序 &    变为后台操作   输入 fg 变为 前台
36     //修改内核中的阻塞信号集
37     sigprocmask(SIG_BLOCK,&set, NULL);
38     int num = 0;//解除阻塞
39     while(1)
40     {
41         num++;
42         //获取当前的未决信号集的数据
43         sigset_t pendingset;
44         sigemptyset(&pendingset);
45         sigpending(&pendingset);
46         //遍历前32位
47         for(int i = 1; i <= 32; i++)
48         {
49             if(sigismember(&pendingset, i) == 1)
50             {
51                 printf("1");
52             }
53             else if(sigismember(&pendingset, i) == 0)
54             {
55                 printf("0");
56             }
57             else
58             {
59                 perror("sigismember");
60                 exit(0);
61             }
62         }
63         printf("\n");
64         sleep(1);
65         if(num == 10)
66         {
67             //解除阻塞
68             sigprocmask(SIG_UNBLOCK, &set, NULL);
69         }
70     }
71     return 0;
72 }
复制代码

信号捕捉函数  sigaction:

复制代码
 1 /*
 2     #include <signal.h>
 3     int sigaction(int signum, const struct sigaction* act,struct sigaction* oldact);
 4         - 功能: 检查或者改变信号的处理, 信号捕捉
 5         - 参数: 
 6             - signum : 需要捕捉的信号的编号或是宏值(信号的名称)
 7             - act : 捕捉到信号之后的处理动作
 8             - oldact : 上一次对信号捕捉相关的设置, 一般不适用, 传递NULL
 9         - 返回值:
10             成功 0
11             失败 -1
12     struct sigaction
13     {
14         //  函数指针,指向的函数就是信号捕捉到之后的处理函数
15         void    (*sa_handler)(int);
16         //  不常用,
17         void    (*sa_sigaction)(int, siginfo_t *, void *);
18         // 临时阻塞信号集,在信号捕捉函数执行过程中,临时阻塞某些信号
19         sigset_t    sa_mask;
20         // 使用哪一个信号处理对捕捉到的信号进行处理
21         // 这个值可以是0,表示使用 sa_handler,也可以是 SA_SIGINFO表示使用sa_sigaction
22         int         sa_flags;
23         // 被废弃掉了 NULL -.-
24         void    (*sa_restorer)(void);
25     }     
26 */
27 #include <stdio.h>
28 #include <sys/time.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 void myAlarm(int num){
32     printf("捕捉到了信号的编号:%d\n", num);
33     printf("xxxxxxxx\n");
34 }
35 //过3s以后,每隔2秒定时一次
36 int main()
37 {
38     struct sigaction act;
39     act.sa_flags = 0;
40     act.sa_handler = myAlarm;
41     //清空临时阻塞信号集
42     sigemptyset(&act.sa_mask);
43     //注册信号捕捉
44     sigaction(SIGALRM, &act, NULL);
45 
46     struct itimerval new_value;
47     //设置间隔额时间
48     new_value.it_interval.tv_sec = 2;//
49     new_value.it_interval.tv_usec = 0;//微秒
50     //设置延迟时间,3秒之后开始第一次定时
51     new_value.it_value.tv_sec = 3;//
52     new_value.it_value.tv_usec = 0;//微秒
53     int ret = setitimer(ITIMER_REAL,&new_value,NULL);//非阻塞的
54     printf("定时器开始了...\n");
55     if(ret == -1)
56     {
57         perror("setitimer");
58         exit(0);
59     }
60     //getchar();    //获取键盘录入
61     while(1){}      //捕捉到了信号的编号是14 xxxxxx
62     return 0;
63 }
复制代码

内核实现信号捕捉的过程:

posted on   廿陆  阅读(37)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示