我有一个脚本来限制命令的执行时间长度。
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 B
( process B
), posix_kill($pid, SIGKILL)
发送KILL
信号给process B
, process 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. }
有关更多信息,请参阅: 杀死所有子进程的最佳方法