perl 从信号处理程序安全访问共享数据结构

r8uurelv  于 8个月前  发布在  Perl
关注(0)|答案(2)|浏览(86)

我试图决定从为x86_64-linux-thread-multi构建的perl(v5.14.2)中的信号处理程序访问一个公共(读:处理程序代码和程序的其余部分共享)数据结构是否安全,但目标平台是solution 11)。
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);
        # ...
   }
}

因此,%children是从while-loop和handler访问的。这似乎没有问题,因为:
1.不会有两个进程具有相同的pid
1.访问是由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;
         # ...
    }
}
wgxvkvu9

wgxvkvu91#

对我来说似乎是个坏主意。**IPC::信号量 * 可能 * 解决这个问题,如果你能让它们在信号处理程序中正常工作--如果控制直到处理程序退出才返回,你就不走运了。然而,你可以通过锁定父进程并让子进程等待锁直到初始化完成来解决这个问题;处理程序不涉及信号量。我认为你实际上只需要一个锁。总之:

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}将不存在于处理程序中。等等,是的,你必须同步。

jum4pzuy

jum4pzuy2#

当信号处理程序和主程序共享全局数据结构时,从主程序访问数据的一般指导原则是“阻止信号”,以避免不一致。

相关问题