linux中inotify机制如何应用
来源:互联网 发布:淘宝嘉年华妈妈装 编辑:程序博客网 时间:2024/06/14 02:26
在前面的博客《用可变参数扩展printf》中讨论到如何在应用中控制log的输出。
我们假设的情景是,一个长期运行的Linux程序,想在不退出运行的情况下,通过某种机制,可以让程序知道要不要打印出log 。
我们当时的实现是:
- 创建一个文件,写进标志位
- 然后每次要打印log之前先读取这个文件,按照标志位是什么来决定要不要打印log
这样子我们在linux系统上,如果不想打印出这些log,可以向这个文件写其他标志位。
但是,这种做法效率太低了。那如何解决上面的问题呢?
有两种做法:
- 一种是linux inotify机制
- 另外一种是linux 信号机制。注册一个信号,文件更改了,向这个程序发信号,printf_my_log再重新读取。
在本节中,讲的是linux inotify机制。
参考资料http://www.man7.org/linux/man-pages/man7/inotify.7.html
Inotify机制作用:
- Inotify 是一个 Linux特性,它监控文件系统操作,比如读取、写入和创建。
- Inotify 反应灵敏,用法非常简单,并且比 cron 任务的繁忙轮询高效得多。
inotify既可以监控文件,也可以监控目录。这里我们用来监视文件printlog。
下面讲一下如何在程序中使用Inotify机制。
第一步是创建 inotify 实例。
int fd = inotify_init ();
也可以用inotifyFd = inotify_init1(IN_NONBLOCK) 实现非阻塞
每一个 inotify 实例对应一个独立的排序的队列。
第二步是添加需要被监视的文件。
下面函数用于添加一个 watch:
参数含义:
fd : inotify_init() 返回的文件描述符
path :被监视的目标的路径名(即文件名或目录名)
mask: 是事件掩码
int wd = inotify_add_watch (fd, path, mask);
第三步是读取inotify_event 数据到buf。
numRead = read(inotifyFd,buf,BUF_LEN); //读取不到会阻塞
第四步是解析读取的数据,一般是根据inotify_event结构中的mask成员来判断的。
for(p=buf;p < buf+numRead;p+=sizeof(struct inotify_event) + event->len) { event = (struct inotify_event *)p; if(event->mask & IN_ACCESS) printf("IN_ACCESS\n"); if(event->mask & IN_DELETE_SELF) printf("DELETE_SELF\n"); if(event->mask & IN_MODIFY) printf("IN_MODIFY\n"); if(event->mask & IN_OPEN) printf("IN_OPEN\n");}
经过上面四个步骤,就可以简单使用inotify机制了。
下面再看看Inotify一些重要知识点。
在头文件 linux/inotify.h 中定义了每一位代表的事件。
Inotify 可以监视的文件系统事件包括:
IN_ACCESS:文件被访问IN_MODIFY:文件被修改IN_ATTRIB:文件属性被修改IN_CLOSE_WRITE:可写文件被关闭IN_CLOSE_NOWRITE:不可写文件被关闭IN_OPEN:文件被打开IN_MOVED_FROM:文件被移走IN_MOVED_TO:文件被移来IN_CREATE:创建新文件IN_DELETE:文件被删除IN_DELETE_SELF:自删除,即一个可执行文件在执行时删除自己IN_MOVE_SELF:自移动,即一个可执行文件在执行时移动自己IN_UNMOUNT:宿主文件系统被卸载IN_CLOSE:文件被关闭IN_MOVE:文件被移动
文件事件用一个 inotify_event 结构表示,应用程序用read从一个inotify文件描述符来读取这个结构体
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 是事件掩码
cookie是一个独特的整数,连接相关的事件
len 是name字符串的长度,每次解析的数据长度为sizeof(struct inotify_event) + inotify_event->len
name 是被监视目标的路径名
好,到这里,inotify的基础知识已经介绍完了,现在可以用它来实现应用程序log的异步控制了。下面实现一个简单的应用程序来打印log。
#include<stdio.h> #include<assert.h> #include<unistd.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<sys/types.h> #include<sys/inotify.h> #include<limits.h> #include<fcntl.h> #define BUF_LEN 1024 void displayInotifyEvent(struct inotify_event *i) { printf(" wd = %2d; ",i->wd); printf("mask = %d",i->mask); if(i->mask & IN_ACCESS) printf("IN_ACCESS\n"); if(i->mask & IN_DELETE_SELF) printf("IN_DELETE_SELF\n"); if(i->mask & IN_MODIFY) printf("IN_MODIFY\n"); if(i->mask & IN_OPEN) printf("IN_OPEN\n"); } int logFlag = 0;//全局变量,是否打印log的标志int main(int argc,char **argv) { int fd;//用于保存文件"printlog"的描述符 char filename[] = "printlog"; char readbuf[8] = {0}; int read_count = 0; int inotifyFd,wd,j; char buf[BUF_LEN]; ssize_t numRead; char *p; struct inotify_event *event; int flags; if(argc < 2 ) { printf("error\n"); } inotifyFd = inotify_init(); if(inotifyFd == -1) { printf("初始化失败"); } /* //Create the file descriptor for accessing the inotify API inotifyFd = inotify_init1(IN_NONBLOCK);//设置非阻塞 if (inotifyFd == -1) { perror("inotify_init1"); //exit(EXIT_FAILURE); }*/ //wd = inotify_add_watch(inotifyFd,argv[1],IN_ALL_EVENTS); wd = inotify_add_watch(inotifyFd,argv[1],IN_MODIFY); if(wd == -1) { printf("error\n"); } printf("Watching %s using wd %d\n",argv[1],wd); while(1) { //Read events. numRead = read(inotifyFd,buf,BUF_LEN); //读取不到会阻塞 if (numRead == -1 && errno != EAGAIN) { perror("read"); //exit(EXIT_FAILURE); } /* If the nonblocking read() found no events to read, then it returns -1 with errno set to EAGAIN. In that case, we exit the loop. */ //if (numRead <= 0) // break; printf("Read %ldbytes from inotify fd\n",(long)numRead); for(p=buf;p < buf+numRead;) { event = (struct inotify_event *)p; if(event->mask & IN_ACCESS) printf("IN_ACCESS\n"); if(event->mask & IN_DELETE_SELF) printf("IN_DELETE_SELF\n"); if(event->mask & IN_MODIFY) { printf("IN_MODIFY\n"); //打开文件,把监视的文件内容读到全局变量logFlag中 //logFlag正是程序是否要打印log的标志 if((fd = open(filename,O_RDWR))<0) { perror("open"); } //读取文件一个字符到readbuf数组中 read_count = read(fd,readbuf,1); //把字符串转化为数字 logFlag = atoi(readbuf); printf("logFlag = %d\n",logFlag); close(fd); } if(event->mask & IN_OPEN) printf("IN_OPEN\n"); //displayInotifyEvent(event); p+=sizeof(struct inotify_event) + event->len; //一个事件的长度,p指向下一个事件 } } return 0; }
gcc编译程序
gcc creatfile.c -o creatfile
执行./creatfile printlog &,后台运行程序
ubuntu:~/test/69test$ ./creatfile printlog &[1] 22392ubuntu:~/test/69test$ Watching printlog using wd 1
修改文件printlog
ubuntu:~/test/69test$ echo 1 > printlog
实验结果
Read 32bytes from inotify fdIN_MODIFYlogFlag = 1IN_MODIFYlogFlag = 1
从上面的程序和结果中可以看到,程序定义了一个logFlag全局变量,然后调用inotify_init函数创建inotify实例,接着调用inotify_add_watch函数监听文件printlog是否被修改。当监听到文件printlog被修改时,打开文件printlog,把文件的内容转化为整数给logFlag全局变量。这是简单的使用inotify机制的一个例子。
回到最初的那个问题:“一个长期运行的Linux程序,想在不退出运行的情况下,通过某种机制,可以让程序知道要不要打印出log”。
这里我们可以创建一个线程,然后让这个线程监视printlog这个文件,像上面的那个例子一样,写文件内容到全局变量logFlag,然后我们每次打印log之前判断一下这个全局变量logFlag是否等于我们约定的那个值,如果是就打印log,如果不是就return。多线程读写logFlag全局变量要用linux锁机制。这样基本能实现我们打印log的需求。
- linux中inotify机制如何应用
- Linux文件系统Inotify机制
- Linux文件系统Inotify机制
- linux inotify通知机制
- linux 中signal机制如何应用(一)
- linux 中signal机制如何应用(二)
- Linux下inotify机制简介
- Linux下inotify机制简介
- Linux下inotify机制简介
- Linux开发--inotify事件机制
- Linux inotify&pthread编程机制
- HDFS inotify:Linux inotify机制在HDFS中的实现
- linux文件系统变化通知机制—inotify
- linux --- inotify 文件系统变化通知机制
- linux --- inotify 文件系统变化通知机制
- Linux inotify监听文件变化机制
- linux文件系统变化通知机制(inotify)
- linux --- inotify 文件系统变化通知机制
- lengthb 函数和 length 函数
- 自顶向下深入分析Netty(七)--ChannelHandlerContext源码实现
- 对于JAVA多态的理解
- 将相对路径转绝对路径
- Android Binder设计与实现 – 设计篇:
- linux中inotify机制如何应用
- LeetCode | 8. String to Integer (atoi)
- 写一篇软文——现在程序猿的现状
- 多路复用I/O模型之select
- RabbitMQ简介和使用
- java内部类学习总结
- 自顶向下深入分析Netty(八)--ChannelHandler
- 给 Scrapy 爬虫项目设置防反爬
- windows 7 下VS2013编译Ceres,含suitesparse的部分