inotify使用

Inotify 是一个 Linux® 特性,它监控文件系统操作,比如读取、写入和创建。Inotify 反应灵敏,用法非常简单,并且比 cron 任务的繁忙轮询高效得多。学习如何将 inotify 集成到您的应用程序中,并发现一组可用来进一步自动化系统管理的命令行工具。

系统管理就 像日常生活一样。就像刷牙和吃蔬菜一样,日常的维护能保持机器的良好状态。您必须定期清空废物,比如临时文件或无用的日志文件,以及花时间填写表单、回复 电话、下载更新和监控进程等。幸好自动化 shell 脚本、使用 Nagios 等工具进行监控、通过常见的 cron 进行任务调度可以减轻这个负担。

但奇怪的是,这些工具没有一个具有响应性。当然,您可以安排一个频繁运行的 cron 任务来监控条件,但这样繁忙的轮询 — 消耗大量资源并且具有不确定性 — 并不是很理想。例如,如果您必须监控输入数据的几个 Transfer Protocol(FTP)收存箱,您可能要通过find 命令扫描每个目标目录,列举新的内容。然而,尽管这个操作看起来并没有什么害处,但每个调用都产生一个新的 shell 和 find 命令,这需要许多系统调用来打开目录,然后扫描目录,等等。这会造成过于频繁的或大量的轮询任务(更糟糕的是,繁忙的轮询并不总是很好。想象一下一个文件系统浏览器,比如 Mac OS X 的 Finder,轮询更新时需要的大量资源及其复杂性)。

那么,管理员应该怎么办呢?令人高兴的是,您可以再次求助于可以信赖的计算机。

了解 inotify

Inotify 是一个 Linux 内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。

使用 inotify 很简单:创建一个文件描述符,附加一个或多个监视器(一个监视器 是一个路径和一组事件),然后使用 read() 方法从描述符获取事件信息。read() 并不会用光整个周期,它在事件发生之前是被阻塞的。

更好的是,因为 inotify 通过传统的文件描述符工作,您可以利用传统的 select() 系统调用来被动地监控监视器和许多其他输入源。两种方法 — 阻塞文件描述符和使用select()— 都避免了繁忙轮询。

现在,让我们深入了解 inotify,写一些 C 代码,然后看看一组命令行工具,您可以构建并使用它们将命令和脚本附加到文件系统事件。Inotify 不会在中途失去控制,但它可以运行catwget,并且在必要时严格执行。

要使用 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 <stdio.h>
#include <stdlib.h>
#include <errno.h>
#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_FROMIN_MOVED_TO,或使用简短的IN_MOVE,它可以监视两个操作。使用 IN_MOVED_FROMIN_MOVED_TO 来测试事件类型。

最后,namelen 包含文件的名称(但不包括路径)和受影响文件的名称的长度。

构建示例应用程序代码

要构建这些代码,请将目录 /home/strike 更改到您的主目录,即将这些代码保存到一个文件中,然后调用 C 编译器 — 在大部分 Linux 系统中为 gcc。然后,运行这个可执行文件,如清单 3 所示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值