05-信号的接收
9.2.2 信号的接收
在用户程序中,为了捕获信号,可以使用signal()函数来设置对应信号的处理函数:
void (signal(int signum, void (handler))(int)))(int);
该函数原型较难理解,它可以分解为:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理函数,若为SIG_IGN,表示忽略该信号;若为SIG_DFL,表示采用系统默认方式处理信号;若为用户自定义的函数,则信号被捕获到后,该函数将被执行。
如果signal()调用成功,它返回最后一次为信号signum绑定的处理函数handler值,失败则返回SIG_ERR。
在进程执行时,按下“Ctrl +c”将向其发出SIGINT信号,kill正在运行的进程将向其发出SIGTERM信号,代码清单9.1的进程捕获这两个信号并输出信号值。
代码清单9.1 signal()捕获信号范例
1 void sigterm_handler(int signo)
2 {
3 printf("Have caught sig N.O. %d\n", signo);
4 exit(0);
5 }
6
7 int main(void)
8 {
9 signal(SIGINT, sigterm_handler);
10 signal(SIGTERM, sigterm_handler);
11 while(1);
12
13 return 0;
14 }
除了signal()函数外,sigaction()函数可用于改变进程接收到特定信号后的行为,它的原型为:
int sigaction(int signum,const struct sigaction act,struct sigaction oldact));
该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号。第二个参数是指向结构体sigaction的一个实例的指针,在结构体sigaction的实例中,指定了对特定信号的处理函数,若为空,则进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理函数,可指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。
先来看一个使用信号实现异步通知的例子,它通过signal(SIGIO, input_handler)对标准输入文件描述符STDIN_FILENO启动信号机制。用户输入后,应用程序将接收到SIGIO信号,其处理函数input_handler()将被调用,如代码清单9.2所示。
代码清单9.2 使用信号实现异步通知的应用程序实例
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <stdio.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <unistd.h>
7 #define MAX_LEN 100
8 void input_handler(int num)
9 {
10 char data[MAX_LEN];
11 int len;
12
13 / 读取并输出STDIN_FILENO上的输入 /
14 len = read(STDIN_FILENO, &data, MAX_LEN);
15 data[len] = 0;
16 printf("input available:%s\n", data);
17 }
18
19 main()
20 {
21 int oflags;
22
23 / 启动信号驱动机制 /
24 signal(SIGIO, input_handler);
25 fcntl(STDIN_FILENO, F_SETOWN, getpid());
26 oflags = fcntl(STDIN_FILENO, F_GETFL);
27 fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
28
29 /* 最后进入一个死循环,仅为保持进程不终止,如果程序中
30 没有这个死循会立即执行完毕 */
31 while (1);
32 }
上述代码24行为SIGIO信号安装input_handler()作为处理函数,第25行设置本进程为STDIN_FILENO文件的拥有者(owner),没有这一步内核不会知道应该将信号发给哪个进程。而为了启用异步通知机制,还需对设备设置FASYNC标志,26~27行代码实现此目的。整个程序的执行效果如下:
[root@localhost driver_study]# ./signal_test
I am Chinese.
input available: I am Chinese.
I love Linux driver.
input available: I love Linux driver.
从中可以看出,当用户输入一串字符后,标准输入设备释放SIGIO信号,这个信号“中断”驱使对应的应用程序中的input_handler()得以执行,将用户输入显示出来。
由此可见,为了在用户空间中能处理一个设备释放的信号,它必须完成3项工作。
(1)通过F_SETOWN IO控制命令设置设备文件的拥有者为本进程,这样从设备驱动发出的信号才能被本进程接收到。
(2)通过F_SETFL IO控制命令设置设备文件支持FASYNC,即异步通知模式。
(3)通过signal()函数连接信号和信号处理函数。