Skip to content

Linux fasync / SIGIO / eventfd Notes

Overview

This note summarizes Linux asynchronous notification mechanisms used with character device drivers.

Topics:

  • blocking read
  • poll / epoll
  • fasync / SIGIO
  • eventfd
  • userspace event loop integration

1. Blocking Read Model

Traditional blocking read flow:

userspace
read()
sleep on wait queue
driver wake_up_interruptible()
read() returns

Characteristics:

  • simple design
  • one thread per blocking operation
  • difficult to scale for many event sources

2. poll / epoll Model

poll/epoll provides fd-based event multiplexing.

Flow:

userspace
epoll_wait()
driver .poll()
wait queue wakeup
epoll event ready
read()

Advantages:

  • scalable
  • multiple fd integration
  • deterministic event loop
  • preferred Linux event-driven architecture

3. fasync / SIGIO

Linux supports asynchronous signal notification through:

.fasync
kill_fasync()
SIGIO

Userspace enables async notification using:

fcntl(fd, F_SETOWN, getpid())
fcntl(fd, F_SETFL, flags | O_ASYNC)

Driver event flow:

event generated
queue push
wake_up_interruptible()
kill_fasync()
SIGIO delivered

Important:

  • SIGIO is only a notification
  • event data is still stored inside the driver queue
  • userspace must still call read()

4. struct fasync_struct

Drivers maintain async listeners using:

struct fasync_struct *async_queue;

Kernel helper:

fasync_helper()

Signal delivery:

kill_fasync()

Typical driver release cleanup:

mypoll_fasync(-1, file, 0);

5. Why Signal Handler Should Stay Minimal

Signal handlers execute asynchronously.

Unsafe operations inside signal handler may cause:

  • deadlock
  • reentrancy problems
  • libc internal corruption
  • race conditions

Avoid inside signal handler:

  • printf()
  • malloc()
  • mutex
  • complex parsing

Recommended pattern:

signal handler
set flag or write(eventfd)
main loop handles real work

6. eventfd

eventfd is a lightweight kernel event counter object exposed as a file descriptor.

Create:

eventfd(0, EFD_NONBLOCK)

Write:

uint64_t value = 1;
write(eventfd, &value, sizeof(value));

Read:

uint64_t counter;
read(eventfd, &counter, sizeof(counter));

Characteristics:

  • fd-based
  • epoll compatible
  • aggregation counter
  • not a message queue

7. eventfd Counter Behavior

eventfd accumulates notifications.

Example:

write(1)
write(1)
write(1)

Single read:

counter = 3

After read:

counter resets to 0

eventfd stores:

pending notification count

NOT:

message content

8. SIGIO + eventfd Bridge

Recommended architecture:

/dev/mypoll
SIGIO
signal handler
write(eventfd)
epoll_wait()
drain /dev/mypoll

Advantages:

  • signal-safe
  • epoll-friendly
  • scalable event loop
  • unified event dispatch

9. Linux Event Architecture Philosophy

Linux commonly converts event sources into file descriptors:

  • socket
  • timerfd
  • signalfd
  • eventfd
  • char device
  • pipe

This allows:

epoll_wait()

to manage heterogeneous event sources in a unified event loop.


10. Comparison Summary

Mechanism Purpose
wait queue kernel sleep/wakeup
poll/epoll fd event multiplexing
SIGIO asynchronous signal notify
eventfd fd-based event counter
POSIX MQ message transport
pipe/socket stream/message IPC

11. Key Takeaways

  • SIGIO is notification only
  • event data still lives in driver queue
  • eventfd is ideal for signal-safe wakeup
  • epoll is the center of Linux event-driven architecture
  • Linux prefers fd-based event aggregation