[PHP]監視文件系統變化——inotify


轉載自:http://sexywp.com/use-inotify-to-monitor-file-system.htm

監控文件系統的變化,不是一個常見的需求,但是隨着對PHP使用的深入,不可避免的會碰到這類問題。我所在的公司,在服務器端,使用PHP進程常駐 內存,來完成一些任務,甚至伺服服務。我們知道,PHP作為服務器動態語言,是不需要編譯的,但是代碼的生命周期是僅限於一次請求的,一次請求結束,下次 請求,就會重新加載代碼,除非安裝了Opcode Cache,但是如果PHP常駐進程,這種自動加載更新代碼的能力就失去了。這時候,我們有一種彌補方案,就是使用inotify。

inotify是系統體層提供的機制,在版本號大於2.6.13的內核中才有提供(之前kernel版本,有dnotify)。PHP官方擴展庫pecl提供了該擴展包。關於inotify的基本原理和用法介紹,可以看IBM的文檔

inotify的API接口非常少,只有5個函數,inotify_init,inotifiy_read,inotify_add_watch,inotify_rm_watch,inotify_qeueue_len, 這幾個函數的含義還是相當直接的,估計比較難理解的,就只有一個init和read函數了。這里簡單解釋一下,inotify是一個類似隊列一樣的東西, 把需要監控的一批文件和目錄,加入到同一個inotify隊列中,所以首先要先init一個空隊列出來,然后用add_watch函數來添加監控對象。然 后,read函數就能大顯身手了,read函數可以產生一個(默認)阻塞的操作,查詢監控的對象中是否有事件發生,如果有,就會返回數據,否則就一直阻 塞。當然,也可以設置成非阻塞的,可以看相關代碼范例。

inotify能夠監控的文件系統事件羅列如下,基本上涵蓋了linux server上的所有的文件事件。根據PHP官方文檔和我實際測試,inotify不支持目錄遞歸遍歷,所以,如果要監控目錄的變化,需要把每一個子目錄 都加入到watch的列表中去。除此之外,因為我在虛擬機上測試,還發現了一點,就是宿主機編輯共享文件,guest系統中的inotify無法監控到文 件的變化。

IN_ACCESS           :1
IN_MODIFY           :2
IN_ATTRIB           :4
IN_CLOSE_WRITE      :8
IN_CLOSE_NOWRITE    :16
IN_OPEN             :32
IN_MOVED_TO         :128
IN_MOVED_FROM       :64
IN_CREATE           :256
IN_DELETE           :512
IN_DELETE_SELF      :1024
IN_MOVE_SELF        :2048
IN_CLOSE            :24
IN_MOVE             :192
IN_ALL_EVENTS       :4095
IN_UNMOUNT          :8192
IN_Q_OVERFLOW       :16384
IN_IGNORED          :32768
IN_ISDIR            :1073741824
IN_ONLYDIR          :16777216
IN_DONT_FOLLOW      :33554432
IN_MASK_ADD         :536870912
IN_ONESHOT          :-2147483648

最后,簡單寫了一個小的目錄監控的類,名字叫DirWatcher,就是目錄監視器的意思,主要功能,就是可以向一個監視器實例注冊監控目錄和回調函數, 從而實現一個文件系統事件觸發動作的目的。特此奉上源代碼,供諸君研究相關api的使用方法,也可用於自己的系統實現中。

<?php
 
 define('DIRWATCHER_CHANGED', IN_MODIFY | IN_CLOSE_WRITE | IN_MOVE | IN_CREATE | IN_DELETE );
 
 /**
  * DirWatcher
  *
  * @author Charles Tang <charlestang AT foxmail DOT com>
  */
 class DirWatcher {
 
     private $_callbacks = array();
     private $_directories = array();
     private $_inotify = null;
 
     public function __construct() {
         $this->_inotify = inotify_init();
     }
 
     public function addDirectory($path, $mask = DIRWATCHER_CHANGED) {
         $key = md5($path);
         if (!isset($this->_directories[$key])) {
             $wd = inotify_add_watch($this->_inotify, $path, $mask);
             $this->_directories[$key] = array(
                 'wd' => $wd,
                 'path' => $path,
                 'mask' => $mask,
             );
         }
     }
 
     public function removeDirectory($path) {
         $key = md5($path);
         if (isset($this->_directories[$key])) {
             $wd = $this->_directories[$key]['wd'];
             if (inotify_rm_watch($this->_inotify, $wd)) {
                 unset($this->_directories[$key]);
             }
         }
     }
 
     public function addDirectories($directories) {
         foreach ($directories as $dir) {
             if (!is_array($dir)) {
                 $this->addDirectory($dir);
             } else {
                 $this->addDirectory($dir['path'], $dir['mask']);
             }
         }
     }
 
     public function addCallback($callback, $params = array(), $priority = 9) {
         $key = md5(var_export($callback, true));
         if (!isset($this->_callbacks[$key])) {
             $this->_callbacks[$key] = array(
                 'callable' => $callback,
                 'params' => $params,
                 'priority' => $priority,
             );
 
             usort($this->_callbacks, create_function('$a, $b', 'return $a["priority"] > $b["priority"];'));
         }
     }
 
     public function removeCallback($callback) {
         $key = md5(var_export($callback, true));
         if (isset($this->_callbacks[$key])) {
             unset($this->_callbacks[$key]);
         }
     }
 
     public function addCallbacks($callbacks) {
         foreach ($callbacks as $callable) {
             if (is_callable($callable)) {
                 $callable = array(
                     'callable' => $callable,
                     'params' => array(),
                     'priority' => 9,
                 );
             }
 
             $this->addCallback($callable['callable'], $callable['params'], $callable['priority']);
         }
     }
 
     public function startWatch() {
         while (TRUE) { //啟動一個常駐進程,監視目錄的變化,事件觸發回調函數
             $event = inotify_read($this->_inotify);
 
             if (defined('DIRWATCHER_DEBUG') && DIRWATCHER_DEBUG) {
                 error_log(vsprintf("[wd:%d][mask:%d][cookie:%s]%s", $event[0]));
             }
 
             foreach ($this->_callbacks as $callable) {
                 call_user_func_array($callable['callable'], array_merge($event, $callable['params']));
             }
         }
     }
 
     public function stopWatch() {
         //沒有實現,可以引入pcntl,優雅退出,退出前記得fclose($this->_inotify)
     }
 
 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM