信号(signal)是一种软件中断,它提供了一种处理异步事件的方法,也是进程间惟一的异步通信方式。在
Linux系统中,根据POSIX标准扩展以后的信号机制,不仅可以用来通知某种程序发生了什么事件,还可以给
进程传递数据。
一、信号的来源
信号的来源可以有很多种试,按照产生条件的不同可以分为硬件和软件两种。
1、 硬件方式
当用户在终端上按下某键时,将产生信号。如按下组合键后将产生一个SIGINT信号。
硬件异常产生信号:除数据、无效的存储访问等。这些事件通常由硬件(如:CPU)检测到,并将其通知给Linux
操作系统内核,然后内核生成相应的信号,并把信号发送给该事件发生时正在进行的程序。
2、 软件方式
用户在终端下调用kill命令向进程发送任务信号。
进程调用kill或sigqueue函数发送信号。
当检测到某种软件条件已经具备时发出信号,如由alarm或settimer设置的定时器超时时将生成SIGALRM信号。
1、可靠信号与不可靠信号
在Linux系统中,信号的可靠性是指信号是否会丢失,或者说该信号是否支持排除。SIGHUP( 1 ) ~
SIGRTMIN(33) ~ SIGRTMAX(64)之间的信号,它们都是可靠信号,也称为实时信号。
内核设置了这个标志,我们就说内核向一个进程递送了一个信号。信号产生(generate)和递送(delivery)之间
的时间间隔,称主信号未决(pending)。
进程可以调用sigpending将信号设为阻塞,如果为进程产生一个阻塞信号,而对信号的动作是捕捉该信号(即不忽
略信号), 则内核将为该进程的此信号保持为未决状态,直到该进程对此信号解除阻塞或者对此信号的响应
更改为忽略。如果在进程解除对某个信号的阻塞之前,这种信号发生 了多次,那么如果信号被递送多次(即
信号在未决信号队列里面排队),则称之为可靠信号;只被递送一次的信号称为不可靠信号。
2、信号的优先级
信号实质上是软中断,中断有优先级,信号也有优先级。如果一个进程有多个未决信号,则对于同一个未决的
实时信号,内核将按照发送的顺序来递送信号。如果存在多个未决信号,则值(或者说编号)越小的越先被递送。
如果即存在不可靠信号,又存在可靠信号(实时信号),虽然POSIX对这一情况没有明确规定,但Linux系统
和大多数遵循POSIX标准的操作系统一样,将优先递送不可靠信号。
三、进程对信号的响应
当信号发生时,用户可以要求进程以下列3种方式之一对信号做出响应。
1、 捕捉信号:对于要捕捉的信号,可以为其指定信号处理函数,信号发生时该函数自动被调用,在该函数内部实
现对该信号的处理。
2、 忽略信号:大多数信号都可使用这种方式进行处理,但是SIGKILL和SIGSTOP这两个信号不能被忽略,同时
这两个信号也不能被捕获和阻塞。此外,如果忽略某某些由硬件异常产生的信号(如非法存储访问或除以0),
则进程的行为是不可预测的。
3、 按照系统默认方式处理。大部分信号的默认操作是终止进程,且所有的实时信号的默认动作都是终止进程。
四、各种信号的默认处理情况
程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
不能恢复至默认动作的信号有:SIGILL,SIGTRAP
默认会导致进程流产的信号有:SIGABRT、SIGBUS、SIGFPE、SIGILL、SIGIOT、SIGQUIT、SIGSEGV、
SIGTRAP、SIGXCPU、SIGXFSZ
默认会导致进程退出的信号有:SIGALRM、SIGHUP、SIGINT、SIGKILL、SIGPIPE、SIGPOLL、SIGPROF、
SIGSYS、SIGTERM、SIGUSR1、SIGUSR2、SIGVTALRM
默认会导致进程停止的信号有:SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU
默认进程忽略的信号有:SIGCHLD、SIGPWR、SIGURG、SIGWINCH
五、信号处理函数与相关结构
1.信号安装
1.signal()
include
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而调用signal()时的handler值;失败则返回
SIG_ERR。
2.sigaction()
#include
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为 信号的值,可以为除SIGKILL及
SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二
个参数是指 向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可
以为空,进程会以缺省方式对信号处理;第三个 参数oldact指向的对象用来保存原来对相应信号的处理,可
指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查 信号的有效性。
2、发送信号函数
(1) int raise(int sig); 对当前进程发送指定信号
(2) int pause(void); 将进程挂起等待信号
(3) int kill(pid_t pid,int sig); 通过进程编号发送信号
(4) unsigned int alarm(unsigned int seconds); 指定时间(秒)发送SIGALRM信号。
seconds 为0 时取消所有已设置的alarm请求;
(5)int sigqueue(pid_t pid,int sig,const union sigval val);类似于kill函数,多了附
带共用体 union sigval形数,将共用体中的成员 int sival_int 或 void *sival_ptr 的值传递给 信
号处理函数中
的定义类型 siginfo_t 中的 int si_int 或 void *si_ptr;
(6)int setitimer(int which,const struct itimerval *value,struct itimerval
*oldvalue); 可定时发送信号,根据which可指定三种信号类型:SIGALRM、SIGVTALRM 和 SIGPROF;作
用
时间也因which值不同而不同;struct itimerval 的成员 it_interval定义间隔时间,it_value 为0
时,使计时器
失效;
(7) void abort(void) 将造成进程终止;除非捕获SIGABORT信号;
3、信号集及信号集操作
sigfillset(sigset_t *set); 设置所有的信号到set信号集中;
sigemptyset(sigset_t *set); 从set信号集中清空所有信号;
sigaddset(sigset_t *set,int sig);在set信号集中加入sig信号;
sigdelset(sigset_t *set,int sig);在set信号集中删除sig信号;
4、阻塞信号相关函数
int sigprocmask(int how,const sigset_t *set,sigset_t *set); 根据how
值,设置阻塞信号集,或释放阻塞的信号集
int sigpending(sigset_t *set); 获取在阻塞中的所有信号;
int sigsuspend(const sigset_t *set); 类似于 pause()函数!