linux手册翻译——sigaction(2)
sigaction, rt_sigaction - 检查和修改信号的Action(这里的Action语义同signal(7)中的disposition)
#include <signal.h>
int sigaction(int signum, const struct sigaction *restrict act,
struct sigaction *restrict oldact);
sigaction() 系统调用用于更改进程在接收到特定信号时采取的Action。 (有关信号的概述,请参阅我翻译的signal手册)
参数 signum 指定信号,可以是除 SIGKILL 和 SIGSTOP 之外的任何有效信号。
如果参数 act 非空,那么将通过 act 指定具体的信号处理动作(区别于信号处理函数),如果参数 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
sa_restorer 字段不适用于应用程序。(POSIX 没有指定 sa_restorer 字段。)可以在 sigreturn(2) 中找到有关此字段用途的更多详细信息。
sa_handler
sa_handler 指定要与 signum 关联的Action,并且是以下之一:
- SIG_DFL 表示默认动作
- SIG_IGN 表示忽略该信号
- 指向信号处理函数的指针。 此函数接收信号编号作为其唯一参数
sa_sigaction
如果在sa_flags 中指定了SA_SIGINFO,则sa_sigaction(而不是sa_handler)指定signum 的信号处理函数。 该函数接收三个参数,如下所述。
sa_mask
sa_mask 指定在信号处理程序执行期间应该被阻塞的信号掩码(即,添加到调用信号处理程序的线程的信号掩码中)。 此外,触发处理程序的信号将被阻塞,除非使用 SA_NODEFER 标志。
sa_flags
sa_flags 指定一组修改信号行为的标志。 它由以下零个或多个的按位或组成:
- SA_NOCLDSTOP
如果 signum 是 SIGCHLD,则在子进程停止(即,当它们收到 SIGSTOP、SIGTSTP、SIGTTIN 或 SIGTTOU 之一)或恢复(即,它们收到 SIGCONT)时不接收通知(请参阅 wait(2))。 此标志仅在为 SIGCHLD 建立处理程序时才有意义。对于SIGCHLD的解释是,当子进程停止或或终止时父进程会收到此信号,此flag表示,仅仅在终止时才会收到此信号。 - SA_NOCLDWAIT
如果 signum 是 SIGCHLD,则不会在子进程终止时将其转变成僵尸。 另请参见 waitpid(2)。 此标志仅在为 SIGCHLD 建立处理程序或将该信号的处置设置为 SIG_DFL 时才有意义。
如果在为 SIGCHLD 建立处理程序时设置了 SA_NOCLDWAIT 标志,则 POSIX.1 不指定是否在子进程终止时生成 SIGCHLD 信号。 在 Linux 上,在这种情况下会生成一个 SIGCHLD 信号; 在其他一些实现中,它不是。 - SA_NODEFER
不要在处理程序执行时将当前信号添加到线程的信号掩码中,除非在 act.sa_mask 中指定了该信号。 因此,当线程正在执行处理程序时,可以将当前信号的另一个实例传递给线程。 此标志仅在建立信号处理程序时才有意义。 - SA_ONSTACK
在 sigaltstack(2) 提供的备用信号堆栈上调用信号处理程序。 如果备用堆栈不可用,则将使用默认堆栈。 此标志仅在建立信号处理程序时才有意义。若想用备用堆栈,不仅需要调用sigaltstack(2) ,还要设置此标志。 - SA_RESETHAND
在进入信号处理程序时将信号操作恢复为默认值。 此标志仅在建立信号处理程序时才有意义。 - SA_RESTART
通过使某些系统调用可跨信号重新启动(即在执行系统调用阻塞时被信号打断后,可以重新执行),提供与 BSD 信号语义兼容的行为。 此标志仅在建立信号处理程序时才有意义。 有关系统调用重新启动的讨论,请参见 signal(7)。 - SA_RESTORER
不适用于应用程序。 C 库使用此标志来指示 sa_restorer 字段包含“signal trampoline”的地址。 有关更多详细信息,请参阅 sigreturn(2)。 - SA_SIGINFO
信号处理程序接受三个参数,而不是一个。 在这种情况下,应设置 sa_sigaction 而不是 sa_handler。 此标志仅在建立信号处理程序时才有意义。
SA_SIGINFO 处理程序的 siginfo_t 参数
当在 act.sa_flags 中指定 SA_SIGINFO 标志时,信号处理程序地址通过 act.sa_sigaction 字段传递。 此处理程序采用三个参数,如下所示:
void handler(int sig, siginfo_t *info, void *ucontext)
{
...
}
- sig
导致调用处理程序的信号代码。 - info
指向 siginfo_t 的指针,该结构包含有关信号的更多信息,如下所述。 - ucontext
这是一个指向 ucontext_t 结构的指针,转换为 void *。 该字段指向的结构包含内核保存在用户空间堆栈上的信号上下文信息; 有关详细信息,请参阅 sigreturn(2)。 有关 ucontext_t 结构的更多信息可以在 getcontext(3) 和 signal(7) 中找到。 通常,处理程序函数不使用第三个参数。
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 */
union sigval 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 Linux 2.6.32) */
void *si_lower; /* Lower bound when address violation
occurred (since Linux 3.19) */
void *si_upper; /* Upper bound when address violation
occurred (since Linux 3.19) */
int si_pkey; /* Protection key on PTE that caused
fault (since Linux 4.6) */
void *si_call_addr; /* Address of system call instruction
(since Linux 3.5) */
int si_syscall; /* Number of attempted system call
(since Linux 3.5) */
unsigned int si_arch; /* Architecture of attempted system call
(since Linux 3.5) */
}
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,将 sigev_value 提供给 mq_notify(3); 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 时间; 这些字段不包括等待子进程使用的时间(与 getrusage(2) 和 times(2) 不同)。 在 2.6 和 2.6.27 之后的内核中,这些字段以 sysconf(_SC_CLK_TCK) 为单位报告 CPU 时间。
- 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))。 当响应 ptrace(2) 事件 (PTRACE_EVENT_foo) 传递 SIGTRAP 时,不会填充 si_addr,但会填充 si_pid 和 si_uid 各自的进程 ID 和负责传递陷阱的用户 ID。 在 seccomp(2) 的情况下,tracee 将显示为传递事件。 BUS_MCEERR* 和 si_addr_lsb 是 Linux 特定的扩展。
SIGSEGV 的 SEGV_BNDERR 子错误填充 si_lower 和 si_upper。
SIGSEGV 的 SEGV_PKUERR 子错误填充 si_pkey。 - SIGIO/SIGPOLL
(这两个名字在Linux上是同义词)填写si_band和si_fd。 si_band 事件是一个位掩码,包含与 poll(2) 在 revents 字段中填充的值相同的值。 si_fd 字段表示发生 I/O 事件的文件描述符; 有关更多详细信息,请参阅 fcntl(2) 中 F_SETSIG 的说明。 - SIGSYS
当 seccomp 过滤器返回 SECCOMP_RET_TRAP 时生成(自 Linux 3.5 起),填充 si_call_addr、si_syscall、si_arch、si_errno 和 seccomp(2) 中描述的其他字段。
The si_code field
传递给 SA_SIGINFO 信号处理程序的 siginfo_t 参数中的 si_code 字段是一个值(不是位掩码),指示发送此信号的原因。 对于 ptrace(2) 事件,si_code 将包含 SIGTRAP 并在高字节中包含 ptrace 事件:
(SIGTRAP | PTRACE_EVENT_foo << 8).
对于非 ptrace(2) 事件,可能出现在 si_code 中的值在本节的其余部分进行了描述。 从 glibc 2.20 开始,大多数这些符号的定义是通过定义功能测试宏(在包含任何头文件之前)从 <signal.h> 获得的,如下所示:
- _XOPEN_SOURCE 值为 500 或更大;
- _XOPEN_SOURCE 和 _XOPEN_SOURCE_EXTENDED; 或者
- _POSIX_C_SOURCE 值为 200809L 或更大。
对于 TRAP_* 常量,仅在前两种情况下提供符号定义。 在 glibc 2.20 之前,不需要特性测试宏来获取这些符号。
对于常规信号,以下列表显示了可以放置在任何信号的 si_code 中的值,以及生成信号的原因:
si_code | 原因 |
---|---|
SI_USER | kill(2). |
SI_KERNEL | Sent by the kernel. |
SI_QUEUE | sigqueue(3). |
SI_TIMER | POSIX timer expired. |
SI_MESGQ | (since Linux 2.6.6) POSIX message queue state changed; see mq_notify(3). |
SI_ASYNCIO | AIO completed. |
SI_SIGIO | Queued SIGIO (only in kernels up to Linux 2.2; from Linux 2.4 onward SIGIO/SIGPOLL fills in si_code as described below). |
SI_TKILL | (since Linux 2.4.19) tkill(2) or tgkill(2). |
可以在 si_code 中为 SIGILL 信号放置以下值:
si_code | 原因 |
---|---|
ILL_ILLOPC | Illegal opcode. |
ILL_ILLOPN | Illegal operand. |
ILL_ILLADR | Illegal addressing mode. |
ILL_ILLTRP | Illegal trap. |
ILL_PRVOPC | Privileged opcode. |
ILL_PRVREG | Privileged register. |
ILL_COPROC | Coprocessor error. |
ILL_BADSTK | Internal stack error |
可以在 si_code 中为 SIGFPE 信号放置以下值:
si_code | 原因 |
---|---|
FPE_INTDIV | Integer divide by zero. |
FPE_INTOVF | Integer overflow. |
FPE_FLTDIV | Floating-point divide by zero. |
FPE_FLTOVF | Floating-point overflow. |
FPE_FLTUND | Floating-point underflow. |
FPE_FLTRES | Floating-point inexact result. |
FPE_FLTINV | Floating-point invalid operation. |
FPE_FLTSUB | Subscript out of range. |
可以在 si_code 中为 SIGSEGV 信号放置以下值:
si_code | 原因 |
---|---|
SEGV_MAPERR | Address not mapped to object. |
SEGV_ACCERR | Invalid permissions for mapped object. |
SEGV_BNDERR | (since Linux 3.19) Failed address bound checks. |
SEGV_PKUERR | (since Linux 4.6) Access was denied by memory protection keys. See pkeys(7). The protection key which applied to this access is available via si_pkey. |
可以在 si_code 中为 SIGBUS 信号放置以下值:
si_code | 原因 |
---|---|
BUS_ADRALN | Invalid address alignment. |
BUS_ADRERR | Nonexistent physical address. |
BUS_OBJERR | Object-specific hardware error. |
BUS_MCEERR_AR | (since Linux 2.6.32) Hardware memory error consumed on a machine check;action required. |
BUS_MCEERR_AO | (since Linux 2.6.32) Hardware memory error detected in process but not consumed; action optional. |
可以在 si_code 中为 SIGTRAP 信号放置以下值:
si_code | 原因 |
---|---|
TRAP_BRKPT | Process breakpoint. |
TRAP_TRACE | Process trace trap. |
TRAP_BRANCH | (since Linux 2.4, IA64 only) Process taken branch trap. |
TRAP_HWBKPT | (since Linux 2.4, IA64 only) Hardware breakpoint/watchpoint. |
可以在 si_code 中为 SIGCHLD 信号放置以下值:
si_code | 原因 |
---|---|
CLD_EXITED | Child has exited. |
CLD_KILLED | Child was killed. |
CLD_DUMPED | Child terminated abnormally. |
CLD_TRAPPED | Traced child has trapped. |
CLD_STOPPED | Child has stopped. |
CLD_CONTINUED | (since Linux 2.6.9) Stopped child has continued. |
可以在 si_code 中为 SIGIO/SIGPOLL 信号放置以下值:
si_code | 原因 |
---|---|
POLL_IN | Data input available. |
POLL_OUT | Output buffers available. |
POLL_MSG | Input message available. |
POLL_ERR | I/O error. |
POLL_PRI | High priority input available. |
POLL_HUP | Device disconnected. |
可以在 si_code 中为 SIGSYS 信号放置以下值:
si_code | 原因 |
---|---|
SYS_SECCOMP | (since Linux 3.5)Triggered by a seccomp(2) filter rule. |
通过 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_NOCLDSTOP、SA_NOCLDWAIT、SA_NODEFER、SA_ONSTACK、SA_RESETHAND、SA_RESTART 和 SA_SIGINFO。在用于旧 UNIX 实现的应用程序中,在 sa_flags 中使用这些后面的值可能不太容易移植。
SA_RESETHAND 标志与同名的 SVr4 标志兼容。
SA_NODEFER 标志与内核 1.3.9 及更高版本下的同名 SVr4 标志兼容。在较旧的内核上,Linux 实现允许接收任何信号,而不仅仅是我们正在安装的信号(有效地覆盖任何 sa_mask 设置)。
可以使用 NULL 第二个参数调用 sigaction() 以查询当前信号处理程序。它还可以用于检查给定信号是否对当前机器有效,方法是使用 NULL 第二和第三个参数调用它。
无法阻止 SIGKILL 或 SIGSTOP(通过在 sa_mask 中指定它们)。这样做的尝试会被默默地忽略。
有关操作信号集的详细信息,请参阅 sigsetops(3)。
有关可以从信号处理程序内部安全调用的异步信号安全函数的列表,请参见信号安全(7)。
C 库/内核差异
sigaction() 的 glibc 包装函数在尝试更改 NPTL 线程实现内部使用的两个实时信号的处置时给出错误 (EINVAL)。有关详细信息,请参阅 nptl(7)。
在信号蹦床驻留在 C 库中的体系结构上,sigaction() 的 glibc 包装函数将蹦床代码的地址放在 act.sa_restorer 字段中,并在 act.sa_flags 字段中设置 SA_RESTORER 标志。请参见 sigreturn(2)。
最初的 Linux 系统调用名为 sigaction()。但是,随着 Linux 2.2 中实时信号的添加,该系统调用支持的固定大小的 32 位 sigset_t 类型不再适用。因此,添加了一个新的系统调用 rt_sigaction() 以支持扩大的 sigset_t 类型。新系统调用采用第四个参数 size_t sigsetsize,它指定 act.sa_mask 和 oldact.sa_mask 中信号集的字节大小。当前需要此参数具有值 sizeof(sigset_t) (或错误 EINVAL 结果)。 glibc sigaction() 包装函数向我们隐藏了这些细节,在内核提供时透明地调用 rt_sigaction()。