linux手册翻译——sigaction(2)

\color{#A00000}{NAME}
sigaction, rt_sigaction - 检查和修改信号的Action(这里的Action语义同signal(7)中的disposition)

\color{#A00000}{SYNOPSIS}

#include <signal.h>
       int sigaction(int signum, const struct sigaction *restrict act,
                     struct sigaction *restrict oldact);

\color{#A00000}{DESCRIPTION}
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.

\color{#A00000}{NOTES}
通过 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()。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • 本文将从以下几个方面来阐述信号: (1)信号的基本知识 (2)信号生命周期与处理过程分析 (3) 基本的信号处理函...
    linux大本营阅读 542评论 0 0
  • 第10章 信号 10.1 引言 信号是软件中断。很多比较重要的应用程序都需处理信号。信号提 供了一种处理异步事件的...
    MachinePlay阅读 337评论 0 0
  • 信号的基本概念 信号被认为是一种软件中断(区别于硬件中断),信号机制提供了一种在单进程/线程下处理异步事件的方法。...
    小叶大孟阅读 1,918评论 0 1
  • 基本规则 信号可以由系统内核程序发出,也能由某些进程发送,但是大部分时候都是由内核程序发出. 当一个信号正在被处理...
    wangxn阅读 956评论 0 0
  • 对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件...
    故事狗阅读 84,943评论 2 62