Linux: File Descriptor Evolution

There's been a quiet evolution going on in the widening use of file descriptors in the Linux world.

In large part because of the recent popularity of the "event driven" model as provided by popular libraries such as libevent and libev, more and more common activities are now being afforded access mechanisms via file descriptors. The unifying paradigm here, which harkens back to the original days of event-driven programming, is that we can now use a single-threaded process that blocks waiting on events from an ever-expanding gallery of choices. Many people find such event-driven processing, utilizing call-back functions, far simpler than using threads and traditional signals.

Here are the successful areas so far:

There was also a notable failure to provide the same mechanism for futexes; up to Linux 2.6.25, it was possible to use FUTEX_FD to monitor a futex, but that was removed from 2.6.26 onward. Instead, I would recommend looking at perhaps the new process-defined events to see if these might be of some use.

Another file descriptor-based approach was dnotify, a predecessor to inotify. It's now obsolete, since inotify covers the same territory with a cleaner interface.

There is also significant kernel development going on with the Linux fanotify system. The idea with fanotify is to receive notifications for simple file accesses, something of a subset of the inotify idea and using the same methodology of receiving notifications via a file desciptor. The motivating factor here has been the needs of the malware-fighting community, who have been using less savory methods to accomplish such monitoring.

For more on fanotify, see here. For an idea of what fanotify code might look like, look at the example code here.

An intriguing variation on the idea of using file descriptor availability is that of /dev/mem_notify. Rather than adding yet another file descriptor-based system call, though, the idea here is to simply create a device called /dev/mem_notify which becomes readable when the system begins to experience low memory. See here and here for more about /dev/mem_notify.

Signals and signalfd(2)

From a C programmer's viewpoint, the asynchronous nature of Linux signals has been a historically troublesome area. From having to handle system calls that might also have woken because of a signal rather than the expected completion to having to worry about the safety of memory operations during the handling of an asynchronous signals, signal handling as always had issues that have complicated their use.

Use of signalfd(2) to take delivery of signals whenever it is convenient removes many of these concerns.

Instead of having to write odd little signal handlers and handling random EINTR returns from system calls, now you can write a simple event handler for signals that you want to handle.

Contrast these two blocks of code, both of which handle SIGUSR1

int signal_file_descriptor; // the file descriptor where we receive signals
int main()
{
  sigset_t signal_mask;

  sigemptyset(&signal_mask);
  sigaddset(&signal_mask, SIGUSR1);
  signal_file_descriptor = signalfd(-1,&signal_mask,0);
  if(signal_file_descriptor == -1)
    {
      perror("signalfd()");
      exit(1);
    }

  // libevent stuff
  struct event event_info;
  event_init();
  event_set(&event_info, signal_fd, EV_READ, handle_sigusr1, &event_info);
  event_dispatch();
}

int handle_sigusr1()
{
}

Contrast that with the old, asynchronous notification regimen:


Timers and timerfd_create(2)

One of the more vexing signals to have to worry about was good old SIGALRM, which had to be controlled by a complex set-up of setitimer(2) and sigaction(2). Now a much simpler mechanism using timerfd_create(2) exists.

Filesystem events and inotify(2)

If you have ever wanted to watch for modifications in a directory, you will appreciate the elegance of the new inotify(2) system. Now if a change is made to a directory, that event can magically be noticed by monitoring a file descriptor.

User-defined events and eventfd(2)

Finally, there are always your own events. You can use the simple eventfd(2) for a lightweight method of propagating notifications.