/bin/sh -c CMD 将分叉 sh,然后执行 CMD。
/bin/sh -c exec CMD 不会分叉,只会执行 CMD。
因此,您可以通过在命令前加 "exec bla bla bla" 来消除这种 hack。
(PHP 5, PHP 7, PHP 8)
proc_terminate — 终止由 proc_open 打开的进程
向 process
(使用 proc_open() 创建)发出终止信号。 proc_terminate() 会立即返回,不会等待进程终止。
proc_terminate() 允许您终止进程并继续执行其他任务。可以使用 proc_get_status() 函数轮询进程(查看进程是否已停止)。
process
将要关闭的 proc_open() 资源。
signal
此可选参数仅在 POSIX 操作系统上有效;您可以使用 kill(2)
系统调用指定要发送给进程的信号。默认值为 SIGTERM
。
返回运行的进程的终止状态。
/bin/sh -c CMD 将分叉 sh,然后执行 CMD。
/bin/sh -c exec CMD 不会分叉,只会执行 CMD。
因此,您可以通过在命令前加 "exec bla bla bla" 来消除这种 hack。
如 http://bugs.php.net/bug.php?id=39992, 中所述,proc_terminate() 会让子进程的子进程继续运行。在我的应用程序中,这些子进程通常有无限循环,因此我需要一种可靠的方法来杀死使用 proc_open() 创建的进程。当我调用 proc_terminate() 时,/bin/sh 进程会被杀死,但具有无限循环的子进程会继续运行。
在 proc_terminate() 被修复之前,我不建议使用它。相反,我的解决方案是
1) 调用 proc_get_status() 获取要杀死的进程的父进程 ID (ppid)。
2) 使用 ps 获取所有具有该 ppid 作为父进程 ID 的进程 ID。
3) 使用 posix_kill() 向这些子进程 ID 中的每一个发送 SIGKILL (9) 信号。
4) 对进程资源调用 proc_close()。
<?php
$descriptorspec = array(
0 => array('pipe', 'r'), // stdin 是一个管道,子进程将从中读取
1 => array('pipe', 'w'), // stdout 是一个管道,子进程将写入其中
2 => array('pipe', 'w') // stderr 是一个管道,子进程将写入其中
);
$process = proc_open('bad_program', $descriptorspec, $pipes);
if(!is_resource($process)) {
throw new Exception('bad_program 无法启动。');
}
// 向程序传递一些输入
fwrite($pipes[0], $lots_of_data);
// 关闭 stdin。通过关闭 stdin,程序应在完成输入处理后退出
//
fclose($pipes[0]);
// 做一些其他事情 ... 进程可能仍然在运行
// 如果我们立即检查它
$status = proc_get_status($process);
if($status['running'] == true) { // 进程运行时间过长,将其杀死
// 关闭所有仍然打开的管道
fclose($pipes[1]); // stdout
fclose($pipes[2]); // stderr
// 获取要杀死的进程的父进程 ID
$ppid = $status['pid'];
// 使用 ps 获取此进程的所有子进程,并将其杀死
$pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`);
foreach($pids as $pid) {
if(is_numeric($pid)) {
echo "杀死 $pid\n";
posix_kill($pid, 9); // 9 是 SIGKILL 信号
}
}
proc_close($process);
}
?>
在 Windows 平台上,proc_terminate() 不会杀死不处理杀死信号的子进程。即使您调用 xxx.exe 并调用 proc_terminate(),进程也会保持活动状态。
解决方案是不要调用 proc_terminate(),而是调用用户定义的 kill() 函数(已经针对 win/unix 进行了优化)
之后需要关闭所有管道并执行 proc_close()。
function kill($pid){
return stripos(php_uname('s'), 'win')>-1 ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
}
function killall($pids) {
$os=stripos(php_uname('s'), 'win')>-1;
($_=implode($os?' /PID ':' ',$pids)) or ($_=$pids);
return preg_match('/success|close/', $os ? exec("taskkill /F /T /PID $_") : exec("kill -9 $_"));
}
示例
$pstatus = proc_get_status($resource);
$PID = $pstatus['pid'];
// 其他命令
kill($PID); // 代替 proc_terminate($resource);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($resource);
只是一个小的说明,这样人们就不用去别的地方查找了
要获取进程列表或按名称查找进程,请使用
$proclist = shell_exec('ps -elF') 用于 linux
$proclist = shell_exec('tasklist') 用于 windows
之后,您可以使用 php 的正常解析函数来获取进程 ID