inotify监控Linux文件系统的必备利器(1)
本文主要向大家介绍了如何使用inotify控制Linux文件系统中的事件。在具体的学习之前我们先来看看什么是inotify以及他的历史简介。我们还会向大家介绍inotify的具体应用情况以及使中遇到问题的解决方案。
inotify 介绍
文件系统事件监控对于从文件管理器到安全工具等多种程序来说都是必要的。自 2.6.13 版本内核开始,Linux 就提供 inotify 功能,这允许监控程序打开一个独立文件描述符,并针对一系列特定的事件来监控一个或多个文件或者目录,例如打开、关闭、移动/重命名、 删除、创建或者改变属性。在以后的版本中还提供更多增强功能,因此在依赖这些特性之前请先检查系统的内核版本。
在本文中,您将学会如何在简单的监控程序当中使用 inotify 函数。下载样例代码 并在系统当中进行编译,来为后面的研究做准备。
历史简介
在 inotify 之前有 dnotify 。不幸的是,dnotify 有局限性,无法满足用户的需求, inotify 的优势如下:
Inotify 采用简单的文件描述符,而 dnotify 需要为每个受监控的目录打开一个文件描述符。 这使得同时监控多个目录成本很高,而且还会遇到每进程文件描述符限制问题。
inotify 所采用的文件描述符通过系统调用获得,并且没有相关的设备或文件。对于 dnotify ,文件描述符与目录关系固定,避免了相关设备未被加载的问题,这是可移动媒体的典型问题。对于 inotify ,如果受监控文件或目录所在的文件系统未被加载, 则会产生一个事件,然后该监控会被自动移除。
Inotify 能够监控文件或目录。Dnotify 监控目录, 因此程序员必须保持 stat 结构或者等效的数据结构,来反映目录当中被监控的文件, 然后在一个事件发生时,将其与当前状态进行对比,来知晓目录当中的条目发生了什么情况。
如前面所述,inotify 采用文件描述符,允许程序员采用标准 select 或者 poll 函数来对事件进行监控。 这允许高效的多重 I/O 或者与 Glib 的 mainloop 集成。相比之下,dnotify 采用信号, 这使得程序员感到难度更大或者不够流畅。 在 2.6.25 版本内核中,inotify 也增加了 Signal-drive I.O 通告功能。
用于 inotify 的 API
Inotify 提供简单的 API ,采用最小的文件描述符并允许细粒度监控。与 inotify 的通信通过系统调用实现。
inotify_init
是用于创建 inotify 实例的系统调用,并返回一个指向该实例的文件描述符。
inotify_init1
与 inotify_init 相似,并带有附加标志。如果这些标志没有指定,将采用与 inotify_init 相同的值。
inotify_add_watch
增加对文件或目录的监控并指定需要监控哪些事件。 标志用于控制是否将事件加入到已有的监控当中,是否只有路径代表一个目录才进行监控, 是否需要追踪符号链接,是否进行一次性监控,当首次出现事件后就停止监控。
inotify_rm_watch
从监控列表中移除监控项目。
read
读取包含一个或多个事件信息的缓存。
close
关闭文件描述符,并移除所有在该描述符上的监控。 当关于某一实例的文件描述符都关闭以后, 资源和下层的对象都将释放,来供内核再次使用。
因此,典型的监控程序要进行如下操作:
利用 inotify_init 打开文件描述符;
增加一个或多个监控;
等待事件;
处理事件,然后返回并等待更多事件;
当没有活跃的监控时或者基于某些信号的指示,关闭文件描述符,清空,然后退出。
在下一部分中,您将看到可以监控的事件,以及它们如何在简单程序当中运行。最后您将看到如何进行事件监控。
通告
当应用程序读取一个通告时,事件的顺序也被读取到缓存中。事件在一个变长结构体中被返回,见清单 1 。如果数据占满了缓存,可能需要对最后一个条目进行局部事件信息或者局部名处理。
清单 1. 用于 inotify 的事件结构体
struct inotify_event
{
int wd; /* Watch descriptor. */
uint32_t mask; /* Watch mask. */
uint32_t cookie; /* Cookie to synchronize two events. */
uint32_t len; /* Length (including NULs) of name. */
char name __flexarr; /* Name. */
};
注意,只有当监控对象是一个目录并且事件与目录内部相关项目有关,而与目录本身无关时,才提供 name 字段。 如果 IN_MOVED_FROM 事件与相应的 IN_MOVED_TO 事件都与被监控的项目有关,cookie 就可用于将两者关联起来。 事件类型在掩码字段中返回,并伴随着能够被内核设置的标志。 例如,如果事件与目录有关,则标志 IN_ISDIR 将由内核设置。
能够监控的事件
有几种事件能够被监控。有一些,比如 IN_DELETE_SELF 只应用到正在被监控的项目,然而另一些比如 IN_ATTRIB 或者 IN_OPEN 可以应用到监控过的项目, 或者如果该项目是目录,则可以应用到其所包含的目录或文件。
IN_ACCESS
被监控项目或者被监控目录当中的条目被访问过。例如,一个打开的文件被读取。
IN_MODIFY
被监控项目或者被监控目录当中的条目被修改过。例如,一个打开的文件被修改。
IN_ATTRIB
被监控项目或者被监控目录当中条目的元数据被修改过。例如,时间戳或者许可被修改。
IN_CLOSE_WRITE
一个打开的,等待写入的文件或目录被关闭。
IN_CLOSE_NOWRITE
一个以只读方式打开的文件或目录被关闭。
IN_CLOSE
是可以很便捷地对前面提到的两个关闭事件IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)进行逻辑或操作的掩码。
IN_OPEN
文件或目录被打开。
IN_MOVED_FROM
被监控项目或者被监控目录当中的条目被移出监控区域。该事件还包含一个 cookie 来实现 IN_MOVED_FROM 与 IN_MOVED_TO 的关联。
IN_MOVED_TO
文件或目录被移入监控区域。该事件包含一个针对 IN_MOVED_FROM 的 cookie 。如果文件或目录只是被重命名,将能看到这两个事件,如果它只是被移入或移出非监控区域,将只能看到一个事件。 如果移动或重命名一个被监控项目,监控将继续进行。参见下面的 IN_MOVE-SELF 。
IN_MOVE
是可以很便捷地对前面提到的两个移动事件IN_MOVED_FROM | IN_MOVED_TO)进行逻辑或操作的掩码。
IN_CREATE
在被监控目录当中创建了子目录或文件。
IN_DELETE
被监控目录当中有子目录或文件被删除。
IN_DELETE_SELF
被监控项目本身被删除。监控被终止并收到一个 IN_IGNORED 事件。
IN_MOVE_SELF
监控项目本身被移动。
除了事件标志以外,还可以在 inotify 头文件/usr/include/sys/inotify.h)中找到其他几个标志。 例如,如果只想监控第一个事件,可以在增加监控时设置 IN_ONESHOT 标志。