在講到inotify這個機制前,可能要說一下它的使用限制,如果OS不是用LINUX,請按上一頁,再者如果Linux版本不是2.6.13以上,也請按上一頁,如果OS條件都符合以上兩項,請繼續往下看
什麼是inotify
我截錄一段inotify創作人Rober Love在Linux Journal上所說的一段話
inotify is a file change notification system—a kernel feature that allows applications to request the monitoring of a set of files against a list of events. When the event occurs, the application is notified. To be useful, such a feature must be simple to use, lightweight with little overhead and flexible. It should be easy to add new watches and painless to receive notification of events.
上面那段話簡單的來說,inotify可以幫助你即時的觀測檔案的改變,並回送通知給你,而inotify把檔案狀態的變化分為以下十類
1.IN_ACCESS:File was read from.
2.IN_MODIFY:File was written to.
3.IN_ATTRIB:File’s metadata (inode or xattr) was changed.
4.IN_CLOSE_WRITE:File was closed (and was open for writing).
5.IN_CLOSE_NOWRITE:File was closed (and was not open for writing).
6.IN_OPEN:File was opened.
7.IN_MOVED_FROM:File was moved away from watch.
8.IN_MOVED_TO:File was moved to watch.
9.IN_DELETE File:was deleted.
10.IN_DELETE_SELF:The watch itself was deleted.
所以不論讀,寫,開啟,刪除,或attribute改變,都可以從inotify收到通知,使用這樣的機制好處是,你再也不用去polling你所有要監測的檔案了,這對embedded device來說是十分重要的功能,因為它可以簡化你所有監測的動作並且節省device的資源
而使用inotify的方法也非常簡單,只需呼叫幾個單純的API即可,比如說我想監測/home/jemic/test目錄下的所有檔案,就寫下面的code
int fd;
queue_t q;
int *wd;
q = queue_create (128);
fd=inotify_init();
wd = inotify_add_watch (fd, "/home/jemic/test/",IN_ATTRIB);
process_inotify_events (q,fd);
queue_destroy (q);
close(fd);
return 0;
從上面的程式碼可看到,一開始我們先initial一個queue儲存inotify傳遞給我們的資料,並呼叫inotify_init取得inotify的file descriptor,接著我們把取得的file descriptor,和要監測的目錄及監測項目當作參數傳遞給inotify_add_watch這個函式,接著我們只要用process_inotify_events觀查檔案是否有變化,結束程式前記得把queue毀掉和把inotify file descriptor關閉
在/proc/sys/fs/inotify下有一些選項可供使用者調整
max_queued_events:最大可queue的事件
max_user_instances:inotify的最大使用量
max_user_watches:可觀測多少檔案
底下是完整的demo code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/file.h>
#include <sys/sysinfo.h>
#include <sys/epoll.h>
#include <signal.h>
#include <linux/inotify.h>
#include <net/if.h>
#include <netinet/in.h>
struct queue_struct
{
int capacity;
int front;
int rear;
int size;
void **array;
};
struct queue_struct;
typedef struct queue_struct *queue_t;
int queue_empty (queue_t q)
{
return q->size == 0;
}
int queue_full (queue_t q)
{
return q->size == q->capacity;
}
queue_t queue_create (int num_elements)
{
queue_t q;
q = malloc (sizeof (struct queue_struct));
if (q == NULL)
exit (-1);
q->array = malloc(sizeof (void *) * num_elements);
if (q->array == NULL)
exit (-1);
q->capacity = num_elements;
queue_make_empty (q);
return q;
}
void queue_destroy (queue_t q)
{
if (q != NULL)
{
if (q->array)
free (q->array);
free (q);
}
}
void queue_make_empty (queue_t q)
{
q->size = 0;
q->front = 1;
q->rear = 0;
}
static int next_position (int v, queue_t q)
{
if (++v == q->capacity) {
v = 0;
}
return v;
}
void queue_enqueue (void *d, queue_t q)
{
if (queue_full (q))
{
return;
}
q->size++;
q->rear = next_position (q->rear, q);
q->array[q->rear] = d;
}
void *queue_front (queue_t q)
{
if (!queue_empty(q))
return q->array [q->front];
return NULL;
}
void queue_dequeue (queue_t q)
{
if (!queue_empty (q))
{
q->size--;
q->front = next_position (q->front, q);
}
}
/* This method does the dirty work of determining what happened,
then allows us to act appropriately
*/
void handle_event (struct inotify_event *event)
{
/* If the event was associated with a filename, we will store it here */
char * cur_event_filename = NULL;
/* This is the watch descriptor the event occurred on */
int cur_event_wd = event->wd;
if (event->len)
{
cur_event_filename = event->name;
}
printf("FILENAME=%s\n", cur_event_filename);
printf("watch descriptor=%d\n",cur_event_wd);
printf("\n");
/* Perform event dependent handler routines */
/* The mask is the magic that tells us what file operation occurred */
switch (event->mask)
{
/* File was accessed */
case IN_ACCESS:
printf("ACCESS EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* File was modified */
case IN_MODIFY:
printf("MODIFY EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* File changed attributes */
case IN_ATTRIB:
printf("ATTRIB EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* File was closed */
case IN_CLOSE:
printf("CLOSE EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* File was opened */
case IN_OPEN:
printf("OPEN EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* File was moved from X */
case IN_MOVED_FROM:
printf("MOVE_FROM EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* File was moved to X */
case IN_MOVED_TO:
printf("MOVE_TO EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* Watched entry was deleted */
case IN_DELETE_SELF:
printf("DELETE_SELF EVENT OCCURRED: File \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
/* Backing FS was unmounted */
case IN_UNMOUNT:
printf("UNMOUNT EVENT OCCURRED: File \"%s\" on WD #%i\n",
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;
case IN_IGNORED:
printf("IGNORED EVENT OCCURRED: \n");
break;
/* Some unknown message received */
default:
printf ("UNKNOWN EVENT OCCURRED for file \"%s\" on WD #%i\n",
cur_event_filename, cur_event_wd);
break;
}
}
void handle_events (queue_t q)
{
struct inotify_event *event;
while (!queue_empty (q))
{
event = queue_front (q);
queue_dequeue (q);
handle_event (event);
free (event);
}
}
int event_check (int fd)
{
struct timeval timeout;
int r;
fd_set rfds;
timeout.tv_sec = 4;
timeout.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
r = select (fd+1, &rfds, NULL, NULL, &timeout);
return r;
}
int read_events (queue_t q, int fd)
{
char buffer[16384];
size_t buffer_i;
struct inotify_event *pevent, *event;
ssize_t r;
size_t 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 = sizeof(struct inotify_event) + pevent->len;
event = malloc(event_size);
memmove(event, pevent, event_size);
queue_enqueue(event, q);
buffer_i += event_size;
count++;
}
return count;
}
int process_inotify_events (queue_t q, int fd)
{
while (1)
{
if (!queue_empty(q))
{
handle_events (q);
}
if (event_check(fd) > 0)
{
int r;
r = read_events (q, fd);
if (r < 0)
break;
}
}
return 0;
}
int main()
{
int fd;
queue_t q;
int *wd;
q = queue_create (128);
fd=inotify_init();
wd = inotify_add_watch (fd, "/home/jemic/test/",IN_ATTRIB);
process_inotify_events (q,fd);
queue_destroy (q);
close(fd);
return 0;
}
留言