信号之sigaction函数

sigaction函数的功能是检查或修改与指定信号相关联的处理动作(或同时执行这两种操作)。

#include <signal.h>
int sigaction( int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
返回值:若成功则返回0,若出错则返回-1

其中,参数signo是要检测或修改其具体动作的信号编号。若act指针非空,则要修改其动作。如果oact指针非空,则系统经由oact指针返回该信号的上一个动作。此函数使用下列结构:

struct sigaction {
    void    (*sa_handler)(int);    /* addr of signal handler, or SIG_IGN, or SIG_DFL */
    sigset_t    sa_mask;           /* additional signals to block */
    int    sa_flags;               /* signal options */

    /* alternate handler */
    void    (*sa_sigaction)(int, siginfo_t *, void *);
};

当更改信号动作时,如果sa_handler字段包含一个信号捕捉函数的地址(与常量SIG_IGN或SIG_DFL相对),则sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。这样,在调用信号处理程序时就能阻塞某些信号。在信号处理程序被调用时,操作系统建立的新信号屏蔽字包括正被递送的信号。因此保证了在处理一个给定的信号时,如果这种信号再次发生,那么它会被阻塞到对前一个信号的处理结束为止。

一旦对给定的信号设置了一个动作,那么在调用sigaction显式地改变它之前,该设置就一直有效。(早期版本并非如此,而是:在进程每次接到信号对其进行处理时,随即将该信号动作复位为默认值。见http://www.cnblogs.com/nufangrensheng/p/3515035.html

act结构的sa_flags字段指定对信号进行处理的各个选项。

                                                        表10-5 处理每个信号的选项标志(sa_flags)

2012082515302617

sa_sigaction字段是一个替代的信号处理程序,当在sigaction结构中使用了SA_SIGINFO标志时,使用该信号处理程序。对于sa_sigaction字段和sa_handler字段这两者,其实现可能使用同一存储区,所以应用程序只能一次使用这两个字段中的一个。

通常,按下列方式调用信号处理程序:

void handler(int signo);

但是,如果设置了SA_SIGINFO标志,那么按下列方式调用信号处理程序:

void handler(int signo, siginfo_t *info, void *context);

siginfo_t结构包含了信号产生原因的有关信息。该结构的大致样式如下所示:

struct siginfo {
    int      si_signo;        /* signal number */
    int      si_errno;        /* if nonzero, errno value from <errno.h> */
    int      si_code;         /* additional info (depends on signal) */
    pid_t    si_pid;          /* sending process ID */
    uid_t    si_uid;          /* sending process real user ID */
    void    *si_addr;         /* address that caused the fault */
    int      si_status;       /* exit value or signal number */
    long     si_band;         /* band number for SIGPOLL */
    /* possibly other fields also */
};

 

各种信号的si_code值(包括上面的相关数据结构和标志选项),可通过man sigaction命令进行查看。

若信号是SIGCHLD,则将设置si_pid、si_status和si_uid字段。

若信号是SIGILL或SIGSEGV,则si_addr包含造成故障的根源地址,尽管该地址可能并不准确。

若信号是SIGPOLL,那么si_band字段将包含STREAMS消息的优先级(priority band),该消息产生POLL_IN、POLL_OUT或POLL_MSG事件。

si_errno字段包含错误编号,它对应于引发信号产生的条件,并由实现定义。

信号处理程序的context参数是无类型指针,它可被强制转换为ucntext_t结构类型,用于标识信号传递时进程的上下文。

 

 实例:signal函数

现在用sigaction实现signal函数。很多平台都是这样做的。

程序清单10-12 用sigaction实现signal函数

#include "apue.h"

/* Reliable version of signal(), using POSIX sigaction(). */
Sigfunc *
signal(int signo, Sigfunc *func)
{
    struct sigaction    act, oact;
    
    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(signo == SIGALRM)
    {
#ifdef    SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;
#endif
    }
    else
    {
#ifdef    SA_RESTART
        act.sa_flags |= SA_RESTART;
#endif
    }
    if(sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

注意,必须用sigemptyset函数初始化act结构的sa_mask成员。不能保证:act.sa_mask = 0;会做同样的事情。

对除SIGALRM以外的所有信号,我们都有尝试设置SA_RESTART标志,于是被这些信号中断的系统调用都能自动重启动。不希望重启动由SIGALRM信号中断的系统调用的原因是:我们希望对I/O操作可以设置时间限制。

实例:signal_intr函数

程序清单10-13是signal函数的另一种版本,它力图阻止任何被中断的系统调用重启动。

程序清单10-13 signal_intr函数

#include "apue.h"

Sigfunc *
signal_intr(int signo, Sigfunc *func)
{
    struct sigaction     act, oact;

    act.sa_handler = func;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
#ifdef    SA_INTERRUPT
    act.sa_flags |= SA_INTERRUPT;
#endif
    if(sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

 

如果系统定义了SA_INTERRUPT标志,那么为了提高可移植性,我们在sa_flags中增加该标志,这样也就阻止了被中断的系统调用重启动。

 

本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

posted @ 2014-01-12 14:43 ITtecman 阅读(...) 评论(...) 编辑 收藏