6

当使用kqueue与该事件类型相关的 ID 注册事件时;例如,文件描述符用于标识要监视的文件

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 */
}

ident现在,如果我还需要响应其他事件类型这样的信号,我们需要一个新的kqueue 实例,以避免与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 从一个线程向另一个线程发送消息?在某些情况下,我可以设想启用和禁用过滤器作为边缘触发另一个 kevent 的一种方式。

4

1 回答 1

7

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在您的情况下返回fdor SIGINT;
  • filter那(仍然是你的情况)返回EVFILT_VNODEEVFILT_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");
        }
    }
}

请注意,这里我们使用单线程,这样效率更高,并且不需要在用户空间中进一步同步。

于 2013-08-28T11:09:01.083 回答