天天看點

用inotify監視檔案/目錄變化

用inotify監視檔案/目錄變化

介紹inotify使用方法的文章已經有很多了,寫得也非常清楚,本來不需要我多此一舉了。不過,我是第一次使用,而且用得不是很順,作為新手,想法畢竟有些不同,是以有些東西還是值得記錄下來的。

最近為桌面增加監控applictions目錄變化的功能,我們的桌面雖然是自己開發的,但遵循了freedesktop的Desktop Entry Specification标準。應用程式的desktop檔案放置在applictions目錄下,這是應用程式的入口,桌面負責把它們放在适當的位置(如開始菜單和狀态欄中)。對于内置的應用程式,它們的desktop檔案不會變化,但是第三方的應用程式可能随時安裝/解除安裝,如果安裝/解除安裝要強迫使用者重起機器,那太不人性化了。為了支援動态安裝和解除安裝,我們通過監控applictions中desktop檔案的變化來實作。

1.       glibc版本偏舊,沒有提供inotify的頭檔案。

較舊的glibc(如2.4之前的)沒有提供inotify的頭檔案,不能直接使用網上介紹的方法,要自己定義系統調用和那些常量,如:

<col>

#include &lt;sys/syscall.h&gt;

#ifndef __NR_inotify_init

#if defined(__i386__)

# define __NR_inotify_init  291

# define __NR_inotify_add_watch 292

# define __NR_inotify_rm_watch  293

#elif defined(__x86_64__)

...

{

    return syscall(__NR_inotify_init);

}

static inline int inotify_add_watch(int fd, const char *name, uint32_t mask)

    return syscall(__NR_inotify_add_watch, fd, name, mask);

static inline int inotify_rm_watch(int fd, uint32_t wd)

    return syscall(__NR_inotify_rm_watch, fd, wd);

struct inotify_event {

    int      wd;

    uint32_t mask;

    uint32_t cookie;

    uint32_t len;

    char     name[];

}; 

/* the following are legal, implemented events that user-space can watch for */

#define IN_ACCESS       0x00000001  /* File was accessed */

#define IN_MODIFY       0x00000002  /* File was modified */

#define IN_ATTRIB       0x00000004  /* Metadata changed */

#define IN_CLOSE_WRITE      0x00000008  /* Writtable file was closed */

#define IN_CLOSE_NOWRITE    0x00000010  /* Unwrittable file closed */

#define IN_OPEN         0x00000020  /* File was opened */

#define IN_MOVED_FROM       0x00000040  /* File was moved from X */

#define IN_MOVED_TO     0x00000080  /* File was moved to Y */

#define IN_CREATE       0x00000100  /* Subfile was created */

#define IN_DELETE       0x00000200  /* Subfile was deleted */

#define IN_DELETE_SELF      0x00000400  /* Self was deleted */

#define IN_MOVE_SELF        0x00000800  /* Self was moved */

2.       從inotify_fd中讀出的buffer可能有多個inotify_event,可以按下列方法一一取出:

inotify_event的長度不是固定的,要根據它的len成員計算它的實際大小,然後找到下一個的開始位置。

static void inotify_events_io_func  (GIOChannel *channel, GIOCondition condition, gpointer data)

    char buf[1024] = {0};

    struct inotify_event* event = {0};

    int index = 0;

    int len = 0;

    int fd = g_io_channel_unix_get_fd (channel);

    while(((len = read (fd, &amp;buf, sizeof (buf))) &lt; 0) &amp;&amp; (errno == EINTR));

    while(index &lt; len)

    {

        event = (struct inotify_event*)(buf+index);

        handle_inotify_event(event, data);

        index += sizeof(struct inotify_event) + event-&gt;len;

        g_debug("len=%d index=%d", len, index);

    }

    return;

3.       與g_main_loop關聯以簡化實作。

應用程式有自己的事情要做,不能一直挂到inotify_fd上,可以與g_main_loop關聯起來,方法如下。

int main(int argc, char* argv[])

    if(argc != 2)

        printf("usage: %s dir/n", argv[0]);

        return 0;

    int fd = 0;

    const char* dir = argv[1];

    GIOChannel *channel = NULL;

    fd = inotify_init ();

    int wd = inotify_add_watch (fd, dir, IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);

    channel = g_io_channel_unix_new (fd);

    g_io_add_watch(channel, G_IO_IN, (GIOFunc) inotify_events_io_func, (gpointer)dir);

    GMainLoop* loop = g_main_loop_new(NULL, FALSE);

    g_main_loop_run(loop);

    return 0;

4.       處理事件。

事件處理與具體應用有關,隻要清楚inotify_event結構中各個成員的意義即可。常用的有三個:mask成員辨別變化的類型,name成員辨別變化的檔案名,wd成員是前面inotify_add_watch傳回的值,用它可以映射到目錄名(自己建映射關系)。

static void handle_inotify_event(struct inotify_event* event, gpointer data)

{      

    const char* action = NULL;

    switch(event-&gt;mask)

    {  

        case IN_MODIFY:

        {  

            action = "IN_MODIFY";

            break;

        }

        case IN_CREATE:

        {

            action = "IN_CREATE";

        case IN_DELETE:

            action = "IN_DELETE";

        case IN_MOVED_FROM:

            action = "IN_MOVED_FROM";

        case IN_MOVED_TO:

            action = "IN_MOVED_TO";

        break;

        default:break;

    g_debug("%s: wd=%x mask=%x cookie=%x len=%x %s/%s", action,

            event-&gt;wd, event-&gt;mask, event-&gt;cookie, event-&gt;len, (char*)data, event-&gt;name);

5.         小心檔案内容更新沒有完成。

在測試過程中,我發現在事件處理函數中讀取檔案内容時,有時檔案内容是殘缺的,可能是檔案内容更新還完全完成。對此,我還沒有想到好的方法,目前隻是簡單的usleep(100)。

~~end~~