sigaction函数的功能是检查或修改(或检查并修改)与指定sigaction函数的功能是检查或修改(或检查并修改)与指定sigaction函数的功能是检查或修改(或检查并修改)与指定信号相关联的处理动作。此函数取代了UNIX早期版本使用的signal函数。在本节末尾用sigaction函数实现了signal。

#include <signal.h>
int sigaction(int signo, const struct sigaction *restrictact,
    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 signalsto block */
   int   sa_flags;        /* signal options, Fig-ure 10.16 */
                               /* alternate handler */
   void  (*sa_sigaction)(int, siginfo_t *, void *);
};

当更改信号动作时,如果 sa_handler 字段包含一个信号捕捉函数的地址(不是常量SIG_IGN或SIG_DFL),则sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加到进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字恢复为原先值。这样,在调用信号处理程序时就能阻塞某些信号。在信号处理程序被调用时,操作系统建立的新信号屏蔽字包括正被递送的信号。因此保证了在处理一个给定的信号时,如果这种信号再次发生,那么它会被阻塞到对前一个信号的处理结束为止。回忆10.8节,若同一种信号多次发生,通常并不将它们加入队列,所以如果在某种信号被阻塞时,它发生了5次,那么对这种信号解除阻塞后,其信号处理函数通常只会被调用一次(上一个例子已经说明了这种特性)。

一旦对给定的信号设置了一个动作,那么在调用sigaction显式地改变它之前,该设置就一直有效。这种处理方式与早期的不可靠信号机制不同,符合POSIX.1在这方面的要求。

act结构的sa_flags字段指定对信号进行处理的各个选项。图10-16详细列出了这些选项的意义。若该标志已定义在基本POSIX.1 标准中,那么 SUS 列包含“•”;若该标志定义在基本POSIX.1标准的XSI扩展中,那么该列包含“XSI”。

图10-16 处理每个信号的可选标志(sa_flags)

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结构包含了信号产生原因的有关信息。该结构的大致样式如下所示。符合POSIX.1的所有实现必须至少包括si_signosi_code成员。另外,符合XSI的实现至少应包含下列字段:

struct siginfo {
    int      si_signo; /* signal number */
    int      si_errno; /* if nonzero, errno valuefrom <errno.h> */
    int      si_code;  /* additional info (dependson signal) */
    pid_t     si_pid;   /* sending process ID */
    uid_t     si_uid;   /* sending process real userID */
    void    *si_addr;  /* address that caused thefault */
    int      si_status; /* exit value or signal num-ber */
    union sigval si_value; /* application-specific value*/
    /* possibly other fields also */
};

sigval联合包含下列字段:

int sival_int;
void *sival_ptr;

应用程序在递送信号时,在si_value.sival_int中传递一个整型数或者在si_value.sival_ptr中传递一个指针值。

图10-17示出了对于各种信号的si_code值,这些信号是由Single UNIX Specification定义的。注意,实现可定义附加的代码值。

若信号是SIGCHLD,则将设置si_pidsi_statussi_uid字段。若信号是SIGBUSSIGILLSIGFPESIGSEGV,则si_addr包含造成故障的根源地址,该地址可能并不准确。si_errno字段包含错误编号,它对应于造成信号产生的条件,并由实现定义。

信号处理程序的context参数是无类型指针,它可被强制类型转换为ucontext_t结构类型,该结构标识信号传递时进程的上下文。该结构至少包含下列字段:

ucontext_t *uc_link;    /* pointer to context resumed when this context returns */
sigset_t  uc_sigmask;  /* signals blocked when this context 
stack_t   uc_stack;   /* stack used by this context is active */
mcontext_t uc_mcontext;  /* machine-specific repre-sentation of saved context */

uc_stack字段描述了当前上下文使用的栈,至少包括下列成员:

void*ss_sp;            /* stack base or pointer */
size_t ss_size;      /* stack size */int  
ss_flags;      /* flags */

当实现支持实时信号扩展时,用SA_SIGINFO标志建立的信号处理程序将造成信号可靠地排队。一些保留信号可由实时应用使用。如果信号由sigqueue函数产生,那么siginfo结构能包含应用特有的数据(参见10.20节)。

实例:signal函数

现在用sigaction实现signal函数。很多平台都是这样做的(POSIX.1的基础阐述部分也说明这是POSIX所希望的)。另一方面,有些系统支持老的不可靠信号语义signal函数,其目的是实现二进制向后兼容。除非特殊地要求老的不可靠语义(为了向后兼容),否则应当使用下面的 signal 实现,或者直接调用sigaction(可以在调用 sigaction 时指定SA_RESETHANDSA_NODEFER选项以实现老语义的signal函数)。本书中所有调用signal的实例均调用图10-18中实现的函数。

图10-17 siginfo_t代码值

#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 {
        act.sa_flags |= SA_RESTART;
    }
    if (sigaction(signo, &act, &oact) < 0)
        return(SIG_ERR);
    return(oact.sa_handler);
}

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

AMY ?: 对除SIGALRM以外的所有信号,我们都有意尝试设置SA_RESTART标志,于是被这些信号中断的系统调用都能自动重启动。不希望重启动由 SIGALRM 信号中断的系统调用的原因是:我们希望对I/O操作可以设置时间限制(请回忆有关图10-10的讨论)。

某些早期系统(如SunOS)定义了SA_INTERRUPT标志。这些系统的默认方式是重新启动被中断的系统调用,而指定此标志则使系统调用被中断后不再重启动。Linux定义SA_INTERRUPT标志,以便与使用该标志的应用程序兼容。但是,如若信号处理程序是用sigaction设置的,那么其默认方式是不重新启动系统调用。Single UNIX Specification的XSI扩展规定,除非说明了SA_RESTART标志,否则sigaction函数不再重启动被中断的系统调用。

实例:signal_intr函数

图10-19给出的是signal函数的另一种版本,它力图阻止被中断的系统调用重启动。

#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中增加该标志,这样也就阻止了被中断的系统调用的重启动。

results matching ""

    No results matching ""