linux c监控文件事件

2018-06-08 05:56:54

某些应用程序需要对文件或目录进行监控,来侦测其发生特定事件。例如当文件加入或移出一目录,图形化文件管理器应能判定此目录是否在其当前显示之列,而守护进程可能也要监控自己的配置文件是否修改。

创建新的inotify实例。

#include <sys/inotify.h>

int inotify_init(void);


//成功返回文件描述符,错误-1

追加新的监控项

#include <sys/inotify.h>

int inotify_add_watch(int fd, const char *pathname, uint32_t mask);

//返回监控描述符,失败-1

参数pathname标识欲创建或修改的监控项所对应的文件。调用者必须有读的权限(就算后来权限改了,只要注册上了,后面还是会收到事件)。

如果pathname没有加入到fd监控列表则加入,已加入的则修改。参数mask为掩码,定义了欲监控的事件。


删除由wd指定的监控项,wd是监控描述符,由inotify_add_watch() 返回。删除监控项会为该监控描述符生成 IN_IGNORED 事件。

#include <sys/inotify.h>

int inotify_rm_watch(int fd, uint32_t wd);

//成功返回0,失败-1

使用inotify_add_watch() 删除或修改监控项时,位掩码参数mask标识了 pathname 要监控的事件。下表 in 列列出可在mask中定义的事件位。

读取inotify事件

事件读取可用read() 读取,默认会阻塞,除非设置了 O_NONBLOCK 标志,若设置了且无事件发生,报错 EAGAIN。

事件发生后,每次调用会返回一个缓冲区,内含一个或多了如下结构,缓冲区大小应至少为 sizeof(struct inotify_event) + NAME_MAX + 1 字节。

struct inotify_event {
    int wd;       /* 发生事件的描述符 */
    uint32_t mask; /* Bits describing event that occurred */
    uint32_t cookie; /* Cookie for related events (for rename()) */
    uint32_t len; /* Size of 'name' field */
    char name[]; /* Optional null-terminated filename */
};

上图是一个有序的队列,字段wd表明是哪个监控描述符,当应用程序要监控同一 inotify 文件描述符下多个文件和目录时,字段wd就非常有用,可以用它来判定发生在哪个目录,哪个文件。不过要做到这一点,必须自己维护监控描述符和路径名之间的关系。

mask 返回描述该事件的位掩码。由表 19-1 out列展示可出现于 mask中的位范围。cookie 字段只有在对文件重命名时才会用到。在待重命名目录产生 IN_MOVED_FROM 事件。目标目录产生 IN_MOVED_TO 事件。如果同一目录,cookie相等。

如果受监控的目录发生事件,name字段以空字符结尾来表示该文件。若为本身,不使用name字段,len为0。

事件队列末尾追加新事件时如果和队列当前尾部事件有相同的wd,mask,cookie,mask则会合并。

队列限制和 /proc 文件

对inotify事件做排队处理要消耗内存。所以内核会施加各种限制。超级用户可以配置 /proc/sys/fs/inotify 路径中的3个文件来调整限制。

max_queued_events inotify实例队列事件数上限。超出系统生成 IN_Q_OVERFLOW 事件,并丢弃多余事件。溢出事件 wd 为-1。
max_user_instances 对每个真实用户id创建的inotify实例数的限制值。
max_user_watches 对每个真实用户id创建的监控项数量的限制值。

例子

#include <sys/inotify.h>
#include <limits.h>
#include <sys/types.h>  /* Type definitions used by many programs */
#include <stdio.h>      /* Standard I/O functions */
#include <stdlib.h>     /* Prototypes of commonly used library functions,
                           plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include <unistd.h>     /* Prototypes for many system calls */
#include <errno.h>      /* Declares errno and defines error constants */
#include <string.h>     /* Commonly used string-handling functions */

static void
displayInotifyEvent(struct inotify_event *i)
{
    printf("    wd =%2d; ", i->wd);
    if (i->cookie > 0)
        printf("cookie =%4d; ", i->cookie);

    printf("mask = ");
    if (i->mask & IN_ACCESS)        printf("IN_ACCESS ");
    if (i->mask & IN_ATTRIB)        printf("IN_ATTRIB ");
    if (i->mask & IN_CLOSE_NOWRITE) printf("IN_CLOSE_NOWRITE ");
    if (i->mask & IN_CLOSE_WRITE)   printf("IN_CLOSE_WRITE ");
    if (i->mask & IN_CREATE)        printf("IN_CREATE ");
    if (i->mask & IN_DELETE)        printf("IN_DELETE ");
    if (i->mask & IN_DELETE_SELF)   printf("IN_DELETE_SELF ");
    if (i->mask & IN_IGNORED)       printf("IN_IGNORED ");
    if (i->mask & IN_ISDIR)         printf("IN_ISDIR ");
    if (i->mask & IN_MODIFY)        printf("IN_MODIFY ");
    if (i->mask & IN_MOVE_SELF)     printf("IN_MOVE_SELF ");
    if (i->mask & IN_MOVED_FROM)    printf("IN_MOVED_FROM ");
    if (i->mask & IN_MOVED_TO)      printf("IN_MOVED_TO ");
    if (i->mask & IN_OPEN)          printf("IN_OPEN ");
    if (i->mask & IN_Q_OVERFLOW)    printf("IN_Q_OVERFLOW ");
    if (i->mask & IN_UNMOUNT)       printf("IN_UNMOUNT ");
    printf("\n");

    if (i->len > 0)
        printf("        name = %s\n", i->name);
}

#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))

int
main(int argc, char *argv[])
{
    int inotifyFd, wd, j;
    char buf[BUF_LEN];
    ssize_t numRead;
    char *p;
    struct inotify_event *event;

    if (argc < 2 || strcmp(argv[1], "--help") == 0){
        printf("%s pathname...\n", argv[0]);
        return 1;
    }
        

    inotifyFd = inotify_init();                 /* Create inotify instance */
    if (inotifyFd == -1)
        perror("inotify_init");

    for (j = 1; j < argc; j++) {
        wd = inotify_add_watch(inotifyFd, argv[j], IN_ALL_EVENTS);
        if (wd == -1)
            perror("inotify_add_watch");

        printf("Watching %s using wd %d\n", argv[j], wd);
    }

    for (;;) {                                  /* Read events forever */
        numRead = read(inotifyFd, buf, BUF_LEN);
        if (numRead == 0){
            printf("read() from inotify fd returned 0!");
            return 1;
        }
            

        if (numRead == -1)
            perror("read");

        printf("Read %ld bytes from inotify fd\n", (long) numRead);

        /* Process all of the events in buffer returned by read() */

        for (p = buf; p < buf + numRead; ) {
            event = (struct inotify_event *) p;
            displayInotifyEvent(event);

            p += sizeof(struct inotify_event) + event->len;
        }
    }

    return 0;
}


总结

本文对linux c监控文件事件做了简单的介绍,如果有疑问可以给我留言。


备注
1.编译器版本gcc4.8,运行环境centos7 64位
2.原文地址http://www.freecls.com/a/2712/4b

©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。