fork/wait/exec

"Operating System Concepts"でこのあたり読んでたので、perlであれこれやってみる。

超基本

子プロセスをwaitし、exit statusを表示するという単純なことをやってみる。

my $pid = fork;
die $! unless defined $pid;

if ( $pid == 0 ) {
    sleep 1;
    exit 1;
}

print "wait for child($pid)\n";
wait;

printf "child exits(%d)\n", $? >> 8;

ちゃんと子プロセスの結果を待って、

wait for child(13465)
child exits(1)

という結果が出た。

$SIG{CHILD}

$SIG{CHILD}='IGNORE'に設定すると、親側でwaitしてなくてもzombieプロセスをつくらずに済む。IGNOREにセットしているとwaitは"-1"を返す。

On most Unix platforms, the "CHLD" (sometimes also known as "CLD") sig-
nal has special behavior with respect to a value of 'IGNORE'. Setting
$SIG{CHLD} to 'IGNORE' on such a platform has the effect of not creat-
ing zombie processes when the parent process fails to "wait()" on its
child processes (i.e. child processes are automatically reaped). Call-
ing "wait()" with $SIG{CHLD} set to 'IGNORE' usually returns "-1" on
such platforms.

$SIG{CHLD} = 'IGNORE';

my $pid = fork;
die $! unless defined $pid;

if ( $pid == 0 ) {
    sleep 1;
    exit 1;
}
print wait; # 実際-1がprintされる

複数のプロセスをfork

試しにこんな感じでやってみる。

use Time::HiRes qw(sleep);

main();

sub main {

    my @children;
    for ( 1..10 ) {
        my $pid = fork;
        child_proc() if ( $pid == 0 );

        push @children, $pid;
        sleep 0.1;
    }

    my $finished = wait;
    print "$finished\n";

    print "not wait for all children\n";
}

sub child_proc {
    sleep 1;
    exec('ls');
}

waitのpodには"returns the pid of the deceased process"と書いてあるので、やはり1つの子プロセスしか回収してくれない模様。当たり前っちゃ当たり前か。


全部のプロセスを待つように変えてみる。

use Time::HiRes qw(sleep);

main();

sub main {

    my %children;
    for ( 1..10 ) {
        my $pid = fork;
        child_proc() if ( $pid == 0 );

        $children{$pid} = 1;
        sleep 0.1;
    }

    while ( keys %children ) {
        my $fin = wait;
        delete $children{$fin};

        printf "pid(%d) end. left: %d\n", $fin, scalar keys %children;
    }

    print "all children finished\n";

}

sub child_proc {
    sleep 1;
    exec('ls /tmp/kotaro');
}
ito.kotaro@mac:~/Dev/Perl/work> perl fork.pl 
foo
pid(14101) end. left: 9
foo
pid(14102) end. left: 8
foo
pid(14103) end. left: 7
foo
pid(14104) end. left: 6
foo
pid(14105) end. left: 5
foo
pid(14106) end. left: 4
foo
pid(14107) end. left: 3
foo
pid(14108) end. left: 2
foo
pid(14109) end. left: 1
foo
pid(14110) end. left: 0
all children finished

まとめ

fork, wait, execがちょっとわかった

NextStep

  • use POSIX qw( :sys_wait_h )とかProc::Wait3とかもうちょっと深堀りしておきたい
  • waitpidとかも
  • そして、Cでも試してみたい