sigaction(2) - Linux 手册页
名称
sigaction - 检查并更改信号动作
概要
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);glibc 的功能测试宏要求(参见 feature_test_macros(7))
- sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
siginfo_t: _POSIX_C_SOURCE >= 199309L
描述
sigaction() 系统调用用于更改进程在接收到特定信号时所采取的动作。(关于信号的概述,请参见 signal(7)。)
signum 指定信号,可以是除了 SIGKILL 和 SIGSTOP 之外的任何有效信号。
如果 act 不为 NULL,则信号 signum 的新动作将根据 act 进行安装。如果 oldact 不为 NULL,则先前的动作将保存在 oldact 中。
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 和 sa_sigaction 进行赋值。
sa_restorer 元素已过时,不应使用。POSIX 未指定 sa_restorer 元素。
sa_handler 指定与 signum 相关联的动作,可以是 SIG_DFL(默认动作)、SIG_IGN(忽略此信号)或指向信号处理函数的指针。此函数接收信号编号作为其唯一参数。
如果 sa_flags 中指定了 SA_SIGINFO,则 sa_sigaction(而不是 sa_handler)指定 signum 的信号处理函数。此函数接收信号编号作为其第一个参数,指向 siginfo_t 的指针作为其第二个参数,以及指向 ucontext_t(强制转换为 void *)的指针作为其第三个参数。(通常,处理函数不会使用第三个参数。关于 ucontext_t 的更多信息,请参见 getcontext(3)。)
sa_mask 指定了一个信号掩码,在该信号处理函数执行期间,这些信号应被阻塞(即,添加到调用信号处理函数的线程的信号掩码中)。此外,除非使用了 SA_NODEFER 标志,否则触发该处理函数的信号本身也会被阻塞。
sa_flags 指定了一组修改信号行为的标志。它由零个或多个以下标志按位或而成
- SA_NOCLDSTOP
- 如果 signum 是 SIGCHLD,则在子进程停止(即,接收到 SIGSTOP、SIGTSTP、SIGTTIN 或 SIGTTOU 之一)或恢复(即,接收到 SIGCONT)时,不接收通知(参见 wait(2))。此标志仅在为 SIGCHLD 建立处理程序时有意义。
- SA_NOCLDWAIT (自 Linux 2.6 起)
- 如果 signum 是 SIGCHLD,则在子进程终止时不要将其变为僵尸进程。另请参见 waitpid(2)。此标志仅在为 SIGCHLD 建立处理程序,或将该信号的配置设置为 SIG_DFL 时有意义。
如果在为 SIGCHLD 建立处理程序时设置了 SA_NOCLDWAIT 标志,POSIX.1 未明确规定在子进程终止时是否生成 SIGCHLD 信号。在 Linux 上,此时会生成 SIGCHLD 信号;而在某些其他实现中则不会。
- SA_NODEFER
- 不要阻止在信号处理程序自身内部接收该信号。此标志仅在建立信号处理程序时有意义。SA_NOMASK 是此标志的一个过时的、非标准的同义词。
- SA_ONSTACK
- 在由 sigaltstack(2) 提供的备用信号栈上调用信号处理程序。如果没有可用的备用栈,将使用默认栈。此标志仅在建立信号处理程序时有意义。
- SA_RESETHAND
- 进入信号处理程序时将信号动作恢复为默认值。此标志仅在建立信号处理程序时有意义。SA_ONESHOT 是此标志的一个过时的、非标准的同义词。
- SA_RESTART
- 通过使某些系统调用在信号中断后可重启,从而提供与 BSD 信号语义兼容的行为。此标志仅在建立信号处理程序时有意义。有关系统调用重启的讨论,请参见 signal(7)。
- SA_SIGINFO (自 Linux 2.2 起)
- 信号处理程序接收三个参数,而不是一个。在这种情况下,应设置 sa_sigaction 而不是 sa_handler。此标志仅在建立信号处理程序时有意义。
- 传递给 sa_sigaction 的 siginfo_t 参数是一个包含以下元素的结构体
-
siginfo_t { int si_signo; /* Signal number */ int si_errno; /* An errno value */ int si_code; /* Signal code */ int si_trapno; /* Trap number that caused hardware-generated signal (unused on most architectures) */ pid_t si_pid; /* Sending process ID */ uid_t si_uid; /* Real user ID of sending process */ int si_status; /* Exit value or signal */ clock_t si_utime; /* User time consumed */ clock_t si_stime; /* System time consumed */ sigval_t si_value; /* Signal value */ int si_int; /* POSIX.1b signal */ void *si_ptr; /* POSIX.1b signal */ int si_overrun; /* Timer overrun count; POSIX.1b timers */ int si_timerid; /* Timer ID; POSIX.1b timers */ void *si_addr; /* Memory location which caused fault */ long si_band; /* Band event (was int in glibc 2.3.2 and earlier) */ int si_fd; /* File descriptor */ short si_addr_lsb; /* Least significant bit of address (since kernel 2.6.32) */ } - si_signo、si_errno 和 si_code 对所有信号都有定义。(si_errno 在 Linux 上通常未使用。)结构体的其余部分可能是一个联合体,因此只应读取对给定信号有意义的字段。
- *
使用 kill(2) 和 sigqueue(3) 发送的信号会填充 si_pid 和 si_uid。此外,使用 sigqueue(3) 发送的信号会使用信号发送者指定的值填充 si_int 和 si_ptr;有关更多详细信息,请参见 sigqueue(3)。
*
由 POSIX.1b 定时器发送的信号(自 Linux 2.6 起)会填充 si_overrun 和 si_timerid。si_timerid 字段是内核用于标识定时器的内部 ID;它与 timer_create(2) 返回的定时器 ID 不同。si_overrun 字段是定时器超时计数;这与通过调用 timer_getoverrun(2) 获取的信息相同。这些字段是非标准的 Linux 扩展。
*
为消息队列通知发送的信号(参见 mq_notify(3) 中 SIGEV_SIGNAL 的描述)会填充 si_int/si_ptr,其中包含提供给 mq_notify(3) 的 sigev_value;si_pid 包含消息发送者的进程 ID;si_uid 包含消息发送者的实际用户 ID。
*
SIGCHLD 填充 si_pid、si_uid、si_status、si_utime 和 si_stime,提供关于子进程的信息。si_pid 字段是子进程的进程 ID;si_uid 是子进程的实际用户 ID。si_status 字段包含子进程的退出状态(如果 si_code 为 CLD_EXITED),或者导致进程状态改变的信号编号。si_utime 和 si_stime 包含子进程使用的用户 CPU 时间和系统 CPU 时间;这些字段不包括已等待子进程所使用的 CPU 时间(与 getrusage(2) 和 time(2) 不同)。在 2.6 及之前版本的内核中,以及自 2.6.27 起,这些字段以 sysconf(_SC_CLK_TCK) 为单位报告 CPU 时间。在 2.6.27 之前的 2.6 内核中,一个 bug 导致这些字段以(可配置的)系统时钟滴答数(jiffy)为单位报告时间(参见 time(7))。
*
SIGILL、SIGFPE、SIGSEGV、SIGBUS 和 SIGTRAP 会填充 si_addr,其中包含故障地址。在某些架构上,这些信号还会填充 si_trapno 字段。SIGBUS 的某些子错误,特别是 BUS_MCEERR_AO 和 BUS_MCEERR_AR,也会填充 si_addr_lsb。该字段指示所报告地址的最低有效位,从而表示损坏的程度。例如,如果整页损坏,si_addr_lsb 包含 log2(sysconf(_SC_PAGESIZE))。BUS_MCERR_* 和 si_addr_lsb 是 Linux 特有的扩展。
*
SIGIO/SIGPOLL(在 Linux 上这两个名称是同义词)填充 si_band 和 si_fd。si_band 事件是一个位掩码,包含与 poll(2) 在 revents 字段中填充的值相同的值。si_fd 字段指示发生 I/O 事件的文件描述符。
- si_code 是一个值(非位掩码),指示发送此信号的原因。以下列表显示了可放置在任何信号的 si_code 中的值,以及信号生成的原因。
- SI_USER
kill(2)
SI_KERNEL
由内核发送。
SI_QUEUE
sigqueue(3)
SI_TIMER
POSIX 定时器到期
SI_MESGQ
POSIX 消息队列状态改变(自 Linux 2.6.6 起);参见 mq_notify(3)
SI_ASYNCIO
异步 I/O (AIO) 完成
SI_SIGIO
排队的 SIGIO(仅在 Linux 2.2 及以前内核中;从 Linux 2.4 开始,SIGIO/SIGPOLL 填充 si_code 的方式如下所述)。
SI_TKILL
- 以下值可放置在 SIGILL 信号的 si_code 中
- ILL_ILLOPC
非法操作码
ILL_ILLOPN
非法操作数
ILL_ILLADR
非法寻址模式
ILL_ILLTRP
非法陷阱
ILL_PRVOPC
特权操作码
ILL_PRVREG
特权寄存器
ILL_COPROC
协处理器错误
ILL_BADSTK
内部栈错误
- 以下值可放置在 SIGFPE 信号的 si_code 中
- FPE_INTDIV
整数除以零
FPE_INTOVF
整数溢出
FPE_FLTDIV
浮点除以零
FPE_FLTOVF
浮点溢出
FPE_FLTUND
浮点下溢
FPE_FLTRES
浮点不精确结果
FPE_FLTINV
浮点非法操作
FPE_FLTSUB
下标越界
- 以下值可放置在 SIGSEGV 信号的 si_code 中
- SEGV_MAPERR
地址未映射到对象
SEGV_ACCERR
映射对象的权限无效
- 以下值可放置在 SIGBUS 信号的 si_code 中
- BUS_ADRALN
无效地址对齐
BUS_ADRERR
不存在的物理地址
BUS_OBJERR
对象特定的硬件错误
- BUS_MCEERR_AR (自 Linux 2.6.32 起)
硬件内存错误在机器检查时被消耗;需要采取行动。
- BUS_MCEERR_AO (自 Linux 2.6.32 起)
硬件内存错误在进程中被检测到但未消耗;行动可选。
- 以下值可放置在 SIGTRAP 信号的 si_code 中
- TRAP_BRKPT
进程断点
TRAP_TRACE
进程追踪陷阱
- TRAP_BRANCH (自 Linux 2.4 起)
进程分支陷阱
- TRAP_HWBKPT (自 Linux 2.4 起)
硬件断点/观察点
- 以下值可放置在 SIGCHLD 信号的 si_code 中
- CLD_EXITED
子进程已退出
CLD_KILLED
子进程被杀死
CLD_DUMPED
子进程异常终止
CLD_TRAPPED
被追踪的子进程已陷阱
CLD_STOPPED
子进程已停止
CLD_CONTINUED
停止的子进程已继续(自 Linux 2.6.9 起)
- 以下值可放置在 SIGIO/SIGPOLL 信号的 si_code 中
- POLL_IN
有数据输入可用
POLL_OUT
输出缓冲区可用
POLL_MSG
有输入消息可用
POLL_ERR
I/O 错误
POLL_PRI
有高优先级输入可用
POLL_HUP
设备已断开连接
返回值
sigaction() 成功时返回 0,出错时返回 -1。
错误
- EFAULT
act 或 oldact 指向的内存不是进程地址空间的有效部分。
EINVAL
指定了无效信号。如果尝试更改 SIGKILL 或 SIGSTOP 的动作(它们不能被捕获或忽略),也会产生此错误。
符合
POSIX.1-2001, SVr4。
说明
通过 fork(2) 创建的子进程继承了其父进程信号配置的副本。在 execve(2) 期间,已处理信号的配置会重置为默认值;被忽略信号的配置则保持不变。
根据 POSIX,进程在忽略非由 kill(2) 或 raise(3) 生成的 SIGFPE、SIGILL 或 SIGSEGV 信号后的行为是未定义的。整数除以零的结果是未定义的。在某些架构上,它会生成 SIGFPE 信号。(此外,将最小负整数除以 -1 也可能生成 SIGFPE。)忽略此信号可能会导致无限循环。
POSIX.1-1990 禁止将 SIGCHLD 的动作设置为 SIG_IGN。POSIX.1-2001 允许此可能性,因此忽略 SIGCHLD 可用于防止僵尸进程的产生(参见 wait(2))。然而,历史上 BSD 和 System V 关于忽略 SIGCHLD 的行为不同,因此确保已终止的子进程不会变成僵尸进程的最完全可移植的方法是捕获 SIGCHLD 信号并执行 wait(2) 或类似操作。
POSIX.1-1990 仅指定了 SA_NOCLDSTOP。POSIX.1-2001 添加了 SA_NOCLDWAIT、SA_RESETHAND、SA_NODEFER 和 SA_SIGINFO。在针对旧版 UNIX 实现的应用程序中,在 sa_flags 中使用这些值可能不那么可移植。
SA_RESETHAND 标志与具有相同名称的 SVr4 标志兼容。
SA_NODEFER 标志在 1.3.9 及更新的内核下与具有相同名称的 SVr4 标志兼容。在较旧的内核上,Linux 实现允许接收任何信号,而不仅仅是我们正在安装的那个(实际上覆盖了任何 sa_mask 设置)。
可以将 sigaction() 的第二个参数设置为 NULL 来查询当前的信号处理程序。它也可通过将第二个和第三个参数设置为 NULL 来检查给定信号对当前机器是否有效。
无法阻塞 SIGKILL 或 SIGSTOP(通过在 sa_mask 中指定它们)。尝试这样做会被静默忽略。
有关操作信号集的详细信息,请参见 sigsetops(3)。
有关可以在信号处理程序内部安全调用的异步信号安全函数的列表,请参见 signal(7)。
未记录
- 在引入 SA_SIGINFO 之前,也可以通过使用带有 struct sigcontext 类型第二个参数的 sa_handler 来获取一些额外信息。详情请参阅相关的 Linux 内核源代码。这种用法现在已过时。
错误
在包括 2.6.13 在内的版本中,在 sa_flags 中指定 SA_NODEFER 不仅会防止已发送的信号在处理程序执行期间被屏蔽,还会防止 sa_mask 中指定的信号被屏蔽。此错误已在 2.6.14 内核中修复。
示例
参见 mprotect(2)。
参见
kill(1), kill(2), killpg(2), pause(2), sigaltstack(2), signal(2), signalfd(2), sigpending(2), sigprocmask(2), sigsuspend(2), wait(2), raise(3), siginterrupt(3), sigqueue(3), sigsetops(3), sigvec(3), core(5), signal(7)