信号

信号的一些概念

信号是软件中断,它给我们提供了一种异步事件处理的方法。

每个信号都有一个名字,都以SIG开头。信号都被定义为正整数,定义在<signal.h>。不存在编号为0的信号,因为kill函数对编号为0有特殊应用。

很多条件下都可以产生信号:

1、用户按下某些终端键时,引发终端产生的信号。例如Ctrl+C产生中断信号。

2、硬件异常产生信号:除数为0、无效内存的引用等。这些条件通常由硬件检测,并将其通知内核。

3、进程调用kill(2)函数可将信号发送给另一个进程或进程组。对此有所限制:接收信号进程和发送信号进程的所有者必须相同(不能夸用户发送信号)或者发送信号的进程的所有者必须是超级用户(root拥有最大权限)。

4、用户可以用kill(1)命令将信号发送给其他进程。此命令只是函数kill的接口。

5、当检测到某种软件条件已经发生,并应将其通知有关进程时产生信号。这里指软件中断,例如SIGURG是在网络连接上传来额外数据时产生、SIGPIPE在管道的读进程终止后,写进程写管道时产生、AIGALRM闹钟超时是产生。

信号是异步事件的经典实例。产生信号对进程而言是随机出现的。进程不能简单的测试一个变量(例如errno)来判断是否出现了一个信号,而是必须告诉内核“在此信号出现时,请执行下列操作”。


可以按照内核要求,在信号出现时,按照下列三种方式之一进行处理

1、忽略此信号。大多数信号都被忽略处理,但是信号SIGKILL和SIGSTOP不能被忽略,因为它们向超级用户提供了进程终止和停止的可靠方法。

2、捕捉信号。为了做到这一点,要通知内核在某种信号发生时调用一个用户函数。在此函数中,执行用户希望对这种事件的处理。

3、执行系统默认动作。



signal函数

signal函数是上述捕捉信号时,将信号和用户函数关联起来的方法。其原型如下:

#include<signal.h>

void  (*signal (int signo, void (*func) (int))) (int);

看起来有点复杂。先解释一下,这个函数有两个参数,一个是int signo,代表信号的名字,另一个是 void (*func) (int),是一个函数指针,这个函数返回值为void,有个int类型的参数,这个函数就信号处理程序。最后面那个(int)和signal前面那个*代表signal函数的返回值是一个函数指针,回值是个函数指针,它有一个int类型参数。

这个函数原型看起来有些复杂,可以这样用:

typedef void  Sigfunc(int);

将signal函数原型写成:

Sigfunc  *signal(int, Sigfunc*);

在<signal.h>中,很可能会找到下列形式声明:

#define  SIG_ERR   (void  (*)())  -1

#define  SIG_DEL   (void  (*)())  0


#define  SIG_IGN   (void  (*)())    1


这些常量可用于代替“指向函数的指针,该函数需要一个整型参数,且无返回值“。它们可以表示signal函数的第二个参数可其返回值。

当一个程序刚刚启动时,所有信号状态都是系统默认或忽略。通常信号都被设置为它们的默认动作,除非调用exec函数忽略该信号。exec调用后,对于设置捕捉的信号都会失效,变为系统默认动作。因为调用exec后,加载了新程序,原来的信号处理函数已经失效。

在创建子进程后,子进程继承了父进程的信号处理方式。



不可靠的信号

不可靠的信号指的是,信号可能会丢失:一个信号发生了,但进程可能一直不知道这一点。同时,进程对信号的控制能力也很差。

在早起的系统中,一个问题就是在进程每次接到信号对其进行处理时,随即该信号动作复位为默认值。这可能导致对信号的错误处理,一个避免的方法就是在信号处理函数中马上再次安装该信号处理函数:

int sig_int();//信号处理函数

……

signal(SIGINT, sig_int);//安装信号处理函数

……

sig_int()

{

  signal(SIGINT, sig_int);//再次安装安装信号处理函数,因为这时系统已经将信号动作复位为默认值

……

}

还有一种情况就是进程不希望某种信号发生,它不能关闭信号,但是可以忽略此信号。



可重入函数

进程捕捉到信号并对其进行处理时,进程正在执行的指令就会被打断,也就是当前正在执行的函数就会被打断。可重入函数指的是在函数执行时的任何时段都可以中断它,去执行另外一段代码,而返回控制时不会出现什么差错。有些函数是不可重入函数,原因是:1、它们使用了静态数据结构。2、它们调用malloc或free。3、它们是标准I/O函数。标准I/O库很多实现都以不可重入方式使用了全局数据结构。

可重入函数还有一点要注意的是保存回复errno变量的值。



可靠信号的术语和语义

当引发信号的事件产生时,为进程产生了一个信号。在产生信号时,内核通常在进程表中设置一个某种形式的标志。当对信号采取了这种动作时,我们说向进程递送了一个信号。在信号产生和递送之间的时间间隔内,称信号是未决的。

进程可以选用递送信号阻塞。每个进程都有一个信号屏蔽字,它规定了当前要阻塞到该进程的信号集。对于每种可能的信号,该屏蔽字都有一位与之对应。如果进程产生了一个选择为阻塞的信号,而且对该信号的动作时系统默认或捕捉该信号,则为该进程将此信号保持为未决状态,直到1、该进程对此信号解除了阻塞,或者2、该进程将此信号的动作改为忽略。



kill函数和raise函数

kill函数将信号发送给进程或进程组。raise函数允许向进程自身发送信号。

#include<signal>

int kill(pid_t pid, int signo);

int raise(in signo);

调用raise(signo)等价于kill(getpid(), signo)。

kill的pid参数分4种情况

1、pid>0。将信号发送给进程ID为pid的进程。

2、pid=0。将信号发送给属于同一组的所有进程。

3、pid<0。将信号发送给进程组ID等于pid绝对值的进程组。

4、pid=-1。将信号发送给该进程具有发送权限的所有进程。



alrm函数和pause函数

alarm函数可以设置一个定时器,当超时时产生SIGALRM信号。

#include<unistd.h>

unsigned alarm(unsigned int seconds);

每个进程只能设置一个闹钟。如果第二次设置,那么第一次设置值会作废。如果seconds=0则取消当前设置。

pause函数使进程挂起直到捕捉到一个信号。

#include<unistd.h>

int pause(void);



信号集

信号集是多个信号的集合,在一些函数中使用,便于告诉内核不允许发生该信号集中的信号。



abort函数

此函数功能是使异常进程程序终止。

#include<stdlib.h>

void abort(void);

此函数将信号SIGABRT发送给调用进程。



sleep函数

#include<unistd.h>

unsigned int sleep(unsigned int seconds);

此函数调用进程被挂起,直到满足下列条件之一:

1、已经经过了seconds秒所指定的墙上时钟。

2、调用进程捕捉到一个信号并从信号处理函数返回。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值