当前位置:嗨网首页>书籍在线阅读

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()函数连接信号和信号处理函数。