From owner-freebsd-questions Wed Sep 27 15:21:55 2000 Delivered-To: freebsd-questions@freebsd.org Received: from monkeys.com (236.dsl9226.rcsis.com [63.92.26.236]) by hub.freebsd.org (Postfix) with ESMTP id 3CC7C37B422; Wed, 27 Sep 2000 15:21:25 -0700 (PDT) Received: from monkeys.com (localhost [127.0.0.1]) by monkeys.com (8.9.3/8.9.3) with ESMTP id PAA24599; Wed, 27 Sep 2000 15:21:24 -0700 (PDT) To: freebsd-questions@freebsd.org, freebsd-net@freebsd.org Subject: An (simple) example multi-client server using kqueue/kevent. From: "Ronald F. Guilmette" Date: Wed, 27 Sep 2000 15:21:24 -0700 Message-ID: <24597.970093284@monkeys.com> Sender: owner-freebsd-questions@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG [[This message doesn't contain a question, so maybe I shouldn't be cross- posting it to freebsd-questions, but I hope that this info may help some folks who have questions about usage of the new kqueue/kevent stuff.]] Recently, I became interested in using the new kqueue/kevent kernel primitives for a multi-client server I wanted to build. I started reading up on these new kernel functions and found that not only are they likely to be vastly more efficient than the alternatives (i.e. poll(2) and select(2)) but also, that they allow for a cleaner program design, due to the availability of an extra data field (udata) in each kqueue structure that can be used (by the application programmer) as a hook to get you vectored off to some appropriate handler routine when some specific event you are monitoring occurs. The only drawback seemed to be that I could find no good and/or complete working example of a multi-client server which used these new kernel functions or which clarified, in concrete terms, exactly what proper usage of these functions would look like. Fortunately, the fellow who designed and implemented a lot (all?) of this kqueue/kevent stuff in the kernel, Jonathan Lemon , was kind enough to converse with me, at length, via e-mail, about correct usage, so now I feel that I have a pretty good handle on how to use this stuff. Just to be sure of that however, I wrote up a trivial example multi-client network server... i.e. an ``echo'' server... and asked Jonathan to take a look at it and to send me a critique, which he obligingly did. He suggested some minor changes to the code, which I then implemented. For those of you who may be interested, the resulting example echo server may be viewed at: http://www.monkeys.com/kqueue/echo.c Some notes about the program: o I ran it and it actually seemed to work and to do what was intended. (Surprisingly, it did so the first time I actually ran it!) o Please be aware that I write code in a style which is mostly the GNU/Stallman coding style, except that I write declarations in a funny way, i.e. with const and volatile modifiers always placed as far to the right in a declaration as possible while maintaining the same semantic intent. Whereas most C coders will write `const int *', I write `int const *' instead. Both are legal C and both are semantically equivalent, but I like the former style because it allows me to read declarations right-to-left and get something that makes sense, even in English, e.g "pointer to constant integer". My apologies if my style confuses you. o The `struct ecb' type, defined in the program, is my ``event control block''. It just contains the read and write function pointers associated with some specific client of the server, along with some ancillary data, specifically, a pointer to the base of, and the length of a currently pending output buffer. Pointers to a given `ecb' thingy are used, within this server as the `udata' field of two different kevent structures, i.e. one which asks for monitoring of readability of the relevant socket, and another which asks for monitoring of writability of that same socket. o Within my event loop, I never check for either of the conditions EV_ERROR or EV_EOF. Rather, I allow the primitive socket read and write handler functions check for error and EOF conditions themselves. According to Jonathan, this approach may be sub- optimal and it would probably be better to check for EV_ERROR within the event loop (but checking for EOF in the read/write functions is entirely sensible). o Even after talking to Johnathan about this, it still isn't 100% clear whether it is better to EV_DELETE a filter that is monitor- ing a condition that you do not wish to pay attention to right now or if it is better to instead just EV_DISABLE it. In my example echo server, I use EV_DISABLE. Please send comments or critiques of the echo.c server to . Some notes about kevent/kqueue in general: o There doesn't seem to be any way, at present, to ask to be alerted ONLY when you can immediately write N bytes to some specified file descriptor *without blocking*. I have mentioned this to Johnathan, and he seemed to agree that this is a problem, and one which may require some small extension to the existing kqueue/kevent semantics in order to provide this additional functionality. o It is somewhat unfortunate that the name of the data structure used by these primitives (struct kevent) is the same as the name of one of the functions provided by the interface. The implication is that you can write: typedef struct kevent kevent; in your own code, if you want to be able to refer to the type tersely, but if you do that, you will thereafter get compile-time syntax errors where you attempt to call the kevent *function*. o If one is REALLY obeying all rules of the ANSI/ISO C standard, then one MAY NOT assign a pointer to any function to the `udata' field of the kevent structure. According to strict ANSI/ISO C rules (which will be applied by gcc when/if you use the -pedant-errors option) pointers to functions and pointers to data may NEVER be mixed in any way. I've mentioned this to Johanathan and suggested that he change the type of `udata' to be some union type that can hold either a pointer to some data item or a pointer to some function. To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-questions" in the body of the message