问题描述
在运行 cronjobs 并使用 check_mk 中的 mk-job 来监控其结果时,我偶然发现了这一点:
重击:
$ /usr/bin/time -f "time_exit: %x" timeout -s SIGKILL 2s sleep 10; echo "shell_exit: $?"
Command terminated by signal 9
time_exit: 0
shell_exit: 137
从 /usr/bin/time
返回的退出代码与其写入格式化输出的退出代码不同:
time_exit != shell_exit
为什么?
$ /usr/bin/time -f "time_exit: %x" timeout -s SIGHUP 2s sleep 10; echo "shell_exit: $?"
Command exited with non-zero status 124
time_exit: 124
shell_exit: 124
与此同时,如果进程仍在运行,我将使用 timeout -k 10s 2s ...
,它将首先发送 SIGHUP,并在 10 秒后发送 SIGKILL。希望 SIGHUP 能妥善阻止。
背景
check_mk 提供 mk-job 来监控作业执行。 mk-job 使用时间来记录执行时间和退出代码。
man time
:
时间命令在程序退出、停止或被信号终止时返回。如果程序正常退出,时间的返回值就是它执行并测量的程序的返回值。否则,返回值为 128 加上导致程序停止或终止的信号数。
man timeout
:
... 可能需要使用 KILL(9) 信号,因为无法捕捉到该信号,在这种情况下,退出状态为 128+9 而不是 124。
解决方法
GNU time 的 %x
仅在进程正常退出时才有意义,而不是被信号杀死。
[STEP 101] $ /usr/bin/time -f "%%x = %x" bash -c 'exit 2'; echo '$? = '$?
Command exited with non-zero status 2
%x = 2
$? = 2
[STEP 102] $ /usr/bin/time -f "%%x = %x" bash -c 'exit 137'; echo '$? = '$?
Command exited with non-zero status 137
%x = 137
$? = 137
[STEP 103] $ /usr/bin/time -f "%%x = %x" bash -c 'kill -KILL $$'; echo '$? = '$?
Command terminated by signal 9
%x = 0
$? = 137
[STEP 104] $
对于time timeout -s SIGKILL 2s sleep 10
,timeout
以137
正常退出,它不会被SIGKILL
杀死,就像我的bash -c 'exit 137'
示例。
更新:
查看时间源code,发现无论进程是否正常退出,%x
都在盲目调用WEXITSTATUS()
。
655 case 'x': /* Exit status. */
656 fprintf (fp,"%d",WEXITSTATUS (resp->waitstatus));
657 break;
在 Git master 中它添加了新的 %Tx
:
549 case 'T':
550 switch (*++fmt)
551 {
...
575 case 'x': /* exit code IF terminated normally */
576 if (WIFEXITED (resp->waitstatus))
577 fprintf (fp,WEXITSTATUS (resp->waitstatus));
578 break;
来自 Git master 的 time --help
输出:
...
%Tt exit type (normal/signalled)
%Tx numeric exit code IF exited normally
%Tn numeric signal code IF signalled
%Ts signal name IF signalled
%To 'ok' IF exited normally with code zero
...