Linux inotify功能及实现原理

http://www.cnblogs.com/jiejnan/archive/2012/05/18/2507476.html

简介: 当需要对
Linux?文件系统进行高效率、细粒度、异步地监控时,可以采用 inotify。可利用它对用户空间进行安全、性能、以及其他方面的监控。

至于inotify的基本介绍可以看下面链接

http://www.ibm.com/developerworks/cn/linux/l-inotify/index.html?ca=drs-

这里主要说下自己试验的总结:

  1. 何时需要自己调用inotify_rm_watch

  2. 合适需要自己调用inotify_add_watch

  3. read调用注意事项

对于1,出现moved_from而没有出现moved_to时,这就需要自
己调用inotify_rm_watch把移走的文件目录(移到非监控目录下)监控删除,这个目录下面的子目录会自动删除的
。像其它操作:删除监控目录,监控目录在大的监控目录下移进移出是不需要自己调用inotify_rm_watch的,因为系统自动处理,产生的事件分别
为delete_self和move_self。

对于2,
这要看你是否需要递归监控每个目录,如果是,那么当你在监控目录下建立一个新目录时,就需要调用inotify_add_watch;放心系统不会出现单
独的moved_to, 如果你从非监控目录下copy一个目录到监控目录下,那么inotify产生的事件是create
(目录或文件),而不会是moved_to的,但可以单独产生moved_from事件,如情况1所说。

对与3,要知道下面红色的限制,

/proc interfaces
     The following interfaces can be
used to limit the amount of kernel memory consumed by inotify:

/proc/sys/fs/inotify/max_queued_events
     
The value in this file is used when an application calls inotify_init(2) to set
an upper limit on the number  of  events  that can 
be  queued to the corresponding inotify instance. 
Events in excess of this limit are dropped, but an N_Q_OVERFLOW event is
always generated.

/proc/sys/fs/inotify/max_user_instances
     
This specifies an upper limit on the number of inotify instances that can be
created per real user ID.

/proc/sys/fs/inotify/max_user_watches
     
This specifies an upper limit on the number of watches that can be created per
real user ID.

如果你监控的目录很大,那么其它限制你也必须考虑,调用read时,要注意返回的是一个完整的

struct inotify_event {

             
int      wd;       /*
Watch descriptor */

             
uint32_t mask;     /* Mask of events */

             
uint32_t cookie;   /* Unique cookie associating related

                                   
events (for rename(2)) */

             
uint32_t len;      /* Size of name field */

             
char     name[];   /* Optional null-terminated
name */
          };

结构。

http://blog.csdn.net/myarrow/article/details/7096460


1. inotify主要功能


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

众所周知,Linux 桌面系统与 MAC 或 Windows
相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能
够更好地管理设备,给用户提供更好的服务,如 hotplug、udev 和 inotify 就是这种需求催生的。Hotplug
是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理,udev 动态地维护 /dev 下的设备文件,inotify
是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,该机制是著名的桌面搜索引擎项目 beagle 引入的,并在 Gamin
等项目中被应用。

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);


3. 内核实现原理


在内核中,每一个 inotify 实例对应一个 inotify_device
结构:




struct inotify_device {
wait_queue_head_t wq; /* wait queue for i/o */
struct idr idr; /* idr mapping wd -> watch */
struct semaphore sem; /* protects this bad boy */
struct list_head events; /* list of queued events */
struct list_head watches; /* list of watches */
atomic_t count; /* reference count */
struct user_struct *user; /* user who opened this dev */
unsigned int queue_size; /* size of the queue (bytes) */
unsigned int event_count; /* number of pending events */
unsigned int max_events; /* maximum number of events */
u32 last_wd; /* the last wd allocated */
};

d_list 指向所有 inotify_device
组成的列表的,i_list 指向所有被监视 inode 组成的列表,count 是引用计数,dev 指向该 watch 所在的 inotify 实例对应的
inotify_device 结构,inode 指向该 watch 要监视的 inode,wd 是分配给该 watch 的描述符,mask 是该 watch
的事件掩码,表示它对哪些文件系统事件感兴趣。

结构 inotify_device 在用户态调用
inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 inotify_watch 在用户态调用
inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。

无论是目录还是文件,在内核中都对应一个 inode
结构,inotify 系统在 inode 结构中增加了两个字段:




struct inotify_watch {
struct list_head d_list; /* entry in inotify_device‘s list */
struct list_head i_list; /* entry in inode‘s list */
atomic_t count; /* reference count */
struct inotify_device *dev; /* associated device */
struct inode *inode; /* associated inode */
s32 wd; /* watch descriptor */
u32 mask; /* event mask for this watch */
};

d_list 指向所有 inotify_device
组成的列表的,i_list 指向所有被监视 inode 组成的列表,count 是引用计数,dev 指向该 watch 所在的 inotify 实例对应的
inotify_device 结构,inode 指向该 watch 要监视的 inode,wd 是分配给该 watch 的描述符,mask 是该 watch
的事件掩码,表示它对哪些文件系统事件感兴趣。

结构 inotify_device 在用户态调用
inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 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
列表的访问。当文件系统发生第一部分提到的事件之一时,相应的文件系统代码将显示调用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_unmount_inodes,它会在文件系统被 umount 时调用来通知 umount 事件给 inotify
系统。

以上提到的通知函数最后都调用
inotify_inode_queue_event(inotify_unmount_inodes直接调用 inotify_dev_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 上。

4. 使用示例

下面是一个使用 inotify 来监视文件系统事件的例子:




#include
#include
#include

_syscall0(int, inotify_init)
_syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask)
_syscall2(int, inotify_rm_watch, int, fd, __u32, mask)

char * monitored_files[] = {
"./tmp_file",
"./tmp_dir",
"/mnt/sda3/windows_file"
};

struct wd_name {
int wd;
char * name;
};

#define WD_NUM 3
struct wd_name wd_array[WD_NUM];

char * event_array[] = {
"File was accessed",
"File was modified",
"File attributes were changed",
"writtable file closed",
"Unwrittable file closed",
"File was opened",
"File was moved from X",
"File was moved to Y",
"Subfile was created",
"Subfile was deleted",
"Self was deleted",
"Self was moved",
"",
"Backing fs was unmounted",
"Event queued overflowed",
"File was ignored"
};
#define EVENT_NUM 16
#define MAX_BUF_SIZE 1024

int main(void)
{
int fd;
int wd;
char buffer[1024];
char * offset = NULL;
struct inotify_event * event;
int len, tmp_len;
char strbuf[16];
int i = 0;

fd = inotify_init();
if (fd < 0) {
printf("Fail to initialize inotify.\n");
exit(-1);
}

for (i=0; i<WD_NUM; wd="inotify_add_watch(fd," add (event- if { len) < buffer) - *)event (((char while *)buffer; inotify_event event len); len=‘%d.\n",‘ happens, printf(?Some offset="buffer;" MAX_BUF_SIZE)) buffer, while(len="read(fd," } wd_array[i].wd="wd;" exit(-1); wd_array[i].name); %s.\n?, for watch printf(?Can?t 0) (wd IN_ALL_EVENTS); wd_array[i].name, wd_array[i].name="monitored_files[i];" i++)>mask & IN_ISDIR) {
memcpy(strbuf, "Direcotory", 11);
}
else {
memcpy(strbuf, "File", 5);
}
printf("Object type: %s\n", strbuf);
for (i=0; iwd != wd_array[i].wd) continue;
printf("Object name: %s\n", wd_array[i].name);
break;
}
printf("Event mask: %08X\n", event->mask);
for (i=0; imask & (1<len;
event = (struct inotify_event *)(offset + tmp_len);
offset += tmp_len;
}
}
}



转自:http://linux.chinaitlab.com/kernel/391147_4.html

时间: 2024-08-09 11:15:21

Linux inotify功能及实现原理的相关文章

Linux 文件操作监控inotify功能及实现原理【转】

转自:http://blog.chinaunix.net/uid-26585427-id-5012973.html 1. inotify主要功能 它是一个内核用于通知用户空间程序文件系统变化的机制. 众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug.udev 和 inotify 就是

Linux网络服务02——DHCP原理与配置

Linux网络服务02--DHCP原理与配置 一.DHCP服务概述 1.DHCP(Dynamic Host Configuration Protocol)动态主机配置协议 DHCP是由Internet工作任务小组设计开发的,专门用于为TCP/IP网络中的计算机自动分配TCP/IP参数的协议. 使用DHCP可以减少管理员的工作量,避免IP地址冲突,当网络更改IP地址网段时不需要再重新配置每个用户的IP地址,提高了IP地址的利用率,方便客户端的配置. 2.可分配的地址信息主要包括: 网卡的IP地址.

(转)linux下文件删除的原理精华讲解(考试题答案系列)

linux下文件删除的原理精华讲解(考试题答案系列) 说明:本文为老男孩linux培训某节课前考试试题及答案分享博文内容的一部分,也是独立成题的,你可以点下面地址查看全部的内容信息.http://oldboy.blog.51cto.com/2561410/791245 5.描述linux下文件删除的原理(记时3分钟)<?xml:namespace prefix="o"> ?xml:namespace> Linux文件删除原理:     Linux是通过link的数量来

负载均衡的的基本功能和实现原理

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://virtualadc.blog.51cto.com/3027116/615836 负载均衡设备作为纵跨网络2-7层协议的设备,往往放置在网络设备和应用设备的连接处,对工程师在网络和应用基本知识方面的要求远高于其他设备,所以我们要在基本功能的理解上下更多的功夫.负载均衡设备还有另外一个称呼:4/7层交换机,但它首先是个2-3层交换机,这要求我们首先掌握2-3层的基本知识,然后才是本

linux小功能

1:第一次登录切换到root用户:su passwd 2:中文输入法安装:1/先到搜狗官网下载搜狗输入法;2/访问这个网址,配置一下:http://jingyan.baidu.com/article/4b07be3c60da3f48b380f3f0.html 3:java安装:http://www.wikihow.com/Install-Oracle-Java-JDK-on-Ubuntu-Linux linux小功能,布布扣,bubuko.com

为小linux添加功能,自动添加依赖的库到相应位置

给小linux添加功能,咱们写一个脚本吧 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 重大知识点:FILE=/etc/local/src 从左往右 echo ${FILE#/}  一个#代表将第一个/向左的内容都去掉 执行结果为etc/local/src echo ${FILE##/} 代表从左往右找,找到最后一个/ 并将向左的内容全部去掉 执行结果为/src    (用于取文件名) 从右向左 echo ${FILE%/*} 从右向左寻找/ 找到第一个 将/和向右的内容全

网站开发之用户重设密码 找回密码功能的实现原理

网站开发之用户重设密码 找回密码功能的实现原理,这个东西基本在现在每个系统中都带有的功能,但是实现的方式也是挺多的,这个是跟数据库没多大关系的方法嘞. 用户流程: 1.用户忘记密码,来到密码重设界面 2.用户输入Email地址,点击重设密码按钮 3.用户收到一封密码重设邮件,里面有重设密码的链接,此链接有过期时间 4.用户点击链接,来到密码重设页面,输入新密码,完成 这个流程并没有什么创新,很多网站都是用这套流程 后端实现方式: 1.当用户输入Email地址后,验证这个Email,如果存在于数据

Zookeeper的功能以及工作原理(转)

Zookeeper的功能以及工作原理 1.ZooKeeper是什么?ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作.最终,将简单易用的接口和性能高效.功能稳定的系统提供给用户 2.ZooKeeper提供了什么? 1)文件系统 2)通知机制 3.Zookeeper文件系统 每个子目录项如 NameService 都被称作为znode,和文件系统一样,我们

【知识强化】第五章 中央处理器 5.4 控制器的功能和工作原理

下面我们进入第五章的第四节,控制器的功能和工作原理. 那么首先,我们来看一下本章的一些内容.我们再对我们之前讲过的内容进行一些梳理,我们本章,第五章,中央处理器,也就是CPU,要学五节的内容.第一节我们讲了CPU的功能和基本结构,我们讲解了运算器的功能和结构,以及控制器的功能和结构.接下来我们讲解了指令的一个执行过程,我们讲解了指令周期的概念,以及一个数据流,几种数据流我花了大量的篇幅给大家做了 原文地址:https://www.cnblogs.com/ZHONGZHENHUA/p/115112