比思論壇

標題: 用 inotify 监控 Linux 文件系统事件 [打印本頁]

作者: 妖刀路过    時間: 2020-11-26 17:09
標題: 用 inotify 监控 Linux 文件系统事件
用于 inotify 的 API
[size=1.0625]Inotify 提供一个简单的 API,使用最小的文件描述符,并且允许细粒度监控。与 inotify 的通信是通过系统调用实现。可用的函数如下所示:
inotify_init是用于创建一个 inotify 实例的系统调用,并返回一个指向该实例的文件描述符。inotify_init1与 inotify_init 相似,并带有附加标志。如果这些附加标志没有指定,将采用与 inotify_init 相同的值。inotify_add_watch增加对文件或者目录的监控,并指定需要监控哪些事件。标志用于控制是否将事件添加到已有的监控中,是否只有路径代表一个目录才进行监控,是否要追踪符号链接,是否进行一次性监控,当首次事件出现后就停止监控。inotify_rm_watch从监控列表中移出监控项目。read读取包含一个或者多个事件信息的缓存。close关闭文件描述符,并且移除所有在该描述符上的所有监控。当关于某实例的所有文件描述符都关闭时,资源和下层对象都将释放,以供内核再次使用。
[size=1.0625]因此,典型的监控程序需要进行如下操作:
[size=1.0625]在下一部分中,您将看到可以监控的事件,它们如何在简单的程序中运行。最后,您将看到事件监控如何进行。
通告
[size=1.0625]当您的应用程序读取到一个通告时,事件的顺序也被读取到您提供的缓存中。事件在一个变长结构中被返回,如清单 1 所示。如果数据占满了您的缓存,您可能需要对最后一个条目进行局部事件信息或者局部名处理。
清单 1. 用于 inotify 的事件结构体
[size=0.875]1

[size=0.875]2

[size=0.875]3

[size=0.875]4

[size=0.875]5

[size=0.875]6

[size=0.875]7

[size=0.875]8

[size=0.875][size=0.875]struct inotify_event
[size=0.875]{
[size=0.875]  int wd;               /* Watch descriptor.  */
[size=0.875]  uint32_t mask;        /* Watch mask.  */
[size=0.875]  uint32_t cookie;      /* Cookie to synchronize two events.  */
[size=0.875]  uint32_t len;         /* Length (including NULs) of name.  */
[size=0.875]  char name __flexarr;  /* Name.  */
[size=0.875]  };




[size=1.0625]请注意,只有当监控对象是一个目录并且事件与目录内部相关项目有关,而与目录本身无关时,才提供 name 字段。如果 IN_MOVED_FROM 事件与相应的 IN_MOVED_TO 事件都与被监控的项目有关,cookie 就可用于将两者关联起来。事件类型在掩码字段中返回,并伴随着能够被内核设置的标志。例如,如果事件与目录有关,则标志 IN_ISDIR 将由内核设置。
可监控的事件
[size=1.0625]有几种事件能够被监控。一些事件,比如 IN_DELETE_SELF 只适用于正在被监控的项目,而另一些,比如 IN_ATTRIB 或者 IN_OPEN 则只适用于监控过的项目,或者如果该项目是目录,则可以应用到其所包含的目录或文件。
IN_ACCESS被监控项目或者被监控目录中的条目被访问过。例如,一个打开的文件被读取。IN_MODIFY被监控项目或者被监控目录中的条目被修改过。例如,一个打开的文件被修改。IN_ATTRIB被监控项目或者被监控目录中条目的元数据被修改过。例如,时间戳或者许可被修改。IN_CLOSE_WRITE一个打开的,等待写入的文件或目录被关闭。IN_CLOSE_NOWRITE一个以只读方式打开的文件或目录被关闭。IN_CLOSE一个掩码,可以很便捷地对前面提到的两个关闭事件(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)进行逻辑操作。IN_OPEN文件或目录被打开。IN_MOVED_FROM被监控项目或者被监控目录中的条目被移出监控区域。该事件还包含一个 cookie 来实现 IN_MOVED_FROM 与 IN_MOVED_TO 的关联。IN_MOVED_TO文件或目录被移入监控区域。该事件包含一个针对 IN_MOVED_FROM 的 cookie。如果文件或目录只是被重命名,将能看到这两个事件,如果它只是被移入或移出非监控区域,将只能看到一个事件。如果移动或重命名一个被监控项目,监控将继续进行。参见下面的 IN_MOVE-SELF。IN_MOVE可以很便捷地对前面提到的两个移动事件(IN_MOVED_FROM | IN_MOVED_TO)进行逻辑操作的掩码。IN_CREATE在被监控目录中创建了子目录或文件。IN_DELETE被监控目录中有子目录或文件被删除。IN_DELETE_SELF被监控项目本身被删除。监控终止,并且将收到一个 IN_IGNORED 事件。IN_MOVE_SELF监控项目本身被移动。
[size=1.0625]除了事件标志以外,还可以在 inotify 头文件(/usr/include/sys/inotify.h)中找到其他几个标志。例如,如果只想监控第一个事件,可以在增加监控时设置 IN_ONESHOT 标志。
一个简单 inotify 应用程序
[size=1.0625]这里的简单应用程序(参见 下载 部分)遵循以上的通用逻辑。我们使用一个信号处理程序来监控 ctrl-c(SIGINT)并且重置一个标志(keep_running)使应用程序了解终止操作。真实的 inotify 调用在 utility 例程当中完成。注意,我们还创建了一个队列,这样能够将事件从 inotify 底层对象中清除,留着稍后处理。在真实的应用程序中,您可能想用一个不同于您处理事件所用的线程(具有更高优先级)来完成这一操作。对于该应用程序,只是为了对一般原理进行举例说明。我们采用了一个简单的事件链表,在其中我们队列中的每个条目都包含原始事件以及指向队列中下一事件指针的空间。
主程序
[size=1.0625]清单 2 中展示了信号处理例程和主例程。在这个简单示例中,在命令行对每个传递进来的文件会目录建立监控,并利用事件掩码 IN_ALL_EVENTS 来监控每个对象的所有事件。在真实的应用程序中,您可能只希望跟踪文件与目录的创建或删除事件,因此您可以屏蔽打开、关闭以及属性改变事件。如果您对文件或目录的重命名和移动不感兴趣,您也可以屏蔽各种移动事件。关于更多细节,参见 inotify 帮助信息。
清单 2. inotify-test.c 的简单主程序
[size=0.875]1

[size=0.875]2

[size=0.875]3

[size=0.875]4

[size=0.875]5

[size=0.875]6

[size=0.875]7

[size=0.875]8

[size=0.875]9

[size=0.875]10

[size=0.875]11

[size=0.875]12

[size=0.875]13

[size=0.875]14

[size=0.875]15

[size=0.875]16

[size=0.875]17

[size=0.875]18

[size=0.875]19

[size=0.875]20

[size=0.875]21

[size=0.875]22

[size=0.875]23

[size=0.875]24

[size=0.875]25

[size=0.875]26

[size=0.875]27

[size=0.875]28

[size=0.875]29

[size=0.875]30

[size=0.875]31

[size=0.875]32

[size=0.875]33

[size=0.875]34

[size=0.875]35

[size=0.875]36

[size=0.875]37

[size=0.875]38

[size=0.875]39

[size=0.875]40

[size=0.875]41

[size=0.875]42

[size=0.875]43

[size=0.875]44

[size=0.875]45

[size=0.875]46

[size=0.875]47

[size=0.875]48

[size=0.875]49

[size=0.875]50

[size=0.875]51

[size=0.875]52

[size=0.875]53

[size=0.875]54

[size=0.875]55

[size=0.875]56

[size=0.875]57

[size=0.875]58

[size=0.875]59

[size=0.875]60

[size=0.875]61

[size=0.875]62

[size=0.875]63

[size=0.875]64

[size=0.875]65

[size=0.875]66

[size=0.875]67

[size=0.875]68

[size=0.875]69

[size=0.875]70

[size=0.875]71

[size=0.875]72

[size=0.875]73

[size=0.875][size=0.875]/* Signal handler that simply resets a flag to cause termination */
[size=0.875]void signal_handler (int signum)
[size=0.875]{
[size=0.875]  keep_running = 0;
[size=0.875]}

[size=0.875]int main (int argc, char **argv)
[size=0.875]{
[size=0.875]  /* This is the file descriptor for the inotify watch */
[size=0.875]  int inotify_fd;

[size=0.875]  keep_running = 1;

[size=0.875]  /* Set a ctrl-c signal handler */
[size=0.875]  if (signal (SIGINT, signal_handler) == SIG_IGN)
[size=0.875]    {
[size=0.875]      /* Reset to SIG_IGN (ignore) if that was the prior state */
[size=0.875]      signal (SIGINT, SIG_IGN);
[size=0.875]    }

[size=0.875]  /* First we open the inotify dev entry */
[size=0.875]  inotify_fd = open_inotify_fd ();
[size=0.875]  if (inotify_fd > 0)
[size=0.875]    {

[size=0.875]      /* We will need a place to enqueue inotify events,
[size=0.875]         this is needed because if you do not read events
[size=0.875]         fast enough, you will miss them. This queue is
[size=0.875]         probably too small if you are monitoring something
[size=0.875]         like a directory with a lot of files and the directory
[size=0.875]         is deleted.
[size=0.875]       */
[size=0.875]      queue_t q;
[size=0.875]      q = queue_create (128);

[size=0.875]      /* This is the watch descriptor returned for each item we are
[size=0.875]         watching. A real application might keep these for some use
[size=0.875]         in the application. This sample only makes sure that none of
[size=0.875]         the watch descriptors is less than 0.
[size=0.875]       */
[size=0.875]      int wd;


[size=0.875]      /* Watch all events (IN_ALL_EVENTS) for the directories and
[size=0.875]         files passed in as arguments.
[size=0.875]         Read the article for why you might want to alter this for
[size=0.875]         more efficient inotify use in your app.      
[size=0.875]       */
[size=0.875]      int index;
[size=0.875]      wd = 0;
[size=0.875]      printf("\n");
[size=0.875]      for (index = 1; (index < argc) && (wd >= 0); index++)
[size=0.875]    {
[size=0.875]      wd = watch_dir (inotify_fd, argv[index], IN_ALL_EVENTS);
[size=0.875]    }

[size=0.875]      if (wd > 0)
[size=0.875]    {
[size=0.875]      /* Wait for events and process them until a
[size=0.875]         termination condition is detected
[size=0.875]      */
[size=0.875]      process_inotify_events (q, inotify_fd);
[size=0.875]    }
[size=0.875]      printf ("\nTerminating\n");

[size=0.875]      /* Finish up by closing the fd, destroying the queue,
[size=0.875]         and returning a proper code
[size=0.875]       */
[size=0.875]      close_inotify_fd (inotify_fd);
[size=0.875]      queue_destroy (q);
[size=0.875]    }
[size=0.875]  return 0;
[size=0.875]}




使用 inotify_init 打开文件描述符
[size=1.0625]清单 3 展示了用于创建 inotify 实例以及获取其文件描述符的简单实用工具函数。文件描述符返回给了调用者。如果出现错误,返回值将为负。
清单 3. 使用 inotify_init
[size=0.875]1

[size=0.875]2

[size=0.875]3

[size=0.875]4

[size=0.875]5

[size=0.875]6

[size=0.875]7

[size=0.875]8

[size=0.875]9

[size=0.875]10

[size=0.875]11

[size=0.875]12

[size=0.875]13

[size=0.875]14

[size=0.875]15

[size=0.875][size=0.875]/* Create an inotify instance and open a file descriptor
[size=0.875]   to access it */
[size=0.875]int open_inotify_fd ()
[size=0.875]{
[size=0.875]  int fd;

[size=0.875]  watched_items = 0;
[size=0.875]  fd = inotify_init ();

[size=0.875]  if (fd < 0)
[size=0.875]    {
[size=0.875]      perror ("inotify_init () = ");
[size=0.875]    }
[size=0.875]  return fd;
[size=0.875]  }




使用 inotify_add_watch 添加一个监控
[size=1.0625]一旦我们有了用于 inotify 实例的文件描述符之后,就需要增加一个或多个监控。您可以使用掩码来设置想要监控的事件。在本例中,采用掩码 IN_ALL_EVENTS,来监控所有可用事件。
清单 4. 使用 inotify_add_watch
[size=0.875]1

[size=0.875]2

[size=0.875]3

[size=0.875]4

[size=0.875]5

[size=0.875]6

[size=0.875]7

[size=0.875]8

[size=0.875]9

[size=0.875]10

[size=0.875]11

[size=0.875]12

[size=0.875]13

[size=0.875]14

[size=0.875]15

[size=0.875]16

[size=0.875]17

[size=0.875]18

[size=0.875]19

[size=0.875][size=0.875]int watch_dir (int fd, const char *dirname, unsigned long mask)
[size=0.875]{
[size=0.875]  int wd;
[size=0.875]  wd = inotify_add_watch (fd, dirname, mask);
[size=0.875]  if (wd < 0)
[size=0.875]    {
[size=0.875]      printf ("Cannot add watch for \"%s\" with event mask %lX", dirname,
[size=0.875]          mask);
[size=0.875]      fflush (stdout);
[size=0.875]      perror (" ");
[size=0.875]    }
[size=0.875]  else
[size=0.875]    {
[size=0.875]      watched_items++;
[size=0.875]      printf ("Watching %s WD=%d\n", dirname, wd);
[size=0.875]      printf ("Watching = %d items\n", watched_items);
[size=0.875]    }
[size=0.875]  return wd;
[size=0.875]}




事件处理循环
[size=1.0625]现在我们已经设置了一些监控,接下来就要等待事件。如果还存在监控,并且 keep_running 标志没有被信号处理程序重置,则循环会一直进行。循环进程等待事件的发生,对有效事件进行排队,并在返回等待状态之前处理队列。在真实应用程序当中,您可能会将事件放入线程队列中,而在另一个线程中处理它们,清单 5 展示了该循环。
清单 5. 事件处理循环
[size=0.875]1

[size=0.875]2

[size=0.875]3

[size=0.875]4

[size=0.875]5

[size=0.875]6

[size=0.875]7

[size=0.875]8

[size=0.875]9

[size=0.875]10

[size=0.875]11

[size=0.875]12

[size=0.875]13

[size=0.875]14

[size=0.875]15

[size=0.875]16

[size=0.875]17

[size=0.875]18

[size=0.875]19

[size=0.875]20

[size=0.875][size=0.875]int process_inotify_events (queue_t q, int fd)
[size=0.875]{
[size=0.875]  while (keep_running && (watched_items > 0))
[size=0.875]    {
[size=0.875]      if (event_check (fd) > 0)
[size=0.875]    {
[size=0.875]      int r;
[size=0.875]      r = read_events (q, fd);
[size=0.875]      if (r < 0)
[size=0.875]        {
[size=0.875]          break;
[size=0.875]        }
[size=0.875]      else
[size=0.875]        {
[size=0.875]          handle_events (q);
[size=0.875]        }
[size=0.875]    }
[size=0.875]    }
[size=0.875]  return 0;
[size=0.875]  }










歡迎光臨 比思論壇 (http://108.170.5.98/) Powered by Discuz! X2.5