为什么不是我拿起从我的子进程的退出状态?(Why aren't I picking up t

2019-10-18 14:35发布

我有我的管理上有叉关闭多个进程的能力(高达规定的极限),监视它们Perl程序,当他们退出,叉掉额外的过程(再一次,到了极限),直到事情运行列表完成。 它工作正常,但由于某种原因,它似乎没有从我的子进程拿起正确的退出状态。

不工作的代码使用Perl的fork()waitpid()和子进程使用POSIX::_exit()退出。 下面是相关的代码中的一些摘录:

分叉代码:

# Initialize process if running in parallel mode
my $pid;
if ($options{'parallel'} > 0) {
    log_status("Waiting to fork test #".$curr_test{'id'}."...\n");

    # Here, wait for child processes to complete so we can fork off new ones without going over the specified limit
    while ( keys(%children) >= $options{'parallel'}) {
        my $kid = waitpid(-1, 0);
        my $kid_status = $?;

        if ($kid > 0) {
            log_status("Child process (PID ".$kid.", test ".$children{$kid}.") exited with status ".$kid_status.".\n");
            $error_status |= $kid_status;
            delete $children{$kid};
        }
    }

    $pid = fork();
    tdie("Unable to fork!\n") unless defined $pid;

    if ($pid != 0) {
        # I'm the parent
        $is_child = 0;
        log_status("Forked child process (PID ".$pid.").\n");

        $children{$pid} = $curr_test{'logstr'};

        next TEST_LOOP;
    }
    else {
        # I'm the child
        $is_child = 1;
        log_status("Starting test = ".$curr_test{'logstr'}."\n");
    }
}

退出子进程的代码:

### finish_child() ###
# Handles exiting the script, like the finish() function, but only when running as a child process in parallel mode.
# Parameters:
#   - The error code to exit with
###
sub finish_child( $ ) {
    my ($error_status) = @_;


    # If running in parallel mode, exit this fork
    if ($options{'parallel'} > 0) {
        log_status("Entering: ".Cwd::abs_path("..")."\n");
        chdir "..";
        log_status("Exiting with status: ".$error_status."\n");
        POSIX::_exit($error_status);
    }
}

这里就是finish_child()被调用在我的例子来看:

# If build failed, log status and gracefully clean up logfiles, then continue to next test in list.
if ($test_status > 0) {
    $email_subject = "Build failed!";
    log_status("Build of ".$testline." FAILED.\n");
    tlog(1, "Build of ".$testline." FAILED.\n");

    log_status("Entering: ".Cwd::abs_path("..")."\n");
    chdir "..";


    log_report(\%curr_test, $test_status);

    # Print out pass/fail status for each test as it completes
    $quietmode = $options{'quiet'}; # Backup quiet mode setting
    $options{'quiet'} = 0;

    if ($test_status == 0) {
        log_status("Test ".$testline." PASSED.\n");
        tlog(0, "Test ".$testline." PASSED.\n");
    }
    else {
        log_status("Test ".$testline." FAILED.\n");
        tlog(1, "Test ".$testline." FAILED.\n");
    }

    $options{'quiet'} = $quietmode;  # Restore quiet mode setting
    finish_logs();


    # Link logs to global area and rename if running multiple tests
    system("ln -sf ".$root_dir."/verify/".$curr_test{'id'}."/".$verify::logfile." ../".(($test_status > 0) ? "fail".$curr_test{'id'}.".log" : "pass".$curr_test{'id'}.".log" )) if (@tests > 1);


    if ($options{'parallel'} > 0 && $pid == 0) {
        # If we're in parallel mode and I'm a child process, I should exit, instead of continuing to loop.
        finish_child($test_status);
    }
    else {
        # If we're not in parallel mode, I should continue to loop.
        next TEST_LOOP;
    }
}

下面是我根据日志从运行我没有看到的行为:

<Parent> Waiting for all child processes to complete...
<Child> [PID 28657] Entering: <trimmed>
<Child> [PID 28657] Running user command: make --directory <trimmed> TARGET=build BUILD_DIR=<trimmed> RUN_DIR=<trimmed>            
<Child> [PID 28657] User command finished with return code: 512
<Child> [PID 28657] Build step finished with return code 512
<Child> [PID 28657] Entering: <trimmed>
<Child> [PID 28657] Build of rx::basic(1) FAILED.
<Child> [PID 28657] Entering: <trimmed>
<Child> [PID 28657] Test rx::basic(1) FAILED.
<Child> [PID 28657] Closing log file.
<Child> [PID 28657] Closing error log file.
<Child> [PID 28657] Entering: <trimmed>
<Parent> Child process (PID 28657, test rx::basic(1)) exited with status 0.

我有一个使用Perl的IPC运行命令(代替的代码system()调用,有更多的灵活性,妥善拿起退出代码,您可以从日志文件中的“用户命令”行看。

还有什么比我做错了,在这里? 我为什么不能够拿起从退出状态$? 在这种情况下? 我在网上找到的例子都似乎表明,这应该能正常运行。

作为参考,我跑Perl v5.10.1 。 这Perl的工具也是开源GitHub上,如果你觉得你需要通过代码的其余部分看: https://github.com/benrichards86/Verify/blob/master/verify.pl

Answer 1:

如果$test_status是512,你叫POSIX::_exit(512) 这是不正确。 子进程应该调用POSIX::_exit在0到255的范围内操作,而且收获的是孩子会得到Perl的父进程$? 设置退出状态 << 8

POSIX::_exit(512)等效于POSIX::_exit(512 % 256)POSIX::_exit(0)



Answer 2:

看来你在做什么,总计为以下几点:

exit($?)

你的意思是传播价值传递给孩子exit ,但是这不是什么$? 包含的内容。

如果孩子被一个信号,打死$? & 0x7F $? & 0x7F含有杀的过程中信号的数量。

如果孩子不是由一个信号,打死$? & 0x7F $? & 0x7F是零, $? >> 8 $? >> 8包含一个价值传递给进程exit

所以,当孩子不exit(1)你做exit(256)这是超出范围在Unix系统。 高比特被砍掉留给你零( 256 & 0xFF = 0 )。


我建议你做什么bash呢:

exit( ($? & 0x7F) ? ($? | 0x80) : ($? >> 8) );

当孩子做exit(1)这确实exit(1)

当孩子,比方说,SIGTERM(15)杀害,这确实exit(128 + 15)



Answer 3:

是的,这可能是解释,但什么我感兴趣的是,你的测试输出不表明孩子实际使用的退出状态。 有一个在代码中的日志消息(“与状态退出...”),但在输出中没有相应的行。

所以,我们真的不能告诉如果有什么是您在代码的这一部分会错。

我首先想到使用POSIX的:: _出口或许可以解释日志问题(这将阻止刷新最终缓冲区),而是看你的代码,我再次看到你已经变成调用finish_child之前注销。

我会建议为你的日志记录工作的权利,这样你可以告诉那里的问题是第一步。 为什么作为最后一件事退出之前做不动日志关闭和日志文件重命名逻辑到终点孩子日常?

至于退出状态的问题,我看到三种可能的解释,所有子进程的代码:

  • 孩子实际上没有通过功能finish_child退出
  • 你认为非零状态被传递给finish_child再到实际退出不被通过
  • 按照以上建议,你的退出状态为> 255

是为什么你正在使用POSIX :: _ exit()退出,而不是退出()和waitpid函数(-1),而不是等待()什么特别的原因?



文章来源: Why aren't I picking up the exit status from my child process?
标签: perl posix fork