When an event is registered with kqueue
an ID relating to that event
type is supplied; for example a file descriptor is used to identify a file to
watch
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 */
}
Now if I also need to respond to other event types such signals we need a new
instance of kqueue so as to avoid a conflict with the ident
argument of
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 */
}
Watching more than one event type appears to necessitate multiple threads that act on shared state (receiving a signal could close a file descriptor for example).
Is there a more general mechanism for sending a message from one thread to another using kqueue? In some cases I can conceive of enabling and disabling a filter as a means of edge-triggering another kevent.
The kevent struct actually provides info about the event that occured:
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 */
};
You must be interested in:
ident
that in your case returns either fd
or SIGINT
;filter
that (still in your case) returns either EVFILT_VNODE
or EVFILT_SIGNAL
;fflag
that in the EVFILT_VNODE
will tell you if the file descriptor event was NOTE_DELETE
or NOTE_RENAME
.You can register two kevent structures to a single queue and then use these structure members to determine if the event was related to a file descriptor or a signal.
Here is a complete example that demonstrates how to do this:
#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");
}
}
}
Note that here we use a single thread, which is way more efficient and requires no further synchronization in the user space.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With