了解 inotify

Inotify 是一个 Linux 内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。
使用 inotify 很简单:创建一个文件描述符,附加一个或多个监视器(一个监视器 是一个路径和一组事件),然后使用 read() 方法从描述符获取事件信息。read() 并不会用光整个周期,它在事件发生之前是被阻塞的。
更好的是,因为 inotify 通过传统的文件描述符工作,您可以利用传统的 select() 系统调用来被动地监控监视器和许多其他输入源。两种方法 — 阻塞文件描述符和使用 select()— 都避免了繁忙轮询。
现在,让我们深入了解 inotify,写一些 C 代码,然后看看一组命令行工具,您可以构建并使用它们将命令和脚本附加到文件系统事件。Inotify 不会在中途失去控制,但它可以运行 cat 和 wget,并且在必要时严格执行。
要使用 inotify,您必须具备一台带有 2.6.13 或更新内核的 Linux 机器(以前的 Linux 内核版本使用更低级的文件监控器 dnotify)。如果您不知道内核的版本,请转到 shell,输入 uname -a:
% uname -a
Linux ubuntu-desktop 2.6.24-19-generic #1 SMP ... i686 GNU/Linux
如果列出的内核版本不低于 2.6.13,您的系统就支持 inotify。您还可以检查机器的 /usr/include/sys/inotify.h 文件。如果它存在,表明您的内核支持 inotify。
注意:FreeBSD 和 Mac OS X 提供一个类似于 inotify 的 kqueue。在 FreeBSD 机器上输入 man 2 kqueue 获取更多信息。
本文基于 Ubuntu Desktop version 8.04.1(即 Hardy),它运行在 Mac OS X version 10.5 Leopard 的 Parallels Desktop version 3.0。

inotify C API

Inotify 提供 3 个系统调用,它们可以构建各种各样的文件系统监控器:
inotify_init() 在内核中创建 inotify 子系统的一个实例,成功的话将返回一个文件描述符,失败则返回 -1。就像其他系统调用一样,如果 inotify_init() 失败,请检查 errno 以获得诊断信息。
顾名思义,inotify_add_watch() 用于添加监视器。每个监视器必须提供一个路径名和相关事件的列表(每个事件由一个常量指定,比如
IN_MODIFY)。要监控多个事件,只需在事件之间使用逻辑操作符或 — C 语言中的管道线(|)操作符。如果
inotify_add_watch() 成功,该调用会为已注册的监视器返回一个惟一的标识符;否则,返回
-1。使用这个标识符更改或删除相关的监视器。
inotify_rm_watch() 删除一个监视器。
此外,还需要 read()
和 close() 系统调用。如果描述符由 inotify_init() 生成,则调用 read()
等待警告。假设有一个典型的文件描述符,应用程序将阻塞对事件的接收,这些事件在流中表现为数据。文件描述符上的由 inotify_init()
生成的通用 close() 删除所有活动监视器,并释放与 inotify
实例相关联的所有内存(这里也用到典型的引用计数警告。与实例相关联的所有文件描述符必须在监视器和 inotify 消耗的内存被释放之前关闭)。
这个强大的工具提供 3 个应用程序编程接口(API)调用,以及简单、熟悉的范例 “所有内容都是文件”。现在,我们看看示例应用程序。
示例应用程序:事件监控
清单 1 是一个监控两个事件的目录的简短 C 程序:文件的创建和删除。
清单 1. 简单的 inotify 应用程序,它监控创建、删除和修改事件的目录
#include
#include
#include
#include <sys/types.h>
#include <sys/inotify.h>

#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )

int main( int argc, char **argv )
{
int length, i = 0;
int fd;
int wd;
char buffer[BUF_LEN];

fd = inotify_init();

if ( fd < 0 ) {
perror( "inotify_init" );
}

wd = inotify_add_watch( fd, "/home/strike",
IN_MODIFY | IN_CREATE | IN_DELETE );
length = read( fd, buffer, BUF_LEN );

if ( length < 0 ) {
perror( "read" );
}

while ( i < length ) { struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ]; if ( event->len ) {
if ( event->mask & IN_CREATE ) {
if ( event->mask & IN_ISDIR ) {
printf( "The directory %s was created.\n", event->name );
}
else {
printf( "The file %s was created.\n", event->name );
}
}
else if ( event->mask & IN_DELETE ) {
if ( event->mask & IN_ISDIR ) {
printf( "The directory %s was deleted.\n", event->name );
}
else {
printf( "The file %s was deleted.\n", event->name );
}
}
else if ( event->mask & IN_MODIFY ) {
if ( event->mask & IN_ISDIR ) {
printf( "The directory %s was modified.\n", event->name );
}
else {
printf( "The file %s was modified.\n", event->name );
}
}
}
i += EVENT_SIZE + event->len;
}

( void ) inotify_rm_watch( fd, wd );
( void ) close( fd );

exit( 0 );
}
这个应用程序通过 fd = inotify_init(); 创建一个 inotify 实例,并添加一个监视器来监控修改、新文件和
/home/strike 中的损坏文件(由 wd = inotify_add_watch(...) 指定)。read()
方法在一个或多个警告到达之前是被阻塞的。警告的详细内容 — 每个文件、每个事件 —
是以字节流的形式发送的;因此,应用程序中的循环将字节流转换成一系列事件结构。
在文件 /usr/include/sys/inotify.h. 中,您可以找到事件结构的定义,它是一种 C 结构,如清单 2 所示。
清单 2. 事件结构的定义
struct inotify_event
{
int wd; /* The watch descriptor */
uint32_t mask; /* Watch mask */
uint32_t cookie; /* A cookie to tie two events together */
uint32_t len; /* The length of the filename found in the name field */
char name __flexarr; /* The name of the file, padding to the end with NULs */
}
wd 字段是指与事件相关联的监视器。如果每个 inotify 有一个以上的实例,您可以使用这个字段确定如何继续以后的处理过程。mask 字段由几个部分组成,它说明发生的事情。分别测试每个部分。
当把一个文件从一个目录移动到另一个目录时,您可以使用 cookie 将两个事件绑在一起。仅当您监视源和目标目录时,inotify
才生成两个移动事件 — 分别针对源和目标 —,并通过设置 cookie 将它们绑定在一起。要监视一个移动操作,必须指定
IN_MOVED_FROM 或 IN_MOVED_TO,或使用简短的 IN_MOVE,它可以监视两个操作。使用 IN_MOVED_FROM 和
IN_MOVED_TO 来测试事件类型。
最后,name 和 len 包含文件的名称(但不包括路径)和受影响文件的名称的长度。
构建示例应用程序代码
要构建这些代码,请将目录 /home/strike 更改到您的主目录,即将这些代码保存到一个文件中,然后调用 C 编译器 — 在大部分 Linux 系统中为 gcc。然后,运行这个可执行文件,如清单 3 所示。
清单 3. 运行可执行文件
% cc -o watcher watcher.c
% ./watcher
在监视程序运行时,打开第二个终端窗口并使用 touch、cat 和 rm 来更改主目录的内容,如清单 4 所示。完成之后,重新启动您的新应用程序。
清单 4. 使用 touch、cat 和 rm
% cd $HOME
% touch a b c
The file a was created.
The file b was created.
The file c was created.

% ./watcher &
% rm a b c
The file a was deleted.
The file b was deleted.
The file c was deleted.

% ./watcher &
% touch a b c
The file a was created.
The file b was created.
The file c was created.

% ./watcher &
% cat /etc/passwd >> a
The file a was modified.

% ./watcher &
% mkdir d
The directory d was created.
试用其他可用的监视标志。要捕捉权限的更改,请将 IN_ATTRIB 添加到 mask。
使用 inotify 的技巧
您还可以使用 select()、pselect()、poll() 和 epoll()
来避免阻塞。如果您想将监视器的监控作为图形应用程序的主事件处理循环的一部分,或作为监视其他输入连接的守护进程的一部分,这是很有用的。将该
inotify 描述符添加到这组描述符中,进行并发监控。清单 5 展示了 select() 的标准形式。
清单 5. select() 的标准形式
int return_value;
fd_set descriptors;
struct timeval time_to_wait;

FD_ZERO ( &descriptors );
FD_SET( ..., &descriptors );
FD_SET ( fd, &descriptors );

...

time_to_wait.tv_sec = 3;
time.to_waittv_usec = 0;

return_value = select ( fd + 1, &descriptors, NULL, NULL, &time_to_wait);

if ( return_value < 0 ) {
/* Error */
}

else if ( ! return_value ) {
/* Timeout */
}

else if ( FD_ISSET ( fd, &descriptors ) ) {
/* Process the inotify events */
...
}

else if ...
select() 方法在 time_to_wait 期间暂停程序。然而,如果在这个延迟期间这组描述符的任意一个文件描述符发生活动,将立即恢复执行程序。否则,调用就会超时,允许应用程序执行其他进程,比如在图形用户界面(GUI)工具中响应鼠标或键盘事件。
下面是使用 inotify 的其他技巧:
如果监视中的文件或目录被删除,它的监视器也会被自动删除(在删除事件发出之后)。
如果在已卸载的文件系统上监控文件或目录,监视器将在删除所有受影响的监视之前收到一个卸载事件。
将 IN_ONESHOT 标志添加到监视器标记中,设置一个一次性警告。警告在发送之后将被删除。
要修改一个事件,必须提供相同的路径名和不同的标记。新监视器将取代老监视器。
考虑到实用性,不可能耗尽任何一个 inotify 实例的监视器。然而,您可能会耗尽事件队列的空间,这取决于处理事件的频率。队列溢出会引起 IN_Q_OVERFLOW 事件。
close() 方法毁坏 inotify 实例和所有相关联的监视器,并清空队列中的所有等待事件。

根据 inotify 自己开发软件监控文件系统活动的更多相关文章

  1. php使用inotify扩展监控文件或目录的变化

    一.安装inotify扩展 1.下载inotify扩展源码 https://pecl.php.net/package/inotify 对于php7以上版本,请下载 inotify-2.0.0.tgz. ...

  2. (转)Linux下通过rsync与inotify(异步文件系统事件监控机制)实现文件实时同步

    Linux下通过rsync与inotify(异步文件系统事件监控机制)实现文件实时同步原文:http://www.summerspacestation.com/linux%E4%B8%8B%E9%80 ...

  3. php使用inotify扩展监控文件或目录,如果发生改变,就执行指定命令

    通过inotify扩展监控文件或目录的变化,如果发生变化,就执行命令. 可以应用于 swoole 中,如果文件发生变化,就执行 kill -USR1 进程PID 来实现热更新. <?php cl ...

  4. [转帖]Linux下inotify监控文件夹状态,发生变化后触发rsync同步

    Linux下inotify监控文件夹状态,发生变化后触发rsync同步 https://www.cnblogs.com/fjping0606/p/6114123.html 1.安装工具--inotif ...

  5. python软件开发规范&分文件对于后期代码的高效管理

    根据本人的学习,按照理解整理和补充了python模块的相关知识,希望对于一些需要了解的python爱好者有帮助! 一.软件开发规范--分文件 当代码存在一个py文件中时: 1.不便于管理 (修改,增加 ...

  6. Hi3559AV100 NNIE开发(3)RuyiStudio软件 .wk文件生成过程-mobilefacenet.cfg的参数配置

    之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(3)RuyiStudio软件 .wk文件生成过程-mobilefacenet.cfg的参数配置,目前项目需要对 ...

  7. 使用inotify+rsync实现服务器间文件同步

      1. rsync 1.1 什么是rsync   rsync是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件.它使用所谓的“Rsync演算法”来使本地和远程两个主机之间的文件达到 ...

  8. Web开发安全之文件上传安全

    很长一段时间像我这种菜鸡搞一个网站第一时间反应就是找上传,找上传.借此机会把文件上传的安全问题总结一下. 首先看一下DVWA给出的Impossible级别的完整代码: <?php if( iss ...

  9. 使用FileSystemWatcher监控文件夹及文件

    引言 这一周主要精力集中学习一个同事开发的本地文件搜索项目上,其中客户端添加共享文件时主要是使用FileSystemWatcher 监控文件,并在各种事件发生时向服务器发送消息. 解决方法 FileS ...

随机推荐

  1. zoj 3228:Searching the String

    Description Little jay really hates to deal with string. But moondy likes it very much, and she's so ...

  2. POJ 1655 Balancing Act【树的重心】

    Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14251   Accepted: 6027 De ...

  3. BZOJ2338: [HNOI2011]数矩形

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2338 中学数学老师告诉我们,一个矩形的两条对角线相等,所以只要把所有的边拿出来,记录下中点坐标 ...

  4. c#中winform窗口的隐藏与显示

    最近在做一个C# 的winform客户端程序,要实现在打开新的窗口时将原来打开的窗口关闭,但是想在关闭新打开的窗口是将原来的那个窗口再次打开,在网上查找各种资料,找了很多代码,都是通过窗口.Hide( ...

  5. rexray在CentOS上不能创建ceph rbd的docker volume问题定位

    背景 我们通过docker的rexray插件来创建ceph rbd设备的docker volume,但总提示创建失败. # docker volume create --driver=rexray - ...

  6. Django App(一) StartApp

    经过配置Pycharm在上一次的笔记中,已经解决了编写Django web程序调试的问题,这篇将记录Django官网提供的例子程序!          1.查看Pycharm terminal是否可用 ...

  7. 怎么去掉织梦网站首页带的index.html/index.php

    方法1. 1)在空间面板里面找到默认首页设置: 我们是需要去掉index.html,这时我们只需要把index.html这个把它移到最顶级去就行,然后点击确定,在打开网站刷新下,就基本可以解决了! 其 ...

  8. Shell常用命令整理

    http://blog.csdn.net/junmail/article/details/4602745 1.   ls: 类似于dos下的dir命令 ls最常用的参数有三个: -a -l -F. l ...

  9. Redis 数据结构与内存管理策略(下)

    Redis 数据结构与内存管理策略(下) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  10. mybatis_SQL映射(4)鉴别器

    摘录自:http://blog.csdn.net/y172158950/article/details/17505739 鉴别器:有时一个单独的数据库查询也许返回很多不同(但是希望有些关联)数据类型的 ...