标准输出影响SIGKILL?

我有一个脚本来限制命令的执行时间长度。

limit.php

<?php declare(ticks = 1); if ($argc<2) die("Wrong parameter\n"); $cmd = $argv[1]; $tl = isset($argv[2]) ? intval($argv[2]) : 3; $pid = pcntl_fork(); if (-1 == $pid) { die('FORK_FAILED'); } elseif ($pid == 0) { exec($cmd); posix_kill(posix_getppid(), SIGALRM); } else { pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');")); sleep($tl); posix_kill($pid, SIGKILL); die("TIMEOUT_KILLED : $pid"); } 

然后我用一些命令testing这个脚本。

testingA

 php limit.php "php -r 'while(1){sleep(1);echo PHP_OS;}'" 3 

3秒之后,我们可以发现这个过程正如我们所预料的那样死亡。

testingB

删除输出代码并再次运行。

 php limit.php "php -r 'while(1){sleep(1);}'" 3 

结果看起来不太好,函数“exec”创build的进程没有像TEST A那样被杀死。

 [alix@s4 tmp]$ ps aux | grep whil[e] alix 4433 0.0 0.1 139644 6860 pts/0 S 10:32 0:00 php -r while(1){sleep(1);} 

系统信息

 [alix@s4 tmp]$ uname -a Linux s4 2.6.18-308.1.1.el5 #1 SMP Wed Mar 7 04:16:51 EST 2012 x86_64 x86_64 x86_64 GNU/Linux [alix@s4 tmp]$ php -v PHP 5.3.9 (cli) (built: Feb 15 2012 11:54:46) Copyright (c) 1997-2012 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies 

为什么在testingA中死亡的进程,而在testingB中没有? 输出是否影响SIGKILL?

任何build议?

php -r 'while(1){sleep(1);echo PHP_OS;}process C )和它的父process Bprocess B ), posix_kill($pid, SIGKILL)发送KILL信号给process Bprocess B终止,但process C不知道任何有关信号,并继续运行,并输出的东西到broken pipe ,当process C收到SIGPIPE信号,但不知道如何处理它,所以它退出。

php limit.php "strace php -r 'while(1){sleep(1); echo PHP_OS;};'" 1 ),你会看到如下所示的内容:

 14:43:49.254809 write(1, "Linux", 5) = -1 EPIPE (Broken pipe) 14:43:49.254952 --- SIGPIPE (Broken pipe) @ 0 (0) --- 14:43:49.255110 close(2) = 0 14:43:49.255212 close(1) = 0 14:43:49.255307 close(0) = 0 14:43:49.255402 munmap(0x7fb0762f2000, 4096) = 0 14:43:49.257781 munmap(0x7fb076342000, 1052672) = 0 14:43:49.258100 munmap(0x7fb076443000, 266240) = 0 14:43:49.258268 munmap(0x7fb0762f3000, 323584) = 0 14:43:49.258555 exit_group(0) = ? 

至于php -r 'while(1){sleep(1);} ,因为在父母死后没有发生broken pipe ,所以它继续按预期运行。

一般来说,如果你想杀掉它的子进程,你应该杀掉整个进程组,但是如果你想杀掉它的子process B ,你可以把process B加到它自己的进程组中,然后杀掉整个进程组,这里是diff你的代码:

 --- limit.php 2012-08-11 20:50:22.000000000 +0800 +++ limit-new.php 2012-08-11 20:50:39.000000000 +0800 @@ -9,11 +9,13 @@ if (-1 == $pid) { die('FORK_FAILED'); } elseif ($pid == 0) { + $_pid = posix_getpid(); + posix_setpgid($_pid, $_pid); exec($cmd); posix_kill(posix_getppid(), SIGALRM); } else { pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');")); sleep($tl); - posix_kill($pid, SIGKILL); + posix_kill(-$pid, SIGKILL); die("TIMEOUT_KILLED : $pid"); } 

你发送杀手信号到你的分叉进程,但是不会传播给它的孩子或孙子。 因此,他们成为孤儿,继续奔跑,直到阻止他们这样做。 (在这种情况下,任何写入stdout的尝试都会导致一个错误,然后强制它们退出,输出的重定向也可能导致无限期运行的孤儿。)

你想发送一个kill信号给进程,所有的都是子进程。 不幸的是我缺乏知识来告诉你一个很好的方法来做到这一点。 我不是很熟悉PHP的过程控制功能。 可以解析ps的输出。

我发现一个简单的方法是使用kill命令向整个进程组发送一个kill信号。 这很麻烦,它增加了一个额外的“杀死”的消息在我的机器上输出,但它似乎工作。

 <?php declare(ticks = 1); if ($argc<2) die("Wrong parameter\n"); $cmd = $argv[1]; $tl = isset($argv[2]) ? intval($argv[2]) : 3; $pid = pcntl_fork(); if (-1 == $pid) { die('FORK_FAILED'); } elseif ($pid == 0) { exec($cmd); posix_kill(posix_getppid(), SIGALRM); } else { pcntl_signal(SIGALRM, create_function('$signo',"die('EXECUTE_ENDED');")); sleep($tl); $gpid = posix_getpgid($pid); echo("TIMEOUT_KILLED : $pid"); exec("kill -KILL -{$gpid}"); //This will also cause the script to kill itself. } 

有关更多信息,请参阅: 杀死所有子进程的最佳方法