使用kqueue响应多个事件类型

时间:2013-04-05 20:41:06

标签: kqueue

当事件向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 */
}

现在,如果我还需要响应其他事件类型这样的信号,我们需要一个新的 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?在某些情况下,我可以设想启用和禁用过滤器作为 边缘触发另一个kevent的手段。

1 个答案:

答案 0 :(得分: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在您的情况下返回fdSIGINT;
  • filter(仍在您的情况下)返回EVFILT_VNODEEVFILT_SIGNAL;
  • fflag EVFILT_VNODE中的NOTE_DELETE会告诉您文件描述符事件是NOTE_RENAME还是#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"); } } }

您可以将两个kevent结构注册到单个队列,然后使用这些结构成员来确定事件是否与文件描述符或信号相关。

以下是演示如何执行此操作的完整示例:

{{1}}

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