【问题标题】:What's the difference between various $SIG{CHLD} values?各种 $SIG{CHLD} 值之间有什么区别?
【发布时间】:2012-01-13 10:25:48
【问题描述】:

这些设置有什么区别?

$SIG{CHLD} = 'IGNORE'  
$SIG{CHLD} = 'DEFAULT'  
$SIG{CHLD} = ''  
$SIG{CHLD} = undef

根据“Advanced Programming in the UNIX Environment, 2nd edition”,图 10.1 SIGCHLD 的默认值为“ignore”。

如果“忽略”意味着“SIG_IGN”,那么任何孩子都不会成为僵尸,而事实并非如此。

从那里并没有变得更清楚:

如果进程专门将其处置设置为 SIG_IGN,则子进程 调用进程不会产生僵尸进程。注意 这与它的默认操作 (SIG_DFL) 不同,后者来自图 10.1 将被忽略。相反,在终止时,这些子进程的状态 被丢弃。

我很难理解各种值(或未定义的非值)的影响是什么。到目前为止,解决方案一直是轮流选择这些选项,直到获得所需的行为,我宁愿准确了解每个值如何定义信号的行为。

行为:子进程正在调用“系统”或使用创建另一个子进程的反引号,并且信号通常会被错误的(父)处理程序捕获。设置本地处理程序可以工作,但如果我希望来自孙子的信号什么都不做,我不明白哪个值最合适。

有人可以给我发光吗?

更新: 根据ikegami的反馈,我做了一些具体的测试。该行为至少部分是特定于平台的。

考虑以下片段:

$SIG{CHLD} = sub {
    while( ( my $child = waitpid( -1, &WNOHANG ) ) > 0 ) {
        print "SIGNAL CHLD $child\n";
    }
};

my $pid = fork();

if( ! $pid ) {
    system( 'echo Grandchild PID = $$' );
    sleep 2;
    exit;
}

print "Child PID = $pid\n";
sleep 5;

Solaris 10 上的 Perl 5.8.6 将为 system() 调用的 PID 显示“SIGNAL CHLD”消息。做任何事,哪怕是微不足道的事情

本地 $SIG{CHLD};

在孩子中会抑制这些消息。

在我尝试过的所有其他口味中,收割者都看不到孩子。

【问题讨论】:

  • local $SIG{CHLD};local $SIG{CHLD} = undef; 相同

标签: perl unix signals signal-handling sigchld


【解决方案1】:

有两种方法可以避免创建僵尸进程:

  1. 显式设置$SIG{CHLD}='IGNORE'
  2. 使用waitwaitpid 调用明确地收割死去的孩子(这可以在 SIGCHLD 处理程序中完成,但不必这样做)

设置$SIG{CHLD}='IGNORE' 会在操作系统级别捕获 SIGCHLD,清理子进程甚至无需向您的 Perl 程序发出信号。

任何其他设置,包括'DEFAULT'undef""sub {}'some_function_name_that_doesnt_even_exist' 都会导致信号被传递到 Perl,并且不会自动收割子节点。

通过使用waitwaitpid 自己获取进程,您可以获得额外的信息,例如子进程的退出状态,以及(或多或少)子进程完成的顺序。如果$SIG{CHLD} 设置为'IGNORE'waitwaitpid 总是返回-1,不要设置$?

SIGCHLD,如果有的话,总是传递给产生子进程的进程,所以我认为你说来自孙进程的SIGCHLD 是不正确的(来自system 调用在子进程中)被父进程捕获。可能发生的情况是您的子进程从其父进程继承了信号处理程序。

system 和反引号将(在大多数系统上)在完成时生成SIGCHLD,并将设置$? 值与命令的退出状态。但是 Perl 会自己获取这些子进程,您将无法捕获 system 或使用 waitwaitpid 的反引号调用的进程 ID。

【讨论】:

  • 3.通过退出隐含地收割死去的孩子。
【解决方案2】:

%SIG

$SIG{CHLD} = 'IGNORE'; 导致您的进程忽略 SIGCHLD 信号。

$SIG{CHLD} = 'DEFAULT'; 使您的进程处理 SIGCHLD 信号,因为它不会让您弄乱$SIG{CHLD} 或等效项。根据 kill(1),我系统上的一个进程默认忽略 SIGCHLD

$SIG{CHLD} = '';$SIG{CHLD} = undef; 不是有效值。

至于收割,其 SIGCHLD 处理程序显式设置为 IGNORE 的父级的子级将在退出后立即被系统自动收割。

$ perl -e'
   my $pid = fork;
   if (!$pid) { sleep(1); exit; }
   sleep(2);
   system "ps -o pid,stat,command $pid";
'
  PID STAT COMMAND
14667 ZN+  [perl] <defunct>

$ perl -e'
   $SIG{CHLD}="IGNORE";
   my $pid = fork;
   if (!$pid) { sleep(1); exit; }
   sleep(2);
   system "ps -o pid,stat,command $pid";
'
  PID STAT COMMAND

$

【讨论】:

  • ''undef 是有效值,它们是 DWIM,delete $SIG{CHLD} 也是如此。
  • @mob,你是说undef 设置为IGNORE 和delete 设置为DEFAULT?尽管如此,这并不能使它们有效。从您在解决方案中所说的来看,当前似乎只是缺少错误检查(因为使用IGNORED 实际上会执行DEFAULT)。
  • 它们都与分配$SIG{CHLD}='DEFAULT'没有区别。它们是有效的,因为它们不会破坏代码或发出警告——我并不是要暗示这些结构是 Perl 规范的一部分。未来的实现可能会禁止或阻止这些分配,或者对它们赋予完全不同的含义。
【解决方案3】:

“忽略”有两种不同的含义,它们发生在两个不同的评估点。

第一个用途涉及是否传递信号。 当子进程退出时,通常由操作系统持有,并向其父进程发送 CHLD 信号。当将 $SIG{CHLD} 设置为 'IGNORE' 时,它告诉系统不要生成信号根本,而只是让子进程退出。父进程没有机会获得子进程的信息,因此不会导致僵尸进程。

但如果 $SIG{CHLD} 不是 'IGNORE',则表示将信号传递给父进程,此时有自定义信号处理程序或默认信号处理程序,默认处理程序将在不同的系统甚至同一操作系统的版本上有所不同。这本书和 signal(7) 手册页讨论了默认情况下会发生什么如果将信号传递给父进程(即处理程序未设置为“IGNORE”)。所以ignore 的第二次使用与为传递的信号采取的动作 相关。例如,SIGINT 将导致您的进程终止,而 SIGSTOP 将导致您的进程“暂停”,尽管自定义信号处理程序会更改这些默认操作。

对于 SIGCHLD 信号,默认是“忽略”它,但在 this 用法中,它只是意味着(父)进程在子进程等待时继续正常执行(即没有终止或中止父进程)。然后,您可以稍后对其进行 wait() 以获取它的信息并避免僵尸。

【讨论】:

    【解决方案4】:

    $SIG{CHLD} = '忽略'

    这是作为 SIG_IGN 传递给操作系统的,根据操作系统文档,这会使子进程终止而不会成为僵尸(或将其退出代码报告给父进程)。

    $SIG{CHLD} = '默认'
    $SIG{CHLD} = ''
    $SIG{CHLD} = undef

    这些在操作系统级别都是相同的(它们将使用 SIG_DFL 调用 signal())。然而,当 $SIG{CHLD} 设置为 'DEFAULT' 时,一些 perl 包,尤其是 AnyEvent::child 将无法工作。

    【讨论】:

    • 你的意思是“没有”在“终止于成为”中
    • 好的,谢谢。 (我正在处理非常旧的电子邮件。)
    猜你喜欢
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 2011-05-24
    • 1970-01-01
    • 2019-03-31
    • 1970-01-01
    • 2020-06-08
    相关资源
    最近更新 更多