|
05 30 |
強大的檔案控管機制-Inotify作者: Joey 日期: 2008-05-30 10:45 |
在講到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
從上面的程式碼可看到,一開始我們先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
什麼是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;
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;
}
#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;
}
評論: 4 |
引用: 0 |
閱讀: 3677
| 安,你好, 逛你的blog學到不少東西, 我剛入門沒多久 ^^" 想請問一個問題: 如果沒有用 cat xxx >> /home/jemic/test/ echo xxx >> /home/jemic/test/ vim 等類的方法來修改被監控的檔案,是不是inotify就不會有所動作@@, 我拿你的code改了一個地方 wd = inotify_add_watch (fd, "/home/jemic/test/",IN_ATTRIB) 改成 wd = inotify_add_watch (fd, "/home/jemic/test/",IN_ALL_EVENTS) ( 看不太懂IIN_ATTRIB的意思,可以請幫我解說一下嗎?3Q ) 我想請問,如果我想監控 /proc/acpi/battery/BAT1/state (筆電的電池容量) 因為筆電沒充電的話,大慨每隔幾秒,cat 這個檔案就會更動, 想知道可以用inotify這個方法來監控嗎?謝謝你 |
| Hi 我記得in_attrib是檔案的屬性諸如日期,讀寫狀態...等 監控/proc下面的資料可能有點難,因為proc運作的原理是 "當你去讀proc裡面的資料時, 資料才會更新",proc資料並不是一有變動 就會自動更新,所以inotify用在proc似乎不太適合 |
發表評論
訂閱
上一篇
返回
下一篇
標籤:


Regular expression-跟brainfuck差不多的東西 (2009-11-13 15:37)
Reading file in kernel-簡單但實用 (2009-10-13 15:18)
Linux file system for dummies-只花你45分鐘 (2009-08-19 15:40)
OPENSSL-TCP SSL初心者之路 (2009-07-16 15:16)
NAPI與pure interrupt driver的效能比較 (2009-04-29 19:06)
usermode helper-來自kernel的呼喚 (2009-04-21 16:19)
kernel module memory detector-抓出有害的kernel module (2009-03-31 13:50)
kernel space coding-如履薄冰 (2009-03-26 09:52)
readahead與posix_advise-預讀取是萬能靈丹? (2009-03-06 15:54)