Linux内核Inotify机制学习笔记

时间:2022-12-11 11:51:25

一、Inotify简介:

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

Inotify 是为替代 dnotify 而设计的,它克服了 dnotify 的缺陷,提供了更好用的,简洁而强大的文件变化通知机制:

(1) Inotify 不需要对被监视的目标打开文件描述符,而且如果被监视目标在可移动介质上,那么在 umount 该介质上的文件系统后,被监视目标对应的 watch 将被自动删除,并且会产生一个 umount 事件。
(2) Inotify 既可以监视文件,也可以监视目录。
(3) Inotify 使用系统调用而非 SIGIO 来通知文件系统事件。
(4) Inotify 使用文件描述符作为接口,因而可以使用通常的文件 I/O 操作select 和 poll 来监视文件系统的变化。

Inotify 可以监视的文件系统事件包括:
IN_ACCESS,即文件被访问
IN_MODIFY,文件被 write
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可写文件被 close
IN_CLOSE_NOWRITE,不可写文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移来,如 mv、cp
IN_CREATE,创建新文件
IN_DELETE,文件被删除,如 rm
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)

二、使用方法

通过inotify_init()创建一个文件描述符,然后使用inotify_add_watch()附加一个或多个监视器(一个监视器是一个路径和一组事件),接着使用 read()方法从描述符获取事件信息,read()函数在事件发生之前是被阻塞的。
也可以在函数inotify_init()返回的文件描述符fd 上使用 select() 或poll(), 也可以在fd上使用ioctl命令FIONREAD来得到当前队列的长度。close(fd)将删除所有添加到fd中的watch并做必要的清理。

系统调用接口:

第一步 创建 inotify 实例

int fd = inotify_init ();

第二步 添加一个 watch

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

每一个inotify 实例对应一个独立的排序的队列。文件系统的变化事件使用Watch对象来描述,每一个Watch是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的inotify 事件,每一个位对应一个 inotify 事件。Watch通过文件或目录的路径名来添加。目录Watch将返回在该目录下的所有文件上面发生的事件。
参数:
fd 是 inotify_init() 返回的文件描述符,path是被监视的目标的路径名(即文件名或目录名),mask是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以使用同样的方式来修改事件掩码,即改变希望被通知的inotify 事件。

第三步 使用read系统调用读取处理事件

1.删除一个watch

int ret = inotify_rm_watch (fd, wd);

fd是inotify_init()返回的文件描述符句柄,wd是 inotify_add_watch()返回的watch描述符句柄。Ret是函数的返回值。文件事件用一个 inotify_event结构表示,它通过读取inotify_init()返回的文件描述符句柄来获得。

struct inotify_event {
__s32 wd; /* 被监视目标的 watch 描述符 */
__u32 mask; /* 事件掩码 */
__u32 cookie; /* cookie to synchronize two events */
__u32 len; /* name字符串的长度 */
char name[]; /* 被监视目标的路径名 */
};

三、使用demo

#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <errno.h>
#include <string.h> /*
* 指定一个目录,当目录中创建或者删除文件时,把相应的信息打印出来
* Usage : inotify <dir>
*/
int main(int argc, char *argv[])
{
int fd;
int result;
char event_buf[];
int event_pos = ;
struct inotify_event *event;
struct inotify_event *pos_event; if( != argc) {
printf("Usage : %s <dir>\n", argv[]);
return -;
} /* 1.初始化一个inotify的实例,获得一个该实例的文件描述符 */
fd = inotify_init();
if (fd == -) {
printf("inotify init error: %s", strerror(errno));
return -;
} /* 2.添加一个用于监视的目录: 监视该目录中文件的添加和移除修改 */
result = inotify_add_watch(fd, argv[], IN_DELETE | IN_CREATE | IN_MODIFY);
if(- == result) {
printf("inotify_add_watch error:%s\n", strerror(errno));
return -;
} /* 不停的监视当前目录中是否有添加或者删除文件 */
while() {
/* 读取inotify的监视事件的信息 */
memset(event_buf, , sizeof(event_buf));
pos_event = (struct inotify_event *)event_buf; /* 阻塞读取 */
result = read(fd, event_buf, sizeof(event_buf));
if (result < (int)sizeof(struct inotify_event)) {
printf("could not read event: %s\n",strerror(errno));
return -;
} /* 将获得的inotify信息打印出来 */
while (result >= (int)sizeof(struct inotify_event)) {
event = pos_event;
if (event->len) {
if (event->mask & IN_CREATE) {
printf("create : file is %s\n", event->name);
} else if (event->mask & IN_DELETE) {
printf("delete : file is %s\n", event->name);
} else if (event->mask & IN_MODIFY) {
printf("modify : file is %s\n", event->name);
}
} /* 更新位置信息,以便获得下一个 inotify_event 对象的首地址 */
pos_event++;
result -= (int)sizeof(struct inotify_event);
}
} /* 关闭这个文件描述符 */
close(fd); return ;
}

测试:

# ./inotify_test /mytest/ &
# touch a.txt
create : file is a.txt
# cp /etc/profile ./
create : file is profile
modify : file is profile
#
t# echo hello > a.txt
modify : file is a.txt
modify : file is a.txt
#
# echo good >> a.txt
modify : file is a.txt
#

四、内核实现

1.在内核中,每一个inotify实例对应一个 inotify_device 结构,inotify_device在用户态调用inotify_init()时创建,当关闭 inotify_init()返回的文件描述符时将被释放。
2.inotify_watch结构在用户态调用inotify_add_watch()时创建,在用户态调用inotify_rm_watch()或close(fd)时被释放。无论是目录还是文件,在内核中都对应一个inode结构,inotify 系统在inode结构中增加了两个字段:

#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct semaphore inotify_sem; /* protects the watches list */
#endif

inotify_watches 指向被监视目标上的watch列表,每当用户调用inotify_add_watch()时,内核就为添加的watch创建一个inotify_watch结构,并把它插入到被监视目标对应的inode的inotify_watches列表。inotify_sem用于同步对inotify_watches列表的访问。

3.当文件系统发生事件时,相应的文件系统将显示调用fsnotify_* 来把相应的事件报告给 inotify 系统,目前实现包括:
fsnotify_move:文件从一个目录移动到另一个目录
fsnotify_nameremove:文件从目录中删除
fsnotify_inoderemove:自删除
fsnotify_create:创建新文件
fsnotify_mkdir:创建新目录
fsnotify_access:文件被读
fsnotify_modify:文件被写
fsnotify_open:文件被打开
fsnotify_close:文件被关闭
fsnotify_xattr:文件的扩展属性被修改
fsnotify_change;文件被修改或原数据被修改

上面的通知函数最后都调用inotify_inode_queue_event,该函数首先判断对应的inode是否被监视,这通过查看inotify_watches列表是否为空来实现,如果发现inode没有被监视,什么也不做,立刻返回,反之,遍历inotify_watches列表,看是否当前的文件操作事件被某个 watch 监视,如果是,调用 inotify_dev_queue_event,否则,返回。
函数inotify_dev_queue_event 首先判断该事件是否是上一个事件的重复,如果是就丢弃该事件并返回,否则,它判断是否 inotify 实例即 inotify_device 的事件队列是否溢出,如果溢出,产生一个溢出事件,否则产生一个当前的文件操作事件,这些事件通过kernel_event 构建,kernel_event 将创建一个 inotify_kernel_event 结构,然后把该结构插入到对应的 inotify_device 的 events 事件列表,然后唤醒等待在inotify_device 结构中的 wq 指向的等待队列。

想监视文件系统事件的用户态进程在inotify 实例(即inotify_init()返回的文件描述符)上调用 read,若此时没有事件时就阻塞在等待队列wq上。

五.Android中对inotify的使用

Android的文件观察Observer机制就是在Linux文件系统的Inotify机制上实现的

FileObserver.java
    |
android_util_FileObserver.cpp
    |
fs/notify/inotify

参考:

https://blog.csdn.net/yangwen123/article/details/13996361

Linux内核Inotify机制学习笔记的更多相关文章

  1. Linux内核策略介绍学习笔记

    主要内容 硬件 策略 CPU 进程调度.系统调用.中断 内存 内存管理 外存 文件IO 网络 协议栈 其他 时间管理 进程调度 内核的运行时间 系统启动.中断发生.系统调用以及内核线程. 进程和线程的 ...

  2. &lbrack;内核同步&rsqb;浅析Linux内核同步机制

    转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...

  3. Linux内核同步机制--转发自蜗窝科技

    Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...

  4. Linux内核同步机制

    http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...

  5. 《Linux内核分析》读书笔记(四章)

    <Linux内核分析>读书笔记(四章) 标签(空格分隔): 20135328陈都 第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行 ...

  6. 浅析Linux内核同步机制

    非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...

  7. Linux内核同步机制之(四):spin lock【转】

    转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...

  8. Linux内核设计与实现笔记&lowbar;1&lowbar;基本概念

    Linux内核设计与实现笔记_1_基本概念 操作系统 系统这个词包含了操作系统和所有运行在它上面的应用程序.操作系统是指在整个系统中负责完成分最基本功能和系统管理的那些部分,这些部分应该包括: 内核, ...

  9. JUC&period;Lock&lpar;锁机制&rpar;学习笔记&lbrack;附详细源码解析&rsqb;

    锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...

随机推荐

  1. S7-1200 与 S7-200 的对比PPT

  2. etc目录名字的意思---挖Linux中的古老缩略语

    Unix已经有35年历史了.许多人认为它开始于中世纪,这个中世纪是相对于计算机技术的产生和发展来说的.在过去的时间里,Unix和它的子分支Linux收集有许多的历史和一些完全古老的语言.在这篇技巧文章 ...

  3. beanutils中Lazy

    public class LazyBean { //    public static void main(String[] args) {//        DynaBean dynaBean=ne ...

  4. transform做2d和3d变形(css动画一)

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 有段时间我是没理清transform.translate.transition和animation之间的关 ...

  5. 【数据结构】算法 Maximum Subarray

    最大子数组:Maximum Subarray 参考来源:Maximum subarray problem Kadane算法扫描一次整个数列的所有数值,在每一个扫描点计算以该点数值为结束点的子数列的最大 ...

  6. R图表入门

    R图表入门 R语言最强的功能就是统计和作图了,在学习了基本语法之后,博主马上体验了一下R的图表功能 条形图 例1 H = c(7,12,28,3,41) M = c("Mar",& ...

  7. day37协程与线程套接字通讯

    协程与线程套接字通讯基于多线程实现套接字服务端支持并发,服务端 from socket import * from threading import Thread def comunicate(con ...

  8. UE4开发安卓遇到的坑

    做个记录,方便以后自己查询. 1.  打包后,应用出现缺乏google key的问题. 理论上,这个网站可以解决:https://wiki.unrealengine.com/How_To_Sign_U ...

  9. 4&period;3&period;6 对象的界定通过编写接口来访问带这类命名结构的表会出问题。如前所述,SQL Server的灵活性不应用作编写错误代码或创建问题对象的借口。 注意在使用Management Studio的脚本工具时,SQL Server会界定所有的对象。这不是因为这么做是必须的,也不是编写代码的最佳方式,而是因为在界定符中封装所有的对象,比编写脚本引擎来查找需要界定的对象更容易。

    如前所述,在创建对象时,最好避免使用内嵌的空格或保留字作为对象名,但设计人员可能并没有遵守这个最佳实践原则.例如,我当前使用的数据库中有一个审核表名为Transaction,但是Transaction ...

  10. QQ彩票任意订阅内容导致骚扰用户

    数据包: POST /my/index.php?mod=securityinfo&op=AjaxUpdateTipsSet HTTP/1.1 Host: 888.sports.qq.com C ...