• 用inotify监视文件/目录变化


    inotify监视文件/目录变化

     

    转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

    作者联系方式:李先静 <xianjimli at hotmail dot com>

    更新时间:2007-6-18

     

    介绍inotify使用方法的文章已经有很多了,写得也非常清楚,本来不需要我多此一举了。不过,我是第一次使用,而且用得不是很顺,作为新手,想法毕竟有些不同,所以有些东西还是值得记录下来的。

     

    最近为桌面增加监控applictions目录变化的功能,我们的桌面虽然是自己开发的,但遵循了freedesktop的Desktop Entry Specification标准。应用程序的desktop文件放置在applictions目录下,这是应用程序的入口,桌面负责把它们放在适当的位置(如开始菜单和状态栏中)。对于内置的应用程序,它们的desktop文件不会变化,但是第三方的应用程序可能随时安装/卸载,如果安装/卸载要强迫用户重起机器,那太不人性化了。为了支持动态安装和卸载,我们通过监控applictions中desktop文件的变化来实现。

     

    1.       glibc版本偏旧,没有提供inotify的头文件。

    较旧的glibc(如2.4之前的)没有提供inotify的头文件,不能直接使用网上介绍的方法,要自己定义系统调用和那些常量,如:

    #include <sys/syscall.h>

    #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 */

     

     

    Hal里有一套定义,我验证过没有问题,直接拿来用就好了,可以省不少麻烦。你可以从http://freedesktop.org/~david/dist/hal-0.5.9.tar.gz获取。

     

    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, &buf, sizeof (buf))) < 0) && (errno == EINTR));

     

        while(index < len)

        {

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

     

            handle_inotify_event(event, data);

     

            index += sizeof(struct inotify_event) + event->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->mask)

        {  

            case IN_MODIFY:

            {  

                action = "IN_MODIFY";

                break;

            }

            case IN_CREATE:

            {

                action = "IN_CREATE";

                break;

            }

            case IN_DELETE:

            {

                action = "IN_DELETE";

                break;

            }

            case IN_MOVED_FROM:

            {

                action = "IN_MOVED_FROM";

                break;

            }

            case IN_MOVED_TO:

            {

                action = "IN_MOVED_TO";

                break;

            break;

            }

            default:break;

        }

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

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

     

        return;

    }

     

    5.         小心文件内容更新没有完成。

    在测试过程中,我发现在事件处理函数中读取文件内容时,有时文件内容是残缺的,可能是文件内容更新还完全完成。对此,我还没有想到好的方法,目前只是简单的usleep(100)

     

    ~~end~~

     
  • 相关阅读:
    Spring笔记②--各种属性注入
    Spring笔记①--helloworld
    Structs2笔记③--局部类型转换案例
    Struct2笔记②--完善登陆代码
    Structs2笔记①--structs的背景、structs2框架的意义、第一个helloworld
    软件项目的开发之svn的使用
    Java基础第一节.Java简介
    Hibernate笔记④--一级二级缓存、N+1问题、saveorupdate、实例代码
    Hibernate笔记③--集合映射、组合映射、联合主键、查询案例
    Hibernate笔记②--hibernate类生成表、id生成策略、级联设置、继承映射
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167714.html
Copyright © 2020-2023  润新知