信号参考文献 下载本文

? ITIMER_REAL: 设定绝对时间;经过指定的时间后,内核将发送SIGALRM信号给本进程;

? ITIMER_VIRTUAL 设定程式执行时间;经过指定的时间后,内核将发送SIGVTALRM信号给本进程; ? ITIMER_PROF 设定进程执行连同内核因本进程而消耗的时间和,经过指定的时间后,内核将发送ITIMER_VIRTUAL信号给本进程;

setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例,结构itimerval形式见附录1。第三个参数可不做处理。

setitimer()调用成功返回0,否则返回-1。

6、abort()

#include

void abort(void);

向进程发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即使SIGABORT被进程配置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。该函数无返回值。

信号安装

假如进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。 linux主要有两个函数实现信号的安装:signal()、sigaction()。其中signal()在可靠信号系统调用的基础上实现, 是库函数。他只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实现: sys_signal连同sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来和 sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。

1、signal()

#include <signal.h>

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 <signal.h>

int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,能够为除

SIGKILL及SIGSTOP外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,能够为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号 的处理,可指定oldact为NULL。假如把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。

第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。

sigaction结构定义如下:

struct sigaction {

union{

__sighandler_t _sa_handler;

void (*_sa_sigaction)(int,struct siginfo *, void *);

}_u

sigset_t sa_mask;

unsigned long sa_flags;

void (*sa_restorer)(void);

}

其中,sa_restorer,已过时,POSIX不支持他,不应再被使用。

1、联合数据结构中的两个元素_sa_handler连同*_sa_sigaction指定信号关联函数,即用户指定的信号处理函数。除了能够是用户自定义的处理函数外,还能够为SIG_DFL(采用缺省的处理方式),也能够为SIG_IGN(忽略信号)。

2、由_sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;由_sa_sigaction是 指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),他指定一个3参数信号处理函数。第一个参数为信号值,第三个参数没有使 用(posix没有规范使用该参数的标准),第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值,参数所指向的结构如下:

siginfo_t {

int si_signo; /* 信号值,对任何信号有意义*/

int si_errno; /* errno值,对任何信号有意义*/

int si_code; /* 信号产生的原因,对任何信号有意义*/

union{ /* 联合数据结构,不同成员适应不同信号 */

//确保分配足够大的存储空间

int _pad[SI_PAD_SIZE];

//对SIGKILL有意义的结构

struct{

...

}...

... ...

... ...

//对SIGILL, SIGFPE, SIGSEGV, SIGBUS有意义的结构

struct{

...

}...

... ...

}

}

注:为了更便于阅读,在说明问题时常把该结构表示为附录2所表示的形式。

siginfo_t结构中的联合数据成员确保该结构适应任何的信号,比如对于实时信号来说,则实际采用下面的结构形式:

typedef struct {

int si_signo;

int si_errno;

int si_code;

union sigval si_value;

} siginfo_t;

结构的第四个域同样为一个联合数据结构:

union sigval {

int sival_int;

void *sival_ptr;

}

采用联合数据结构,说明siginfo_t结构中的si_value要么持有一个4字节的整数值,要么持有一个指针,这就构成了和信号相关的数 据。在信号的处理函数中,包含这样的信号相关数据指针,但没有规定具体如何对这些数据进行操作,操作方法应该由程式研发人员根据具体任务事先约定。

前面在讨论系统调用sigqueue发送信号时,sigqueue的第三个参数就是sigval联合数据结构,当调用sigqueue时,该数 据结构中的数据就将拷贝到信号处理函数的第二个参数中。这样,在发送信号同时,就能够让信号传递一些附加信息。信号能够传递信息对程式研发是很有意义 的。

信号参数的传递过程可图示如下:

3、sa_mask指定在信号处理程式执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,除非指定SA_NODEFER或SA_NOMASK标志位。

注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。

4、sa_flags中包含了许多标志位,包括刚刚提到的SA_NODEFER及SA_NOMASK标志位。另一个比较重要的标志位是 SA_SIGINFO,当设定了该标志位时,表示信号附带的参数能够被传递到信号处理函数中,因此,应该为sigaction结构中的 sa_sigaction指定处理函数,而不应该为sa_handler指定信号处理函数,否则,配置该标志变得毫无意义。即使为 sa_sigaction指定了信号处理函数,假如不配置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信 息的访问都将导致段错误(Segmentation fault)。

注:很多文献在阐述该标志位时都认为,假如配置了该标志位,就必须定义三参数信号处理函数。实际不是这样的,验证方法很简单:自己实现一个单一 参数信号处理函数,并在程式中配置该标志位,能够察看程式的运行结果。实际上,能够把该标志位看成信号是否传递参数的开关,假如配置该位,则传递参数;否 则,不传递参数。

Linux信号阻塞和信号未决

1. 信号掩码——被阻塞的信号集

每个进程都有一个用来描述哪些信号传送来将被阻塞的信号集,如果某种信号在某个进程的阻塞信号集中,则传送到该进程的此种信号将会被阻塞。当前被进程阻塞的信号集也叫信号掩码,类型为sigset_t。每个进程都有自己 的信号掩码,且创建子进程时,子进程会继承父进程的信号掩码。

2. 信号阻塞和忽略的区别

阻塞的概念与忽略信号是不同的:操作系统在信号被进程解除阻塞之前不会将信号传递出去,被阻塞的信号也不会影响进程的行为,信号只是暂时被阻止传递;当进程忽略一个信号时,信号会被传递出去,但进程将信号丢弃。

3. 信号集的操作

信号集可以由以下几个函数操作:

int sigemptyset(sigset_t *set); //清空信号集

int sigfillset(sigset_t *set); //将所有信号填充进set中

int sigaddset(sigset_t *set, int signum); //往set中添加信号signum

int sigdelset(sigset_t *set, int signum); //从set中移除信号signum

int sigismember(const sigset_t *set, int signum); //判断signnum是不是包含在set中,在返回1,不在返回0

初始化往往可以用sigemptyset()将信号集清空,再用sigaddset()向信号集中添加信号;或者可以使用sigfillset()将所有信号添加到信号集,再用sigdelset()将某信号从中删除掉。

4. sigprocmask()介绍

可以使用函数sigprocmask()来检查或者修改进程的信号掩码。函数信息如下: