Jonathan Lemon designed kqueue so Unix programs could register what to watch and receive notifications of activity

Illustrative photograph of an HP Visualize PC workstation tower on a neutral white background


In FreeBSD 4.1, Jonathan Lemon’s kqueue could combine repeated event triggers into a single notification. Its manual, dated April 14, 2000, made that behavior explicit: an application could learn that a condition needed attention without receiving a separate record of every occurrence.

Lemon wanted to address two limitations of existing Unix event handling: scaling as programs watched more descriptors, and reporting activity beyond sockets.

Kqueue let applications register the conditions they cared about and collect notifications through one interface. That design came with a distinction developers needed to understand: a notification could summarize repeated activity rather than preserve a chronological history.

A problem broader than readable sockets

In the abstract of his 2001 FREENIX paper, Kqueue: A generic and scalable event notification facility, Lemon described the limitations he wanted to address. Unix applications traditionally used the select() or poll() system calls to wait for activity on sockets or other descriptors. A descriptor is a handle through which a program refers to an open resource.

Lemon identified scaling with increasing descriptor counts as one problem. The range of events was another: applications could also care about signals, filesystem changes and the completion of asynchronous input/output, where an operation finishes after it has been submitted.

His abstract proposed a notification mechanism covering different event sources, with room to add sources without changing the application interface. The paper’s year should not be mistaken for the interface’s debut: the historical manual says kqueue() and kevent() first appeared in FreeBSD 4.1.

Register an interest, then collect notifications

The basic arrangement has two calls: kqueue() creates a kernel event queue and returns its descriptor; kevent() registers interests with that queue and retrieves pending notifications. The kernel is the operating system’s core; it evaluates the conditions being watched.

A registration combines an identifier with a filter. The identifier names what to watch, often through a file descriptor. The filter determines which condition matters. The historical manual describes filters for readable data, writable resources, file changes, process activity and signals.

For example, consider an application watching an open socket for readable data. It registers that socket with the read filter. When the filter determines that the condition should be reported, a notification becomes available for retrieval.

The application can submit registration changes and collect results in the same kevent() call. The manual specifies the order: all submitted changes are applied before pending events are read. Re-adding the same identifier-and-filter pair modifies the existing registration rather than creating a duplicate.

One notification can summarize repeated activity

The queue’s name can invite an assumption that every trigger becomes another entry. The documented contract says otherwise: multiple events triggering a filter are aggregated into one kevent structure.

Aggregation does not mean the returned information is always a bare yes or no. Its meaning depends on the filter. For sockets, the historical read-filter documentation describes a data field containing the number of bytes in the socket buffer. Current OpenBSD documentation describes counts for signal occurrences and timer expirations.

Those fields answer specific questions. An accumulated count is still different from separate records showing each occurrence in order. An application requiring that history must account for the distinction when choosing how to record its own activity.

Kqueue also checks whether a notification remains relevant. The filter runs when an interest is first registered, when relevant activity occurs and again when the application retrieves the notification. If the triggering condition no longer holds at retrieval, the manual says the notification is removed instead of returned.

Delivery rules are part of the design

The historical interface includes controls for the lifetime of a registration. EV_ONESHOT deletes it after the application retrieves its notification. EV_CLEAR resets its state after retrieval, which the manual describes as useful for filters reporting state transitions.

These choices change how later activity is reported. A developer therefore needs to read the contract for the selected filter and flags together. Knowing that a queue contains an event does not, by itself, explain what the application will receive or what happens afterward.

There are practical limits too. The kevent() call returns at most as many records as fit in the output array supplied by the caller. Creating a queue or registering an event can fail because memory or descriptor resources are exhausted. A common interface still requires error handling.

A shared interface with system-specific details

The interface spread beyond FreeBSD. OpenBSD’s manual records availability since OpenBSD 2.9; NetBSD’s records its arrival in NetBSD 2.0. Their documentation retains the central registration, aggregation and retrieval model.

Individual capabilities differ. NetBSD’s current manual explicitly marks its asynchronous input/output filter as unimplemented. A filter name in one system’s documentation is therefore insufficient evidence that another system supports the same operation.

The useful mental model starts with the application’s question: which registered condition needs attention? In Lemon’s documented interface, repeated triggers can become one notification, and retrieval checks the condition again. The program learns which condition needs attention, not a full history of what happened.

Related stories

Sources

Share this article
FacebookThreadsLinkedInX

Leave a Reply

Your email address will not be published. Required fields are marked *