next up previous
Next: Why use FUSD? Up: FUSD: A Linux Framework Previous: FUSD: A Linux Framework

Subsections

Introduction

What is FUSD?

FUSD (pronounced fused) is a Linux framework for proxying device file callbacks into user-space, allowing device files to be implemented by daemons instead of kernel code. Despite being implemented in user-space, FUSD devices can look and act just like any other file under /dev which is implemented by kernel callbacks.

A user-space device driver can do many of the things that kernel drivers can't, such as perform a long-running computation, block while waiting for an event, or read files from the file system. Unlike kernel drivers, a user-space device driver can use other device drivers--that is, access the network, talk to a serial port, get interactive input from the user, pop up GUI windows, or read from disks. User-space drivers implemented using FUSD can be much easier to debug; it is impossible for them to crash the machine, are easily traceable using tools such as gdb, and can be killed and restarted without rebooting even if they become corrupted. FUSD drivers don't have to be in C--Perl, Python, or any other language that knows how to read from and write to a file descriptor can work with FUSD. User-space drivers can be swapped out, whereas kernel drivers lock physical memory.

Of course, as with almost everything, there are trade-offs. User-space drivers are slower than kernel drivers because they require three times as many system calls, and additional memory copies (see section 10). User-space drivers can not receive interrupts, and do not have the full power to modify arbitrary kernel data structures as kernel drivers do. Despite these limitations, we have found user-space device drivers to be a powerful programming paradigm with a wide variety of uses (see Section 2).

FUSD is free software, released under a BSD-style license.

How does FUSD work?

FUSD drivers are conceptually similar to kernel drivers: a set of callback functions called in response to system calls made on file descriptors by user programs. FUSD's C library provides a device registration function, similar to the kernel's devfs_register_chrdev() function, to create new devices. fusd_register() accepts the device name and a structure full of pointers. Those pointers are callback functions which are called in response to certain user system calls--for example, when a process tries to open, close, read from, or write to the device file. The callback functions should conform to the standard definitions of POSIX system call behavior. In many ways, the user-space FUSD callback functions are identical to their kernel counterparts.

Perhaps the best way to show what FUSD does is by example. Program 1 is a simple FUSD device driver. When the program is run, a device called /dev/hello-world appears under the /dev directory. If that device is read (e.g., using cat), the read returns Hello, world! followed by an EOF. Finally, when the driver is stopped (e.g., by hitting Control-C), the device file disappears.


\begin{Program}
% latex2html id marker 71\listinginput[5]{1}{helloworld.c.exam...
...d.c: A simple program using FUSD to
create {\tt /dev/hello-world}}\end{Program}

On line 44 of the source, we use fusd_register() to create the /dev/hello-world device, passing pointers to callbacks for the open(), close() and read() system calls. (Lines 36-39 use the GNU C extension that allows initializer field naming; the 2.4 series of Linux kernels use also that extension for the same purpose.) The ``Hello, World'' read() callback itself is virtually identical to what a kernel driver for this device would look like. It can inspect and modify the user's file pointer, copy data into the user-provided buffer, control the system call return value (either positive, EOF, or error), and so forth.

The proxying of kernel system calls that makes this kind of program possible is implemented by FUSD, using a combination of a kernel module and cooperating user-space library. The kernel module implements a character device, /dev/fusd, which is used as a control channel between the two. fusd_register() uses this channel to send a message to the FUSD kernel module, telling the name of the device the user wants to register. The kernel module, in turn, registers that device with the kernel proper using devfs. devfs and the kernel don't know anything unusual is happening; it appears from their point of view that the registered devices are simply being implemented by the FUSD module.

Later, when kernel makes a callback due to a system call (e.g. when the character device file is opened or read), the FUSD kernel module's callback blocks the calling process, marshals the arguments of the callback into a message and sends it to user-space. Once there, the library half of FUSD unmarshals it and calls whatever user-space callback the FUSD driver passed to fusd_register(). When that user-space callback returns a value, the process happens in reverse: the return value and its side-effects are marshaled by the library and sent to the kernel. The FUSD kernel module unmarshals this message, matches it up with a corresponding outstanding request, and completes the system call. The calling process is completely unaware of this trickery; it simply enters the kernel once, blocks, unblocks, and returns from the system call--just as it would for any other blocking call.

One of the primary design goals of FUSD is stability. It should not be possible for a FUSD driver to corrupt or crash the kernel, either due to error or malice. Of course, a buggy driver itself may corrupt itself (e.g., due to a buffer overrun). However, strict error checking is implemented at the user-kernel boundary which should prevent drivers from corrupting the kernel or any other user-space process--including the errant driver's own clients, and other FUSD drivers.

What FUSD Isn't

FUSD looks similar to certain other Linux facilities that are already available. It also skirts near a few of the kernel's hot-button political issues. So, to avoid confusion, we present a list of things that FUSD is not.

Related Work

FUSD is a new implementation, but certainly not a new idea--the theory of its operation is the same as any microkernel operating system. A microkernel (roughly speaking) is one that implements only very basic resource protection and message passing in the kernel. Implementation of device drivers, file systems, network stacks, and so forth are relegated to userspace. Patrick Bridges maintains a list of such microkernel operating systems.

Also related is the idea of a user-space filesystem, which has been implemented in a number of contexts. Some examples include Klaus Schauser's UFO Project for Solaris, and Jeremy Goop's (no longer maintained) UserFS for Linux 1.x. The UFO paper is also notable because it has a good survey of similar projects that integrate user-space code with system calls.

Limitations and Future Work

In its current form, FUSD is useful and has proven to be quite stable--we use it in production systems. However, it does have some limitations that could benefit from the attention of developers. Contributions to correct any of these deficiencies are welcomed! (Many of these limitations will not make sense without having read the rest of the documentation first.)

Author Contact Information and Acknowledgments

FUSD was written by Jeremy Elson (jelson@circlemud.org) at Sensoria Corporation. Lewis Girod also provided critical insights into the design, as well as contributing code to the C library and test suite. His contributions were an enormous help. If you have bug reports, patches, suggestions, or any other comments, please feel free to contact the author.

FUSD has two SourceForge-host mailing lists: a low-traffic list for announcements (fusd-announce) and a list for general discussion (fusd-devel). Subscription information for both lists is available at the SourceForge's FUSD mailing list page.

For the latest releases and information about FUSD, please see the official FUSD home page.

Licensing Information

FUSD is licensed under a BSD-style license, enumerated below.

Copyright (c) 2001, Sensoria Corporation. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


next up previous
Next: Why use FUSD? Up: FUSD: A Linux Framework Previous: FUSD: A Linux Framework