如果子发出信号,Perl 从 fork/exec 中检测到错误的退出代码

Posted

技术标签:

【中文标题】如果子发出信号,Perl 从 fork/exec 中检测到错误的退出代码【英文标题】:Perl detects wrong exit code from fork/exec if child signalled 【发布时间】:2017-08-10 09:56:31 【问题描述】:

我从 perl fork/exec 一个 C++ 程序并想检测孩子的退出状态(特别是当孩子因段错误退出时)

perl 行为不一致取决于子调用exit()、外部发出信号还是实际遇到段错误。

在 shell 中,C++ 的退出代码总是正确的。例如。 echo $? 与程序所做的匹配。

从 perl 中,perl 检测到的退出代码只有在调用 exit() 时才是正确的。如果程序因信号或段错误而退出,则 perl 检测到的退出代码始终为 0。

为什么?

我想在 perl 脚本中始终如一地获取 C++ 程序的退出代码。

perl 5.10.1、Centos 6 32 位、g++ 4.8.2

测试结果

实际段错误(boom 已启用):Perl Complete: 27322 exited (exit 0, sig 11, core 0) a.out 通过kill -11 <pid> 杀死:Perl Complete: 27248 exited (exit 0, sig 11, core 0) a.out 完成,调用 exit(139): Perl Complete: 27237 exited (exit 139, sig 0, core 0)

在上述所有三种情况下,从 shell 调用 a.out 始终显示正确的退出代码。

C++ A.OUT

(根据测试注释掉boom

#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>

int
main(int argc, char  **argv)

    printf("Started with PID %d\n", getpid());

    int d = 1;
    if (argc > 1)
        d = atoi(argv[1]);

    int loops = 10;
    while(loops-- > 0)
    
        printf("Ping\n");
        sleep(1);
    

    printf("MAKE SEGFAULT\n");
    int boom = *(int*)0x00;

    printf("exit with %d\n", d);
    exit(d);

PERL

#!/usr/bin/perl

use POSIX ":sys_wait_h";

my $fullprocname = "./a.out 139";
my $childPID = fork();

if ($childPID == 0)        # child

    open(STDERR, ">&SAVEERR");  # restore stderr
    $< = $>;                    # set real to effective uid, otherwise
                                # some env variables not passed in ??
    exec ($fullprocname) or die "exec $fullprocname failed: $!\n"; 

else                       # parent

    print("DVMon.pl: Forked $childPID for $fullprocname\n");


print("Waiting for child process\n");
my $child = wait();

my $exitStatus = $? >> 8;   # similar to WEXITSTATUS
my $termSig    = $? & 127;  # similar to WTERMSIG
my $coreDump   = $? & 128;  # similar to WCOREDUMP

my $msg = "$childPID exited (exit $exitStatus, sig $termSig, core $coreDump)";
print("Perl Complete: $msg\n");

【问题讨论】:

如果您使用 139 以外的其他值运行 C 代码(看不到 C++ 代码在哪里)会发生什么? 尝试使用 SIGUSR1 (10) 会得到相同的行为。 我怀疑退出代码在有信号的情况下是否有意义。 @PerlDuck 实际上应该是 128+信号号 如果是 C++,你会使用 cout 而不是 printf 并且包括“iostream”而不是“stdio.h” 【参考方案1】:

这是按设计工作的。在 shell 中,有两种方法可以从命令中获取退出代码 139。

    该命令进行系统调用exit(139) 命令以信号 11 终止

但是 POSIX-y 系统可以区分这两个事件,并且 Perl 挂钩到这些方法以使其 $? 变量比 shell 提供的信息更丰富。

【讨论】:

【参考方案2】:

如果程序因信号或段错误而退出,perl 检测到的退出代码始终为 0。为什么?

它从未到达exit,因此WEXITSTATUS 没有意义,因此系统将其设置为零。

Perl 显示的值是系统提供的值,它们是预期的值。例如,您将在 C 中获得与在 Perl 中相同的结果。

当一个进程被信号杀死时,系统会提供该信息。另一方面,sh 不提供该信息。如果您想知道为什么 sh$? 与 Perl 的 $? 不同,这是因为 shell 将退出代码分为 8 位,如下所示:

 $?<sh> = ( $?<real> & 0x7F ) ? ( $?<real> | 0x80 ) : ( $?<real> >> 8 );

【讨论】:

以上是关于如果子发出信号,Perl 从 fork/exec 中检测到错误的退出代码的主要内容,如果未能解决你的问题,请参考以下文章

如果子进程崩溃,是不是会发出 QProcess::finished?

逐行读取文件 fork/exec - Perl

Perl - 与 fork/exec'ed 进程通信

Perl:关闭信号处理程序中的子进程管道挂起?

shell调用另一个脚本的三种方式fork/exec/source

如何从命令行发出 dbus 信号