zoukankan      html  css  js  c++  java
  • php使用inotify扩展监控文件或目录的变化

    一、安装inotify扩展

    1、下载inotify扩展源码

    https://pecl.php.net/package/inotify
    

    对于php7以上版本,请下载 inotify-2.0.0.tgz。

    2、编译安装

    tar xf inotify-2.0.0.tgz
    cd inotify-2.0.0
    /data/nmp/php/bin/phpize
    ./configure --with-php-config=/data/nmp/php/bin/php-config
    make && make install
    

    3、修改php.ini,添加

    extension = inotify.so
    

      

    二、使用inotify进行文件监控

    php的inotify扩展提供了监控文件或目录的功能,可以用来实现,服务的热更新,或安全监控。

    inotify一共提供了5个函数:

    1、inotify_init() 用于初始化一个实例,返回的是一个 stream 资源对象,可以被标准stream函数使用,如stream_set_blocking()。

    2、inotify_add_watch() 将一个文件或目录添加到监控列表中,如果存在,则替换。

    3、inotify_read() 从 inotify 实例中读取事件,如果没有返回false,如果有,返回一个 inotify 事件数组。

    4、inotify_queue_len() 返回队列中事件的个数,可以理解为系统把 inotify_add_watch 关注的事件一个个加入队列,通过 inotify_read() 读取事件。

    5、inotify_rm_watch() 取消监控。

    一个简单的阻塞监控代码:

    <?php
    
    //初始化一个inotify实例
    $fd = inotify_init();
    
    //设置为阻塞模式,当inotify_read()无法从$fd中读取到事件时,会一直阻塞在那里。
    stream_set_blocking($fd, 1);
    
    //事件掩码
    $eventMask = [
        IN_ACCESS => 'File was accessed (read)',
        IN_MODIFY => 'File was modified',
        IN_ATTRIB => 'Metadata changed',
        IN_CLOSE_WRITE => 'File opened for writing was closed',
        IN_CLOSE_NOWRITE => 'File not opened for writing was closed',
        IN_OPEN => 'File was opened',
        IN_MOVED_TO => 'File moved into watched directory',
        IN_MOVED_FROM => 'File moved out of watched directory',
        IN_CREATE => 'File or directory created in watched directory',
        IN_DELETE => 'File or directory deleted in watched directory',
        IN_DELETE_SELF => 'Watched file or directory was deleted',
        IN_MOVE_SELF => 'Watch file or directory was moved',
        IN_CLOSE => 'Equals to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE',
        IN_MOVE => 'Equals to IN_MOVED_FROM | IN_MOVED_TO',
        IN_ALL_EVENTS => 'Bitmask of all the above constants',
        IN_UNMOUNT => 'File system containing watched object was unmounted',
        IN_Q_OVERFLOW => 'Event queue overflowed (wd is -1 for this event)',
        IN_IGNORED => 'Watch was removed (explicitly by inotify_rm_watch() or because file was removed or filesystem unmounted',
        IN_ISDIR => 'Subject of this event is a directory',
        IN_ONLYDIR => 'Only watch pathname if it is a directory',
        IN_DONT_FOLLOW => 'Do not dereference pathname if it is a symlink',
        IN_MASK_ADD => 'Add events to watch mask for this pathname if it already exists',
        IN_ONESHOT => 'Monitor pathname for one event, then remove from watch list.',
        1073741840 => 'High-bit: File not opened for writing was closed',
        1073741856 => 'High-bit: File was opened',
        1073742080 => 'High-bit: File or directory created in watched directory',
        1073742336 => 'High-bit: File or directory deleted in watched directory',
    ];
    
    //添加监控,修改,创建,删除事件
    $watch = inotify_add_watch($fd, './tmp', IN_MODIFY | IN_CREATE | IN_DELETE | IN_ISDIR);
    
    //循环读取事件
    while ($events = inotify_read($fd)) {
        // $events会是一个二维数组
        // wd 表示inotify_add_watch()返回的监控描述符
        // mask 表示事件掩码
        // cookie 表示唯一的ID,用于关联相关的事件
        // name 表示文件的名称
        echo date('Y-m-d H:i:s'), PHP_EOL;
        foreach ($events as $event) {
            $eventName = isset($eventMask[$event['mask']]) ? $eventMask[$event['mask']] : '';
            echo $event['name'], PHP_EOL;
            echo $eventName, PHP_EOL;
        }
    }
    
    //取消监控
    inotify_rm_watch($fd, $watch);
    
    //关闭资源
    fclose($fd);
    

      

    三、我们通过非阻塞的方式来监控文件

    <?php
    
    //初始化一个inotify实例
    $fd = inotify_init();
    
    //设置为非阻塞模式
    stream_set_blocking($fd, 0);
    
    //事件掩码
    $eventMask = [
        IN_ACCESS => 'File was accessed (read)',
        IN_MODIFY => 'File was modified',
        IN_ATTRIB => 'Metadata changed',
        IN_CLOSE_WRITE => 'File opened for writing was closed',
        IN_CLOSE_NOWRITE => 'File not opened for writing was closed',
        IN_OPEN => 'File was opened',
        IN_MOVED_TO => 'File moved into watched directory',
        IN_MOVED_FROM => 'File moved out of watched directory',
        IN_CREATE => 'File or directory created in watched directory',
        IN_DELETE => 'File or directory deleted in watched directory',
        IN_DELETE_SELF => 'Watched file or directory was deleted',
        IN_MOVE_SELF => 'Watch file or directory was moved',
        IN_CLOSE => 'Equals to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE',
        IN_MOVE => 'Equals to IN_MOVED_FROM | IN_MOVED_TO',
        IN_ALL_EVENTS => 'Bitmask of all the above constants',
        IN_UNMOUNT => 'File system containing watched object was unmounted',
        IN_Q_OVERFLOW => 'Event queue overflowed (wd is -1 for this event)',
        IN_IGNORED => 'Watch was removed (explicitly by inotify_rm_watch() or because file was removed or filesystem unmounted',
        IN_ISDIR => 'Subject of this event is a directory',
        IN_ONLYDIR => 'Only watch pathname if it is a directory',
        IN_DONT_FOLLOW => 'Do not dereference pathname if it is a symlink',
        IN_MASK_ADD => 'Add events to watch mask for this pathname if it already exists',
        IN_ONESHOT => 'Monitor pathname for one event, then remove from watch list.',
        1073741840 => 'High-bit: File not opened for writing was closed',
        1073741856 => 'High-bit: File was opened',
        1073742080 => 'High-bit: File or directory created in watched directory',
        1073742336 => 'High-bit: File or directory deleted in watched directory',
    ];
    
    //添加监控,修改,创建,删除事件
    $watch = inotify_add_watch($fd, './tmp', IN_MODIFY | IN_CREATE | IN_DELETE | IN_ISDIR);
    
    
    //循环
    while (true) {
        //所有可读的流,监听读事件
        $reads = [$fd];
        //可写的流,设为空,表示我们不需要监听写事件
        $write = [];
        //异常事件
        $except = [];
        //参数四,表示超时时间
        //如果设置成大于0,表示等待事件发生的超时时间
        //如果设置等于0,表示不断调用select,执行后立马返回
        //如果设置null,表示阻塞一直到监听发生变化
        if (stream_select($reads, $write, $except, 3) > 0) {
            //注意$reads是引用传递,select每次会把可读的流放到$reads中,所以$reads变量是会改变的
            if (!empty($reads)) {
                foreach ($reads as $read) {
                    //从可读流中读取数据
                    $events = inotify_read($read);
                    foreach ($events as $event) {
                        echo $event['name'], ' --- ', $eventMask[$event['mask']], PHP_EOL;
                    }
                }
            }
        } else {
            echo 'select timeout ...', PHP_EOL;
        }
    }
    
    //取消监控
    inotify_rm_watch($fd, $watch);
    
    //关闭资源
    fclose($fd);
    

      

    四、封装一个类,用来监控文件或目录。

    <?php
    
    class InotifyMonitor
    {
        //需要监控的事件
        const MONITOR_EVENT = IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_CLOSE_WRITE;
    
        //事件掩码
        const EVENT_MASK = [
            IN_ACCESS => 'File was accessed (read)',
            IN_MODIFY => 'File was modified',
            IN_ATTRIB => 'Metadata changed',
            IN_CLOSE_WRITE => 'File opened for writing was closed',
            IN_CLOSE_NOWRITE => 'File not opened for writing was closed',
            IN_OPEN => 'File was opened',
            IN_MOVED_TO => 'File moved into watched directory',
            IN_MOVED_FROM => 'File moved out of watched directory',
            IN_CREATE => 'File or directory created in watched directory',
            IN_DELETE => 'File or directory deleted in watched directory',
            IN_DELETE_SELF => 'Watched file or directory was deleted',
            IN_MOVE_SELF => 'Watch file or directory was moved',
            IN_CLOSE => 'Equals to IN_CLOSE_WRITE | IN_CLOSE_NOWRITE',
            IN_MOVE => 'Equals to IN_MOVED_FROM | IN_MOVED_TO',
            IN_ALL_EVENTS => 'Bitmask of all the above constants',
            IN_UNMOUNT => 'File system containing watched object was unmounted',
            IN_Q_OVERFLOW => 'Event queue overflowed (wd is -1 for this event)',
            IN_IGNORED => 'Watch was removed (explicitly by inotify_rm_watch() or because file was removed or filesystem unmounted',
            IN_ISDIR => 'Subject of this event is a directory',
            IN_ONLYDIR => 'Only watch pathname if it is a directory',
            IN_DONT_FOLLOW => 'Do not dereference pathname if it is a symlink',
            IN_MASK_ADD => 'Add events to watch mask for this pathname if it already exists',
            IN_ONESHOT => 'Monitor pathname for one event, then remove from watch list.',
            1073741840 => 'High-bit: File not opened for writing was closed',
            1073741856 => 'High-bit: File was opened',
            1073742080 => 'High-bit: File or directory created in watched directory',
            1073742336 => 'High-bit: File or directory deleted in watched directory',
        ];
    
        //用于保存inotify_init返回的资源
        public $fds = [];
        //用于保存监控的文件路径
        public $paths = [];
        //用于保存inotify_add_watch返回的监控描述符
        public $wds = [];
        //超时时间
        public $timeout = 3;
    
        /**
         * $paths添加监控的路径数组,可以是目录或文件
         */
        public function __construct($paths = [])
        {
            if (!empty($paths)) {
                foreach ($paths as $path) {
                    if (file_exists($path)) {
                        if (is_dir($path)) {
                            $this->addDir($path);
                        } else {
                            $this->addFile($path);
                        }
                    }
                }
            }
        }
    
        public function __destruct()
        {
            if (!empty($this->fds)) {
                foreach ($this->fds as $fd) {
                    fclose($fd);
                }
            }
        }
    
        /**
         * 添加文件监控
         */
        public function addFile($file)
        {
            $file = realpath($file);
            $fd = inotify_init();
            $fid = (int)$fd;
            //保存inotify资源
            $this->fds[$fid] = $fd;
            //设置为非阻塞模式
            stream_set_blocking($this->fds[$fid], 0);
            //保存文件路径
            $this->paths[$fid] = $file;
            //保存监控描述符
            $this->wds[$fid] = inotify_add_watch($this->fds[$fid], $file, self::MONITOR_EVENT);
        }
    
        /**
         * 添加目录监控
         */
        public function addDir($dir)
        {
            $dir = realpath($dir);
            if ($dh = opendir($dir)) {
                //将目录加入监控中
                $fd = inotify_init();
                //一般文件的资源描述符是一个整形,可以用来当索引
                $fid = (int)$fd;
                $this->fds[$fid] = $fd;
                stream_set_blocking($this->fds[$fid], 0);
                $this->paths[$fid] = $dir;
                $this->wds[$fid] = inotify_add_watch($this->fds[$fid], $dir, self::MONITOR_EVENT);
                //遍历目录下文件
                while (($file = readdir($dh)) !== false) {
                    if ($file == '.' || $file == '..') {
                        continue;
                    }
                    $file = $dir . DIRECTORY_SEPARATOR . $file;
                    if (is_dir($file)) {
                        $this->addDir($file);
                    }
                }
                closedir($dh);
            }
        }
    
        /**
         * 移除监控
         */
        public function remove($fid)
        {
            unset($this->paths[$fid]);
            fclose($this->fds[$fid]);
            unset($this->fds[$fid]);
        }
    
        /**
         * 运行监控
         */
        public function run()
        {
            while (true) {
                $reads = $this->fds;
                $write = [];
                $except = [];
                if (stream_select($reads, $write, $except, $this->timeout) > 0) {
                    if (!empty($reads)) {
                        foreach ($reads as $read) {
                            //从可读流中读取数据
                            $events = inotify_read($read);
                            //资源描述符,整形
                            $fid = (int)$read;
                            //获取inotify实例的路径
                            $path = $this->paths[$fid];
                            foreach ($events as $event) {
                                $file = $path . DIRECTORY_SEPARATOR . $event['name'];
                                switch ($event['mask']) {
                                    case IN_CREATE:
                                    case 1073742080:
                                        if (is_dir($file)) {
                                            echo 'add ...', PHP_EOL;
                                            echo 'fid : ', $fid, PHP_EOL;
                                            $this->addDir($file);
                                        }
                                        break;
                                    case IN_DELETE_SELF:
                                        if (is_dir($file)) {
                                            echo 'remove ...', PHP_EOL;
                                            echo 'fid : ', $fid, PHP_EOL;
                                            $this->remove($fid);
    
                                            $key = array_search($read, $reads);
                                            unset($reads[$key]);
                                        }
                                        break;
                                }
                                echo $event['name'], ' --- ', self::EVENT_MASK[$event['mask']], PHP_EOL;
                            }
                        }
                    }
                } else {
                    echo '------------------', PHP_EOL;
                    echo '当前监控的路径列表', PHP_EOL;
                    print_r($this->paths);
                    echo '------------------', PHP_EOL;
                }
            }
        }
    }
    
    (new InotifyMonitor(['./tmp', './test.php']))->run();
    

      

  • 相关阅读:
    Truck History POJ
    Constructing Roads POJ
    Building a Space Station POJ
    Networking POJ
    Jungle Roads POJ
    How Many Answers Are Wrong HDU
    harbor磁盘爆满,执行垃圾回收清理镜像
    阿里云服务器安装mysql镜像
    jenkins Publish over SSH 的配置与使用
    npm install报错 SyntaxError: Unexpected end of JSON input while parsing near '...=GmVg -----END PGP'
  • 原文地址:https://www.cnblogs.com/jkko123/p/10861236.html
Copyright © 2011-2022 走看看