The documentation of kqueue is quite decent but it lacks some examples. After reading its main manual pages (kqueue(9) and kevent(9)), I wasn't sure about how it worked, so I had to write a test program to verify its behavior.
Let's start by analyzing the test program to later see its full code. The program will monitor changes to the /tmp/foo file and will print messages whenever it is deleted, modified or their attributes change. The program finishes when the file being monitoring is deleted.
The steps to use kqueue are the following:
- Call kqueue(2) to create a new kernel event queue. The descriptor it returns will be later used by kevent(2).
- Open the file to monitor and keep its descriptor around. We'll need this to attach an event monitor to it.
- Initialize a vector of struct kevent elements that describes the changes to monitor. Since we are only monitoring a single file, we need a one-element vector. This vector is filled up with calls to the EV_SET macro. This macro takes: the descriptor of the kqueue, the descriptor of the file to monitor (ident), the filter to apply to it, several flags and optional arguments to the filter. Note that an entry in this table is identified by its ident/filter pair.
- Call the kevent(2) function. This system call takes the list of changes to monitor we constructed before and does not return until at least one event is received (or when an associated timeout is exhausted). The function returns the number of changes received and stores information about them in another vector of struct kevent elements (we'll only get notifications of one event at a time, hence we don't use a vector, but a simple variable).
- Interpret the results. If kevent(2) returned a number greater than 0, we have to inspect the output vector and see which events were received. Each filter has its semantics about the results. For example, we are using the EVFILT_VNODE filter, which takes a list of conditions to monitor in the fflags field and modifies it to include only the conditions that triggered the filter.
With these concepts clear and with help of the manual pages, you should be able to interpret the following code easily:
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
int f, kq, nev;
struct kevent change;
struct kevent event;
kq = kqueue();
if (kq == -1)
perror("kqueue");
f = open("/tmp/foo", O_RDONLY);
if (f == -1)
perror("open");
EV_SET(&change, f, EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_ONESHOT,
NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
0, 0);
for (;;) {
nev = kevent(kq, &change, 1, &event, 1, NULL);
if (nev == -1)
perror("kevent");
else if (nev > 0) {
if (event.fflags & NOTE_DELETE) {
printf("File deleted\n");
break;
}
if (event.fflags & NOTE_EXTEND ||
event.fflags & NOTE_WRITE)
printf("File modified\n");
if (event.fflags & NOTE_ATTRIB)
printf("File attributes modified\n");
}
}
close(kq);
close(f);
return EXIT_SUCCESS;
}
Now compile and run the program in one terminal. In another one, modify the /tmp/foo file and see how our test program shows the events! If you delete the file, the program will terminate. (Note that we are not monitoring all possible events; we'd watch for file renames, as well as other conditions if we needed to).
22 comments:
[Originally posted at 2004-10-21 14:45 pm UTC]
I found this example helpful. Many thanks for posting it!
[Originally posted at 2004-11-24 12:14 pm UTC]
Nice example for first-time kevent/kqueue users such as myself.
[Originally posted at 2004-12-12 20:58 pm UTC]
Very useful. :)
[Originally posted at 2005-02-24 16:03 pm UTC]
Very very usefull :)
[Originally posted at 2005-05-23 14:24 pm UTC]
Always useful to have the simplest of examples for newbies such as myself.
A great and clear example. It gave me enough info to make a Mac OS X Cocoa class that creates and manages hot folders :-)
Thanks, Simon. But... are you sure that Cocoa does not provide you with a higher-level API to monitor files? It feels strange to me that you have to go with the BSD API from inside a Cocoa program.
If you're monitoring a folder, how can you tell WHICH file was added? I can receive a notification that the folder changed, but nothing to indicate which file was added or deleted. Or do I have to do a full directory scan every time I get the notification?
Mike: Not that I know of, but I haven't checked the documentation to see if I'm right.
Hi,I am getting these error while compilation...
error: `uintptr_t' does not name a type
error: `u_short' does not name a type
error: `u_int' does not name a type
error: 'struct kevent' has no member named 'ident'
error: 'struct kevent' has no member named 'flags'
plz help me...
Excellent post! Thanks a lot!
Thanks Julio. Very helpful example.
Chris, it sounds like you are not including some required header files. The example in Julio's post was for a particular system. On my system (Mac OS X), I have to include sys/types.h, sys/event.h, and sys/time.h. I suggest you run "man kqueue" from the command line to see what header files you need on your system.
Hooray, it works. Thanks!
Hi, I would like to be notified of changes made within a folder but not have to rescan the folder to get an updated listing everytime I am notified of a change.
Is there a method using Kqueues by which I can monitor a folder and then, when say, a file is added retrieve that specific info? i.e, "file x added in folder y" as opposed to just getting "something happened in folder y"
Many thanks.
Anonymous: Please ask NetBSD's tech-kern@ mailing list.
Thanks Julio
Hi, I am using kqueues and am after some advice on the following.
I initialize an array of kevent structs using EV_SET and set kevent to wait for events.
I then initialise a new kevent struct for another directory and add it to the original array.
I then need to reload my array into the waiting kevent call.
I would like to avoid using a timer as I do not want my program looping round and round.
Is there a way to either: post a dummy message to the queue, or to cancel the waiting kevent call so that I can reload my array when I decide to?
Many thanks.
"Is there a way to either: post a dummy message to the queue, or to cancel the waiting kevent call so that I can reload my array when I decide to?"
You can create a dummy UDP socket and add a _disabled_ write filter. Whenever you want to wake up your kqueue thread, just enable the write filter (then disable it again when it wakes up). That's what I do.
Thank you !!
Many thanks for posting it!
How can I monitor change of directory tree( Current directory and all children ) ?
This was really useful.
It would be really great if you can remove typo from line
"nev = kevent(kq, &change, 1, &event, 1, NULL);"
Thanks.
Post a Comment