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

时间:2012-05-15 11:09:35

标签: perl signal-handling

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

因此,从while循环和处理程序访问%children。这似乎没有问题:

  1. 不会有两个进程具有相同的pid
  2. 访问权限由pid键入(我不确定$childer{pid}=1是否是原子的,可中断而不会导致损坏。)
  3. 现在,我正在尝试在我的处理程序中做更多事情:

    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导出,但我认为这也可能存在问题(例如,当执行计算的子程序在求和时被中断时)。

    所以我的问题是:

    • 我需要以某种方式同步吗?

    并注意到:

    • 是否可以在perl 5.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;
             # ...
        }
    }
    

1 个答案:

答案 0 :(得分:1)

对我来说似乎是一个坏主意。 IPC :: Semaphore 可能解决问题,如果你能让它们在信号处理程序中正常工作 - 如果控制权在处理程序退出之前没有返回,你就是运气不好但是,您可以通过锁定父级并让子级等待锁定直到初始化完成来解决这个问题。处理程序不涉及信号量。我认为你实际上只需要一把锁。无论如何:

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}。等等,是的,你必须同步。