Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to monitor Linux file system events

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

This article will explain in detail how to monitor Linux file system events. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

Brief introduction to history

There was dnotify before inotify. Unfortunately, dnotify has its limitations and users need better products. The advantages of inotify over dnotify are as follows:

Inotify uses a separate file descriptor, while dnotify needs to open a file descriptor for each monitored directory. The cost can be very high when you monitor multiple directories at the same time, and you will encounter a per-process file descriptor limit.

The file descriptors used by Inotify are available through system calls, and there are no associated devices or files. With dnotify, the file descriptor fixes the directory, preventing the standby device from uninstalling, which is a typical problem with removable media. For inotify, a monitoring file or directory on the unmounted file system generates an event, and the monitoring is automatically removed.

Inotify can monitor files or directories. Dnotify only monitors directories, so programmers must also maintain the stat structure or an equivalent data structure to reflect the files in the monitored directory, and then compare it with the current state when an event occurs to see what happens to the entries in the current directory.

As mentioned above, inotify uses file descriptors to allow programmers to use standard select or poll functions to monitor events. This allows for efficient multiplexing or integration with Glib's mainloop. By contrast, dnotify uses signals, which makes programmers find it difficult or not smooth enough. Inotify also added Signal-drive I.O notifications in the 2.6.25 kernel.

API for inotify

Inotify provides a simple API that uses the smallest file descriptor and allows fine-grained monitoring. Communication with inotify is achieved through system calls. The available functions are as follows:

Inotify_init

Is a system call used to create an instance of inotify and returns a file descriptor that points to that instance.

Inotify_init1

Similar to inotify_init with additional logos. If these additional flags are not specified, they will take the same value as inotify_init.

Inotify_add_watch

Increase monitoring of files or directories and specify which events need to be monitored. Flags are used to control whether events are added to existing monitoring, whether only the path represents a directory, whether to track symbolic links, whether to monitor at one time, and to stop monitoring when * events occur.

Inotify_rm_watch

Remove the monitoring item from the monitoring list.

Read

Reads a cache that contains one or more event information.

Close

Close the file descriptor and remove all monitoring on that descriptor. When all file descriptors for an instance are turned off, resources and underlying objects are released for re-use by the kernel.

Therefore, a typical monitor needs to do the following:

Open a file descriptor using inotify_init

Add one or more monitors

Waiting event

Handle events, then go back and wait for more events

When the monitoring is no longer active, or after receiving a signal, close the file descriptor, empty it, and then exit.

In the next section, you'll see events that can be monitored and how they run in simple programs. *, you will see how event monitoring is performed.

Circular

When your application reads an announcement, the order of events is also read into the cache you provide. The event is returned in a variable-length structure, as shown in listing 1. If the data is full of your cache, you may need to perform local event information or local name processing on an entry.

Listing 1. Event structure for inotify

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. * /}

Note that the name field is provided only if the monitoring object is a directory and the event is related to related items within the directory and has nothing to do with the directory itself. If both the IN_MOVED_FROM event and the corresponding IN_MOVED_TO event are related to the monitored project, cookie can be used to associate the two. The event type is returned in the mask field, accompanied by flags that can be set by the kernel. For example, if the event is directory-related, the flag IN_ISDIR will be set by the kernel.

Monitorable event

There are several events that can be monitored. Some events, such as IN_DELETE_SELF, apply only to projects being monitored, while others, such as IN_ATTRIB or IN_OPEN, apply only to monitored projects, or if the project is a directory, it can be applied to the directories or files it contains.

IN_ACCESS

Entries in the monitored project or directory have been accessed. For example, an open file is read.

IN_MODIFY

Entries in the monitored project or directory have been modified. For example, an open file is modified.

IN_ATTRIB

The metadata of items in the monitored project or directory has been modified. For example, the timestamp or license is modified.

IN_CLOSE_WRITE

An open file or directory waiting to be written is closed.

IN_CLOSE_NOWRITE

A file or directory opened as read-only is closed.

IN_CLOSE

A mask that makes it easy to logically operate on the two previously mentioned shutdown events (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE).

IN_OPEN

The file or directory is opened.

IN_MOVED_FROM

Items in the monitored project or directory are moved out of the monitoring area. The event also contains a cookie to implement the association between IN_MOVED_FROM and IN_MOVED_TO.

IN_MOVED_TO

The file or directory is moved into the monitoring area. This event contains a cookie for IN_MOVED_FROM. If the file or directory is only renamed, you will see these two events, and if it is simply moved into or out of an unmonitored area, you will see only one event. If you move or rename a monitored project, monitoring will continue. See IN_MOVE-SELF below.

IN_MOVE

A mask that makes it easy to logically manipulate the two previously mentioned mobile events (IN_MOVED_FROM | IN_MOVED_TO).

IN_CREATE

A subdirectory or file is created in the monitored directory.

IN_DELETE

Subdirectories or files in the monitored directory have been deleted.

IN_DELETE_SELF

The monitored project itself is deleted. Monitoring terminates and an IN_IGNORED event is received.

IN_MOVE_SELF

The monitoring project itself is moved.

In addition to the event flags, several other flags can be found in the inotify header file (/ usr/include/sys/inotify.h). For example, if you only want to monitor * events, you can set the IN_ONESHOT flag when you add monitoring.

A simple inotify application

The simple application here (see the download section) follows the general logic above. We use a signal handler to monitor the ctrl-c (SIGINT) and reset a flag (keep_running) to make the application aware of the termination operation. The real inotify call is done in the utility routine. Notice that we have also created a queue so that events can be purged from the underlying inotify object for later processing. In a real application, you might want to do this with a different thread (with a higher priority) than the one you use to handle events. For this application, it is just to illustrate the general principle. We adopted a simple event list in which each entry in our queue contains the original event and the space for the pointer to the next event in the queue.

Main program

The signal processing routine and the main routine are shown in listing 2. In this simple example, a directory is monitored for each file passed on the command line, and all events for each object are monitored using the event mask IN_ALL_EVENTS. In a real application, you may only want to track file and directory creation or deletion events, so you can mask open, close, and property change events. If you are not interested in renaming and moving files or directories, you can also block various movement events. See the inotify help information for more details.

Listing 2. Simple main program for inotify-test.c

/ * Signal handler that simply resets a flag to cause termination * / void signal_handler (int signum) {keep_running = 0;} int main (int argc, char * * argv) {/ * This is the file descriptor for the inotify watch * / int inotify_fd; keep_running = 1 / * Set a ctrl-c signal handler * / if (signal (SIGINT, signal_handler) = = SIG_IGN) {/ * Reset to SIG_IGN (ignore) if that was the prior state * / signal (SIGINT, SIG_IGN);} / * First we open the inotify dev entry * / inotify_fd = open_inotify_fd () If (inotify_fd > 0) {/ * We will need a place to enqueue inotify events, this is needed because if you do not read events fast enough, you will miss them. This queue is probably too small if you are monitoring something like a directory with a lot of files and the directory is deleted. * / queue_t Q; Q = queue_create (128); / * This is the watch descriptor returned for each item we are watching. A real application might keep these for some use in the application. This sample only makes sure that none of the watch descriptors is less than 0. * / int wd; / * Watch all events (IN_ALL_EVENTS) for the directories and files passed in as arguments. Read the article for why you might want to alter this for more efficient inotify use in your app. * / int index; wd = 0; printf ("\ n"); for (index = 1; (index)

< argc) && (wd >

= 0); index++) {wd = watch_dir (inotify_fd, argv [index], IN_ALL_EVENTS);} if (wd > 0) {/ * Wait for events and process them until a termination condition is detected * / process_inotify_events (Q, inotify_fd);} printf ("\ nTerminating\ n") / * Finish up by closing the fd, destroying the queue, and returning a proper code * / close_inotify_fd (inotify_fd); queue_destroy (Q);} return 0;}

Open a file descriptor using inotify_init

Listing 3 shows a simple utility function for creating an instance of inotify and getting its file descriptor. The file descriptor is returned to the caller. If an error occurs, the return value will be negative.

Listing 3. Use inotify_init

/ * Create an inotify instance and open a file descriptor to access it * / int open_inotify_fd () {int fd; watched_items = 0; fd = inotify_init (); if (fd

< 0) { perror ("inotify_init () = "); } return fd; } 使用 inotify_add_watch 添加一个监控 一旦我们有了用于 inotify 实例的文件描述符之后,就需要增加一个或多个监控。您可以使用掩码来设置想要监控的事件。在本例中,采用掩码 IN_ALL_EVENTS,来监控所有可用事件。 清单 4. 使用 inotify_add_watch int watch_dir (int fd, const char *dirname, unsigned long mask){ int wd; wd = inotify_add_watch (fd, dirname, mask); if (wd < 0) { printf ("Cannot add watch for \"%s\" with event mask %lX", dirname, mask); fflush (stdout); perror (" "); } else { watched_items++; printf ("Watching %s WD=%d\n", dirname, wd); printf ("Watching = %d items\n", watched_items); } return wd;} 事件处理循环 现在我们已经设置了一些监控,接下来就要等待事件。如果还存在监控,并且 keep_running 标志没有被信号处理程序重置,则循环会一直进行。循环进程等待事件的发生,对有效事件进行排队,并在返回等待状态之前处理队列。在真实应用程序当中,您可能会将事件放入线程队列中,而在另一个线程中处理它们,清单 5 展示了该循环。 清单 5. 事件处理循环 int process_inotify_events (queue_t q, int fd){ while (keep_running && (watched_items >

0)) {if (event_check (fd) > 0) {int r; r = read_events (Q, fd); if (r

< 0) { break; } else { handle_events (q); }} } return 0; } 等待事件 在我们的示样例应用程序中,循环会不停地进行下去,直至监控事件出现或者收到了中断信号。清单 6 展示了相关代码。 清单 6. 等待事件或者中断 int event_check (int fd){ fd_set rfds; FD_ZERO (&rfds); FD_SET (fd, &rfds); /* Wait until an event happens or we get interrupted by a signal that we catch */ return select (FD_SETSIZE, &rfds, NULL, NULL, NULL); } 读取事件 当事件发生时,程序会依照缓存区的大小来读取尽量多的事件,然后把这些事件放入队列等待事件处理程序来处理。样例代码不能处理这种情况 — 可用事件超出 16.384 byte 缓存中可存储的事件。要处理这类情况,需要在缓存末端处理部分事件。目前,对名字长度进行限制不成问题,但是优秀的防御式编程会检查名字,来确保不会溢出缓存。 清单 7. 读取事件并排队 int read_events (queue_t q, int fd){ char buffer[16384]; size_t buffer_i; struct inotify_event *pevent; queue_entry_t event; ssize_t r; size_t event_size, q_event_size; int count = 0; r = read (fd, buffer, 16384); if (r len; q_event_size = offsetof (struct queue_entry, inot_ev.name) + pevent->

Len; event = malloc (q_event_size); memmove (& (event- > inot_ev), pevent, event_size); queue_enqueue (event, Q); buffer_i + = event_size; count++;} printf ("\ n% d events queued\ n", count); return count;}

Deal with events

*, we need to deal with the incident. For this application, we simply report the events that occurred. If a name appears in the event structure, we report that it is a file or directory. When a move occurs, cookie information related to the move or rename event is also reported. Listing 8 shows some of the code, including the handling of some events. See the download section for the complete code.

Listing 8. Deal with events

Void handle_event (queue_entry_t event) {/ * If the event was associated with a filename, we will store it here * / char * cur_event_filename = NULL; char * cur_event_file_or_dir = NULL; / * This is the watch descriptor the event occurred on * / int cur_event_wd = event- > inot_ev.wd; int cur_event_cookie = event- > inot_ev.cookie; unsigned long flags If (event- > inot_ev.len) {cur_event_filename = event- > inot_ev.name;} if (event- > inot_ev.mask & IN_ISDIR) {cur_event_file_or_dir = "Dir";} else {cur_event_file_or_dir = "File" } flags = event- > inot_ev.mask & ~ (IN_ALL_EVENTS | IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED) / * Perform event dependent handler routines * / / * The mask is the magic that tells us what file operation occurred * / switch (event- > inot_ev.mask & (IN_ALL_EVENTS | IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED)) {/ * File was accessed * / case IN_ACCESS: printf ("ACCESS:% s\"% s\ "on WD #% I\ n", cur_event_file_or_dir, cur_event_filename Cur_event_wd) Break; / * File was modified * / case IN_MODIFY: printf ("MODIFY:% s\"% s\ "on WD #% I\ n", cur_event_file_or_dir, cur_event_filename, cur_event_wd); break / * File changed attributes * / case IN_ATTRIB: printf ("ATTRIB:% s\"% s\ "on WD #% I\ n", cur_event_file_or_dir, cur_event_filename, cur_event_wd); break / * File open for writing was closed * / case IN_CLOSE_WRITE: printf ("CLOSE_WRITE:% s\"% s\ "on WD #% I\ n", cur_event_file_or_dir, cur_event_filename, cur_event_wd); break / * File open read-only was closed * / case IN_CLOSE_NOWRITE: printf ("CLOSE_NOWRITE:% s\"% s\ "on WD #% I\ n", cur_event_file_or_dir, cur_event_filename, cur_event_wd); break / * File was opened * / case IN_OPEN: printf ("OPEN:% s\"% s\ "on WD #% I\ n", cur_event_file_or_dir, cur_event_filename, cur_event_wd); break / * File was moved from X * / case IN_MOVED_FROM: printf ("MOVED_FROM:% s\"% s\ "on WD #% I. Cookie=%d\ n", cur_event_file_or_dir, cur_event_filename, cur_event_wd, cur_event_cookie); break;.. (other cases). / * Watch was removed explicitly by inotify_rm_watch or automatically because file was deleted, or file system was unmounted. * / case IN_IGNORED: watched_items--; printf ("IGNORED: WD #% d\ n", cur_event_wd); printf ("Watching =% d items\ n", watched_items); break / * Some unknown message received * / default: printf ("UNKNOWN EVENT\"% X\ "OCCURRED for file\"% s\ "on WD #% I\ n", event- > inot_ev.mask, cur_event_filename, cur_event_wd); break;} / * If any flags were set other than IN_ISDIR, report the flags * / if (flags & (~ IN_ISDIR)) {flags = event- > inot_ev.mask Printf ("Flags=%lX\ n", flags);}}

This simple example shows how inotify works and what events you can monitor. Your actual needs will determine which events to monitor and how to handle them.

Examples of usage

In this section, we create a simple two-level directory structure with files, and then run a simple program to illustrate some of the events that inotify can monitor. We will start the inotify sample program in a terminal session, but run (use &) in the background so that the output of the program alternates with the commands we enter. You can run the program in one terminal window and run instructions in one or more other windows. Listing 9 shows the simple file structure and the creation of an empty file, as well as the output when the sample program is initially started.

Listing 9. Create a sample environment

Ian@attic4:~/inotify-sample$ mkdir-p dir1/dir2ian@attic4:~/inotify-sample$ touch dir1/dir2/file1ian@attic4:~/inotify-sample$. / inotify_test dir1/ dir1/dir2/ dir1/dir2/file1& [2] 8733ianfang attic4 inotify_test dir1/ dir1/dir2/ dir1/dir2/file1& Sample1 Watching dir1/ WD=1Watching = 1 itemsWatching dir1/dir2/ WD=2Watching = 2 itemsWatching dir1/dir2/file1 WD=3Watching = 3 itemsian@attic4:~/inotify-sample$

In listing 10, the output from the dir2 content list is shown. The * event reports are about dir1, showing some content, namely dir2, which is opened in the directory monitored by the monitoring descriptor 1. The second entry is about monitor descriptor 2, which shows that the monitored item (in this case, dir2) is open. If you are monitoring multiple items in the directory tree, you may often encounter this dual output.

Listing 10. List the contents of dir2

Ian@attic4:~/inotify-sample$ ls dir1/dir2file14 events queuedOPEN: Dir "dir2" on WD # 1OPEN: Dir (null) "on WD # 2CLOSE_NOWRITE: Dir" dir2 "on WD # 1CLOSE_NOWRITE: Dir" (null) "on WD # 2

In listing 11, we add some text to file1. Once again, note the double open, close, and modify events for the file and the directory where the file is located. Also note that all events are not read immediately. The queuing routine is called three times, each with two events. If you run the program again and do the same each time, you may not encounter this particular situation again.

Listing 11. Add text to file1

Ian@attic4:~/inotify-sample$ echo "Some text" > dir1/dir2/file12 events queuedOPEN: File "file1" on WD # 2OPEN: File "(null)" on WD # 32 events queuedMODIFY: File "file1" on WD # 2MODIFY: File "(null)" on WD # 32 events queuedCLOSE_WRITE: File "file1" on WD # 2CLOSE_WRITE: File "(null)" on WD # 3

In listing 12, we changed the properties of file1. Once again, we get double output about the monitored project and its directory.

Listing 12. Change file properties

Ian@attic4:~/inotify-sample$ chmod Aguilw dir1/dir2/file12 events queuedATTRIB: File "file1" on WD # 2ATTRIB: File "(null)" on WD # 3

Now move the file file1 to the previous directory dir1. The output is shown in listing 13. There is no double entry this time. We actually get three entries, one for each directory and one for the file itself. Note that cookie allows you to associate MOVED-FROM events with MOVED_TO events.

Listing 13. Move file1 to dir1

Ian@attic4:~/inotify-sample$ mv dir1/dir2/file1 dir13 events queuedMOVED_FROM: File "file1" on WD # 2. Cookie=569MOVED_TO: File "file1" on WD # 1.Cookie=569MOVE_SELF: File "(null)" on WD # 3

Now create a hard link from file1 to file2. When the number of links to inode should change, we will have an ATTRIB event about file1 and a CREATE event about file2.

Listing 14. Create a hard link

Ian@attic4:~/inotify-sample$ ln dir1/file1 dir1/file22 events queuedATTRIB: File "(null)" on WD # 3CREATE: File "file2" on WD # 1

Now move the file file1 to the current directory and rename it file3. The current directory is not monitored, so there are no MOVED_TO events associated with MOVED_FROM events.

Listing 15. Move file1 to a directory that is not monitored

Ian@attic4:~/inotify-sample$ mv dir1/file1. / file32 events queuedMOVED_FROM: File "file1" on WD # 1. Cookie=572MOVE_SELF: File "(null)" on WD # 3

At this point, the dir2 is empty, so you can move it. Notice that we got an IGNORED event for monitoring descriptor 2, so now we are only monitoring two projects.

Listing 16. Remove dir2

Ian@attic4:~/inotify-sample$ rmdir dir1/dir23 events queuedDELETE: Dir "dir2" on WD # 1DELETE_SELF: File "(null)" on WD # 2IGNORED: WD # 2Watching = 2 items

Remove the file file3. Notice that we didn't get the IGNORED event this time. Why? Why did you get the ATTRIB event (the original dir1/dir2/file1) about file 3?

Listing 16. Delete file3

Ian@attic4:~/inotify-sample$ rm file31 events queuedATTRIB: File "(null)" on WD # 3

Remember that we created a hard link from file1 to file2. Listing 17 shows that we are still monitoring file2 by monitoring descriptor 3, even though file 2 does not exist at first!

Ian@attic4:~/inotify-sample$ touch dir1/file26 events queuedOPEN: File "file2" on WD # 1OPEN: File "on WD # 3ATTRIB: File" file2 "on WD # 1ATTRIB: File" (null) "on WD # 3CLOSE_WRITE: File" file2 "on WD # 1CLOSE_WRITE: File" (null) "on WD # 3

So now let's delete the dir1 and monitor the event cascading because it no longer monitors anything and has to end itself.

Listing 18. Delete dir1

Ian@attic4:~/inotify-sample$ rm-rf dir18 events queuedOPEN: Dir "(null)" on WD # 1ATTRIB: File "(null)" on WD # 3DELETE_SELF: File "(null)" on WD # 3IGNORED: WD # 3Watching = 1 itemsDELETE: File "file2" on WD # 1CLOSE_NOWRITE: Dir "(null)" on WD # 1DELETE_SELF: File "(null)" on WD # 1IGNORED: WD # 1Watching = 0 itemsTerminatinginotify

You can use inotify for a variety of goals. Here are some possible scenarios:

Performance monitoring

You may want to determine which file the application opens most frequently. If you find that a small file is frequently opened and closed, you may consider using the memory version, or change the application to share the data in other ways.

Meta information

You may want to record additional information about the file, such as the start time of creation or the id of the user who changed the file.

Safety

You may need to monitor all access to a specific file or directory for security reasons.

Our sample code monitors and reports all events. In fact, you may want to view a specific subset of these events based on your needs. You may want to monitor different events for different monitored projects. For example, you might want to monitor file opening and closing events, but for directories you only want to monitor creation and deletion events. Whenever possible, you can monitor the minimum set of events you are interested in.

This is the end of this article on "how to monitor Linux file system events". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report