![为什么“退出 130 与死于 SIGINT 不同”?](https://linux22.com/image/116385/%E4%B8%BA%E4%BB%80%E4%B9%88%E2%80%9C%E9%80%80%E5%87%BA%20130%20%E4%B8%8E%E6%AD%BB%E4%BA%8E%20SIGINT%20%E4%B8%8D%E5%90%8C%E2%80%9D%EF%BC%9F.png)
来自 Stéphane Chazelas 的回复https://unix.stackexchange.com/a/230568
理想情况下,我们希望向父母报告我们死于 SIGINT(因此,如果是另一个 bash 脚本,则该 bash 脚本也会被中断)。退出 130 与死于 SIGINT 不同(尽管有些 shell 会将 $? 设置为两种情况下的相同值),但是它通常用于通过 SIGINT 报告死亡(在 SIGINT 为 2 的系统上,这是最多的)。
然而对于 bash、ksh93 或 FreeBSD sh,这是行不通的。 SIGINT 不会将 130 退出状态视为死亡并且父脚本不会在那里中止。
- 关于“退出130与死于SIGINT不同”,它们之间有什么区别?
- 为什么“对于 bash、ksh93 或 FreeBSD sh,这不起作用。SIGINT 不会将 130 退出状态视为死亡”?
来自 Bash 手册:
当命令因编号为 N 的致命信号而终止时,Bash 使用值 128+N 作为退出状态
SIGINT 的信号编号是 2,因此在 SIGINT 上终止的命令的退出状态是 130。所以在我看来,执行 exit 130 与 SIGINT 的死亡相同,并且 130 退出状态被认为是死亡信号。
谢谢。
答案1
$?
您在最后一个命令因 SIGINT 死亡后看到的 130 (128+SIGINT)是由某些 shell(例如bash
.其他 shell 将使用不同的表示形式(例如 ksh93 中的 256+signum、yash 中的 128+256+signum、/中的sigint
或等文本表示形式)。看sigquit+core
rc
es
进程终止时的默认退出代码?了解更多详情。
进程可以等待其子进程并查询其状态:
- 如果停止(用哪个信号)
- 如果恢复的话
- 如果它被杀死(用什么信号)
- 如果有被困(为了跟踪d 过程)
- 如果它倾倒了核
- 是否通过系统调用正常退出
_exit()
(使用哪个退出代码)
为此,他们使用wait()
、waitpid()
、waitid()
(另请参阅过时的wait3()
、wait4()
)之一或 SIGCHLD 系统调用上的处理程序。
这些系统调用返回上述所有信息。 (除了waitid()
某些系统外,只有传递给正常终止的子进程的数字的最低 8 位_exit()
可用)。
但是bash
(以及大多数类似 Bourne 和 csh 的 shell)将所有信息捆绑在一个 8 位数字中$?
($?
是正常终止的进程的退出代码的最低 8 位,并且 128+signum 它被杀死或挂起或被困,所有其他信息均不可用)。显然,有些信息丢失了。特别是,仅凭$?
一己之力,人们无法判断进程是否_exit(130)
因 SIGINT 执行或死亡。
bash
明显知道进程何时被终止。例如,当后台进程被终止时,您会看到:
[1]+ Interrupt sleep 20
但是在 中$?
,它没有给您足够的信息来判断它是被 SIGINT 杀死还是被调用_exit(130)
。
由于大多数 shell 都会进行这种转换,因此应用程序知道_exit(number_greater_than_127)
除了通过信号报告死亡之外什么都不做。
尽管如此,如果一个进程执行了 a _exit(130)
,则等待该进程的进程将检测到该进程正常终止,而不是被信号杀死。在C中,WIFEXITED()
将返回真的,WIFSIGNALED()
将返回 false。
bash
它本身不会认为该进程已死于 SIGINT(即使它让您认为它可能通过$?
包含与已死于 SIGINT 相同的值而具有相同的值)。
因此,这不会触发 SIGINT 的特殊处理bash
。在脚本中,bash
脚本中当前运行的命令都将收到 SIGINT ^C
(因为它们都在同一进程组中)。
bash
仅当它正在等待的命令也死于 SIGINT 时,才会在收到 SIGINT 时死亡(其想法是,例如在您的脚本中,您运行 vi 或 less 并使用 ^C 中止不会导致vi
/less
死亡的某些内容,您的脚本不会在退出vi
/less
稍后返回时消失)。
如果该命令bash
正在等待_exit(130)
SIGINT 处理程序中的执行,bash
则不会在该 SIGINT 时终止(它不会认为自己已被中断,因为它不相信子进程已被中断)。
这就是为什么当您想要举报SIGINT 导致死亡, 那即使您在处理程序中接收到该信号后实际上正在执行一些额外的处理,但您确实已被中断,您不应该执行 a _exit(130)
,而是实际上用 SIGINT 杀死自己(在恢复 SIGINT 的默认处理程序之后)。在 shell 中,它是:
trap '
extra processing
trap - INT # restore SIGINT handler
kill -s INT "$$" # report to the parent that we have indeed been
# interrupted
' INT