我有一个脚本来限制命令的执行时间。
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");
}然后我用一些命令测试这个脚本。
试验A
php limit.php "php -r 'while(1){sleep(1);echo PHP_OS;}'" 3在3s之后,我们可以发现过程如我们所期望的被杀死了。
试验B
删除输出代码并再次运行。
php limit.php "php -r 'while(1){sleep(1);}'" 3结果看起来不太好,函数"exec“创建的过程没有像测试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为什么进程在测试A中而不是在测试B中终止?输出会影响SIGKILL吗?
有什么建议吗?
发布于 2012-08-10 06:33:45
PIPE在php -r 'while(1){sleep(1);echo PHP_OS;} (process C)和它的父级(process B)之间,posix_kill($pid, SIGKILL)向process B发送KILL信号,然后process B终止,但是process C不知道信号的任何信息,继续运行并向broken pipe输出一些东西,当process C接收SIGPIPE信号时,但不知道如何处理它以使其退出。
您可以使用strace (运行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,所以它继续按预期运行。
一般来说,您应该杀死整个进程组,但不仅仅是进程本身,如果您也想杀死它的子进程,使用PHP,您可以将process B添加到它自己的进程组中,然后杀死整个组,下面是您的代码的区别:
--- 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");
}发布于 2012-08-10 06:02:02
您将杀死信号发送到您的分叉过程,但这不会传播到它的子代或孙辈。因此,他们成为孤儿,并继续运行,直到有什么东西阻止他们这样做。(在这种情况下,任何向stdout写入的尝试都会导致一个错误,然后迫使它们退出。重定向输出也可能导致无限期运行的孤儿。)
你想要发送一个杀死信号给进程和所有的孩子。不幸的是,我缺乏知识来告诉你一个很好的方法来做到这一点。我不太熟悉PHP的过程控制功能。可以解析ps的输出。
不过,我发现一种简单的工作方式是使用杀死命令向整个进程组发送一个杀死信号。这很麻烦,它为我的机器上的输出添加了一个额外的“杀死”消息,但它似乎是有效的。
<?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.
}有关更多信息,请参见:Best way to kill all child processes
https://stackoverflow.com/questions/11894729
复制相似问题