Linux监听器 -- inotify

inotify作为Linux系统的一个监听器,能够监听文件或者目录的变化。

inotify接口

inotify的接口主要有三个,分别是inotify_init、inotify_add_watch 和 inotify_rm_watch。下面分别进行详细介绍。
inotify_init 函数用于创建inotify句柄,函数原型int inotify_init(void);
inotify_add_watch 负责添加监听的文件或者目录,函数原型int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
fd: inotify_init 创建的inotify句柄。
pathname: 要监听的文件或者目录路径。
mask: 要监听的事件类型。
在这里插入图片描述
inotify_rm_watch 用于删除被监听的文件或者目录,函数原型int inotify_rm_watch(int fd, int wd);
fd: inotify_init 创建的inotify句柄。
wd: inotify_add_watch 创建的句柄

inotify使用

结构体 inotify_event 主要用于保存被监听文件或目录的信息,具体定义如下。
在这里插入图片描述
实现一个基于inotify的简单例子,代码如下。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <pthread.h>

#define EVENTS_BUF_SIZE 1024

static void PrintEvent(const char *base, struct inotify_event *event)
{
    
    
    char *operate;
    int mask = event->mask;

    if (mask & IN_ACCESS)        operate = "ACCESS";
    if (mask & IN_ATTRIB)        operate = "ATTRIB";
    if (mask & IN_CLOSE_WRITE)   operate = "CLOSE_WRITE";
    if (mask & IN_CLOSE_NOWRITE) operate = "CLOSE_NOWRITE";
    if (mask & IN_CREATE)        operate = "CREATE";
    if (mask & IN_DELETE_SELF)   operate = "DELETE_SELF";
    if (mask & IN_MODIFY)        operate = "MODIFY";
    if (mask & IN_MOVE_SELF)     operate = "MOVE_SELF";
    if (mask & IN_MOVED_FROM)    operate = "MOVED_FROM";
    if (mask & IN_MOVED_TO)      operate = "MOVED_TO";
    if (mask & IN_OPEN)          operate = "OPEN";
    if (mask & IN_IGNORED)       operate = "IGNORED";
    if (mask & IN_DELETE)        operate = "DELETE";
    if (mask & IN_UNMOUNT)       operate = "UNMOUNT";

    printf("%s: %s\n", base, operate);
}

static void Thread_Inotify(void *arg)
{
    
    
    char *path = (char *)arg;
    char events[EVENTS_BUF_SIZE];
    int fd = inotify_init();  // 创建 inotify 句柄
    if (fd < 0) {
    
    
        printf("Failed to initalize inotify\n");
        return;
    }
    int ret = inotify_add_watch(fd, path, IN_ALL_EVENTS);   // 添加监听事件类型
    if (ret == -1) {
    
    
        printf("Failed to add file or directory watch\n");
        return;
    }

    while (1) {
    
    
        memset(events, 0, sizeof(events));

        int nbytes = read(fd, events, sizeof(events));
        if (nbytes <= 0) {
    
    
            printf("Failed to read events\n");
            continue;
        }

        int offset = 0;
        struct inotify_event event;
        do {
    
    
            memset(&event, 0x00, sizeof(event));
            memcpy(&event, &events[offset], sizeof(event));
            PrintEvent(path, &event);
            offset += sizeof(struct inotify_event) + event.len;
        } while (offset < nbytes);
    }  
}

static void Thread_HandleFile(void *arg)
{
    
    
    char *path = (char *)arg;
    while (1)
    {
    
    
        FILE *fp = fopen(path, "wb+");
        char wbuf[] = "123";
        fwrite(wbuf, sizeof(wbuf), sizeof(char), fp);
        fclose(fp);

        sleep(2);

        fp = fopen(path, "r");
        char rbuf[16];
        fread(rbuf, sizeof(rbuf), sizeof(char), fp);
        fclose(fp);
        
        sleep(2);
        break;
    }
}

int main(int argc, char *argv[])
{
    
    
    char *path = argv[1];
    pthread_t thid_inotify, thid_handle;

    pthread_create(&thid_inotify, NULL, (void *)Thread_Inotify, path);

    sleep(2);

    pthread_create(&thid_handle, NULL, (void *)Thread_HandleFile, path);

    while (1) {
    
    
        sleep(2);
    }

    return 0;
}

inotify原理

以read为例进行说明,当用户调用read时,内核会将事件保存在inotify_device中的队列events(struct list_head events;)中,然后唤醒等待该events的进程(wait_queue_head_t wq;)。具体的调用关系如下图:
在这里插入图片描述
当用户调用读写等系统调用时,内核会创建相应的触发函数来产生一个事件,并将该事件添加到inotify_device的事件队列events中,并唤醒等待该事件的进程。当进程被唤醒,便可以通过read来读取inotify事件队列中的事件。

猜你喜欢

转载自blog.csdn.net/liang_zhaocong/article/details/130589095