用於監控 Linux文件系統事件的inotify接口學習筆記



參考
http://www.ibm.com/developerworks/cn/linux/l-inotify/index.html

從 Linux 2.6.13 內核開始,Linux 就推出了 inotify,允許監控程序打開一個獨立文件描述符,並針對事件集監控一個或者多個文件,例如打開、關閉、移動/重命名、刪除、創建或者改變屬性。

提供的接口

inotify提供的結構和接口(見/usr/include/sys/inotify.h)如下:

inotify_init
    是用於創建一個 inotify 實例的系統調用,並返回一個指向該實例的文件描述符。
inotify_add_watch
    增加對文件或者目錄的監控,並指定需要監控哪些事件。標誌用於控制是否將事件添加到已有的監控中,是否只有路徑代表一個目錄才進行監控,是否要追蹤符號鏈接,是否進行一次性監控,當首次事件出現後就停止監控。
inotify_rm_watch
    從監控列表中移出監控項目。
read
    讀取包含一個或者多個事件信息的緩存。
close
    關閉文件描述符,並且移除所有在該描述符上的所有監控。當關於某實例的所有文件描述符都關閉時,資源和下層對象都將釋放,以供內核再次使用。

因此,典型的監控程序需要進行如下操作:

   1. 使用 inotify_init 打開一個文件描述符
   2. 添加一個或者多個監控
   3. 等待事件
   4. 處理事件,然後返回並等待更多事件
   5. 當監控不再活動時,或者接到某個信號之後,關閉文件描述符,清空,然後退出。

可監控的事件


有幾種事件能夠被監控。一些事件,比如 IN_DELETE_SELF 只適用於正在被監控的項目,而另一些,比如 IN_ATTRIB 或者 IN_OPEN 則只適用於監控過的項目,或者如果該項目是目錄,則可以應用到其所包含的目錄或文件。

IN_ACCESS
    被監控項目或者被監控目錄中的條目被訪問過。例如,一個打開的文件被讀取。
IN_MODIFY
    被監控項目或者被監控目錄中的條目被修改過。例如,一個打開的文件被修改。
IN_ATTRIB
    被監控項目或者被監控目錄中條目的元數據被修改過。例如,時間戳或者許可被修改。
IN_CLOSE_WRITE
    一個打開的,等待寫入的文件或目錄被關閉。
IN_CLOSE_NOWRITE
    一個以只讀方式打開的文件或目錄被關閉。
IN_CLOSE
    一個掩碼,可以很便捷地對前面提到的兩個關閉事件(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)進行邏輯操作。
IN_OPEN
    文件或目錄被打開。
IN_MOVED_FROM
    被監控項目或者被監控目錄中的條目被移出監控區域。該事件還包含一個 cookie 來實現 IN_MOVED_FROM 與 IN_MOVED_TO 的關聯。
IN_MOVED_TO
    文件或目錄被移入監控區域。該事件包含一個針對 IN_MOVED_FROM 的 cookie。如果文件或目錄只是被重命名,將能看到這兩個事件,如果它只是被移入或移出非監控區域,將只能看到一個事件。如果移動或重命名一個被監控項目,監控將繼續進行。參見下面的 IN_MOVE-SELF。
IN_MOVE
    可以很便捷地對前面提到的兩個移動事件(IN_MOVED_FROM | IN_MOVED_TO)進行邏輯操作的掩碼。
IN_CREATE
    在被監控目錄中創建了子目錄或文件。
IN_DELETE
    被監控目錄中有子目錄或文件被刪除。
IN_DELETE_SELF
    被監控項目本身被刪除。監控終止,並且將收到一個 IN_IGNORED 事件。
IN_MOVE_SELF
    監控項目本身被移動。

使用實例

class InotifyInstance
{
    int m_fd;
    unsigned int m_watched_items;

public:

    InotifyInstance() : m_fd(-1), m_watched_items(0) { }

public:

    int open()
    {
        m_watched_items = 0;
        m_fd = inotify_init();

        if (m_fd < 0)
        {
            perror("inotify_init () = ");
            return m_fd;
        }
        return 0;
    }

    int close()
    {
        m_watched_items = 0;
        if (m_fd <= 0)
            return 0;

        return ::close(m_fd);
    }

public:

    int add_watch(const char *name, unsigned long mask)
    {
        int wd;
        wd = inotify_add_watch(m_fd, name, mask);
        if (wd < 0)
        {
            perror("inotify_add_watch () = ");
        } else
        {
            m_watched_items++;
        }
        return wd;
    }

    int rm_wd(int wd)
    {
        int r = inotify_rm_watch(m_fd, wd);
        if (r < 0)
        {
            perror("inotify_rm_watch(fd, wd) = ");
        } else
        {
            m_watched_items--;
        }
        return r;
    }

public:

    template <typename EventCallback>
    int watch_once(EventCallback& cb)
    {
        int ret = 0;
        if (event_check() > 0)
        {
            ret = handle_events(cb);
        }
        return ret;
    }

    template <typename EventCallback>
    int watch_loop(EventCallback& cb)
    {
        int ret = 0;
        while (m_watched_items > 0)
        {
            ret = watch_once(cb);
            if (ret != 0)
                break;
        }
        return ret;
    }

private:

    int event_check() const
    {
        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(m_fd, &rfds);
        /* Wait until an event happens or we get interrupted
           by a signal that we catch */
        return select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
    }

    template <typename EventCallback>
    int handle_events(EventCallback& cb)
    {
        int buf_len = 16384;
        char* buffer = new char[buf_len];

        ssize_t r = read(m_fd, buffer, buf_len);
        if (r <= 0)
            return r;

        size_t buffer_i = 0;
        int ret = 0;
        while (buffer_i < r)
        {
            struct inotify_event *pevent = (struct inotify_event *) &buffer[buffer_i];
            size_t event_size = offsetof(struct inotify_event, name) + pevent->len;
            ret = cb.process_event(pevent);
            if (ret != 0)
                break;
            buffer_i += event_size;
        }

        delete[] buffer;
        return ret;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章