sigaction函数
sigaction函数允许我们检查或修改(或两者)特定信号的动作。这个函数在早期的UNIX版本中代替了signal函数。在本节的最后,我们提供了一个使用sigaction的signal实现。
|
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, Figure 10.16 */
/* alternate handler */
void (*sa_sigaction)(int, siginfo_t *, void *);
};
|
当为信号改变动作时,如果sa_handler字段包含了信号处理函数(signal-catching function)的地址(不包含SIG_IGN或SIG_DFL),那么在信号处理函数(signal-catching function)被调用前sa_mask字段为进程指定了一套信号掩码。当且仅当信号处理函数返回时,进程的信号掩码重置成它之前的值。这种方法,我们能阻塞某种信号无论信号处理函数(signal handler)何时被调用。因此,我们可以保证无论何时进程收到信号时,其它相同信号都被阻塞,直到我们完成进程第一个收到的信号。回忆APUE10.8节,额外发生的相同信号通常不会排队。当我们未解锁信号时如果信号发生5次都被阻塞,为该信号设置的信号处理函数仅将会被调用一次。
一旦我们为某一信号“安装”了动作, 直到我们明显的通过sigaction改变它,它都会一直保留这个动作。
参数act结构的sa_flags字段为处理信号指定了多种选项。FIGURE10.16有这些选项的详细信息。SUS列中包含•表示这个标记是做为基于POSIX.1一部分定义的。包含XSI表示是基于XSI的。
|
当SA_SIGINFO标记被用于sigaction时,sa_sigaction字段是一个可选的信号处理函数。具体实现可能会使用相同的存储空间存放sa_sigaction字段和sa_handler字段,所以应用程序在同一时刻只能使用其中的一个。
一般来说,信号处理函数(signal handler)调用如下:
void handler(int signo);
|
但是如果SA_SIGINFO标记被设置,信号处理函数调用如下:
void handler(int signo, siginfo_t *info, void *context);
|
siginfo_t结构包含了关于为什么信号被产生的信息。下面有一个都能显示什么内容的例子。所有POSIX.1兼容实现必须至少包含si_signo和si_code成员。另外,XSI兼容的具体实现至少包含如下字段:
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 */
};
|
FIGURE 10.17显示了si_code对于各种信号的值,做为Single UNIX Specification。注意:具体实现可能定义了额外的值。
|
如果信号是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参数是无类型指针,它能被强制转换成ucontext_t结构,用于在信号传送时识别进程内容。
当一个系统支持实时信号扩展时,信号处理函数使用SA_SIGINFO标记建立将会让信号可靠的排队。一个单独保留的信号范围平均用于实时应用程序。如果信号是被sigqueue产生的,siginfo结构能包含应用程序指定数据。本书并不讨论实时扩展功能。
signal函数例子
现在让我们使用sigaction实现signal函数功能。很多平台都是这么做的。系统使用二进制兼容约束,换言之,可以让signal函数支持更老的、不可靠的语义。除非你指定要求更老的、不可靠的语义,不然你应该直接使用当前实现的signal或sigaction。(因为你可能猜到了,一个使用老语义的signal实现,可以调用sigaction函数指定SA_RESETHAND和SA_NODEFER选项实现。)本文中所说的signal都在figure10.18中实现。
注意,我们必须使用sigemptyset去初始化sa_mask结构的成员。使用act.sa_mask=0不能保证和使用sigemptyset具有相同效果。
我们有意的尝试对除了SIGALRM信号外的所有信号使用SA_RESTART标记,所以任何系统调用被中断后都会自动重新启动。不对SIGALRM信号使用SA_RESTART标记的原因是我们要为I/O操作设置超时。
一些老系统,就像SunOS,定义了SA_INTERRUPT标记。这些系统默认会重新启动被中断的系统调用,所以指定这个标记会导致系统调用被中断(我估计应该是不能重新启动了)。Linux也定义了这个标记,为了兼容使用该标记的程序,但是当使用sigaction安装信号处理函数时,默认是不重启系统调用。Single UNIX Specification的XSI扩展指定了sigaction函数不重启被中断的系统调用,除非SA_RESTART标记被指定。
Figure 10.18. An implementation of signal using sigaction#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);
}
|
signal_intr函数例子
Figure 10.19显示了一个signal函数的版本,它试图防止任何系统调用中断后重启。
为了改善移植性,我们指定了SA_INTERRUPT标记,如果由系统定义了,去防止被中断的系统调用自动重启。
Figure 10.19. The signal_intr function#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);
}
|