Grijesh Chauhan's answer is technically correct but difficult to understand, so I am going to write out my own exposition of basically the same points. With footnotes.0
Dima asks what happens when one thread installs a do-nothing handler for SIGSEGV
and then another thread uses kill
1 to generate SIGSEGV
on that thread. The one-sentence answer is that the handler runs, does nothing, and then control returns to normal flow within the interrupted thread. Unlike when the kernel generates SIGSEGV
as a response to an actual memory access violation, this scenario does not trigger undefined behavior2. However, the intended purpose of SIGSEGV
and its friends (SIGBUS
, SIGFPE
, and SIGILL
) is for the kernel to tell your program that it has done something so heinous that there must be a bug, normal execution cannot continue, would you like to clean up a little before you get killed? It is therefore unwise to use them for anything else. There are several signals (SIGUSR1
, SIGUSR2
, and SIGRTMIN
through SIGRTMAX
) reserved for each application to use however it likes; you should use one of those instead.
For the longer answer, I am going to crib from the POSIX standard,3 subsection Signal Actions. First, the four signals SIGFPE
, SIGILL
, SIGSEGV
, and SIGBUS
, like any other signal, can be delivered "asynchronously"—at no particular time—because some piece of code used a system call (such as kill
) to generate them. When this happens, they are treated exactly the same as any other signal whose default "action" happens to be "terminate the program abnormally"; note that many other signals have this property, including those reserved for application use. If your program only ever has to worry about receiving SIGFPE
, SIGILL
, SIGSEGV
, and SIGBUS
when they are generated by kill
and friends, it can do all of the normal things with them: block them, ignore them, establish signal handlers that do anything that is valid for an asynchronous signal handler,4 receive them via sigwait
or signalfd
rather than the normal asynchronous system trap-like delivery mechanism.
然而。SIGFPE
, SIGILL
, SIGSEGV
, and SIGBUS
也由内核“同步”生成,以响应触发硬件异常的不同类型的错误程序行为,例如尝试访问未映射的内存。同步意味着信号传递立即地在执行有问题的 CPU 指令时,并且在同一线程上,而不是在进程中碰巧使其解锁的任何线程上。我们真的不是在开玩笑“立即”部分:如果有一个信号处理程序,当它执行时,保存的程序计数器将指向导致任何类型的硬件异常的确切指令。内核不允许执行导致 CPU 发出硬件异常的指令,因此 POSIX 表示尝试丢弃这些信号,而不是终止进程或采取一些激烈的恢复操作:
进程在忽略某个事件后的行为是未定义的SIGFPE
, SIGILL
, SIGSEGV
, or SIGBUS
信号不是由产生的kill()
, sigqueue()
, or raise()
.
(上下文中的“忽略之后”意味着“如果在操作设置为时内核尝试同步生成这些信号之一”SIG_IGN
.)
对于不是由kill()、sigqueue()或raise()生成的SIGBUS、SIGFPE、SIGILL或SIGSEGV信号,进程从信号捕获函数正常返回后,进程的行为是未定义的。
(“正常返回”的意思是“不通过调用(sig)longjmp
". It is至少就内核而言,展开堆栈并在其他地方恢复执行是有效的;不过,如果您没有充分修复最初导致故障的损坏数据结构,您可能会遇到麻烦。正如 Basile 提到的,扰乱已保存的处理器状态也是有效的,这样“正常”返回就不仅仅是尝试再次运行相同的错误指令;而是返回“正常”状态。但正在做that往往涉及手动解释机器指令和其他此类黑魔法。)
如果任何 SIGFPE、SIGILL、SIGSEGV 或 SIGBUS 信号在被阻塞时生成,则结果是未定义的,除非该信号是由另一个进程的操作或由 Kill()、pthread_kill() 函数之一生成的、raise() 或 sigqueue()。
(我不确定为什么这个措辞与其他两个有点不同;可能只是因为写这个的人具体文档sigprocmask没有与撰写该内容的人协调信号操作的一般文档.)
0 Dima tagged their question "Linux", but I am going to put in this caveat anyway, for the benefit of future readers: Everything I write here should be assumed to apply only to POSIX-conformant operating systems; to first order, that means "all OSes you are likely to encounter, except Windows." The exception is important. Windows has a rather different notion of the relationship between threads and processes than POSIX does, and a completely different (superior!) fundamental mechanism for reporting CPU-generated erroneous-program exceptions. Signals on Windows are emulated by the C library and probably do not behave as I describe.
1 presumably actually pthread_kill.
2 please read this entire series of blog posts
3 specifically, the online copy of The Open Group Base Specifications Issue 7, 2013 edition, which is also "simultaneously" the 2013 edition of IEEE Standard 1003.1, POSIX.
4 not a lot, but more than nothing; there's a list in the "Signal Actions" document, but no fragment ID allowing me to point you right at it.