赞
踩
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息。
进程对信号的处理方式不同可分为三类:
第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。
第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。
第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分的信号的缺省操作是使得进程终止。进程通过系统调用signal来指定进程对某个信号的处理行为。
根据可靠性可将信号分为可靠信号和不可靠信号,与时间的关系又可将信号分为实时信号与非实时信号。
Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中的信号机制比较简单和原始,信号值小于SIGRTMIN的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是信号可能丢失。
随着时间的发展,实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已有许多应用,不好再做改动,最终只好又新增加了一些信号,并在一开始就把它们定义为可靠信号,这些信号支持排队,不会丢失。
信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号克服了信号可能丢失的问题。Linux在支持新版本的信号安装函数sigation()以及信号发送函数sigqueue()的同时,仍然支持早期的signal()信号安装函数,支持信号发送函数kill()。
信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。目前linux中的signal()是通过sigation()函数实现的,因此,即使通过signal()安装的信号,在信号处理函数的结尾也不必再调用一次信号安装函数。同时,由signal()安装的实时信号支持排队,同样不会丢失。
对于目前linux的两个信号安装函数:signal()及sigaction()来说,它们都不能把SIGRTMIN以前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于,经过sigaction安装的信号都能传递信息给信号处理函数,而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。
早期Unix系统只定义了32种信号,前32种信号已经有了预定义值,每个信号有了确定的用途及含义,并且每种信号都有各自的缺省动作。如按键盘的CTRL ^C时,会产生SIGINT信号,对该信号的默认反应就是进程终止。后32个信号表示实时信号,等同于前面阐述的可靠信号。这保证了发送的多个实时信号都被接收。
非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。
我们可以通过 kill -l 命令来查看当前系统所支持的信号,不同系统支持的信号是不相同的。
函数原型
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。
传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。
通常,我们把 kill 理解为杀死一个进程,其实这样的描述是不准确的,kill 其实是作为一个信号,由其中一个进程发给另一个进程,而发送的信号就是终止信号。
#include <stdio.h> #include <signal.h> #define print_words "Hello World" #define sleep_time 1 int main(int argc ,char **argv) { for( ; ; ) { printf("%s\n",print_words); sleep(sleep_time); } printf("I'll exit.\n"); return 0; }
编译后运行程序,将会每隔一秒打印"Hello World",当我们按下Ctrl + c 后,或者使用命令查看到进程号后使用 kill 终止程序,我们可以通过 signal() 函数来捕捉信号,让某些终止信号被忽略。程序如下:
#include <stdio.h> #include <signal.h> #define sleep_time 2 #define print_words "Hello World" void sig_handle(int signum) { printf("Catch signal [%d]\n",signum); } int main(int argc,char **argv) { signal(SIGINT,sig_handle); signal(SIGTERM,sig_handle); for( ; ; ) { printf("%s\n",print_words); sleep(sleep_time); } printf("I'll exit.\n"); return 0; }
编译运行程序
可以发现,信号被捕捉后程序将不会退出,但是,通过上面的常用信号表可以得知有一个信号是不可以被捕捉的!
观察程序,我们可以发现,程序最后的 " I’ll exit "并没有打印,这是因为程序在接收到终止信号后会立刻退出程序,而不会执行后面的内容,但很多情况下,我们希望在程序退出前做某些事情,例如关掉某些开关或者文件描述符,我们只需要对程序进行简单修改即可!
#include <stdio.h> #include <signal.h> int found = 0; #define sleep_time 2 #define print_words "Hello World" void sig_handle(int signum) { printf("Catch signal [%d]\n",signum); found = 1; } int main(int argc,char **argv) { signal(SIGINT,sig_handle); signal(SIGTERM,sig_handle); while(!found) { printf("%s\n",print_words); sleep(sleep_time); } printf("I'll exit.\n"); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。