使用 inotify_read 时,pnctl_signal 不会中断 inotify_read

Posted

技术标签:

【中文标题】使用 inotify_read 时,pnctl_signal 不会中断 inotify_read【英文标题】:When using inotify_read, pnctl_signal does not interrupt the inotify_read 【发布时间】:2018-03-06 07:23:38 【问题描述】:

我正在用 php 构建一个日志解析器程序。日志解析器从 ProFTPD 创建的日志文件中读取数据,然后在检测到特定命令时运行一些操作。为了能够检测日志文件中的更改,我使用了 Inotify。如果日志文件太大,我想通过向日志解析器发送信号来旋转日志以完成当前文件的处理,然后终止日志解析器。然后,Logrotate 会在确保正在读取的原始文件被清空后再次重新启动日志解析器。

问题是当我使用 Inotify 并且 inotify 处于阻塞状态时,中断将不起作用。

例如:

#!/usr/bin/php -q
<?php
declare(ticks = 1);

$log_parser = new LogParser();
$log_parser->process_log();

class LogParser 

  private $ftp_log_file = '/var/log/proftpd/proftpd.log';
  # file descriptor for the log file
  private $fd;
  # inotify instance
  private $inotify_inst;
  # watch id for the inotifier
  private $watch_id;

  public function process_log() 
    // Open an inotify instance
    $this->inotify_inst = inotify_init();
    $this->watch_id = inotify_add_watch($this->inotify_inst, $this->ftp_log_file, IN_MODIFY);

    $this->fd = fopen($this->ftp_log_file, 'r');
    if ($this->fd === false)
      die("unable to open $this->ftp_log_file!\n");

    pcntl_signal(SIGUSR1,  function($signal) 
      $this->sig_handler($signal);
    );

    while (1) 
      # If the thread gets blocked here, the signals do not work
      $events = inotify_read($this->inotify_inst);
      while ($line = trim(fgets($this->fd))) 
        // Parse the log ...
      
    
    fclose($this->fd);
    // stop watching our directory
    inotify_rm_watch($this->inotify_inst, $this->watch_id);
    // close our inotify instance
    fclose($this->inotify_inst);
  

  private function sig_handler($signo) 
    switch ($signo) 
      case SIGUSR1:
        // Do some action ... 
    
  


我知道一种解决方案可能是我启动父进程,然后将信号处理程序添加到该父进程。父进程应该启动日志解析器,日志解析器会被 inotify_read 阻塞,但父进程不会,但想知道是否有不涉及父进程的解决方案 - 如果 inotify 能够支持中断?

谢谢

【问题讨论】:

我正在考虑删除 inotify 并添加以下内容而不是 inotify 以检查日志文件是否被修改:while(1) if (filemtime($this-&gt;ftp_log_file) != $last_modification_time) break; sleep(1); 【参考方案1】:

在这里找到解决方案:php inotify blocking but with timeout

最终代码:

#!/usr/bin/php -q
<?php
declare(ticks = 1);

$log_parser = new LogParser();
$log_parser->process_log();

class LogParser 

  private $ftp_log_file = '/var/log/proftpd/proftpd.log';
  # file descriptor for the log file
  private $fd;
  # inotify instance
  private $inotify_inst;
  # watch id for the inotifier
  private $watch_id;

  public function process_log() 
    // Open an inotify instance
    $this->inotify_inst = inotify_init();
    stream_set_blocking($this->inotify_inst, false);
    $this->watch_id = inotify_add_watch($this->inotify_inst, $this->ftp_log_file, IN_MODIFY);

    $this->fd = fopen($this->ftp_log_file, 'r');
    if ($this->fd === false)
      die("unable to open $this->ftp_log_file!\n");

    pcntl_signal(SIGUSR1,  function($signal) 
      $this->sig_handler($signal);
    );

    while (1) 
      while (1) 
        $r = array($this->inotify_inst);
        $timeout = 60;
        $w = array();
        $e = array();
        $time_left = stream_select($r, $w, $e, $timeout);
        if ($time_left != 0) 
          $events = inotify_read($this->inotify_inst);
          if ($events) 
            break;
          
        
      
      while ($line = trim(fgets($this->fd))) 
        // Parse the log ...
      
    
    fclose($this->fd);
    // stop watching our directory
    inotify_rm_watch($this->inotify_inst, $this->watch_id);
    // close our inotify instance
    fclose($this->inotify_inst);
  

  private function sig_handler($signo) 
    switch ($signo) 
      case SIGUSR1:
        // Do some action ... 
    
  


建议的解决方案不会阻塞中断,它还将线程设置为非阻塞状态。

【讨论】:

以上是关于使用 inotify_read 时,pnctl_signal 不会中断 inotify_read的主要内容,如果未能解决你的问题,请参考以下文章

使用 php artisan migrate 时,迁移时更改表名,使用 tinker 保存对象时出错

使用前台服务时允许“使用应用程序时”而不是“始终允许”是不是可以?

为啥在使用自适应查询执行时要使用广播时火花洗牌

使用自定义效果时缺少网格颜色,但使用 BasicEffect 时没有

使用google可视化api时,使用dashboard时如何使用getChartLayoutInterface()

使用PUTTY+xming时出错