Имам програма на Perl, която управлявам, която има способността да разклонява множество процеси (до определен лимит), да ги наблюдава и когато излизат, да разклонява допълнителни процеси (отново, до лимита), докато списъкът с неща за изпълнение не бъде завършен. Работи добре, с изключение на това, че по някаква причина изглежда, че не улавя правилното състояние на изход от моите дъщерни процеси.
Кодът, който не работи, използва fork()
, waitpid()
на Perl, а дъщерните процеси използват 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.
Имам код, който използва IPC на Perl за изпълнение на команди (вместо извикването system()
, за по-голяма гъвкавост, който улавя изходния код правилно, което можете да видите в редовете „Потребителска команда“ от регистрационния файл.
Какво може да правя грешно тук? Защо в този случай не мога да получа състоянието на изход от $?
? Примерите, които намерих онлайн, изглежда показват, че това трябва да работи добре.
За справка, изпълнявам Perl v5.10.1
. Този инструмент Perl също е с отворен код в GitHub, ако смятате, че трябва да прегледате останалата част от кода: https://github.com/benrichards86/Verify/blob/master/verify.pl
finish_child()
, поради което не се появи във фрагмента от регистрационния файл, който публикувах по-горе. Това обаче беше единствената разлика. Оттук и несъответствието. Съжалявам за това. - person Ben Richards   schedule 05.09.2013