Используйте kqueue для ответа на более чем один тип события

Когда событие регистрируется с помощью kqueue, предоставляется идентификатор, относящийся к этому типу события; например, дескриптор файла используется для идентификации файла для просмотра

int kq;
struct kevent ke;

kq = kqueue();
fd = open(argv[1], O_RDONLY);
EV_SET(&ke, fd, EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME, 0, NULL);
kevent(kq, &ke, 1, NULL, 0, NULL);

while (1) {
    kevent(kq, NULL, 0, &ke, 1, NULL);
    /* respond to file system event */
}

Теперь, если мне также нужно реагировать на другие типы событий, такие как сигналы, нам нужен новый экземпляр kqueue, чтобы избежать конфликта с аргументом ident для kevent().

kq_sig = kqueue();
struct kevent ke_sig;

/* set the handler and ignore SIGINT */
signal(SIGINT, SIG_IGN);
EV_SET(&ke_sig, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
kevent(kq_sig, &ke_sig, 1, NULL, 0, NULL);
while (1) {
    kevent(kq_sig, NULL, 0, &ke_sig, 1, NULL);
    /* respond signals */
}

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

Есть ли более общий механизм отправки сообщения из одного потока в другой с помощью kqueue? В некоторых случаях я могу представить себе включение и отключение фильтра как средство запуска другого события по фронту.


person eradman    schedule 05.04.2013    source источник
comment
Но разве идентификатор не уникален, чтобы вы могли смешивать разные типы событий в одном и том же событии q? Или я неправильно понял ваш вопрос?   -  person dhein    schedule 19.08.2013
comment
Абсолютно верно; идентификатор должен быть уникальным, поскольку структура kevent не хранит тип события. Реагирование на один тип события — это очень простой цикл, который блокируется на kevent(), но обработка нескольких типов событий (VNODE и SIGNAL в моем примере) значительно сложнее, поскольку, по-видимому, требуется некоторая координация между потоками (или процессами).   -  person eradman    schedule 21.08.2013
comment
Хм, тогда меня нет, извините... но мне это понадобится и в будущем... Я просто подумал, что не будет никаких проблем с несколькими потоками или процессами, потому что идентификатор уникален в системном диапазоне . Но я буду следить за этим.   -  person dhein    schedule 21.08.2013
comment
Я не могу найти документы, но разве номер сигнала не должен быть в полях данных, а не идентификатор? этот EV_SET не имеет для меня смысла. Но я точно не знаю, мой код тоже не работает.   -  person Lothar    schedule 05.08.2015


Ответы (1)


Структура kevent фактически предоставляет информацию о произошедшем событии:

struct kevent {
         uintptr_t       ident;          /* identifier for this event */
         int16_t         filter;         /* filter for event */
         uint16_t        flags;          /* general flags */
         uint32_t        fflags;         /* filter-specific flags */
         intptr_t        data;           /* filter-specific data */
         void            *udata;         /* opaque user data identifier */
 };

Вам должно быть интересно:

  • ident, который в вашем случае возвращает либо fd, либо SIGINT;
  • filter, который (все еще в вашем случае) возвращает либо EVFILT_VNODE, либо EVFILT_SIGNAL;
  • fflag, который в EVFILT_VNODE сообщит вам, было ли событие файлового дескриптора NOTE_DELETE или NOTE_RENAME.

Вы можете зарегистрировать две структуры kevent в одной очереди, а затем использовать эти элементы структуры, чтобы определить, было ли событие связано с файловым дескриптором или сигналом.

Вот полный пример, демонстрирующий, как это сделать:

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>

int
main(int argc, char** argv)
{
    /* A single kqueue */
    int kq = kqueue();
    /* Two kevent structs */
    struct kevent *ke = malloc(sizeof(struct kevent) * 2);

    /* Initialise one struct for the file descriptor, and one for SIGINT */
    int fd = open(argv[1], O_RDONLY);
    EV_SET(ke, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME, 0, NULL);
    signal(SIGINT, SIG_IGN);
    EV_SET(ke + 1, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);

    /* Register for the events */
    if(kevent(kq, ke, 2, NULL, 0, NULL) < 0)
        perror("kevent");

    while(1) {
        memset(ke, 0x00, sizeof(struct kevent));
        if(kevent(kq, NULL, 0, ke, 1, NULL) < 0)
            perror("kevent");

        switch(ke->filter)
        {
            /* File descriptor event: let's examine what happened to the file */
            case EVFILT_VNODE:
                printf("Events %d on file descriptor %d\n", ke->fflags, (int) ke->ident);

                if(ke->fflags & NOTE_DELETE)
                    printf("The unlink() system call was called on the file referenced by the descriptor.\n");
                if(ke->fflags & NOTE_WRITE)
                    printf("A write occurred on the file referenced by the descriptor.\n");
                if(ke->fflags & NOTE_EXTEND)
                    printf("The file referenced by the descriptor was extended.\n");
                if(ke->fflags & NOTE_ATTRIB)
                    printf("The file referenced by the descriptor had its attributes changed.\n");
                if(ke->fflags & NOTE_LINK)
                    printf("The link count on the file changed.\n");
                if(ke->fflags & NOTE_RENAME)
                    printf("The file referenced by the descriptor was renamed.\n");
                if(ke->fflags & NOTE_REVOKE)
                    printf("Access to the file was revoked via revoke(2) or the underlying fileystem was unmounted.");
                break;

            /* Signal event */
            case EVFILT_SIGNAL:
                printf("Received %s\n", strsignal(ke->ident));
                exit(42);
                break;

            /* This should never happen */
            default:
                printf("Unknown filter\n");
        }
    }
}

Обратите внимание, что здесь мы используем один поток, который более эффективен и не требует дополнительной синхронизации в пользовательском пространстве.

person Menthos    schedule 28.08.2013