Linux listener -- inotify

As a listener in the Linux system, inotify can monitor changes in files or directories.

inotify interface

There are three main interfaces of inotify, namely inotify_init, inotify_add_watch and inotify_rm_watch. Each is introduced in detail below.
The inotify_init function is used to create an inotify handle. The function prototype int inotify_init(void);
inotify_add_watch is responsible for adding the monitored files or directories. The function prototype int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
fd: inotify_init creates the inotify handle.
pathname: The file or directory path to be monitored.
mask: The event type to listen for.
Insert image description here
inotify_rm_watch is used to delete the monitored files or directories. The function prototype int inotify_rm_watch(int fd, int wd);
fd: inotify handle created by inotify_init.
wd: handle created by inotify_add_watch

inotify use

The structure inotify_event is mainly used to save the information of the monitored files or directories. The specific definition is as follows.
Insert image description here
To implement a simple example based on inotify, the code is as follows.

#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 principle

Taking read as an example, when the user calls read, the kernel will save the event in the queue events (struct list_head events;) in inotify_device, and then wake up the process waiting for the events (wait_queue_head_t wq;). The specific calling relationship is as shown below:
Insert image description here
When the user calls system calls such as reading and writing, the kernel will create a corresponding trigger function to generate an event, add the event to the event queue events of inotify_device, and wake up the process waiting for the event. When the process is awakened, the events in the inotify event queue can be read through read.

Guess you like

Origin blog.csdn.net/liang_zhaocong/article/details/130589095