Devfs, also called the Device Filesystem, is designed with the single purpose of giving us a new (and more sane) way to manage all the block and character devices that normally populate /dev. As you probably know, a typical /dev tree contains hundreds of block and character special files, all of them sitting on your root filesystem. Each of these special files allow for user-space processes to easily interact with kernel devices. By performing operations on these special files, your X server is able to access your video hardware, fsck can perform filesystem checks and lpd is able to send data through the parallel port to your printer, for example.
In fact, one of the cooler things about Linux and Unix in general is that our devices aren't simply hidden away behind some obscure API, but actually exist on filesystem proper, alongside normal files, directories and symbolic links. Because our character and block devices are mapped into our normal filesystem namespace, we're often able to interact with our hardware in meaningful ways simply by using standard Unix commands, such as cat and dd. Besides being fun, this us to have more expressive power, which in turn allows us to be more productive.
However, while device special files are a good thing in themselves, typical Linux systems manage these special files in a suboptimal and cumbersome way. These days, Linux supports a lot of different kinds of hardware. This means that most of us have literally hundreds of special files in /dev to represent all of these devices. Not only that, but most of these special files don't even map to a existing device on our system (but they need to be there just in case we eventually add new hardware/drivers to our system), making things even more confusing.
From just this one aspect, we can see that /dev is in need of an overhaul, and devfs was created with the express purpose of whipping /dev back into shape. To get a good understanding of how devfs solves the vast majority of /dev management problems, let's take a look at devfs from the perspective of the device driver.
To get a good understanding of devfs, it's best to first understand how devfs changes things from the perspective of the device driver. Traditionally (without devfs), a kernel-based device driver registers a device with the rest of the system by calling either register_blkdev() or register_chrdev(), depending on whether it is registering a block or character device.
A major number (an unsigned 8-bit integer) must be provided as an argument to either register_blkdev() or register_chrdev(); then, after the device has been registered, the kernel will understand that this particular major number corresponds to the particular device driver that performed the register_???dev() call.
So, what major number does the device driver developer supply with the call to register_???dev()? Well, if the developer doesn't plan to share the device driver with the outside world, then any number will do, as long as it doesn't conflict with any other major numbers used by his or her current kernel. Alternatively, the developer could opt to have a major number dynamically assigned to the driver by the register_???dev() call. However, these solutions are normally only feasible if the driver won't be used by anyone else.
However, if the developer wants to share the driver with the rest of the world (and most Linux developers tend to take this approach), then simply pulling a major number out of thin air or using dynamic major number allocation won't work. Instead, the developer must contact the Linux kernel developers so that his or her particular device can be assigned an "official" major number. Then, throughout the Linux world, this particular device (and only this device) will be associated with this particular major number.
It's important to have an "official" major number because, in order to interact with this particular device, the administrator must create a special file in /dev. When the device node (special file) is created, it must sport the exact same major number that is used internally by the kernel. That way, when a process performs an operation on the device, the kernel knows what device driver should be referenced. The mapping from special file to kernel driver is made possible by the major number, not the actual device name, which is irrelevant to a non-devfs system.
Once a device driver has an official major number, the device can be used publicly, and the device node can start to become incorporated into the various distributions' /dev tree, as well as their offical /dev/MAKEDEV script, a special script used to assist the superuser in creating device nodes with the correct major and minor numbers, permissions and ownership.
Unfortunately, there are lots of scalability problems with this kind of approach. Not only is it a pain for the device driver developer to contact the kernel developers in order to receive an official major number, but it must be even more annoying for the kernel developers to keep track of how they've allocated all these major numbers. In many ways, this task is a lot like the sysadmin's job of tracking the allocation of static IP addresses on a company LAN -- it's not too fun. And just like the sysadmin can take advantage of DHCP to ease this administration burden, it would be nice if there were some similar approach for registering devices.
Not only that, but Linux is running out of major and minor numbers. While this problem could be solved by simply expanding the number of bits used for major and minor numbers, it's already a pain to maintain these major number mappings in the first place, so again, one wonders if there is a better way to do things. Fortunately, there is; enter devfs.
Here's a quick rundown of how devfs does its thing and solves all these problems in one fell swoop. Once devfs is configured correctly, which involves adding devfs support to the kernel as well as performing a number of semi-tricky changes to startup scripts, the superuser reboots the system. Then, the kernel starts to boot and device drivers begin to register devices with the rest of the system. You'll recall that on a non-devfs system, the register_blkdev() and register_chrdev() calls (along with supplied major numbers) are used for this purpose. However, now that devfs is enabled, the device drivers use a new and improved kernel call to register their devices, called devfs_register().
Here's what's interesting about the devfs_register() call. While it's possible to specify a major and minor number as arguments for compatibility purposes, this is no longer required. Instead, the devfs_register() call accepts the path to the device as it should appear under /dev as an argument. For example, let's say that the foo device driver wants to register a device with devfs. It would supply an argument of foo0 to devfs_register(), thus telling the kernel that a new foo0 device should be created at the root of the devfs namespace. In response, devfs_register() adds the foo0 device node to the root of the devfs namespace, and records that this new foo0 node should map to the foo device driver in the kernel.
Once all device drivers have started up and registered the appropriate devices with the kernel, the kernel starts /sbin/init and the system initialization scripts begin to execute. Early in the boot process (before filesystem checks), the rc scripts mount a devfs filesystem to /dev, which contains a representation of the devfs namespace. This means that after /dev is mounted, all registered devices (such as /dev/foo0, above) can be accessed just as one would on a non-devfs system. When they are accessed, the kernel maps to the appropriate device driver by devfs device name, rather than by major number.
The beauty of this system is that all required device nodes (and no more) are created automatically by the kernel. Not only does this mean that MAKEDEV is no longer needed (since all registered devices simply "appear" in /dev), but it means that /dev is no longer cluttered with hundreds of "dud" device nodes. In fact, with devfs, you can simply look in /dev to see what devices are on the system. So, if you have a laptop with hotplug support, this means that you can even have devices magically appear and disappear from /dev as you insert and remove PC Cards from your system. This makes devfs a very clean and functional solution to something that was previously an unweildly mess.
Devfs makes a lot of things much easier. Consider the challenge of creating a Linux bootable CD-ROM, which consists of a boot loader, an initrd, a kernel, and a loopback filesystem sitting on the CD. When the CD boots, the boot loader loads the kernel and the initrd, and then the /linuxrc script on the initrd is then executed by the kernel. /linuxrc's primary task is to get the CD mounted so that the loopback filesystem can itself be mounted and accessed.
Without devfs, linuxrc needs to "probe" a number of special files in /dev that may or may not represent actual hardware that is connected to the system. For example, linuxrc will need to scan /dev/hdc, /dev/scd0, /dev/hdb and other devices in order to detect the "live" CD-ROM device. Along the way, it's very likely that it will hit several "dud" device nodes.
However, with devfs, linuxrc simply looks in /dev/cdroms, which contains all special files associated with the active CD-ROMs in the system, whether IDE or SCSI. Thanks to this handy new devfs convention, there's no more guessing; only active devices are listed, and the device probing code doesn't even need to concern itself about the particulars of the underlying CDROM, such as what IDE channel or SCSI ID it uses. In fact, this is another major benefit of devfs; as we'll see in my next article, devfs has completely new default locations for devices in /dev.
In fact, when you want to access a particular block device (such as a disks, partition, CD-ROM drive, etc) you'll actually have several different special files to which you can refer. For example, my server has a single SCSI CD-ROM; with devfs enabled, I can access it by mounting either /dev/cdroms/cdrom0 or /dev/scsi/host0/bus0/target4/lun0/cd. Both refer to the same device, and I can refer to the special file that I find most convenient. I can also access my CD-ROM using an old-style device name (/dev/sr0) if I like, thanks to a very handy little program called devfsd. devfsd is an extremely versatile program that takes care of creating old-style "compatibility" special files, and also allows you to customize /dev in a tremendous number of ways. We'll take a close look at devfsd in my next article, when I guide you through the process of getting devfs up and running on your own system. Until then, check out the following resources for more information about devfs:
- Read Daniel's other articles in this series, where he describes:
- the benefits of journalling and ReiserFS (Part 1)
- setting up a ReiserFS system (Part 2)
- using the tmpfs virtual memory filesystem and bind mounts (Part 3)
- beginning the conversion to devfs (Part 5)
- completing the conversion to devfs using an init wrapper (Part 6)
- O'Reilly's Linux
Device Drivers, 2nd Edition is a truly excellent book and a great
resource for those who would like to learn more about device registration, and
Linux device driver programming in general.
- Be sure to read Richard Gooch's (creator of Linux devfs)Linux Devfs
FAQ. It's complete, verbose, and up-to-date. What more could you ask
- There's a devfs mailing list available. To subscribe, send an email to
firstname.lastname@example.org with the word
subscribe in the body of the message. Devfs list archives are available
- You may also want to visit Richard Gooch's main page;
it contains devfs as well as other neat things.
- Linux Weekly News is a great resource
for keeping up with the latest kernel developments.
- Browse more Linux resources on developerWorks.
- Browse more Open source resources on developerWorks.
Residing in Albuquerque, New Mexico, Daniel Robbins is the President/CEO of Gentoo Technologies, Inc., the creator of Gentoo Linux, an advanced Linux for the PC, and the Portage system, a next-generation ports system for Linux. He has also served as a contributing author for the Macmillan books Caldera OpenLinux Unleashed, SuSE Linux Unleashed, and Samba Unleashed. Daniel has been involved with computers in some fashion since the second grade, when he was first exposed to the Logo programming language as well as a potentially dangerous dose of Pac Man. This probably explains why he has since served as a Lead Graphic Artist at SONY Electronic Publishing/Psygnosis. Daniel enjoys spending time with his wife, Mary, and his daughter, Hadassah. You can contact Daniel at email@example.com.