Linux inotify功能及原理(inotify_init、inotify_add_watch、inotify_rm_watch、read)

1. inotify主要功能

它是一个内核用于通知用户空间程序文件系统变化的机制。

开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug、udev 和 inotify 就是这种需求催生的。Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理,udev 动态地维护 /dev 下的设备文件,inotify 是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知。

inotify是文件系统变化通知机制,在监听到文件系统变化后,会向相应的应用程序发送事件。典型的应用场景是文件管理器,理想情况下是用户修改了文件内容后立刻显示出文件最新的内容,而刷新后才显示,如果没有inotify机制,一般会采用轮询的方式实现这种功能,这不能再第一时间反应文件系统的变化,而且浪费CPU时间。

2. 用户接口

在用户态,inotify 通过三个系统调用和在返回的文件描述符上的文件 I/ 操作来使用,使用 inotify 的第一步是创建 inotify 实例:

 int fd = inotify_init ();

  每一个 inotify 实例对应一个独立的排序的队列。文件系统的变化事件被称做 watches 的一个对象管理,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。Watch 对象通过 watch描述符引用,watches 通过文件或目录的路径名来添加。目录 watches 将返回在该目录下的所有文件上面发生的事件。

下面函数用于添加一个 watch:

int wd = inotify_add_watch (fd, path, mask);

fd 是 inotify_init() 返回的文件描述符,path 是被监视的目标的路径名(即文件名或目录名),mask 是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以使用同样的方式来修改事件掩码,即改变希望被通知的inotify 事件。Wd 是 watch 描述符。

    下面的函数用于删除一个 watch:

 int ret = inotify_rm_watch (fd, wd);

fd 是 inotify_init() 返回的文件描述符,wd 是 inotify_add_watch() 返回的 watch 描述符。Ret 是函数的返回值。

文件事件用一个 inotify_event 结构表示,它通过由 inotify_init() 返回的文件描述符使用通常文件读取函数 read 来获得

struct inotify_event {
        __s32           wd;             /* watch descriptor */
        __u32           mask;           /* watch mask */
        __u32           cookie;         /* cookie to synchronize two events */
        __u32           len;            /* length (including nulls) of name */
        char            name[0];        /* stub for possible name */
};

结构中的 wd 为被监视目标的 watch 描述符,mask 为事件掩码,len 为 name字符串的长度,name 为被监视目标的路径名,该结构的 name 字段为一个桩,它只是为了用户方面引用文件名,文件名是变长的,它实际紧跟在该结构的后面,文件名将被 0 填充以使下一个事件结构能够 4 字节对齐。注意,len 也把填充字节数统计在内。

通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。

size_t len = read (fd, buf, BUF_LEN);

 buf 是一个 inotify_event 结构的数组指针,BUF_LEN 指定要读取的总长度,buf 大小至少要不小于 BUF_LEN,该调用返回的事件数取决于 BUF_LEN 以及事件中文件名的长度。Len 为实际读去的字节数,即获得的事件的总长度。

    可以在函数 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 来得到当前队列的长度。close(fd)将删除所有添加到 fd 中的 watch 并做必要的清理。

int inotify_init (void);
int inotify_add_watch (int fd, const char *path, __u32 mask);
int inotify_rm_watch (int fd, __u32 mask);
#include <sys/inotify.h>
 
int inotify_init()//初始化inotify,每个inotify实例对应一个排队的序列
 
int inotify_add_watch(int fd,const char *path,uint32_t mask)//通过文件名和事件掩码添加一个watch对象,返回值是watch对象的描述符
//fd:inotify_init的返回值
//path:要监控的文件路径
//mask:监听文件的哪些事件
/*
    IN_ATTRIB,文件属性被修改
    IN_CLOSE_WRITE,可写文件被关闭
    IN_CLOSE_NOWRITE,不可写文件被关闭
    IN_CREATE,文件/文件夹被创建
    IN_DELETE,文件/文件夹被删除
    IN_DELETE_SELF,被监控的对象本身被删除
    IN_MODIFY,文件被修改
    IN_MOVE_SELF,被监控的对象本身被移动
    IN_MOVED_FROM,文件被移出被监控目录
    IN_MOVED_TO,文件被移入被监控目录
    IN_OPEN,文件被打开
*/
//返回值:表示对那个文件的监视
 
int inotify_rm_watch(int fd,uint32_t wd)//删除监视对象
//fd:inotify_init的返回值
//wd:inotify_add_watch的返回值
 
//文件事件用struct inotify_event表示,通过fd由read函数来读取
//buf是inotify_event结构的数组的指针,BUF_LEN 指定要读取的总长度,buf的长度不能小于BUF_LEN<br>//read读取到的事件数取决于BUF_LEN以及事件中文件名的长度,返回实际读取的长度
size_t len = read (fd, buf, BUF_LEN);
 
struct inotify_event
{
        __s32           wd;             /* watch descriptor */
        __u32           mask;           /* watch mask */
        __u32           cookie;         /* cookie to synchronize two events */
        __u32           len;            /* length (including nulls) of name */
        char            name[0];        /* stub for possible name */
};<br>//文件名是变长的,实际紧跟在该结构的后面,文件名被0填充以保证下一个事件结构能够4字节对齐。<br>//len字段也把name的填充字段统计在内

 

3. 内核实现原理

4. 使用示例

使用 inotify 来监视文件系统事件的例子

  • 5
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要避免inotify_add_watch函数返回no such file or directory错误,需要确保要监视的目录存在。可以通过调用access或stat函数来判断目录是否存在,如果目录不存在,则可以先创建该目录。 以下是一个示例代码片段: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/inotify.h> #include <sys/stat.h> int main(int argc, char **argv) { int fd, wd; char *path = "/path/to/dir"; // 检查目录是否存在 struct stat st; if (stat(path, &st) == -1) { perror("stat"); exit(EXIT_FAILURE); } // 创建 inotify 实例 fd = inotify_init(); if (fd == -1) { perror("inotify_init"); exit(EXIT_FAILURE); } // 添加监视器 wd = inotify_add_watch(fd, path, IN_ALL_EVENTS); if (wd == -1) { perror("inotify_add_watch"); exit(EXIT_FAILURE); } // 等待事件发生 // ... // 移除监视器 inotify_rm_watch(fd, wd); // 关闭 inotify 实例 close(fd); return 0; } ``` 在上面的代码中,首先通过调用stat函数来检查要监视的目录是否存在。如果目录不存在,程序会输出一个错误信息并退出。如果目录存在,则继续创建inotify实例,并调用inotify_add_watch函数添加一个监视器。如果inotify_add_watch返回了no such file or directory错误,则说明要监视的目录不存在,程序会输出一个错误信息并退出。如果inotify_add_watch成功添加了监视器,则可以继续等待事件的发生。最后,需要调用inotify_rm_watch函数移除监视器,并关闭inotify实例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值