inotif机制,监控文件系统变化

INOTIFY 机制

inotif可以对文件系统进行监控,监控文件系统中发生的事情。为了截取文件系统的变化,inotif机制在文件系统的各个操作中加入了hook函数,当文件系统调用了这些操作函数并改变了文件系统中的文件或目录的时候,就会调用hook函数发出对应的时事件。并将这个事件放到内核的一个队列中,应用层可以取走这些事件,同时如果事件过多而没用及时取走事件的话就有可能丢失事件。

​ 说人话就是这东西可以监控文件或目录被读了,被写了,创建了,删除了,被移动了,被访问了等等。这个在linux内核 二点几之后才支持的。 具体哪个版本等我想起来了我补充上来~。

​ 具体那些事情被监控可以查看头文件 #include <sys/inotify.h>

#ifndef	_SYS_INOTIFY_H
#define	_SYS_INOTIFY_H	1

#include <stdint.h>

/* Get the platform-dependent flags.  */
#include <bits/inotify.h>


/* Structure describing an inotify event.  */
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.  */
};


/* Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.  */
#define IN_ACCESS	 0x00000001	/* File was accessed. 文件被访问 */
#define IN_MODIFY	 0x00000002	/* File was modified. 文件被修改 */
#define IN_ATTRIB	 0x00000004	/* Metadata changed.  元数据修改*/
#define IN_CLOSE_WRITE	 0x00000008	/* Writtable file was closed. 可写文件被关闭  */
#define IN_CLOSE_NOWRITE 0x00000010	/* Unwrittable file closed.  */
#define IN_CLOSE	 (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. 文件被关闭 */
#define IN_OPEN		 0x00000020	/* File was opened. 文件被打开 */
#define IN_MOVED_FROM	 0x00000040	/* File was moved from X. 文件被移动过来 */
#define IN_MOVED_TO      0x00000080	/* File was moved to Y. 文件被移动到 */
#define IN_MOVE		 (IN_MOVED_FROM | IN_MOVED_TO) /* Moves.  */
#define IN_CREATE	 0x00000100	/* Subfile was created. 子文件被创建 */
#define IN_DELETE	 0x00000200	/* Subfile was deleted.  */
#define IN_DELETE_SELF	 0x00000400	/* Self was deleted. 文件被删除 */
#define IN_MOVE_SELF	 0x00000800	/* Self was moved. 文件被移动 */

/* Events sent by the kernel.  */
#define IN_UNMOUNT	 0x00002000	/* Backing fs was unmounted. 目录被卸载 */
#define IN_Q_OVERFLOW	 0x00004000	/* Event queued overflowed. 内核的队列满了 */
#define IN_IGNORED	 0x00008000	/* File was ignored. 不知道是啥, 知道了给我说一下 */

/* Helper events.  */
#define IN_CLOSE	 (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)	/* Close.  */
#define IN_MOVE		 (IN_MOVED_FROM | IN_MOVED_TO)		/* Moves.  */

/* Special flags. */
#define IN_ONLYDIR	 0x01000000	/* Only watch the path if it is a
					   directory.  */
#define IN_DONT_FOLLOW	 0x02000000	/* Do not follow a sym link.  */
#define IN_EXCL_UNLINK	 0x04000000	/* Exclude events on unlinked
					   objects.  */
#define IN_MASK_CREATE	 0x10000000	/* Only create watches.  */
#define IN_MASK_ADD	 0x20000000	/* Add to the mask of an already
					   existing watch.  */
#define IN_ISDIR	 0x40000000	/* Event occurred against dir.  */
#define IN_ONESHOT	 0x80000000	/* Only send event once.  */

/* All events which a program can wait on.  */
#define IN_ALL_EVENTS	 (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)


__BEGIN_DECLS

/* Create and initialize inotify instance.  */
extern int inotify_init (void) __THROW;

/* Create and initialize inotify instance.  */
extern int inotify_init1 (int __flags) __THROW;

/* Add watch of object NAME to inotify instance FD.  Notify about
   events specified by MASK.  */
extern int inotify_add_watch (int __fd, const char *__name, uint32_t __mask)
  __THROW;

/* Remove the watch specified by WD from the inotify instance FD.  */
extern int inotify_rm_watch (int __fd, int __wd) __THROW;

__END_DECLS

#endif /* sys/inotify.h */

我直接把这个头文件拷贝过来了,对上面的事件进行了部分备注。 可以看到整个头文件还是十分简洁的。上面包含了所有可以监控的事件。

流程如下:

  1. int fd = inotify_init(); //创建一个inotify文件描述符
  2. int wd = inotify_add_watch(fd, path, mask); //添加一个watch,path是被监控的文件路径,mask是事件掩码,wd是watch描述符
  3. size_t len = read (fd, buf, BUF_LEN); //读取多个事件, 这个fd也可以使用select进行监控
  4. int ret = inotify_rm_watch(fd, wd); //删除一个监控
  5. close(fd);

上述就是程序的一个流程了。详细内容见下面的代码

#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

char *monitored_file[] = {
    "/home/lq/桌面/111.txt",
    "/home/lq/1.txt",
    "/home/lq/桌面/"
};

struct wd_name {
    int wd;
    char *name;
};

#define WD_NUM 3 //定义要监控的数量
struct wd_name wd_array[WD_NUM];

const 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 EVEBT_NUM 16
#define MAX_BUF_SIZE 1024

int main()
{
    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();   // 1 初始化文件描述符
    if (fd < 0) {
        printf("初始化inotify err = %d  ", fd);
        _exit(-1);
    }

    for (i = 0; i < WD_NUM; i++) {
        wd_array[i].name = monitored_file[i];
        wd = inotify_add_watch(fd, wd_array[i].name, IN_ALL_EVENTS);  //添加文件
        if (wd < 0) {
            printf("Can not add watch for %s \n", wd_array[i].name);
            _exit(-2);
        }

        wd_array[i].wd = wd;
    }

    while (len = read(fd, buffer, MAX_BUF_SIZE)) { //读取文件, 可以使用select监控该文件描述符
        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);
            } else {
                memcpy(strbuf, "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); //打印事件掩码, 事件通过掩码的形式放在mask中
            for (i = 0; i < EVEBT_NUM; i++) {
                if (event_array[i][0] == '\0')
                    continue;

                if (event->mask &(i << 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;
        }
    }

    return 0;
}

gcc inotfy.cpp -o inotfy

然后执行结果如下:
在这里插入图片描述
我们可以看到文件打开和关闭的都被监控到了。

在这里需要主要的是 如果文件被删除了或者被移动。 在创建一个1.txt那么这个时候1.txt是不会被监控到的。原因是内核中的文件节点已经发生了变化。所以当监控到被删除或被移动了之后应当重新添加一次。

但是存在以下问题:

  1. 需要手动封装select这样会让编码变得复杂一些。
  2. 当前对目录的监控只能是对当前目录的监控, 例如上面的例子中监控了/home/lq/桌面 这个目录,但是桌面上的子目录却无法被监控,如果在桌面上再在创建一个目录也无法进行监控。如果需要监控只能说遍历目录将每一个文件和目录都进行监控,这样以来又增加了代码量。虽然不是很难,但是很麻烦。

现在可以介绍一个程序 inotifywait, 查看帮助信息如下:

在这里插入图片描述

执行命令: inotifywait -rm /home/lq/桌面 可以对桌面的所有文件进行监控,并且是递归的。这就比较厉害了呀。 具体的操作截图就不放了。

通过对inotifywait的源码进行查看发现,这个程序封装了一个库叫做libinotifytools.so

这个库中直接对inotif进行了封装, 反正好用就对了。

sudo apt install  libinotifytools0-dev

之后看代码:

#include <unistd.h>
#include <string.h>
#include <stdio.h>

 #include <inotifytools/inotifytools.h>
 #include <inotifytools/inotify.h>

// 需要安装libinotifytools0-dev
void test()
{
    int ret = inotifytools_initialize(); //初始化时间
    ret = inotifytools_watch_recursively("/home/lq/桌面/", IN_CLOSE); //写入要监控的目录
    ret = inotifytools_watch_file("/home/lq/桌面/", IN_CLOSE);

    while (1) {
        struct inotify_event *s = inotifytools_next_event(0);     //获取事件
        static char *filename = inotifytools_filename_from_wd(s->wd); //获取发送事件的文件名称
        inotifytools_printf(s, "%w %,e %f\n");
        static char *str = inotifytools_event_to_str(s->mask);
        printf("str = %s\n", str);
    }
}

int main()
{
    test(); // inotif 进行监控磁盘
    return 0;
}

g++ inotify1.cpp -linotifytools -o inotify1

运行结果如下:
在这里插入图片描述
从图中可以看到111为桌面的子目录,但是仍然被监控到了。 并且如果该目录新建立文件,那么这个文件也会被自动加入到监控的队列中,这样子就很方便了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值