linux中inotify机制如何应用

在前面的博客《用可变参数扩展printf》中讨论到如何在应用中控制log的输出。
我们假设的情景是,一个长期运行的Linux程序,想在不退出运行的情况下,通过某种机制,可以让程序知道要不要打印出log 。
我们当时的实现是:

  1. 创建一个文件,写进标志位
  2. 然后每次要打印log之前先读取这个文件,按照标志位是什么来决定要不要打印log

这样子我们在linux系统上,如果不想打印出这些log,可以向这个文件写其他标志位。
但是,这种做法效率太低了。那如何解决上面的问题呢?
有两种做法:

  1. 一种是linux inotify机制
  2. 另外一种是linux 信号机制。注册一个信号,文件更改了,向这个程序发信号,printf_my_log再重新读取。

在本节中,讲的是linux inotify机制。

参考资料http://www.man7.org/linux/man-pages/man7/inotify.7.html
Inotify机制作用:

  1. Inotify 是一个 Linux特性,它监控文件系统操作,比如读取、写入和创建。
  2. 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  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  


#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] 22392
ubuntu:~/test/69test$ Watching printlog using wd 1

修改文件printlog

ubuntu:~/test/69test$ echo 1 > printlog 

实验结果

Read 32bytes from inotify fd
IN_MODIFY
logFlag = 1
IN_MODIFY
logFlag = 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)