Linux inotify功能及实现原理【转】

转自: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-10-02 07:49:33

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

Linux inotify功能及实现原理

1. inotify主要功能 它是一个内核用于通知用户空间程序文件系统变化的机制. 众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug.udev 和 inotify 就是这种需求催生的.Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备

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

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

Ubuntu 12.04上享用新版本Linux的功能

Ubuntu 12.04上享用新版本Linux的功能 我司有一批Ubuntu 12.04的服务器暂时没有升级计划,但是像编译Android N代码等需求要求Linux的版本更新. 如何在不升级Ubuntu 12.04的情况下实现升级Linux版本的需求呢?我们有两大利器可以使用:docker和虚拟机. Docker大法 Docker安装 升级内核 Docker需要64位的Linux支持,幸好,这条是满足的. Docker需要kernel 3.10以上版本的支持. 首先我们查看一下当前的kerne

云端-个人开发手机软件如何实现软件内的网络备份资料信息功能,求原理介绍

问题描述 个人开发手机软件如何实现软件内的网络备份资料信息功能,求原理介绍 比如开发一款记事本软件,该软件可以将用户记的东西直接同步到云端,即使手机遗失了在其他手机上下载一个同样的软件点击同步就可以将保存在云端的数据下载下来继续使用.个人开发一款包含这样功能的软件如何才能增加这一功能呢?请问原理是什么,个人设计的软件实现这一功能难度大吗?

azure- Azure上有什么样的push功能? 实现原理是什么样的? 国内能用么?

问题描述 Azure上有什么样的push功能? 实现原理是什么样的? 国内能用么? 大家好!! Azure上有什么样的push功能? 实现原理是什么样的? 国内能用么? 求解答.. 谢啦.... 解决方案 Hi, 在Azure中,我们可以使用通知中心服务来实现推送功能,更多详细请阅读:http://www.windowsazure.cn/documentation/services/notification-hubs/,文章包含了关于通知中心服务的一些概念,和几种使用场景,希望对你会有所帮助.

12121-百度地图截图功能的实现原理

问题描述 百度地图截图功能的实现原理 各位大神你们好,请问百度地图截图的原理是什么啊,憋了一天了,试了很多插件都不行,比如说imgAreaSelect或者是jquery的截图插件,这些插件都是对图片进行截取,对于百度地图截图完全不实用.所以还请大神们指点,谢谢了.

Android微信抢红包功能的实现原理浅析

快到过农历年了,微信红包也越来越多了,出现了好多红包外挂程序,就很好奇如何实现的,于是自己研究了一番,亲自写了个微信抢红包的APP.现在就一步一步来实现它. 实现思路 微信抢红包程序开启时候,他就可以随时识别.捕获红包,服务可以实现正在功能,当我们开启服务的时候,服务就不停的在后台运行,不停地轮询着微信里面的消息,当发现红包时候就立即打开微信红包所在的界面.但是他怎识别红包呢?需要找到微信抢红包里面节点的view,当找到对应的view,在获取view的关键字或者id,根据关键字或者id,自动的模

Linux调试器的工作原理(一):基础篇

这是调试器工作原理系列文章的第一篇,我不确定这个系列会有多少篇文章,会涉及多少话题,但我仍会从这篇基础开始. 这一篇会讲什么 我将为大家展示 Linux 中调试器的主要构成模块 - ptrace 系统调用.这篇文章所有代码都是基于 32 位 Ubuntu 操作系统.值得注意的是,尽管这些代码是平台相关的,将它们移植到其它平台应该并不困难. 缘由 为了理解我们要做什么,让我们先考虑下调试器为了完成调试都需要什么资源.调试器可以开始一个进程并调试这个进程,又或者将自己同某个已经存在的进程关联起来.调

Linux下实现断点续传的原理介绍

  断点续传是一种结合本地存储和网络存储的技术,主要用来解决网络失效时的视频丢失问题.DVS通常本身没有视频存储功能,而是必须由后端的NVR来实现视频的存储,因此对于网络稳定性要求很高,网络连接失败.丢包严重.抖动等各种因素都可能造成视频数据的丢失.断点续传支持从文件上次中断的地方开始传送数据,而并非是从文件开头传送.这就是断点续传的定义.系统都默认可以断点续传,但我们很少知道他的原理,下面就来看看小编的介绍吧. 断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有