以下脚本监视/dev/shm/test
新文件,并实时输出有关它的信息。
问题在于,当用户关闭浏览器时,inotifywait
进程仍处于打开状态,依此类推。
有什么方法可以避免这种情况吗?
<?php
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "a") // stderr is a file to write to
);
$process = proc_open('inotifywait -mc -e create /dev/shm/test/', $descriptorspec, $pipes);
if (is_resource($process)) {
header("Content-type: text/html;charset=utf-8;");
ob_end_flush(); //ends the automatic ob started by PHP
while ($s = fgets($pipes[1])) {
print $s;
flush();
}
fclose($pipes[1]);
fclose($pipes[0]);
fclose($pipes[2]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
?>
答案 0 :(得分:1)
这是因为inotifywait
会等到文件/dev/shm/test/
发生变更,然后会输出标准错误和标准输出事件信息的诊断信息,fgets()
将等到它可以读取一行:当读取$length
- 1个字节(第二个参数)时读取结束,或者换行(包含在返回值中)或EOF(以先到者为准)。如果没有指定长度,它将继续从流中读取,直到它到达行尾。
基本上,您应该使用stream_set_blocking($pipes[1], 0)
从子进程'stdout pipe 非阻塞模式中读取数据,或者手动检查该管道上是否有数据{{3} }。
此外,您需要使用ignore_user_abort(true)
忽略用户中止。
答案 1 :(得分:0)
您可以使用ignore_user_abort
指定在用户关闭浏览器窗口时脚本不应停止执行。这将解决问题的一半,因此您还需要检查窗口是否在connection_aborted
循环内关闭,以确定何时需要以有序的方式关闭所有内容:
header("Content-type: text/html;charset=utf-8;");
ignore_user_abort(true);
ob_end_flush(); //ends the automatic ob started by PHP
while ($s = fgets($pipes[1])) {
print $s;
flush();
if (connection_aborted()) {
proc_terminate($process);
break;
}
}
答案 2 :(得分:0)
这有帮助吗?
$proc_info = proc_get_status($process);
pcntl_waitpid($proc_info['pid']);
答案 3 :(得分:0)
当inotifywait
作为自己的进程运行时,基本上永远不会结束,你需要向它发送一个KILL信号。如果您在cli上运行脚本,Ctrl + C信号也会被发送到inotifywait进程 - 但是在Web服务器中运行时没有。
您可以在一个由register_shutdown_function
或__destruct
在一个类中调用的函数中发送信号。
这个围绕proc_open的简单包装可以提供帮助:
class Proc
{
private $_process;
private $_pipes;
public function __construct($cmd, $descriptorspec, $cwd = null, $env = null)
{
$this->_process = proc_open($cmd, $descriptorspec, $this->_pipes, $cwd, $env);
if (!is_resource($this->_process)) {
throw new Exception("Command failed: $cmd");
}
}
public function __destruct()
{
if ($this->isRunning()) {
$this->terminate();
}
}
public function pipe($nr)
{
return $this->_pipes[$nr];
}
public function terminate($signal = 15)
{
$ret = proc_terminate($this->_process, $signal);
if (!$ret) {
throw new Exception("terminate failed");
}
}
public function close()
{
return proc_close($this->_process);
}
public function getStatus()
{
return proc_get_status($this->_process);
}
public function isRunning()
{
$st = $this->getStatus();
return $st['running'];
}
}
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "a") // stderr is a file to write to
);
$proc = new Proc('inotifywait -mc -e create /dev/shm/test/', $descriptorspec);
header("Content-type: text/html;charset=utf-8;");
ob_end_flush(); //ends the automatic ob started by PHP
$pipe = $proc->pipe(1);
while ($s = fgets($pipe)) {
print $s;
flush();
}
fclose($pipe);
$return_value = proc->close($process);
echo "command returned $return_value\n";
或者您可以使用执行exactly the same的Symfony Process Component(以及其他有用的东西)