Очереди ядра: альтернатива событиям файловой системы

API очередей ядра обеспечивает путь к приложению для получения уведомлений каждый раз, когда данный файл или каталог изменяется всегда, включая изменения в содержании файла, атрибутах, имени или длине. Ваше приложение может также получить уведомление, если Вы наблюдаете блочное устройство или устройство посимвольного ввода-вывода, и доступ отклоняется через вызов к revoke.

API очередей ядра также обеспечивает способ контролировать дочерние процессы и узнать, когда они вызывают exit, fork, exec, и т.д. Это использование очередей ядра выходит за рамки этого документа. Для получения дополнительной информации об очередях ядра и процессах, необходимо считать документацию FreeBSD для очередей ядра. Можно найти ссылки к этой документации в http://people .freebsd.org/~jmg/kq.html.

Выбор механизма события

События файловой системы предназначаются для обеспечения уведомления об изменениях с гранулярностью уровня каталога. В большинстве целей это достаточно. В некоторых случаях, однако, Вы, возможно, должны получить уведомления с более прекрасной гранулярностью. Например, Вы, возможно, должны были бы наблюдать только изменения, сделанные к единственному файлу. С этой целью очередь ядра (kqueue) система уведомления является более соответствующей.

При контроле большой иерархии содержания необходимо использовать события файловой системы вместо этого, однако, потому что очереди ядра являются несколько более сложными, чем события ядра и могут быть более интенсивно использующими ресурсы из-за дополнительной включенной коммуникации пользовательского ядра.

Используя очереди ядра

Очереди ядра (kqueue) и события ядра (kevent) механизм чрезвычайно мощны и гибки, позволяя Вам получить поток событий уровня ядра (включая модификации файла) и определить ряд фильтров, ограничивающих, какие события поставлены Вашему приложению.

Для использования очередей ядра необходимо сделать четыре вещи:

Краткий пример

Перечисление a-1 является кратким примером, показывающим, как контролировать единственный файл с помощью очередей ядра. Для более сложного примера, контролирующего каталоги, смотрите на пример кода FileNotification.

  Часы перечисления a-1 файл Используя очереди ядра

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <inttypes.h>
 
#define NUM_EVENT_SLOTS 1
#define NUM_EVENT_FDS 1
 
char *flagstring(int flags);
 
int main(int argc, char *argv[])
{
    char *path = argv[1];
    int kq;
    int event_fd;
    struct kevent events_to_monitor[NUM_EVENT_FDS];
    struct kevent event_data[NUM_EVENT_SLOTS];
    void *user_data;
    struct timespec timeout;
    unsigned int vnode_events;
 
    if (argc != 2) {
        fprintf(stderr, "Usage: monitor <file_path>\n");
        exit(-1);
    }
 
    /* Open a kernel queue. */
    if ((kq = kqueue()) < 0) {
        fprintf(stderr, "Could not open kernel queue.  Error was %s.\n", strerror(errno));
    }
 
    /*
       Open a file descriptor for the file/directory that you
       want to monitor.
     */
    event_fd = open(path, O_EVTONLY);
    if (event_fd <=0) {
        fprintf(stderr, "The file %s could not be opened for monitoring.  Error was %s.\n", path, strerror(errno));
        exit(-1);
    }
 
    /*
       The address in user_data will be copied into a field in the
       event.  If you are monitoring multiple files, you could,
       for example, pass in different data structure for each file.
       For this example, the path string is used.
     */
    user_data = path;
 
    /* Set the timeout to wake us every half second. */
    timeout.tv_sec = 0;        // 0 seconds
    timeout.tv_nsec = 500000000;    // 500 milliseconds
 
    /* Set up a list of events to monitor. */
    vnode_events = NOTE_DELETE |  NOTE_WRITE | NOTE_EXTEND |                            NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE;
    EV_SET( &events_to_monitor[0], event_fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, vnode_events, 0, user_data);
 
    /* Handle events. */
    int num_files = 1;
    int continue_loop = 40; /* Monitor for twenty seconds. */
    while (--continue_loop) {
        int event_count = kevent(kq, events_to_monitor, NUM_EVENT_SLOTS, event_data, num_files, &timeout);
        if ((event_count < 0) || (event_data[0].flags == EV_ERROR)) {
            /* An error occurred. */
            fprintf(stderr, "An error occurred (event count %d).  The error was %s.\n", event_count, strerror(errno));
            break;
        }
        if (event_count) {
            printf("Event %" PRIdPTR " occurred.  Filter %d, flags %d, filter flags %s, filter data %" PRIdPTR ", path %s\n",
                event_data[0].ident,
                event_data[0].filter,
                event_data[0].flags,
                flagstring(event_data[0].fflags),
                event_data[0].data,
                (char *)event_data[0].udata);
        } else {
            printf("No event.\n");
        }
 
        /* Reset the timeout.  In case of a signal interrruption, the
           values may change. */
        timeout.tv_sec = 0;        // 0 seconds
        timeout.tv_nsec = 500000000;    // 500 milliseconds
    }
    close(event_fd);
    return 0;
}
 
/* A simple routine to return a string for a set of flags. */
char *flagstring(int flags)
{
    static char ret[512];
    char *or = "";
 
    ret[0]='\0'; // clear the string.
    if (flags & NOTE_DELETE) {strcat(ret,or);strcat(ret,"NOTE_DELETE");or="|";}
    if (flags & NOTE_WRITE) {strcat(ret,or);strcat(ret,"NOTE_WRITE");or="|";}
    if (flags & NOTE_EXTEND) {strcat(ret,or);strcat(ret,"NOTE_EXTEND");or="|";}
    if (flags & NOTE_ATTRIB) {strcat(ret,or);strcat(ret,"NOTE_ATTRIB");or="|";}
    if (flags & NOTE_LINK) {strcat(ret,or);strcat(ret,"NOTE_LINK");or="|";}
    if (flags & NOTE_RENAME) {strcat(ret,or);strcat(ret,"NOTE_RENAME");or="|";}
    if (flags & NOTE_REVOKE) {strcat(ret,or);strcat(ret,"NOTE_REVOKE");or="|";}
 
    return ret;
}

Для получения дополнительной информации

Для получения дополнительной информации об очередях ядра, см. страницу руководства для kqueue), пример кода FileNotification и документация FreeBSD для очередей ядра в http://people .freebsd.org/~jmg/kq.html..