KQUEUE(9)

✨ KQUEUE(9) ✨

okay bestie, welcome to the kqueue page of the Kernel Developer's Manual πŸ’– this is the whole event delivery subsystem, and yes it's giving main character energy. the functions living here are kqueue_add_filteropts, kqueue_del_filteropts, kqfd_register, knote_fdclose, knlist_init, knlist_init_mtx, knlist_init_rw_reader, knlist_add, knlist_remove, knlist_remove_inevent, knlist_empty, knlist_clear, knlist_delete, knlist_destroy, KNOTE_LOCKED, and KNOTE_UNLOCKED. slay. let's get into it πŸ¦‹


🌸 SYNOPSIS 🌸

#include <sys/event.h>

int
kqueue_add_filteropts(int filt, struct filterops *filtops);

int
kqueue_del_filteropts(int filt);

int
kqfd_register(int fd, struct kevent *kev, struct thread *td,
    int waitok);

void
knote_fdclose(struct thread *td, int fd);

void
knlist_init(struct knlist *knl, void *lock, void (*kl_lock)(void *),
    void (*kl_unlock)(void *), int (*kl_locked)(void *));

void
knlist_init_mtx(struct knlist *knl, struct mtx *lock);

void
knlist_init_rw_reader(struct knlist *knl, struct rwlock *lock);

void
knlist_add(struct knlist *knl, struct knote *kn, int islocked);

void
knlist_remove(struct knlist *knl, struct knote *kn, int islocked);

void
knlist_remove_inevent(struct knlist *knl, struct knote *kn);

int
knlist_empty(struct knlist *knl);

void
knlist_clear(struct knlist *knl, int islocked);

void
knlist_delete(struct knlist *knl, struct thread *td, int islocked);

void
knlist_destroy(struct knlist *knl);

void
KNOTE_LOCKED(struct knlist *knl, long hint);

void
KNOTE_UNLOCKED(struct knlist *knl, long hint);

πŸ’… DESCRIPTION πŸ’…

πŸŽ€ adding and removing filter types πŸŽ€

so kqueue_add_filteropts() and kqueue_del_filteropts() are the ones that let you add and remove a filter type, periodt. the filter itself is statically defined by the EVFILT_* macros. kqueue_add_filteropts() is the girl that makes filt available. and the struct filterops has these members, bestie:

  • f_isfd β€” if f_isfd is set, then ident in struct kevent is taken to be a file descriptor. in that case, the knote passed into f_attach will have the kn_fp member initialized to the struct file * that represents the file descriptor. cute, right?
  • f_attach β€” f_attach gets called when attaching a knote to the object. the method should call knlist_add() to add the knote to the list that was initialized with knlist_init(). ngl, the call to knlist_add() is only necessary if the object can have multiple knotes associated with it. if there's no knlist to call knlist_add() with, then f_attach must clear the KN_DETACHED bit of kn_status in the knote. the function shall return 0 on success, or the appropriate error for the failure β€” like when the object is being destroyed, or does not exist. during f_attach, it is valid to change the kn_fops pointer to a different pointer. this will change the f_event and f_detach functions called when processing the knote. iconic.
  • f_detach β€” f_detach gets called to detach the knote if the knote has not already been detached by a call to knlist_remove(), knlist_remove_inevent() or knlist_delete(). heads up: the list lock will not be held when this function is called.
  • f_event β€” f_event gets called to update the status of the knote. if the function returns 0, it will be assumed that the object is not ready (or no longer ready) to be woken up. the hint argument will be 0 when scanning knotes to see which are triggered. otherwise, the hint argument will be the value passed to either KNOTE_LOCKED or KNOTE_UNLOCKED. the kn_data value should be updated as necessary to reflect the current value β€” such as number of bytes available for reading, or buffer space available for writing. if the note needs to be removed, knlist_remove_inevent() must be called. knlist_remove_inevent() will remove the note from the list, the f_detach function will not be called, and the knote will not be returned as an event.
🚨 okay important one: locks must not be acquired in f_event. if a lock is required in f_event, it must be obtained in the kl_lock function of the knlist that the knote was added to. no exceptions bestie πŸ’€

πŸ¦‹ kqfd_register and knote_fdclose πŸ¦‹

kqfd_register() will register the kevent on the kqueue file descriptor fd. if it is safe to sleep, waitok should be set. simple as that 🌈

knote_fdclose() is used to delete all knotes associated with fd. once it returns, there will no longer be any knotes associated with the fd. the knotes removed will never be returned from a kevent(2) call, so if userland uses the knote to track resources, they will be leaked. 😭 and the FILEDESC_LOCK() lock must be held over the call to knote_fdclose() so that file descriptors cannot be added or removed.


πŸ’œ the knlist_*() family πŸ’œ

the knlist_*() family of functions is for managing knotes associated with an object. a knlist is not required, but it's commonly used (iykyk). if used, the knlist must be initialized with either knlist_init(), knlist_init_mtx() or knlist_init_rw_reader(). the knlist structure may be embedded into the object structure. and the lock will be held over f_event calls. 🫢

for knlist_init(), if lock is NULL, a shared global lock will be used and the remaining arguments must be NULL. the function pointers kl_lock, kl_unlock and kl_locked will be used to manipulate the argument lock. if any of the function pointers are NULL, a function operating on MTX_DEF style mutex(9) locks will be used instead.

knlist_init_mtx() may be used to initialize a knlist when lock is a MTX_DEF style mutex(9) lock.

knlist_init_rw_reader() may be used to initialize a knlist when lock is a rwlock(9) read lock. lock is acquired via the rw_rlock() function.


✨ the rest of the list crew ✨

knlist_empty() returns true when there are no knotes on the list. the function requires that the lock be held when called. πŸ‘‘

knlist_clear() removes all knotes from the list. the islocked argument declares if the lock has been acquired. all knotes will have EV_ONESHOT set so that the knote will be returned and removed during the next scan. the f_detach function will be called when the knote is deleted during the next scan.

πŸ’€ do not use knlist_clear() when f_isfd is set in struct filterops, because the td argument of fdrop() will be NULL. that's a whole mess.

knlist_delete() removes and deletes all knotes on the list. the function f_detach will not be called, and the knote will not be returned on the next scan. using this function could leak userland resources if a process uses the knote to track resources. πŸ™ˆ

both knlist_clear() and knlist_delete() may sleep. they also may release the lock to wait for other knotes to drain. sleepy girls πŸŒ™

knlist_destroy() is used to destroy a knlist. there must be no knotes associated with the knlist (knlist_empty() returns true) and no more knotes may be attached to the object. a knlist may be emptied by calling knlist_clear() or knlist_delete().


🌟 KNOTE_LOCKED and KNOTE_UNLOCKED 🌟

the macros KNOTE_LOCKED() and KNOTE_UNLOCKED() are used to notify knotes about events associated with the object. they iterate over all knotes on the list calling the f_event function associated with the knote. KNOTE_LOCKED() must be used if the lock associated with the knl is held. KNOTE_UNLOCKED() will acquire the lock before iterating over the list of knotes. ate. πŸ”₯


πŸŽ€ RETURN VALUES πŸŽ€

  • kqueue_add_filteropts() returns zero on success, EINVAL in the case of an invalid filt, or EEXIST if the filter has already been installed.
  • kqueue_del_filteropts() returns zero on success, EINVAL in the case of an invalid filt, or EBUSY if the filter is still in use.
  • kqfd_register() returns zero on success, EBADF if the file descriptor is not a kqueue, or any of the possible values returned by kevent(2).

🫢 SEE ALSO 🫢

kevent(2), kqueue(2)


πŸ’– AUTHORS πŸ’–

this manual page was written by John-Mark Gurney <jmg@FreeBSD.org>. thank you legend πŸ™


and that's the whole page bestie, no cap πŸ’… sending you off with love β€” this one came from the Debian crew, dated March 26, 2012. go forth and deliver those events, you got this βœ¨πŸŒΈπŸ¦‹

πŸ“¦ where this page comes from