频 道 直 达 - 新闻 - 培训 - 软件 - 教程 - 前沿 - 组网 - 系统应用 - 安全 - 编程 - 存储 - 操作系统 - 数据库 - 服务器 - 专题 - 产品 - 案例库 - 读书 - 博客 - BBS
51CTO.COM_中国最大的网络技术网站
找资料:

2.4.5 用户空间inotify机制

作者: 倪继利 著 出处:电子工业出版社博文视点  2007-12-22 16:40    砖    好    评论   进入论坛
阅读提示:《Linux安全体系分析与编程》第二章分析了日志系统的记录机制,阐述了审计系统的原理,并说明了文件系统变化监视机制。本文主要介绍的是用户空间inotify机制。

2.4.5  用户空间inotify机制

1.inotify机制用户空间接口

在用户空间,inotify机制提供了inotify_init、inotify_add_watch和inotify_rm_watch3个系统调用,还提供了文件系统操作。这样,应用程序可以使用inotify机制进行文件系统监视处理。

应用程序调用inotify机制的接口函数的方法列出如下:

int fd = inotify_init ();  //创建inotify机制文件描述符
//添加一个watch,path 是被监视的目标的路径,mask 是事件掩码,wd是watch描述符
int wd = inotify_add_watch (fd, path, mask); 
//buf是一个inotify_event 结构的数组指针,BUF_LEN指定要读取的总长度,buf不小于 BUF_LEN
size_t len = read (fd, buf, BUF_LEN);  //读取多个事件,文件描述符 fd 上使用 select() 或poll()
int ret = inotify_rm_watch (fd, wd); //删除一个 watch
close(fd);

2.应用程序使用inotify机制样例

以下的样例程序监视几个文件或目录的所有文件系统事件,并打印出事件说明。应用程序使用inotify机制的样例程序列出如下:

$cat inotify_usespace.c
#include 
#include 
#include 

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];  //用于存储文件路径名和watch描述符

//inotify机制事件对应说明数组
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; i++) {
wd_array[i].name = monitored_files[i];  //存入文件路径名
//给路径wd_array[i].name添加一个watch,监视所有事件,wd为返回的watch描述符
wd = inotify_add_watch(fd, wd_array[i].name, IN_ALL_EVENTS); 
if (wd < 0) { 
printf("Can't add watch for %s.\n", wd_array[i].name); 
exit(-1); 
} 

wd_array[i].wd = wd; 
} 

while(len = read(fd, buffer, MAX_BUF_SIZE)) { //读取多个事件
offset = buffer; 
printf("Some event happens, len = %d.\n", len);  //打印实际读取的长度
event = (struct inotify_event *)buffer; //得到事件

while (((char *)event - buffer) < len) { 
if (event-> mask & IN_ISDIR) { //目录
memcpy(strbuf, "Direcotory", 11);  
//“Direcotory”字符串长度为11
} 
else { 
memcpy(strbuf, "File", 5); 
//“File”字符串长度为5,包括后面加一个空格
} 
printf("Object type: %s\n", strbuf); 

for (i=0; i< WD_NUM; i++) {
if (event-> wd != 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; i< EVENT_NUM ; i++) {
if (event_array[i][0] == '\0') 
//空字符串行,说明事件对应说明不存在
continue; 
//将事件掩码的位匹配数组成员,如:00000020,即100000,对应i为5
if (event-> mask & (1 << i)) {
printf("Event: %s\n", event_array[i]);  
//打印事件对应的说明字符串
} 
} 

tmp_len = sizeof(struct inotify_event) + event-> len; 
event = (struct inotify_event *)(offset + tmp_len);  //得到下一个事件
offset += tmp_len; 
} 
} 
}

编译应用程序的方法如下:

$gcc -o inotify_userspace -I . inotify_usersapce.c

在一个虚拟终端上运行应用程序inotify_userspace,在另一个虚拟终端执行命令 cat ./tmp_file。程序的输出列出如下:

Some event happens, len = 48.
Object type: File 
Object name: ./tmp_file 
Event mask: 00000020 
Event: File was opened 
Object type: File 
Object name: ./tmp_file 
Event mask: 00000001 
Event: File was accessed 
Object type: File 
Object name: ./tmp_file 
Event mask: 00000010 
Event: Unwrittable file closed 

3.用户空间inotify机制的数据结构

用户空间inotify机制是inotify机制的扩展,因此,它的数据结构从inotify机制的数据结构继承。

结构inotify_user_watch是从基类结构inotify_watch派生的类结构,它在继承基类的基础上加入了成员inotify_device。其列出如下(linux26/fs/inotify_user.c中):

struct inotify_user_watch {
struct inotify_device*dev;/*可以看作设备,含有链表,用于将事件传递回用户空间*/
struct inotify_watchwdata;/*inotify机制的watch(监视实例)*/
};

结构inotify_device是从基类结构inotify_handle派生的类结构,代表了监视处理实例。它含有事件队列events,用于存储发生在节点的inotify机制事件,应用程序可以通过伪文件系统inotifyfs的读操作函数从这个事件队列中读取事件。为了协调事件的读取和写入,结构inotify_device还添加了等待队列成员wq,用于同步事件的读取和写入的操作。

结构inotify_device列出如下:

struct inotify_device {
wait_queue_head_t wq;/* i/o 的等待队列*/
struct mutexev_mutex;/* 用于保护事件队列*/
struct mutexup_mutex;/*用于同步watch更新*/
struct list_head events;/*排队事件的链表*/
atomic_tcount;/*引用计数 */
struct user_struct*user;/*打开这个设备的用户*/
struct inotify_handle *ih;/*监视处理实例 */
unsigned intqueue_size;/*队列的大小 (bytes) */
unsigned intevent_count;/* 挂起事件的数量*/
unsigned intmax_events;/* 最大事件数*/
};

4.用户空间inotify机制的系统调用

内核提供了系统调用sys_inotify_init、sys_inotify_add_watch和sys_inotify_rm_watch支持用户空间的inotify机制。

1)系统调用sys_inotify_init

系统调用sys_inotify_init调用了inotify_init初始监视处理实例,监视处理实例含有操作函数集inotify_user_ops,其列出如下(在linux26/fs/inodify_user.c中):

static const struct inotify_operations inotify_user_ops = {
.handle_event= inotify_dev_queue_event,
.destroy_watch= free_inotify_user_watch,
};

当文件系统被改变时,inotify机制的hook函数会从节点的watch中得到监视处理实例,再找到操作监视处理实例的函数集inotify_user_ops,并调用其中的函数inotify_dev_queue_event处理inotify机制的事件。

函数inotify_dev_queue_event将事件写入到结构inotify_device的事件队列中,并唤醒伪文件系统inotifyfs的读操作进程。由读操作将事件传递给用户空间的应用程序。

2)系统调用sys_inotify_add_watch

系统调用sys_inotify_add_watch查找文件路径所对应的节点,在节点的watch链表中查找与监视处理实例匹配的watch,如果没有找到就创建watch,并将watch加到节点的watch链表中。

3)系统调用sys_inotify_rm_watch

系统调用sys_inotify_rm_watch从监视处理实例的watch链表中删除watch。

5.用户空间inotify机制的伪文件系统inotifyfs

模块初始化函数inotify_user_setup注册了伪文件系统inotifyfs,其列出如下:

static int __init inotify_user_setup(void)
{
int ret;

ret = register_filesystem(&inotify_fs_type);//注册文件系统
if (unlikely(ret))
panic("inotify: register_filesystem returned %d!\n", ret);

inotify_mnt = kern_mount(&inotify_fs_type);  //挂接文件系统
if (IS_ERR(inotify_mnt))
panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));

inotify_max_queued_events = 16384;
inotify_max_user_instances = 128;
inotify_max_user_watches = 8192;

watch_cachep = kmem_cache_create("inotify_watch_cache",
 sizeof(struct inotify_user_watch),
 0, SLAB_PANIC, NULL, NULL);
event_cachep = kmem_cache_create("inotify_event_cache",
 sizeof(struct inotify_kernel_event),
 0, SLAB_PANIC, NULL, NULL);

return 0;
}

文件系统类型结构实例列出如下(在linux26/fs/inotify_user.c中):

static struct file_system_type inotify_fs_type = {
.name           = "inotifyfs",//文件系统类型名
.get_sb         = inotify_get_sb,      //得到文件系统超级块
.kill_sb        = kill_anon_super, //删除超级块
};

函数inotify_get_sb创建并初始化伪文件系统超级块,其列出如下:

static int  inotify_get_sb(struct file_system_type *fs_type, int flags,
       const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA, mnt);  //文件系统模数为0xBAD1DEA
}

当应用程序调用函数sys_inotify_init初始化监视处理实例并创建inotify机制文件描述符时,监视处理实例绑定了操作函数集inotify_user_ops,文件描述符绑定了文件操作函数集inotify_fops。

文件操作函数集inotify_fops定义了伪文件系统inotifyfs的文件操作函数,其列出如下(在linux26/fs/inotify_user.c中):

.poll           = inotify_poll,   //从文件等待接收数据
.read           = inotify_read,   //从文件读取数据
.release        = inotify_release, //释放描述符的各种结构实例所占空间
.unlocked_ioctl = inotify_ioctl,
.compat_ioctl= inotify_ioctl,
};

函数inotify_read从事件队列中读出事件到用户空间的缓冲区buf中。当事件队列为空时,当前进程便进入等待状态。当事件队列不为空时,当前进程被唤醒,它从事件队列中取出事件,将事件和事件名拷贝到用户空间。

函数inotify_read列出如下:

static ssize_t inotify_read(struct file *file, char __user *buf,
    size_t count, loff_t *pos)
{
size_t event_size = sizeof (struct inotify_event);
struct inotify_device *dev;
char __user *start;
int ret;
DEFINE_WAIT(wait); //声明等待队列wait

start = buf;
dev = file->private_data;

while (1) {
int events;
  //将dev->wq加入等待队列wait,设置当前进程状态为TASK_INTERRUPTIBLE状态
prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);

mutex_lock(&dev->ev_mutex);
events = !list_empty(&dev->events); //如果事件队列不为空
mutex_unlock(&dev->ev_mutex);
if (events) {//如果有事件存在,中断循环
ret = 0;
break;
}
   //运行到这里,说明事件队列中不存在事件
if (file->f_flags & O_NONBLOCK) { //如果是非阻塞标识,则返回
ret = -EAGAIN;
break;
}

if (signal_pending(current)) { //如果当前进程是挂起状态,则中断循环
ret = -EINTR;
break;
}

schedule();//调度
}

finish_wait(&dev->wq, &wait); //从等待中唤醒
if (ret)
return ret;

mutex_lock(&dev->ev_mutex);
while (1) {
struct inotify_kernel_event *kevent;

ret = buf - start;
if (list_empty(&dev->events)) //如果事件队列为空,跳出循环
break;

kevent = inotify_dev_get_event(dev); //得到事件
if (event_size + kevent->event.len > count)
break;

if (copy_to_user(buf, &kevent->event, event_size)) { 
//将事件拷贝到用户空间的缓冲区buf中
ret = -EFAULT;
break;
}
buf += event_size;
count -= event_size;

if (kevent->name) {//将事件名拷贝到用户空间的缓冲区buf中
if (copy_to_user(buf, kevent->name, kevent->event.len)){ 
ret = -EFAULT;
break;
}
buf += kevent->event.len;
count -= kevent->event.len;
}

remove_kevent(dev, kevent);
}
mutex_unlock(&dev->ev_mutex);

return ret;
}

【责任编辑:董书 TEL:(010)68476606】

回书目   上一节   下一节
专题
开源虚拟化技术Xen
Linux——从菜鸟到高手
程序员如何成长?
Linux/Solaris服务器的安全配置
虚拟化技术还有点“虚”
我也说两句

匿名发表

(如果看不清请点击图片进行更换)


中 国 最 大 的 网 络 技 术 网 站 ·
技 术 成 就 梦 想
订阅技术快讯
电子杂志下载
名称:SQL Server数据库管理精品黄皮书
简介:书中文章经过精挑细选,便于用户能根据自己的实际工作和学习,快速在本书寻找到相关资料。内容涵盖了SQL Server的安装与升级、语句查询、数据备份和恢复、自动化任务、数据同步、数据字典、安全和预防、性能和优化、集群等各方面应用信息,以及DBA管理人员在数据库管理工作中
名称:2007路由技术大全
简介:《2007路由技术大全》由51CTO.com网站特别策划制作,该书包括路由器技术、路由器产品、路由器配置、安全设置、路由器故障处理、路由器密码恢复,以及广大网友在实践使用中的心得经验和技巧文章,内容注重实用性,适用于初学者入门,也适合多年从业者提高,是一本实践和理论完
名称:网络安全精品应用黄皮书
简介:《2007精品网络安全黄皮书》包括了9个大类24个小类, 800余篇文章,内容包含了熊猫烧香病毒、DDOS攻击、ARP病等热点问题的介绍及解决方案。从病毒查杀、防范、系统、数据等各方面的安全设置到黑客技术的了解、防范,涉及到了安全应用的全部领域, 由浅至深内容全面。
浏览器的战国时代
浏览器的战国时代
ARP攻击防范与解决方案
ARP攻击防范与解决方案
NAC安全访问控制
NAC安全访问控制
· NAC安全访问控制
· 网络布线测试仪器
· Windows Server 2008专..
· Windows远程桌面应用
· 网络故障排除宝典
· 运营商封堵ADSL共享 中..
· 解析35岁技术人的价值..
· 世纪枭雄比尔盖茨的王..
· 主流品牌防火墙配置
· ASP.NET开发教程
· 超级计算机TOP500专题
· Vista SP1对决XP SP3
· SQL Server 2008/2005..
· 程序员如何成长?
· C#技术开发指南
· 虚拟化技术还有点“虚”
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SOA 面向服务架构
SOA 面向服务架构
· SOA 面向服务架构
· SQL Server 2008/2005..
· Apache技术专题
· 三层交换技术专题
· SQL Server入门到精通
· Windows远程桌面应用
· C#技术开发指南
· Apache技术专题
· Windows集群服务应用
· C#技术开发指南
· 国际文档格式标准开战
· 路由器设置与口令恢复
· Linux 集群技术专题
· PHP开发应用手册
· SOA 面向服务架构
· 企业数据恢复指南
ARP攻击防范与解决方案
ARP攻击防范与解决方案
SQL Server 2008/2005全解
SQL Server 2008/2005全解
SQL Server入门到精通
SQL Server入门到精通
· SQL Server入门到精通
· SQL Server 2008/2005..
· SOA 面向服务架构
· Apache技术专题
· C#技术开发指南
· 三层交换技术专题
· Apache技术专题
· C#技术开发指南
· Windows远程桌面应用
· 企业数据恢复指南
· Windows集群服务应用
· 路由器设置与口令恢复
· Linux 集群技术专题
· SOA 面向服务架构
· 了解统一威胁管理(UTM)..
· 反垃圾邮件技术应用