【linux编程】函数sigaction

函数函数sigaction

1. 函数sigaction原型:

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

分析:

  • 参数 signum :要捕获的信号。
  • 参数act:truct sigaction 结构体,后面具体讲解传入参数,新的处理方式
  • 参数oldact:返回旧的 struct sigaction 结构体,传出参数,旧的处理方式

 

1.2 struct sigaction 结构体

1 struct sigaction
2 {
3     void(*sa_handler)(int);
4     void(*sa_sigaction)(int, siginfo_t *, void *);
5     sigset_t sa_mask;
6     int sa_flags;
7     void(*sa_restorer)(void);
8 };

分析:

  • sa_handler : 不带附加参数的信号处理函数指针
  • sa_sigaction: 带有附加参数的信号处理函数指针(两个信号处理函数指针只能二选一)
  • sa_mask: 在执行信号处理函数时,应该屏蔽掉哪些信号
  • sa_flags: 用于控制信号行为,它的值可以是下面选项的组合。
  • SA_SIGINFO:如果指定该选项,则向信号处理函数传递参数(这时应该使用 sa_sigaction 成员而不是 sa_handler).
  • sa_restorer:该成员在早期是用来清理函数栈的,如今已被废弃不用。

注意:sa_flags 的选项比较多,大部分可又自己做实验验证,有些是需要额外的知识,比如 SA_ONESTACK 和 SA_RESTART,这些放到后面讲解。本节示例中,只需要把 sa_flags 设置为 0 即可。

 

信号捕捉特性:

  • 进程正常运行时,默认PCB有一个信号屏蔽字,假定为☆,它决定了进程自动屏蔽哪些信号,当注册了某个信号步捕捉函数,捕捉到该信号以后,要调用该函数,而该函数有可能执行很长时间,在这期间所屏蔽的信号不由☆指定,而由sa_mask来指定,调用完信号处理函数,再恢复为☆。
  • xxx信号捕捉函数执行期间,XXX信号自动屏蔽。
  • 阻塞的常规信号不支持排队,产生多次只记录一次(后32个实时信号支持排队)

 

1. 测试代码:

 1 #include<stdio.h>
 2 #include<signal.h>
 3 #include<stdlib.h>
 4 #include<unistd.h>
 5  
 6 void docatch(int signo)
 7 {
 8     printf("%d signal is catch\n", signo);
 9 }
10  
11 int main()
12 {
13     int ret;
14     struct sigaction act;
15     act.sa_handler = docatch;
16     sigemptyset(&act.sa_mask);
17     sigaddset(&act.sa_mask, SIGQUIT);
18     act.sa_flags = 0;  //默认属性:信号捕捉函数执行期间,自动屏蔽本信号
19     ret = sigaction(SIGINT, &act, NULL);
20     if(ret < 0) 
21     {
22         perror("sigaction error");
23         exit(1);
24     }
25     while(1)
26       sleep(1);
27     return 0;
28 }

输出结果:

 

 2. 测试代码:

 1 #include<stdio.h>
 2 #include<signal.h>
 3 #include<stdlib.h>
 4 #include<unistd.h>
 5  
 6 void docatch(int signo)
 7 {
 8     printf("%d signal is catch\n", signo);
 9     sleep(10);
10     printf("--------------finish-\n");
11 }
12  
13 int main()
14 {
15     int ret;
16     struct sigaction act;
17     act.sa_handler = docatch;
18     sigemptyset(&act.sa_mask);
19     sigaddset(&act.sa_mask, SIGQUIT);
20     act.sa_flags = 0;  
21     ret = sigaction(SIGINT, &act, NULL);
22     if (ret < 0) 
23     {
24         perror("sigaction error");
25         exit(1);
26     }
27     while (1)
28         sleep(1);
29     return 0;
30 }

输出结果:

 

3. 测试代码:

 1 #include <unistd.h>
 2 #include <signal.h>
 3 #include <stdio.h>
 4 
 5 void printsigset(const sigset_t *set)
 6 {
 7     for (int i = 1; i <= 64; i++)
 8     {
 9         if (i == 33) putchar(' ');
10         if (sigismember(set, i) == 1)
11             putchar('1');
12         else
13             putchar('0');
14     }
15     puts("");
16 }
17 
18 void handler(int sig)
19 {
20     if (sig == SIGTSTP) 
21         printf("hello SIGTSTP\n");
22     if (sig == SIGINT) 
23         printf("hello SIGINT\n");
24     sleep(5);
25     sigset_t st;
26     sigpending(&st);
27     printsigset(&st);
28 }
29 
30 int main()
31 {
32     printf("I'm %d\n", getpid());
33     struct sigaction act, oldact;
34     act.sa_handler = handler;   // 设置普通信号处理函数                     
35     sigemptyset(&act.sa_mask);  // 向 sa_mask 中添加 SIGINT
36     sigaddset(&act.sa_mask, SIGINT);
37     act.sa_flags = 0; // 先置 0
38 
39     sigaction(SIGTSTP, &act, &oldact);
40     sigaction(SIGINT, &act, &oldact);
41 
42     while (1) 
43     {
44         write(STDOUT_FILENO, ".", 1);
45         pause();
46     }
47     return 0;
48 }

输出结果:

分析:

  • 当程序运行的时候,Ctrl C 进入 handler,然后立即 Ctrl Z 发现 handler 还未执行完就被 SIGTSTP 打断.
  • 当程序运行的时候,Ctrl Z 进入 handler,然后立即 Ctrl C 发现并不会被 SIGINT 打断,这是因为该 handler 注册的时候被设置了 SA_MASK = SIGINT。最后 handler 结束的时候打印了未决信号集,发现里头有 SIGINT。所以 handler 结束后,又去继续对 SIGINT 进行处理。

 

 

参考资料

1. C++笔记(使用sigaction进行信号捕捉)

2. Linux下多线程中的信号处理

 

posted @ 2019-01-14 10:47  苏格拉底的落泪  阅读(2846)  评论(0编辑  收藏  举报