一个信号是一条小消息,它通知系统进程中发生了一个某种类型的事件,提供了一种处理异步事件的方法。
每一种信号都有一个名字,在头文件<signal.h>中,信号名都被定义为正整数常量。1~31号实时普通信号和34~64号实时信号
每种信号类型都对应某种系统事件,低层的硬件异常是由内核异常处理程序处理的,正常情况下,对用户进程是不可见的。信号提供一种机制,通知用户进程发生了这些异常。
那么信号是如何产生的?
1.用户按某些终端键时,引起终端发生的信号。比如:在终端按delete(或ctrl+c)通常产生中断信号。
2.硬件异常产生信号。比如:除0,无效的内存引用等。
3.进程调用kill函数可将任意发送给另一个进程或进程组。
4.当检测到某种软件条件条件已发生,并应将其通知有关进程时也产生信号。
信号是异步事件的经典实例。产生信号的事件对进程而言是随机出现的。进程不能简单的测试一个变量来判断是否发生了一个信号,而是必须告诉内核“在此信号发生时,请执行下列操作”。
当某个信号出现时,可以告诉内核按照下列3种方式之一进行处理。
1.忽略此信号(9和19号不能被忽略)
2.捕捉此信号,执行自定义工作。(9和19不能被捕捉,它们是管理员的信号)
3.执行系统默认动作。大多数信号的系统默认动作是终止该进程。
捕捉信号:
如果信号的处理动作是用户自定义函数,在信号抵达时就调用这个函数,这称为信号捕捉。
下图给出了信号处理程序捕捉信号的基本思想:
接收信号:
当目的进程被内核强迫以某种方式对信号的发送做出反应时,它就接收了信号。进程可以忽略这个信号,也可以通过执行一个称为“信号处理程序”的用户层函数来捕捉这个信号。
一个发出而没有被接受的信号叫做待处理信号。在任何时候,一种信号都只有一个待处理信号。如果一个进程有一个类型为K的待处理信号,那么任何接下来发送到这个进程的类型为K的信号都不会排队,它们会被简单的抛弃,知道进程取消对这种信号的阻塞。
那么,系统是如何控制一个处理信号只被接受一次的呢? 内核为每个进程在pending位向量中维护着一个待处理信号的集合,而在blocked位向量中维护者被阻塞的信号集合。只要传送了一个类型为K的信号,内核就会清楚pending的第K位。
阻塞信号:
进程可以选择阻塞某个信号。被阻塞的信号发生时将保持在未决状态,知道进程解除对此信号的阻塞,才执行抵达的动作。当信号被阻塞时,它仍可以被发送,但是产生的待处理信号不会被接受。
linux提供两种阻塞信号的机制:(1)隐式阻塞机制。内核默认阻塞任何当前处理程序正在处理信号类型的待处理的信号。(2)应用程序可以使用sigpromask函数和它的辅助函数,明确的阻塞和解除阻塞选定的信号。
下面,我用信号的几个接口函数写了一个mysleep模拟实现sleep的功能。
1 #include<stdio.h>
2 #include<signal.h>
3 #include<unistd.h>
4
5 void sig_alrm(int signo)
6 {
7 //do nothing
8 }
9
10
11 unsigned int mysleep(unsigned int nsecs)
12 {
13 struct sigaction new,old;
14 unsigned int unslept = 0;
15 new.sa_handler = sig_alrm;
16 sigemptyset(&new.sa_mask);
17 new.sa_flags = 0;
18 sigaction(SIGALRM,&new,&old);
19 alarm(nsecs);
20 pause();
21 unslept = alarm(0);
22 sigaction(SIGALRM,&old,NULL);
23 return unslept;
24 }
25
26
27