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;
 }- 需要代码可以直接在下面我学习的链接中进行下载