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 eitherfd
orSIGINT
;filter
that (still in your case) returns eitherEVFILT_VNODE
orEVFILT_SIGNAL
;fflag
that in theEVFILT_VNODE
will tell you if the file descriptor event wasNOTE_DELETE
orNOTE_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.