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_handle
r 字段包含一个信号捕捉函数的地址(不是常量SIG_IG
N或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_signo
和si_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_pid
、si_status
和si_uid
字段。若信号是SIGBUS
、SIGILL
、SIGFPE
或SIGSEGV
,则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_RESETHAND
和SA_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
中增加该标志,这样也就阻止了被中断的系统调用的重启动。