在講到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;
}
最後修改日期: 3 6 月, 2022

作者

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。