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事件队列中的事件。