函数函数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 进行处理。