4

のために構築されたperl(v5.14.2)のシグナルハンドラーから共通の(ハンドラーコードと残りのプログラム間で共有される)データ構造にアクセスしても安全かどうかを判断しようとしていますx86_64-linux-thread-multiが、ターゲットプラットフォームはsolaris11)です。

perlipcには次のサンプルコードがあります。

use POSIX ":sys_wait_h"; # for nonblocking read
my %children;
$SIG{CHLD} = sub {
    # don't change $! and $? outside handler
    local ($!, $?);
    my $pid = waitpid(-1, WNOHANG);
    return if $pid == -1;
    return unless defined $children{$pid};
    delete $children{$pid};
    cleanup_child($pid, $?);
};
while (1) {
    my $pid = fork();
    die "cannot fork" unless defined $pid;
    if ($pid == 0) {
        # ...
        exit 0;
    } else {
        $children{$pid}=1;
        # ...
        system($command);
        # ...
   }
}

したがって、%childrenwhileループとハンドラーからアクセスされます。これは問題ないようです:

  1. 同じものを持つ2つのプロセスはありませんpid
  2. アクセスは次のように調整されます(ただし、破損を引き起こすことなくアトミックで割り込み可能pidかどうかはわかりません)。$childer{pid}=1

今、私は私のハンドラーでさらに多くのことをしようとしています:

my %categoryForPid;
my %childrenPerCategory;

$SIG{CHLD} = sub {
    # ... acquire pid like above
    my $category = $categoryForPid{$pid};
    $childrenPerCategory{$category}--;
    delete $categoryForPid{$pid};
}

while (1) {
    # ... same as above
    } else {
        $children{$pid}=1;
        my $category = # ... chose some how
        $childrenPerCategory{$category}++;
        $categoryForPid{$pid} = $category;
        # ...
    }
}

ここでの考え方は、すべての子が特定のカテゴリ(Nから1)に属しているということです。カテゴリごとに何人の子供が存在するかを追跡したいと思います。その情報はから導き出すことができますが$categoryForPid、それも問題になる可能性があると思います(たとえば、計算を実行しているサブルーチンが合計中に中断された場合)。

だから私の質問は:

  • どういうわけかここで同期する必要がありますか?

ちなみに:

  • シグナルハンドラのネストされた呼び出しはperl5.12で可能ですか、それともインタプリタによって線形化されていますか?

アップデート

@goldilocksと彼が提案した解決策によって発見された問題に加えて、「原子性」を確保するためにデータ構造を更新している間、信号をブロックします。

my $sigset = POSIX::SigSet->new(SIGCHLD);

sub ublk {
    unless (defined sigprocmask(SIG_UNBLOCK, $sigset)) {
        die "Could not unblock SIGCHLD\n";
    }
}

sub blk {
    unless (defined sigprocmask(SIG_BLOCK, $sigset)) {
        die "Could not block SIGCHLD\n";
    }
}

while (1) {
    # ... same as above
    } else {
         blk;
         $children{$pid}=1;
         my $category = # ... chose some how
         $childrenPerCategory{$category}++;
         $categoryForPid{$pid} = $category;
         ublk;
         # ...
    }
}
4

1 に答える 1

1

私には悪い考えのようです。 IPC::Semaphoreで問題 解決する可能性があります。シグナル ハンドラで適切に動作させることができる場合です。ハンドラが終了するまで制御が返されない場合は、不運です。ただし、親をロックし、初期化が完了するまで子をロックで待機させることで、これを回避できます。ハンドラーはセマフォに関与しません。そのために実際に必要なロックは1つだけだと思います。ともかく:

my @array = (1..10);
my $x = 'x';

$SIG{'USR1'} = sub {
    print "SIGUSER1\n";
    undef @array;
    $x = '!!';
};

print "$$\n";

foreach (@array) {
    print "$_:\n";
    sleep(2);
    print "\t$x\n";
    print "\t$array[$_ - 1]\n";
}

当然のことながら、これは次のことを行います。

2482
1:
    x
    1
2:
    x
    2
3:
SIGUSER1
    !!
Use of uninitialized value within @array in concatenation (.) or string at ./test.pl line 42.

この時点で信号をキャッチすると、次のようになります。

    my $category = # ... chose some how

$categoryForPid{$pid}ハンドラーには存在しません。など。つまり、はい、同期する必要があります。

于 2012-05-15T11:48:50.250 に答える