inotify机制监控文件系统事件原理及使用

1.基本描述

inotify提供了一种监控文件系统事件的机制,可以用来监控单个的文件以及目录。当一个目录被监控,inotify会返回该目录以及该目录下面文件的事件。

2.原理以及使用

2.1内核原理

inotify机制借用了内核里面的notify通知链技术,针对文件系统里面的使用主要是在inode结构体里面加入了相关的字段(内核版本3.10.0-327):

struct inode {
     。。。
  #ifdef CONFIG_FSNOTIFY
      __u32 i_fsnotify_mask; /* all events this inode cares about */  具体可能监控的事件,事件基本上是一些位段
      struct hlist_head i_fsnotify_marks;  /* 具体的链表,链表上可以挂入多个mask结构(事件) */
  #endif
  。。。
}

struct fsnotify_mark {
     __u32 mask; /* mask this mark is for */
     atomic_t refcnt; /* active things looking at this mark */
     struct fsnotify_group *group; /* group this mark is for */
     struct list_head g_list; /* list of marks by group->i_fsnotify_marks */
     spinlock_t lock; /* protect group and inode */
     union {
          struct fsnotify_inode_mark i;
          struct fsnotify_vfsmount_mark m;
     };
     __u32 ignored_mask; /* events types to ignore */
     unsigned int flags; /* vfsmount or inode mark? */
     struct list_head destroy_list;
     void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
};

具体的关系结构图如图所示:

                                                                 图1 inotify机制内核下面各结构体关系图

2.2用户态接口原理

用户态接口的基本思路是初始化一个具体的inotify实例,并设置实例以及具体监控哪些事件。当有具体的事件以后可以读取对应的

结构体来解析。事件结构体为:

struct inotify_event {
     int wd; /* Watch descriptor */ 监控描述符
     uint32_t mask; /* Mask of events */  具体的事件(文件创建、删除、属性修改等)
     uint32_t cookie; /* Unique cookie associating related
                                events (for rename(2)) */
     uint32_t len; /* Size of name field */
     char name[]; /* Optional null-terminated name */  具体的文件名
};

具体可以监控的事件主要有:(注释比较清晰了)

IN_ACCESS File was accessed (read) (*).
IN_ATTRIB Metadata changed, e.g., permissions, timestamps, extended attributes, link count (since Linux 2.6.25), UID, GID, etc. (*).
IN_CLOSE_WRITE File opened for writing was closed (*).
IN_CLOSE_NOWRITE File not opened for writing was closed (*).
IN_CREATE File/directory created in watched directory (*).
IN_DELETE File/directory deleted from watched directory (*).
IN_DELETE_SELF Watched file/directory was itself deleted.
IN_MODIFY File was modified (*).
IN_MOVE_SELF Watched file/directory was itself moved.
IN_MOVED_FROM Generated for the directory containing the old filename when a file is renamed (*).
IN_MOVED_TO Generated for the directory containing the new filename when a file is renamed (*).
IN_OPEN File was opened (*).

主要的用户态接口函数:

int inotify_init(void);
int inotify_init1(int flags);

初始化一个inotify实例并且返回一个文件描述符,inotify实例和一个evnet队列挂钩,失败返回-1。

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

添加一个watch对象,fd为具体的inotify实例描述符,pathname为监控的目录或者文件,mask为具体的事件,成功返回非负整数,失败返

回-1.

int inotify_rm_watch(int fd, int wd);

删除一个watch,fd为inotify实例描述符,wd为watch描述符。成功返回0,失败返回-1.

3.测试程序

#include
#include
#include
#include
#include
#include
#include
#include
/*
 * struct inotify_event {
 *    int      wd;       // Watch descriptor
 *    uint32_t mask;     // Mask of events
 *    uint32_t cookie;   // Unique cookie associating related  events (for rename(2))
 *    uint32_t len;      // Size of name field
 *    char     name[];   // Optional null-terminated name
 * };
 *
 */

int giNotifyFd;
int giaWds[20];
int giCount;
int watch_inotify_events(int fd)
{
    char event_buf[512];
    int ret;
    int event_pos = 0;
    int event_size = 0;
    struct inotify_event *event;
    time_t tNow;
    struct tm *pTimeNow;

    /* 读事件是否发生,没有发生就会阻塞 */
    ret = read(fd, event_buf, sizeof(event_buf));

    /* 如果read的返回值,小于inotify_event大小出现错误 */
    if (ret < (int)sizeof(struct inotify_event)){
        printf("counld not get event!\n");
        return -1;
    }
    /* 因为read的返回值存在一个或者多个inotify_event对象,需要一个一个取出来处理 */
    while (ret >= (int)sizeof(struct inotify_event)) {
        event = (struct inotify_event*)(event_buf + event_pos);
        if (event->len) {
            // time(&tNow);
            // pTimeNow = localtime(&tNow);
            // printf("Local time is:%s", asctime(pTimeNow));

            if(event->mask & IN_CREATE){
                printf("watch is %d, create file: %s\n", event->wd, event->name);
            } else {
                printf("watch is %d, delete file: %s\n", event->wd, event->name);
            }
            if (event->mask & IN_ATTRIB) {
                printf("watch is %d, modify file attribute: %s\n", event->wd, event->name);
            }
        }
        /* event_size就是一个事件的真正大小 */
        event_size = sizeof(struct inotify_event) + event->len;
        ret -= event_size;
        event_pos += event_size;
    }
    return 0;
}

void init_all_iwds(char *pcName)
{
    int iWd; 
    struct stat tStat;
    DIR *pDir;
    struct dirent *ptDirent;
    char caNametmp[100];
 
    iWd = inotify_add_watch(giNotifyFd, pcName, IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MODIFY);
    giaWds[giCount] = iWd;
    giCount++;
 
    if (-1 == stat(pcName, &tStat)) {
printf("stat %s error\n", pcName);
        return;    
    }
    if (!S_ISDIR(tStat.st_mode))
        return;
    /* now the child dir */
    pDir = opendir(pcName);
    if (NULL == pDir) {
    printf("opendir %s error\n", pcName);
return;
    }
    while (NULL != (ptDirent = readdir(pDir))) {
    if ((0 == strcmp(ptDirent->d_name, ".")) || (0 == strcmp(ptDirent->d_name, "..")))
continue;
// printf("sub name is %s, d_type is 0x%x\n", ptDirent->d_name, ptDirent->d_type);
sprintf(caNametmp, "%s/%s", pcName, ptDirent->d_name);
if (-1 == stat(caNametmp, &tStat)) {
printf("stat error:%s\n", caNametmp);
return;
}
if (!S_ISDIR(tStat.st_mode))
continue;

printf("sub absoulte dir name is %s\n", caNametmp);
        // iWd = inotify_add_watch(giNotifyFd, caNametmp, IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MODIFY);
init_all_iwds(caNametmp);
    }  
    closedir(pDir);
}

int main(int argc, char** argv)
{
    int iNotifyRet;
    fd_set fds;
    int iaWd[10];
    int icount = 0;

    if (argc != 2) {
        printf("Usage: %s \n", argv[0]);
        return -1;
    }

    /* inotify初始化 */
    giNotifyFd = inotify_init();
    if (giNotifyFd == -1) {
        printf("inotify_init error!\n");
        return -1;
    }

    /* 添加watch对象 */
    init_all_iwds(argv[1]);

    /* 处理事件 */
    while (1) {
        FD_ZERO(&fds);
        FD_SET(giNotifyFd, &fds);

        if (select(giNotifyFd+1, &fds, NULL, NULL, NULL) > 0) {
            iNotifyRet = watch_inotify_events(giNotifyFd);
if (-1 == iNotifyRet)
break;
        }
    }

    /* 删除inotify的watch对象 */
    // if (inotify_rm_watch(giNotifyFd, iWd) == -1) {
    if (inotify_rm_watch(giNotifyFd, 1) == -1) {
        printf("notify_rm_watch error!\n");
        return -1;
    }

    /* 关闭inotify描述符 */
    close(giNotifyFd);

    return 0;
}

文件命名为:inotify.c

编译: gcc -o inotify inotify.c    生成可执行文件

执行: ./inotify输出  Usage: ./inotify

 提示需要输入具体监控的目录或者文件。

执行: ./inotify /home/work/0604_inotify/ &

        创建 aaa 文件,打印出  watch is 1, create file: aaa

        修改aaa文件属性,打印出  watch is 1, modify file attribute: aaa

你可能感兴趣的:(linux系统编程)