进程终止时的默认退出代码?

Cor*_*ein 73 kill signals exit exit-status

当一个进程被一个可处理的信号杀死时,例如SIGINTorSIGTERM但它不处理该信号,该进程的退出代码是什么?

对于无法处理的信号,例如SIGKILL?

据我所知,杀死进程SIGINT可能会导致退出代码130,但这会因内核或外壳实现而异吗?

$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130
Run Code Online (Sandbox Code Playgroud)

我不确定如何测试其他信号...

$ ./myScript &
$ killall myScript
$ echo $?
0  # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0  # same problem
Run Code Online (Sandbox Code Playgroud)

Sté*_*las 74

进程可以使用整数参数调用_exit()系统调用(在 Linux 上,另请参阅 参考资料exit_group())以向其父进程报告退出代码。虽然它是一个整数,但只有 8 个最低有效位可供父级使用(例外情况是在父级中的 SIGCHLD 上使用waitid()或 处理程序来检索该代码,但在 Linux 上则不然)。

父级通常会执行 await()waitpid()以将其子级的状态作为整数获取(尽管waitid()也可以使用稍微不同的语义)。

在 Linux 和大多数 Unices 上,如果进程正常终止,则该状态编号的第8 到 15 位将包含传递给exit(). 如果不是,则 7 个最低有效位(0 到 6)将包含信号编号,如果核心被转储,则第 7 位将被设置。

perl$?例如包含由设置的数字waitpid()

$ perl -e 'system q(kill $$); printf "%04x\n", $?'
000f # killed by signal 15
$ perl -e 'system q(kill -ILL $$); printf "%04x\n", $?'
0084 # killed by signal 4 and core dumped
$ perl -e 'system q(exit $((0xabc))); printf "%04x\n", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status
Run Code Online (Sandbox Code Playgroud)

类似 Bourne 的 shell 也会在自己的$?变量中设置上次运行命令的退出状态。但是,它不直接包含由 返回的数字waitpid(),而是对其进行了转换,并且在不同的shell 之间有所不同。

如果进程正常终止,所有 shell 之间的共同点是$?包含退出代码的最低 8 位(传递给 的数字exit())。

不同之处在于进程何时被信号终止。在所有情况下,这是 POSIX 所要求的,该数字将大于 128。POSIX 未指定该值可能是什么。但实际上,在我知道的所有 Bourne-like shell 中,最低 7 位$?将包含信号编号。但是,n信号号在哪里,

  • 在 ash、zsh、pdksh、bash 中,Bourne shell$?128 + n. 这意味着在这些 shell 中,如果你得到 a $?of 129,你不知道是因为进程退出exit(129)还是被信号杀死1HUP在大多数系统上)。但基本原理是,当 shell 自己退出时,默认情况下会返回上次退出命令的退出状态。通过确保$?永远不会大于 255,这允许具有一致的退出状态:

    $ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    bash: line 1: 16720 Terminated              sh -c "kill \$\$"
    8f # 128 + 15
    $ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    bash: line 1: 16726 Terminated              sh -c "kill \$\$"
    8f # here that 0x8f is from a exit(143) done by bash. Though it's
       # not from a killed process, that does tell us that probably
       # something was killed by a SIGTERM
    
    Run Code Online (Sandbox Code Playgroud)
  • ksh93$?256 + n。这意味着根据$?您的值可以区分已终止和未终止的进程。的较新版本ksh,在退出时,如果$?大于 255,会用相同的信号杀死自己,以便能够向其父级报告相同的退出状态。虽然这听起来是个好主意,但这意味着ksh如果进程被核心生成信号杀死,则会生成额外的核心转储(可能会覆盖另一个):

    $ ksh -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    ksh: 16828: Terminated
    10f # 256 + 15
    $ ksh -c 'sh -c "kill -ILL \$\$"; exit'; printf '%x\n' "$?"
    ksh: 16816: Illegal instruction(coredump)
    Illegal instruction(coredump)
    104 # 256 + 15, ksh did indeed kill itself so as to report the same
        # exit status as sh. Older versions of `ksh93` would have returned
        # 4 instead.
    
    Run Code Online (Sandbox Code Playgroud)

    你甚至可以说有一个错误,ksh93即使$?来自return 257一个函数的完成,它也会杀死自己:

    $ ksh -c 'f() { return "$1"; }; f 257; exit'
    zsh: hangup     ksh -c 'f() { return "$1"; }; f 257; exit'
    # ksh kills itself with a SIGHUP so as to report a 257 exit status
    # to its parent
    
    Run Code Online (Sandbox Code Playgroud)
  • yash. yash提供妥协。它返回256 + 128 + n。这意味着我们还可以区分被杀死的进程和正确终止的进程。退出时,它会报告128 + n而不必自杀及其可能产生的副作用。

    $ yash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    18f # 256 + 128 + 15
    $ yash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    8f  # that's from a exit(143), yash was not killed
    
    Run Code Online (Sandbox Code Playgroud)

要从 的值中获取信号$?,可移植的方法是使用kill -l

$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM
Run Code Online (Sandbox Code Playgroud)

(为了可移植性,你不应该使用信号编号,只使用信号名称)

在非伯恩战线上:

  • csh/tcsh并且fish与 Bourne shell 相同,除了状态是 in$status而不是$?(请注意,zsh还设置$status了与csh(除了$?)的兼容性)。
  • rc:退出状态也是 in $status,但是当被信号杀死时,该变量包含信号的名称(例如sigtermsigill+core如果生成了一个核心)而不是数字,这是该 shell 良好设计的又一个证明.
  • es. 退出状态不是一个变量。如果你关心它,你可以运行命令:

    status = <={cmd}
    
    Run Code Online (Sandbox Code Playgroud)

    这将返回一个数字sigtermsigsegv+core类似的rc.

也许为了完整性,我们应该提到包含最后一个管道组件退出状态的zsh's$pipestatusbash's$PIPESTATUS数组。

并且为了完整性,当涉及到 shell 函数和源文件时,默认情况下,函数返回上次命令运行的退出状态,但也可以使用return内置命令显式设置返回状态。我们在这里看到了一些差异:

  • bash并且mksh(自 R41 起,回归^Wchange 显然是有意引入的)会将数字(正数或负数)截断为 8 位。因此,例如return 1234将设置$?210return -- -1将设置$?为 255。
  • zshpdksh(以及除 之外的衍生产品mksh)允许任何有符号的 32 位十进制整数(-2 31到 2 31 -1)(并将数字截断为 32 位)。
  • ashyash允许从 0 到 2 31 -1 的任何正整数,并为其中的任何数字返回错误。
  • ksh93forreturn 0按原样return 320设置$?,但对于其他任何内容,请截断为 8 位。请注意,如前所述,返回 256 到 320 之间的数字可能会导致ksh退出时自杀。
  • rces允许返回任何甚至列表。

另请注意,某些 shell 还使用$?/ 的特殊值$status来报告一些不是进程退出状态的错误条件,例如127or 126for command not found不可执行(或源文件中的语法错误)...

  • 这里有3个数字。_exit code_:传递给`exit()`的数字。_exit status_:`waitpid()` 获得的数字,包括退出代码、信号编号以及是否有内核转储。一些 shell 在它们的一个特殊变量(`$?`、`$status`)中提供的数字是 _exit status_ 的一种转换,它确实包含 _exit code_,以防万一有一个正常的终止,但如果进程被终止,也会携带信号信息(通常也称为 _exit status_)。这在我的回答中都有解释。 (4认同)

Gil*_*il' 34

当进程退出时,它会向操作系统返回一个整数值。在大多数 unix 变体上,该值取模 256:除低位之外的所有内容都被忽略。子进程的状态通过一个 16 位整数返回给它的父进程,其中

  • 位 0-6(低 7 位)是用于终止进程的信号编号,如果进程正常退出,则为 0;
  • 如果进程被信号和转储核心杀死,则设置第 7 位;
  • 如果进程正常退出,位 8-15 是进程的退出代码,如果进程被信号杀死,则位 0。

状态由wait系统调用或其同级之一返回。POSIX 没有指定退出状态和信号编号的确切编码;它只提供

  • 一种判断退出状态对应于信号还是正常退出的方法;
  • 如果进程正常退出,则访问退出代码的方法;
  • 如果进程被信号杀死,则访问信号编号的方法。

严格来说,当进程被信号杀死时没有退出代码:取而代之的是退出状态

在 shell 脚本中,命令退出状态通过特殊变量 报告$?。此变量以一种模棱两可的方式对退出状态进行编码:

  • 如果进程正常退出,则$?是其退出状态。
  • 如果进程被信号杀死,那么$?在大多数系统上是 128 加上信号编号。$?在这种情况下,POSIX 只要求大于 128;ksh93 增加了 256 而不是 128。我从来没有见过一个 unix 变体除了向信号编号添加一个常数之外还做任何事情。

因此,在 shell 脚本中,除了 ksh93 之外,您无法确切地判断命令是被信号杀死还是以大于 128 的状态代码退出。程序以大于 128 的状态代码退出的情况非常罕见,部分原因是程序员由于$?歧义而避免这种情况。

SIGINT 在大多数 unix 变体上是信号 2,因此$?对于被 SIGINT 杀死的进程来说是 128+2=130。你会看到 129 表示 SIGHUP,137 表示 SIGKILL,等等。

  • 比我的措辞要好得多,更重要,即使它本质上说的是相同的东西。您可能想澄清一下 `$?` 仅适用于 Bourne-like shell。另请参阅“yash”以了解不同的(但仍然是 POSIX)行为。同样按照 POSIX+XSI (Unix),`kill -2 "$pid"` 会向进程发送一个 SIGINT,但实际的信号数可能不是 2,所以 $? 不一定是 128+2(或 256+2 或 384+2),尽管 `kill -l "$?"` 会返回 `INT`,这就是为什么我建议为了可移植性不要参考数字本身. (2认同)

Ign*_*ams 12

这取决于你的外壳。从bash(1)手册页,SHELL GRAMMAR部分,Simple Commands小节:

一个的返回值简单的命令是[...] 128+ Ñ如果命令是通过信号终止Ñ

由于SIGINT在您的系统上是 2 号信号,因此在 Bash 下运行时返回值为 130。

  • 你到底是怎么找到这个的,甚至不知道去哪里找的?我在你的天才面前鞠躬。 (2认同)