在Perl中,如何检测外部命令的分段错误?

8
以下是将导致崩溃的 C 代码:
#include<stdio.h>
#include<stdlib.h>

int main() {
    char *p = NULL;
    printf("Value at P: %c\n", *p);
    return 0;
}

当我在RH4机器上使用gcc 4.5.2进行编译和运行时,可预见地会出现分段错误:

%  ./a.out
Segmentation fault

%  echo $status
139

如果我使用Perl v5.8.5运行它,会发生以下情况:

%  perl -e 'system("./a.out") and die "Status: $?"'
Status: 11 at -e line 1.

perlvar 文档中关于$?的解释为:

因此,子进程的退出值实际上是 ($?>> 8 )$? & 127 可以获得进程死亡时的信号(如果有的话),$? & 128 则表示是否有内核转储(core dump)。

11 >> 8 的结果是011 & 127 的结果是11

为什么会有不同的退出状态?如果我们不能依赖退出状态,那么如何检测外部命令的分段错误(segmentation fault)呢?


$status 是如何设置的? - Zaid
4
看起来实际上你已经得到了进程崩溃的信号:11代表分段错误,而139&127=11 - Miklos Aubert
我刚刚用Cygwin(perl版本为5.14.2)尝试了一下,它给了我这个:状态:-e行1处的139。 - Miklos Aubert
1
请注意,139&127等同于11&127。任何大于127的数字都将别名为n%128 - Zaid
2个回答

14

阅读 system 的文档可能会回答你的问题:

system('a.out');

if ($? == -1) {
    print "failed to execute: $!\n";
}
elsif ($? & 127) {
    printf "child died with signal %d, %s coredump\n",
        ($? & 127),  ($? & 128) ? 'with' : 'without';
}
else {
    printf "child exited with value %d\n", $? >> 8;
}

输出:

child died with signal 11, without coredump

Shell只是以不同的方式在状态中编码信号:139 - 128 = 11。例如,man bash中说:

简单命令的返回值是其退出状态,如果命令被信号n终止,则为128 + n。


1
@Unos,信号编号从1开始,最高可达127。来自shell的退出代码超过128是不明确的,因为它将“被信号杀死”和“以错误退出”数字混合在一起。例如,139是由SEGFAULT杀死还是exit(139)?无法从shell中确定,但可以从Perl中确定。 - ikegami
好的,如果我理解正确,shell看到了一个段错误的信号00001011,并设置了高位,因此状态变为10001011,即139。Perl正确解码并将$?设置为11。 - Unos

3
操作系统一直看到的都是wait(2)函数族返回的东西。system(3)等调用的都是wait(2),导致差异的原因在于信息传递方式的不同。Shell和其他程序所做的事情以及报告方式也不同。要在Shell程序中右移8位以获得最常见的退出状态将非常麻烦,还会让技术程度较低的用户感到困惑。
虽然我使用的第一个UNIX系统(真正的UNIX)返回值相同,但我一直想知道预发布版本是否有所不同,而返回信号和core/dump是否是后来添加的。
我的首选退出状态报告方式是让计算机自动拆分我的位。
my $x = $?; #保存状态 print( "退出状态:%d.%d%s\n", $x>>8, $x&127, ($x&128)?"*":"" );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接