inotify是Linux为满足某些应用的安全需求或者监控某些文件变化需求等功能而设计出来的接口,这里作一些简单介绍
inotify的API:
Inotify 提供一个简单的 API,使用最小的文件描述符,并且允许细粒度监控。与 inotify 的通信是通过系统调用实现。可用的函数如下所示:
inotify_init
是用于创建一个 inotify 实例的系统调用,并返回一个指向该实例的文件描述符。inotify_init1
与 inotify_init 相似,并带有附加标志。如果这些附加标志没有指定,将采用与 inotify_init 相同的值。inotify_add_watch
增加对文件或者目录的监控,并指定需要监控哪些事件。标志用于控制是否将事件添加到已有的监控中,是否只有路径代表一个目录才进行监控,是否要追踪符号链接,是否进行一次性监控,当首次事件出现后就停止监控。inotify_rm_watch
从监控列表中移出监控项目。read
读取包含一个或者多个事件信息的缓存。close
关闭文件描述符,并且移除所有在该描述符上的所有监控。当关于某实例的所有文件描述符都关闭时,资源和下层对象都将释放,以供内核再次使用。
因此,典型的监控程序需要进行如下操作:
- 使用 inotify_init 打开一个文件描述符
- 添加一个或者多个监控
- 等待事件
- 处理事件,然后返回并等待更多事件
- 当监控不再活动时,或者接到某个信号之后,关闭文件描述符,清空,然后退出。
事件
事件的通过顺序的方式被读取到缓存当中。
用于inotify
中的事件结构体
1 | struct inotify_event |
可监控的事件类型
有几种事件能够被监控。一些事件,比如 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
监控项目本身被移动。
除了事件标志以外,还可以在inotify
头文件(/usr/include/sys/inotify.h
)中找到其他几个标志。例如,如果只想监控第一个事件,可以在增加监控时设置IN_ONESHOT
标志。
示例
最简单的例子,可监视到某文件被操作,但由于事件的发生方式比较复杂,因此以下例子只作最简单的示例用
1 |
|
一个简单的官方例子,这个例子可以较为全面地解决事件排队问题,具体解释直接看代码注释吧~
- inotify_test
1 |
|
inotyfy_utils.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
extern int keep_running;
static int watched_items;
/* 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;
}
/* Close the open file descriptor that was opened with inotify_init() */
int close_inotify_fd(int fd)
{
int r;
if ((r = close(fd)) < 0)
{
perror("close (fd) = ");
}
watched_items = 0;
return r;
}
/* This method does the work of determining what happened,
then allows us to act appropriately
@param event 被处理的事件
*/
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;
/* File was moved to X */
case IN_MOVED_TO:
printf("MOVED_TO: %s \"%s\" on WD #%i. Cookie=%d\n",
cur_event_file_or_dir, cur_event_filename, cur_event_wd,
cur_event_cookie);
break;
/* Subdir or file was deleted */
case IN_DELETE:
printf("DELETE: %s \"%s\" on WD #%i\n",
cur_event_file_or_dir, cur_event_filename, cur_event_wd);
break;
/* Subdir or file was created */
case IN_CREATE:
printf("CREATE: %s \"%s\" on WD #%i\n",
cur_event_file_or_dir, cur_event_filename, cur_event_wd);
break;
/* Watched entry was deleted */
case IN_DELETE_SELF:
printf("DELETE_SELF: %s \"%s\" on WD #%i\n",
cur_event_file_or_dir, cur_event_filename, cur_event_wd);
break;
/* Watched entry was moved */
case IN_MOVE_SELF:
printf("MOVE_SELF: %s \"%s\" on WD #%i\n",
cur_event_file_or_dir, cur_event_filename, cur_event_wd);
break;
/* Backing FS was unmounted */
case IN_UNMOUNT:
printf("UNMOUNT: %s \"%s\" on WD #%i\n",
cur_event_file_or_dir, cur_event_filename, cur_event_wd);
break;
/* Too many FS events were received without reading them
some event notifications were potentially lost. */
case IN_Q_OVERFLOW:
printf("Warning: AN OVERFLOW EVENT OCCURRED: \n");
break;
/* 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);
}
}
/**
* 处理事件队列
* @param q 事件队列
*/
void handle_events(queue_t q)
{
queue_entry_t event;
while (!queue_empty(q))
{
event = queue_dequeue(q);
handle_event(event);
free(event);
}
}
/**
* 将存储在结构体q中,并排队
* @param q 事件结构体队列
* @param fd inotify描述符
* @return 此次进行排队的事件数量
*/
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 <= 0)
return r;
buffer_i = 0;
while (buffer_i < r)
{
/* Parse events and queue them. */
pevent = (struct inotify_event *)&buffer[buffer_i];
event_size = offsetof(struct inotify_event, name) + pevent->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;
}
// 等待事件或者中断
int event_check(int fd)
{
fd_set rfds;
//将指定的fd_set清零
FD_ZERO(&rfds);
//然后调用宏FD_SET将需要测试的fd加入fd_set
FD_SET(fd, &rfds);
/* Wait until an event happens or we get interrupted
by a signal that we catch */
//调用函数select测试fd_set中的所有fd
//select 的用法
/**
* 可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,
* 以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,
* 若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,
* 它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。
返回值:准备就绪的描述符数,若超时则返回0,若出错则返回-1。
*
* 使用select函数的过程一般是:
先调用宏FD_ZERO将指定的fd_set清零,
然后调用宏FD_SET将需要测试的fd加入fd_set,
接着调用函数select测试fd_set中的所有fd,
最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。
*/
return select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
}
/**
* 对事件进行处理的循环
* @param q 存储事件的数据结构
* @param fd inotify描述符
* @return 0
*/
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;
}
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;
}
int ignore_wd(int fd, int wd)
{
int r;
r = inotify_rm_watch(fd, wd);
if (r < 0)
{
perror("inotify_rm_watch(fd, wd) = ");
}
else
{
watched_items--;
}
return r;
}event_queue.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/* Simple queue implemented as a singly linked list with head
and tail pointers maintained
*/
int queue_empty (queue_t q)
{
return q->head == NULL;
}
queue_t queue_create ()
{
queue_t q;
q = malloc (sizeof (struct queue_struct));
if (q == NULL)
exit (-1);
q->head = q->tail = NULL;
return q;
}
void queue_destroy (queue_t q)
{
if (q != NULL)
{
while (q->head != NULL)
{
queue_entry_t next = q->head;
q->head = next->next_ptr;
next->next_ptr = NULL;
free (next);
}
q->head = q->tail = NULL;
free (q);
}
}
void queue_enqueue (queue_entry_t d, queue_t q)
{
d->next_ptr = NULL;
if (q->tail)
{
q->tail->next_ptr = d;
q->tail = d;
}
else
{
q->head = q->tail = d;
}
}
queue_entry_t queue_dequeue (queue_t q)
{
queue_entry_t first = q->head;
if (first)
{
q->head = first->next_ptr;
if (q->head == NULL)
{
q->tail = NULL;
}
first->next_ptr = NULL;
}
return first;
}需要代码可以直接在下面我学习的链接中进行下载