内核通过信号来控制程序. 程序接收到信号后, 停止所有事务, 调用handler funciton来响应对应的信号.
如图 :
![Interprocess communication - 2 signal handler - 德哥@Digoal - The Heart,The World.](http://img0.ph.126.net/glR02aoIJV8LSxJImQYGlw==/2626161532728203040.jpg)
进程有自己的signal mappings table :
![Interprocess communication - 2 signal handler - 德哥@Digoal - The Heart,The World.](http://img7.ph.126.net/HohmFa1pJUw9mQWEh-Jclw==/6597139035889755427.jpg)
正因为有了signal mappings table, 所以可以创建自己的handler function , 响应各种信号.
1. 首先要创建handler函数.
2. 然后创建一个sigaction结构体, 记录handler function, mask(在执行handler function时不响应其他信号的掩码)等, sa_flag. 具体参考man sigaction.
结构体如下 :
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
因此自定义的handler函数需要符合 返回值void, 参数int 的要求.
3. 使用sigaction函数注册第二步创建的结构体以及信号到signal mappings table.
oldact不关心的话直接使用NULL替代. (If act is non-null, the new action for signal signum is installed from act. If oldact is non-null, the previous action is saved in oldact.)
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
RETURN VALUE
sigaction() returns 0 on success and -1 on error.
oldact不关心的话直接使用NULL替代. (If act is non-null, the new action for signal signum is installed from act. If oldact is non-null, the previous action is saved in oldact.)
例如 :
写一个 handler function 并注册 SIGINT信号 . 使用 Ctrl+C
或者kill -INT 向进程发送SIGINT信号.
执行如下 :
执行结果 :
从这个结果返回去推算前面没有注释exit(1)的执行结果, 其实信号是一个一个来处理的, 不是并行处理的. 别看前面是3个输出一起出来的. 其实是在一个信号结束后另一个信号才触发的. 如下
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// 错误输出函数
void error(char * msg) {
fprintf(stdout, "%s: %s\n", msg, strerror(errno));
exit(1);
}
// 自定义的sigint函数.
void sigint (int sig) {
fprintf(stdout, "sig:%i, signal sigint received.\n", sig);
exit(1);
}
// 注册signal table的函数.
int reg_signal(int sig, void (*handler)(int) ) {
struct sigaction action;
action.sa_handler = handler;
// 以下表示在handler function执行过程中同样接收其他信号.
sigemptyset(&action.sa_mask);
// 不设置任何sa_flags
action.sa_flags = 0;
// 注册signal table.
return sigaction(sig, &action, NULL);
}
int main() {
char name[80];
// 注册一个处理的SIGINT信号的自定义函数.
if( reg_signal(SIGINT, sigint) == -1 ) {
error("reg_signal error");
}
fprintf(stdout, "please enter your name:");
fgets(name, 80, stdin);
fprintf(stdout, "hello, %s\n", name);
return 0;
}
执行如下 :
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./d.c -o d
[root@db-172-16-3-150 zzz]# ./d
please enter your name:
此时键盘按键ctrl+c, 发出一个SIGINT信号, 结果如下 :
sig:2, signal sigint received.
当然也可以使用 "kill -INT 进程号". 如kill -INT 2999 , 这里不进行测试.
signal handler 函数体内如果没有exit()的话, 那么handler函数执行完后, 进程将会接着收到信号开始的那刻继续往后处理. 如果handler函数体内有exit()则会直接退出进程.
另外就是初始化sigaction 结构体的时候, 有一个设置sa_mask的地方, 如果这里没有设置, 那么signal handler函数处理过程中可以接收其他的信号, 也就是说也可以被打断. 例如下面的例子写了一个无限循环的signal handler过程.
[root@db-172-16-3-150 zzz]# cat d.c
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
void error(char * msg) {
fprintf(stdout, "%s: %s\n", msg, strerror(errno));
exit(1);
}
void sig_handler (int sig) {
sleep(1);
switch(sig) {
case SIGALRM:
fprintf(stdout, "sig:%i, signal sigalrm received.\n", sig);
raise(SIGINT);
break;
case SIGINT:
fprintf(stdout, "sig:%i, signal sigint received.\n", sig);
raise(SIGALRM);
// fprintf(stdout, "if exit(1) not commeted, this line will printed.");
break;
default:
fprintf(stdout, "sig:%i, this handler cann't handler the singal.\n", sig);
break;
}
fprintf(stdout, "sig:%i outside the switch code.\n", sig);
// 这里要注意, 如果把exit(1)的注释去掉就会有意想不到的结果. 这个无限循环将被打破.
//exit(1);
}
int reg_signal(int sig, void (*handler)(int) ) {
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(sig, &action, NULL);
}
int main() {
char name[80];
if( reg_signal(SIGINT, sig_handler) == -1 ) {
error("reg_signal error");
}
if( reg_signal(SIGALRM, sig_handler) == -1 ) {
error("reg_signal error");
}
fprintf(stdout, "please enter your name:");
fgets(name, 80, stdin);
fprintf(stdout, "hello, %s\n", name);
return 0;
}
执行结果 :
[root@db-172-16-3-150 zzz]# ./d
please enter your name: 键入CTRL+C
// 1 second elapsed
sig:2, signal sigint received.
// 1 second elapsed
sig:14, signal sigalrm received.
sig:14, outside the switch code.
sig:2, outside the switch code.
// 1 second elapsed
sig:2, signal sigint received.
// 1 second elapsed
sig:14, signal sigalrm received.
sig:14, outside the switch code.
sig:2, outside the switch code.
// 1 second elapsed
sig:2, signal sigint received.
持续循环
// 从上面的结果看出, kernel在处理多个信号同时触发时.
// 同时这里还使用了计时器, 考虑到内核的效率问题, 内核给一个进程只分配一个计时器. 如果给一个进程可以有多个计时器的话, 内核管理计时器会非常复杂, 内核变得很慢.
如果使用kill -ALRM 进程号 会是什么情况呢?
结果如下 :
[root@db-172-16-3-150 zzz]# ./d
please enter your name: 另一个会话输入kill -ALRM ./d进程号
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
sig:14, outside the switch code.
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
sig:14, outside the switch code.
// 1 second elapsed
sig:14, signal sigalrm received.
如此循环.
从两次发出的信号得到的结果来看, 内核没有对哪个信号有优先处理的权力.
将sig_handler函数中的exit(1)注释去掉, 重新编译测试 :
[root@db-172-16-3-150 zzz]# ./d
please enter your name: 键入CTRL+C
// 1 second elapsed
sig:2, signal sigint received.
// 1 second elapsed
sig:14, signal sigalrm received.
sig:14, outside the switch code.
[root@db-172-16-3-150 zzz]# ./d
please enter your name:另一个SESSION键入kill -ALRM ./d进程号
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
从这个结果返回去推算前面没有注释exit(1)的执行结果, 其实信号是一个一个来处理的, 不是并行处理的. 别看前面是3个输出一起出来的. 其实是在一个信号结束后另一个信号才触发的. 如下
[root@db-172-16-3-150 zzz]# ./d
please enter your name: 另一个会话输入kill -ALRM ./d进程号
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
// sig:2 sig_handler处理结束
sig:14, outside the switch code.
// sig:14 sig_handler处理结束
// 1 second elapsed
sig:14, signal sigalrm received.
// 1 second elapsed
sig:2, signal sigint received.
sig:2, outside the switch code.
// sig:2 sig_handler处理结束
sig:14, outside the switch code.
// sig:14 sig_handler处理结束
// 1 second elapsed
sig:14, signal sigalrm received.
常用信号如下 :
![Interprocess communication - 2 signal handler - 德哥@Digoal - The Heart,The World.](http://img0.ph.126.net/epm8rQBj-AwdOfRZOiEh0A==/6597148931494416020.jpg)
【注意】
1. 有两个信号不能自定义handler function , 那就是 SIGSTOP 和 SIGKILL .
【参考】
man 7 signal