sigaction函数及信号集操作函数的使用

信号是与一定的进程相联系的。也就是说,一个进程可以决定在进程中对哪些信号进行什么样的处理。例如,一个进程可以忽略某些信号而只处理其他一些信号;另外,一个进程还可以选择如何处理信号。总之,这些总与特定的进程相联系的。因此,首先要建立其信号和进程的对应关系,这就是信号的安装登记。

Linux主要有两个函数实现信号的安装登记:signal和sigaction。其中signal在系统调用的基础上实现,是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32个非实时信号的安装;而sigaction是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与sigqueue系统调用配合使用。当然,sigaction同样支持非实时信号的安装,sigaction优于signal主要体现在支持信号带有参数。

对于应用程序自行处理的信号来说,信号的生命周期要经过信号的安装登记、信号集操作、信号的发送和信号的处理四个阶段。信号的安装登记指的是在应用程序中,安装对此信号的处理方法。信号集操作的作用是用于对指定的一个或多个信号进行信号屏蔽,此阶段对有些应用程序来说并不需要。信号的发送指的是发送信号,可以通过硬件(如在终端上按下Ctrl-C)发送的信号和软件(如通过kill函数)发送的信号。

信号的处理指的是操作系统对接收信号进程的处理,处理方法是先检查信号集操作函数是否对此信号进行屏蔽,如果没有屏蔽,操作系统将按信号安装函数中登记注册的处理函数完成对此进程的处理。

一、信号的种类

信号的名称是在头文件signal.h中定义的,信号都以SIG开头,常用的信号并不多,常用的信号如下:
SIGABRT      程序异常终止
SIGFPE         算术运算出错 如:除以0
SIGINT         中断信号 如:ctrl-C
SIGSEGV      非法访问不存在的内存单元
SIGTERM      发送给本程序的终止请求信号
SIGHUP        挂起信号
SIGQUIT       退出信号

二、signal()函数

在signal函数中,有两个形参,分别代表需要处理的信号编号值和处理信号函数的指针。它主要是用于前32种非实时信号的处理,不支持信号的传递信息。但是由于使用简单,易于理解,因此在许多场合被程序员使用。

对于Unix系统来说,使用signal函数时,自定义处理信号函数执行一次后失效,对该信号的处理回到默认处理方式。下面以一个例子进行说明,例如一程序中使用signal(SIGQUIT, my_func)函数调用,其中my_func是自定义函数。应用进程收到SIGQUIT信号时,会跳转到自定义处理信号函数my_func处执行,执行后信号注册函数my_func失效,对SIGQUIT信号的处理回到操作系统的默认处理方式,当应用进程再次收到SIGQUIT信号时,会按操作系统默认的处理方式进行处理(即不再执行my_func处理函数)。而在Linux系统中,signal函数已被改写,由sigaction函数封装实现,则不存在上述问题。

函数头文件:

#include <signal.h>
函数原型:
void (*signal(int signum,void(* handler)(int)))(int)
函数说明:设置信号处理方式。signal()会依参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行

函数参数:

signum是指定信号的编号,handler是自定义信号函数处理指针

注意如果参数handler是SIG_IGN的话表示忽略参数signum指定的信号,如果是SIG_DFL则是将参数signum指定的信号重设为核心预设的信号处理方式,即采用系统默认方式处理信号

函数返回值:成功返回先前的信号处理函数指针,出错则返回SIG_ERR(-1)

三、sigaction函数

sigaction函数用来查询和设置信号处理方式,它是用来替换早期的signal函数。sigaction函数原型及说明如下:
函数头文件:

#include <signal.h>
函数原型:
int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact)
函数说明:sigaction()会依参数signum指定的信号编号来设置该信号的处理函数
函数参数:

signum是指定信号的编号,除SIGKILL和SIGSTOP信号以外

act参数如下:

参数结构sigaction定义如下

struct sigaction{
void (*sa_handler) (int);
void  (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer) (void);
}
①    sa_handler:此参数和signal()的参数handler相同,此参数主要用来对信号旧的安装函数signal()处理形式的支持
②    sa_sigaction:新的信号安装机制,处理函数被调用的时候,不但可以得到信号编号,而且可以获悉被调用的原因以及产生问题的上下文的相关信息。
③    sa_mask:用来设置在处理该信号时暂时将sa_mask指定的信号搁置
④    sa_restorer: 此参数没有使用
⑤    sa_flags:用来设置信号处理的其他相关操作,下列的数值可用,可用OR 运算(|)组合:
   A_NOCLDSTOP:如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程
   SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式
   SA_RESTART:被信号中断的系统调用会自行重启
   SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来
   SA_SIGINFO:信号处理函数是带有三个参数的sa_sigaction

oldact参数:如果参数oldact不是NULL指针,则原来的信号处理方式会由此结构sigaction返回


函数返回值:成功返回0,出错则返回-1,错误原因存于error中

四.   信号集操作函数

由于有时需要把多个信号当作一个集合进行处理,这样信号集就产生了,信号集用来描述一类信号的集合,Linux所支持的信号可以全部或部分的出现在信号集中。信号集操作函数最常用的地方就是用于信号屏蔽。比如有时候希望某个进程正确执行,而不想进程受到一些信号的影响,此时就需要用到信号集操作函数完成对这些信号的屏蔽。

 信号集操作函数按照功能和使用顺序分为三类,分别为创建信号集函数,设置信号屏蔽位函数和查询被搁置(未决)的信号函数。创建信号集函数只是创建一个信号的集合,设置信号屏蔽位函数对指定信号集中的信号进行屏蔽,查询被搁置的信号函数是用来查询当前“未决”的信号集。信号集函数组并不能完成信号的安装登记工作,信号的安装登记需要通过sigaction函数或signal函数来完成。

查询被搁置的信号是信号处理的后续步骤,但不是必需的。由于有时进程在某时间段内要求阻塞一些信号,程序完成特定工作后解除对该信号阻塞,这个时间段内被阻塞的信号称为“未决”信号。这些信号已经产生,但没有被处理,sigpending函数用来检测进程的这些“未决”信号,并进一步决定对它们做何种处理(包括不处理)。

1. 创建信号集函数

创建信号集函数有如下5个:
①    sigemptyset:初始化信号集合为空。
②    sigfillset:把所有信号加入到集合中,信号集中将包含Linux支持的64种信号。
③    sigaddset:将指定信号加入到信号集合中去。
④    sigdelset:将指定信号从信号集中删去。
⑤    sigismember:查询指定信号是否在信号集合之中。


2.设置信号屏蔽位函数

每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。调用函数sigprocmask可设定信号集内的信号阻塞或不阻塞。其函数原型及说明如下:

3. 查询被搁置(未决)信号函数

sigpending函数用来查询“未决”信号。其函数原型及说明如下:


4. 对信号集操作函数的使用方法

 对信号集操作函数的使用方法和顺序如下:
①      使用signal或sigaction函数安装和登记信号的处理。
②      使用sigemptyset等定义信号集函数完成对信号集的定义。
③      使用sigprocmask函数设置信号屏蔽位。
④      使用sigpending函数检测未决信号,非必需步骤。



参考文献:

[1]. https://blog.csdn.net/guoping16/article/details/6583957

[2]. http://www.cnblogs.com/52php/p/5813867.html



猜你喜欢

转载自blog.csdn.net/yzy1103203312/article/details/79799197
今日推荐