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

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

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

对于像这样无法处理的信号怎么办SIGKILL

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

$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130

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

$ ./myScript &
$ killall myScript
$ echo $?
0  # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0  # same problem

答案1

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

父级通常会执行 await()waitpid()以获得地位他们的孩子作为整数(尽管waitid()也可以使用稍微不同的语义)。

在 Linux 和大多数 Unices 上,如果进程正常终止,则该进程的第 8 到 15 位地位number 将包含传递给 的退出代码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

$?类似 Bourne 的 shell 还在自己的特殊参数中设置最后运行命令的退出状态。但是,它并不直接包含 所返回的数字waitpid(),而是对其进行转换,并且在 shell 之间有所不同。

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

不同之处在于进程被信号终止时。在所有情况下,POSIX 都要求该数字大于 128。POSIX 没有指定该值可能是什么。但在实践中,在我所知道的所有 Bourne 类 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
    
  • 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.
    

    你甚至可以说有一个错误,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
    
  • yashyash提供了一个折衷方案。它返回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
    

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

$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM

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

在非伯恩战线上:

  • csh/tcshfishBourne shell 相同,只是状态是 in$status而不是$?(请注意,zsh还设置了与(除了)$status的兼容性)。csh$?

  • rc:退出状态$status也是如此,但是当被信号杀死时,该变量包含信号的名称(例如sigtermsigill+core如果生成了核心)而不是数字,这是该 shell 良好设计的另一个证明。

  • es。退出状态不是变量。如果您关心它,请运行命令:

     status = <={cmd}
    

它将返回一个数字或sigterm类似sigsegv+core的数字rc

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

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

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

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

答案2

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

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

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

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

严格来说,没有出口代码当进程被信号杀死时:存在的是退出地位

在 shell 脚本中,命令的退出状态通过特殊变量报告$?。该变量以不明确的方式对退出状态进行编码:

  • 如果进程正常退出,则$?为其退出状态。
  • 如果进程被信号杀死,则$?在大多数系统上为 128 加上信号编号。在这种情况下,POSIX 仅要求$?大于 128; ksh93 添加 256 而不是 128。我从未见过除了向信号编号添加常量之外执行任何其他操作的 UNIX 变体。

因此,在 shell 脚本中,您无法确定命令是被信号终止还是以大于 128 的状态代码退出,ksh93 除外。程序以大于 128 的状态代码退出的情况非常罕见,部分原因是程序员由于$?不明确而避免这样做。

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

答案3

这取决于你的外壳。从bash(1)手册页来看,外壳文法部分,简单命令小节:

a的返回值简单的命令是 [...] 128+n如果命令由信号终止n

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

答案4

似乎是正确的地方提到 SVr4 在 1989 年引入了 waitid(),但到目前为止似乎还没有重要的程序使用它。 waitid() 允许从 exit() 代码中检索完整的 32 位。

大约 2 个月前,我重写了 Bourne Shell 的等待/作业控制部分,使用 waitid() 而不是 waitpid()。这样做是为了消除用 0xFF 屏蔽退出代码的限制。

waitid() 接口比以前的 wait() 实现要简洁得多,除了 1980 年来自 UNOS 的 cwait() 调用之外。

您可能有兴趣阅读以下位置的手册页:

http://schillix.sourceforge.net/man/man1/bosh.1.html

并检查当前从第 8 页开始的“参数替换”部分。

为 waitid() 接口引入了新变量 .sh.*。这个接口不再对 $? 已知的数字有不明确的含义。并使接口变得更加容易。

请注意,您需要有一个兼容 POSIX 的 waitid() 才能使用此功能,因此 Mac OS X 和 Linux 目前不提供此功能,但 waitid() 是在 waitpid() 调用上模拟的,因此非 POSIX 平台您仍然只能从退出代码中获得 8 位。

简而言之:.sh.status 是数字退出代码,.sh.code 是数字退出原因。

为了更好的可移植性,有: .sh.codename 表示退出原因的文本版本,例如“DUMPED”和 .sh.termsig ,表示终止进程的信号的信号名称。

为了更好地使用,有两个与退出无关的 .sh.codename 值:“NOEXEC”和“NOTFOUND”,当程序根本无法启动时使用。

FreeBSD 在我报告后 20 小时内修复了他们的 waitid() 内核错误,Linux 还没有开始修复。我希望在 POSIX 中引入此功能 26 年后,所有操作系统都将很快正确支持它。

相关内容