Understanding Unix/Linux Programming-信号与play_again4.c的准备知识
- Ctrl-C做了什么?
Ctrl-C终止当前运行的程序,这个中断由一个称为信号的内核机制产生。信号是一个简单而重要的概念,下面将探讨信号的基本概念。
终端驱动程序在这里起到了相应的作用:
- 用户输入Ctrl-C
- 驱动程序收到字符
- 匹配VINTR和ISIG的字符被开启
- 驱动程序调用信号系统
- 信号系统发送SIGINT到进程
- 进程收到SIGINT
- 进程消亡
当然未必一定是Ctrl-C作为中断的控制字符,这是可以更改驱动设置的。
- 什么是信号:
信号是由单个词组成的消息。绿灯、停止标牌、裁判手势等都是信号,而这个物体和事件不是消息,走、停和出界才是消息。当按下Ctrl-C时,内核向当前正在运行的进程发送中断信号,每个信号都有一个数字编码,中断信号编码通常是2。
信号从何处来?
信号来自内核,生成信号请求来自于3个地方:
- 用户:用户通过输入控制字符请求内核产生信号
- 内核:当进程执行出错时,内核给进程发送一个信号,例如:非法段存取、浮点数溢出等等;内核也利用信号通知进程特定事件的发生。
- 进程:一个进程可以通过系统调用kill命令给另一个进程发送信号,进程之间可以通过信号通信。
- 由进程某个操作产生的信号被成为同步信号,例如,被零除。
- 由用户击键这样的外部事件产生的信号被成为异步信号。
哪里可以找到信号列表?信号编号以及它们的名字一般出现在/usr/include/signal.h中。
例如,中断信号被成为SIGINT,退出信号为SIGQUIT,非法段存取的信号是SIGSGEV。
信号的作用?视情况而定。很多信号杀死进程。某时刻进程还在运行,下一秒它就消亡了。从内存中被删除,相应的所有的文件描述符被关闭,并且从进程表中被删除。使用SIGINT可以消灭一个进程,但是进程也有办法保护自己不被杀死。
- 进程如何处理信号?
当进程接收到SIGINT时,并不一定要消亡,进程能够通过系统调用signal告诉内核,它要如何处理信号,进程有3个选择:
- 接收组织(内核)安排:通常是消亡
手册上会列出对每个信号的默认处理,SIGINT的默认处理是消亡,进程并不一定要使用默认处理,但是可以通过一下调用来恢复默认处理:
signal(SIGINT , SIG_DFL);
- 忽略信号(喂?你好?什么?我听不清!喂?我听不清!)
程序可以通过以下调用告诉内核它要忽略SIGINT信号
signal(SIG_INT , SIG_IGN);
- 调用一个函数
这当然是最灵活有用的一种咯,考虑play_again3的例子。当用户输入Ctrl-C的时候,当前程序立即退出而不调用恢复终端驱动设置恢复的函数。更好的做法时,程序在接收到SIGINT后,调用一个恢复设置的函数,然后再退出。
调用signal的第三种选择允许这种类型的响应。程序能够告诉内核,当信号到来时,应该调用哪个函数。在信号到来时调用的函数被成为信号处理函数(越看越觉得像是单片机编程中的中断处理函数)。为安装信号处理函数,程序调用:
signal( signum , functionname );
signal的返回值为-1时表示错误;而正常情况下,返回一个指向(前一个处理)函数的指针。
来看看信号处理的例子吧:
#include <stdio.h> #include <stdlib.h> #include <signal.h> void f(int) ; int main() { void f(int) ; int i ; signal(SIGINT , f) ; // Declare the handler for(i = 0 ; i < 5 ; i ++ ){ printf("Hello! " ); sleep(1) ; } return 0 ; } void f(int signum ) { printf("OUCH! "); }
很简单的例子,哈哈,下面一个例子会忽略Ctrl-C的信号,和书上不太一样,直接改上面的了:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <signal.h> 4 5 void f(int) ; 6 7 int main() 8 { 9 void f(int) ; 10 int i ; 11 12 signal(SIGINT , SIG_IGN) ; // Declare the handler 13 14 for(i = 0 ; i < 5 ; i ++ ){ 15 printf("Hello! " ); 16 sleep(1) ; 17 } 18 19 return 0 ; 20 } 21 22 void f(int signum ) 23 { 24 printf("OUCH! "); 25 }
在输出Hello!的过程中,Ctrl-C是不能产生中断的,而使用Ctrl-是可以的,因为这个程序没有忽略或者捕捉SIGQUIT。相当于屏蔽了一个中断,却没有屏蔽另外一个中断。
- 进程终止和为设备编程
程序使用signal来告诉内核它需要忽略哪些信号,但是,对于程序员而言,有两个信号是不能被忽略和捕捉的,书上让我们自己去找。。。
这个我就偷个懒,以后再找咯
- 为设备编程:
终端控制程序的三个方面:
- 驱动程序的属性和设置
- 以应用程序的特定需求来调整驱动程序,满足这些需求
- 处理信号报告的错误或者处理特定事件