无法处理的 POSIX 信号的返回码
Posted
技术标签:
【中文标题】无法处理的 POSIX 信号的返回码【英文标题】:Return code for POSIX signals that cannot be handled 【发布时间】:2012-11-16 04:50:48 【问题描述】:这是关于在 POSIX (Linux) 环境中运行的应用程序。大多数信号(例如 Ctrl+C - 信号 2,SIGINT),很少有其他信号被处理。完成后,将使用所需的退出代码从处理程序调用exit()
系统调用。
但是,有些信号(例如 Signal 9 和 Signal 15)无法处理。
不幸的是,如果信号 9 或 15 是终止的原因,启动给定应用程序的父进程(外部脚本)需要知道并清理一些内容。
是否有预定义的退出代码可以被父进程接收以了解上述情况?
启动应用程序的脚本是 bash_script。应用程序本身在 C 中。
【问题讨论】:
【参考方案1】:来自wait()
或waitpid()
的返回状态编码了您需要的信息。
POSIX 宏是:
如果孩子通过exit()
或其亲属之一退出,WIFEXITED(status)
返回 true。
WEXITSTATUS(status)
告诉您退出状态是什么 (0..255)。
如果孩子因信号(任何信号)而退出,WIFSIGNALED(status)
返回 true。
WTERMSIG(status)
返回杀死孩子的信号编号。
非标准但常见的宏WCOREDUMP(status)
告诉您进程是否转储核心。您还可以判断状态是反映进程已停止还是继续(以及停止信号是什么)。
请注意,信号 15 通常是 SIGTERM 并且 SIGTERM 可以被应用程序捕获。不能被捕获的信号是 SIGKILL (9) 和 SIGSTOP(在 Mac OS X 上是 17;可能在所有地方都不一样)。
那么问题是
bash
是否为脚本提供此信息。
答案是肯定的,但只是间接的,而不是 100% 明确的。 bash
报告的状态值将是 128 + <signum>
对于由于信号 <signum>
而终止的进程,但是您无法区分以状态 130
退出的进程和被中断的进程SIGINT,又名信号 2。
【讨论】:
我想问题是 bash 是否为脚本提供了这些信息。 很好的答案 - 从 google 的其他发现中认为不可能。【参考方案2】:15 (SIGTERM
) 可以被应用程序捕获和处理,如果它选择这样做的话,但目前可能没有
9 (SIGKILL
) 显然不能被任何应用程序捕获。
但是,操作系统通常会设置退出状态,以便可以识别终止进程的信号。通常只有 exit(3) 函数的状态参数的低 8 位 [以及 _exit(2) 系统调用] 被复制到由 wait(2) 返回给父进程的status
值中(正在运行的 shell您示例中的外部脚本)。因此,在status
值中留下了sizeof(int)-1
字节空间,供操作系统用于填写有关已终止进程的其他信息。通常,wait(2) 手册页将描述解释等待状态的方式,从而将有关进程终止的任何附加信息与进程传递给 _exit(2) 的状态分开,IFF 进程退出。
不幸的是,脚本是否可以使用这些额外信息取决于执行脚本的 shell 可能如何处理它。
首先查看您的 shell 手册页,了解如何解释 $?
的详细信息。
如果 shell 将整个 status
int
值逐字提供给脚本(在 $?
变量中),则可以解析该值并确定程序退出的方式和原因。大多数 shell 似乎并没有完全做到这一点(出于各种原因,其中最重要的可能是标准合规性),但它们至少做得足够远,可以解决您的查询(并且必须是 POSIX兼容)。
例如,我在 Mac OS X 上运行 AT&T 版本的 KSH。我的 ksh(1) 手册页说,如果程序正常运行,则退出状态为 0-255(其中的值大概是什么如果进程被信号终止(编号为“signum”),则传递给 _exit(2)) 和 256+signum。我不知道在 Linux 上,但在 OS X 上,bash 给出的退出状态与 Ksh 不同(bash 使用第 8 位来表示信号,因此只允许 0-127 作为有效的退出值)。 (在 POSIX 标准中,wait(2) 声称 _exit(2) 的 8 个低位可用,而 shell 将等待状态转换为 $?
仅保留 7 位之间存在差异。看图!Ksh 的行为违反了 POSIX,但更安全,因为严格兼容的 shell 可能无法区分将 128-255 值传递给 _exit(2) 的进程和已被信号终止的进程。)
所以,无论如何,我启动一个cat
进程,然后从终端发送SIGQUIT
(按^)(我使用SIGQUIT
,因为没有简单的方法从终端发送SIGTERM
键盘):
22:01 [2389] $ cat
^\Quit(coredump)
ksh: exit code: 259
(我定义了一个 shell EXIT
陷阱来打印 $? 如果它不为零,那么你也可以在上面看到它)
22:01 [2390] $ echo $?
259
(259是一个整数值,代表wait(2)返回给shell的状态)
22:02 [2391] $ bc
obase=16
259
103
^D22:03 [2392] $
(看到 259 的十六进制值是 0x0103,注意 0x0100 是十进制的 256)
22:03 [2392] $ signo SIGQUIT
#define SIGQUIT 3 /* quit */
(我有一个名为 signo
的 shell 别名,它搜索标头以查找代表符号信号名称的数字。请参见此处,状态值中的 0x03 与 SIGQUIT
的数字相同。)
对 wait(2) 系统调用的进一步探索,以及来自<sys/wait.h>
的相关宏将使我们能够更多地了解正在发生的事情。
在 C 中,解码等待状态的基本逻辑使用来自 <sys/wait.h>
的宏:
if (!WIFEXITED(status))
if (WIFSIGNALED(status))
termsig = WTERMSIG(status);
else if (WIFSTOPPED(status))
stopsig = WSTOPSIG(status);
else
exit_value = WEXITSTATUS(status));
希望对你有帮助!
【讨论】:
你的 C 代码片段应该首先有if (WIFEXITES(status))
,然后是当前在遥远的else
块中的计算,然后是else if (WIFSIGNALED(status))
及其操作,然后是else if (WIFSTOPPED(status))
,也许还有@987654349 @ 并且可能是最后一个 else
虽然我不知道如果你测试 WIFCONTINUED(status)
代码什么时候会到达 else
子句。
有趣的是ksh
处理信号信息的方式与bash
不同;明确地处理它是有意义的(并且它的编码确实如此)。这个问题确实提到了bash
脚本,不过……我已经给了你一个+1;答案中有有用的信息。
另外,WIFCONTINUED() 是一项相对较新的发明,并非在每个系统中都存在,除非您使用 waitpid() 并明确要求,否则无论如何都不会有效或有用。因为我什至没有提到 waitpid(),所以这种不必要的复杂性是无关紧要的 :-)【参考方案3】:
父进程不可能检测到 SIGKILL 或 Signal 9 - 鉴于 SIGNAL 发生在用户空间之外。
一个建议是让您的父进程检测您的子进程是否已经消失并相应地处理它。在 mysqld-safe 等中可以看到一个很好的例子。
【讨论】:
不,这不是真的。从wait
或waitpid
返回的状态码可以用 WIFSIGNALED 进行询问,如果非零,则表明孩子被未处理的信号杀死。然后可以使用 WTERMSIG 确定信号本身。以上是关于无法处理的 POSIX 信号的返回码的主要内容,如果未能解决你的问题,请参考以下文章