In designing any operating system, one question that has to be answered is, “How do programs talk to physical devices?” The UNIX answer is for the system to provide device files. When a program accesses them, they act almost exactly like ordinary files, but the kernel passes the accesses to those special files to device drivers. These files are traditionally found in the /dev directory.
To understand device files, you should start by looking at the contents of the /dev directory:
ls -l /dev
You'll see that these special files have a few differences from normal files. First, the first character of the permissions is either “b” or “c”, indicating that the device is either a block or character device. Second, instead of a file size, there are two numbers. These are the major and minor device numbers. When you access one of these files, the operating system looks up the major device number in either the block or character driver table to find the device driver (possibly with the help of kerneld, in the case of modules). The minor number is used by the device driver for such things as multiple instances of the same device or different aspects of the device.
You may also note a few files whose first permission character is “s”, indicating that they are sockets. Unlike devices, which are handled by the kernel, these are created by daemons when the system boots. For example, /dev/printer is a socket controlled by the lpd daemon. If you kill the lpd process, /dev/printer will vanish.
If you have the Linux kernel source installed, you can find out what a device is used for by looking up its device type and number in the file /usr/src/linux/Documentation/devices.txt. With most Linux distributions, you'll find that device files exist for a number of strange operations that you wouldn't expect to use. However, you should be very careful about deleting any devices; removing the wrong files can result in making it impossible to use your system until you restore the file, after rebooting from a floppy. So make sure you have a boot floppy to rescue your system in case you make a mistake. Of course, you can't do much to mess up the /dev directory unless you are running as root.
There are two ways to create device files. The recommended way is to use a program called MAKEDEV. You can also create them manually using the mknod program. We will consider them here in reverse order.
To use mknod, you need to know the name of the device, the type (character or block), and the major and minor device numbers. As an example, I once erased /dev/zero. After rebooting from a floppy and mounting my normal file system on /mnt, I wanted to create /mnt/dev/zero. I knew that /dev/zero is supposed to be a character device, with the major number 1 and the minor number 5. The command I used to create the device was:
mknod /mnt/dev/zero c 5 1
I then had to use chmod, chown and chgrp to set the access permissions and ownership, just as with a regular file. You'll probably want to read the man page for mknod before using it.
If that sounded like a pain, you're right. Fortunately, there's a better way. Instead of calling mknod directly, you can use a script called MAKEDEV that already knows the device types, numbers, permissions and ownership for all the standard devices. MAKEDEV uses mknod to create the devices but saves you the work of figuring out the details. For example, typing MAKEDEV generic will create most, if not all, of the devices you would want for a typical system. It is a good idea to read the man page for MAKEDEV before using it.
/dev/null is the proverbial bit bucket. Shell scripts often redirect the output of commands to /dev/null to avoid cluttering the display with unwanted output. When used for input, it acts as an empty file.
/dev/random and /dev/urandom access the kernel's random number generator. The kernel keeps track of various “random” events, such as disk and keyboard activity, and uses a cryptographic hash to generate highly random numbers. It also keeps track of an estimate of how much random information it has. If you use /dev/random, it will only give you as much information as it has available. If you use /dev/urandom, it will give you an unlimited amount of random information, but without as strong a guarantee on its unpredictability. Although the kernel uses this feature internally, very few programs access it through the device files.
/dev/zero generates an infinite stream of zeros. It is used every time you start up an ELF process that uses shared libraries, making it one of the most important files in /dev.
IDE hard drives and CD-ROM drives are accessed through /dev/hd*. Most newer PC motherboards include two IDE interfaces, each capable of controlling a master and a slave. hda and hdb are the master and slave drives on the first interface, and hdc and hdd are the master and slave on the second interface. The number at the end of the device name refers to the partition number, with 1 through 4 being the primary partitions and 5 and up being extended partitions.
Those familiar with other versions of UNIX will be surprised to find that all the IDE devices are block devices. Traditionally, there would also be character devices for the drives, and you would have to be careful which one you used for a given program. (For example, fsck wants to use the character device, but you mount the block device—not a good idea.) Linux allows programs to access a block device as if it were a character device, so there is no need for character devices for hard drives.
SCSI devices are a bit more complicated than IDE. If you have a SCSI card with multiple devices, you probably already know that each device has a SCSI ID number between 0 and 7. When the SCSI card is initialized, the operating system asks it which devices are attached. These devices are separated into several categories. The first hard drive found (the one with the lowest ID number) is sda, the first CD-ROM drive is sr0, the first tape drive is sta, and the first generic device (usually a scanner) is sga. So when accessing SCSI devices under Linux, you don't need to know the device ID number (unlike other versions of UNIX). As with IDE drives, there are no character devices for the drives, and partition numbers are appended to the drive device file.
Tape drives typically have at least two different device files. The primary device automatically rewinds the tape when you've finished reading or writing to it. The device with an “n” prepended to its name doesn't rewind the tape for you. Typically you would use this to write more than one archive on a tape.
tty devices are among the most numerous and the most confusing of all the files in /dev. Every time you log in or launch a new xterm, a tty device is assigned to the shell. This device is responsible for displaying the shell's output and providing its input. It is known as the controlling tty for that shell and its child processes. The device /dev/tty is a magic device that refers to whichever tty device is the controlling tty for that process.
Besides /dev/tty, there are three types of tty devices: consoles, serial ports and pseudo devices.
Console ttys are used when the keyboard and monitor are directly connected to the system without running the X Window System. Since you can have several virtual consoles, the devices are tty0 through tty63. In theory you can have 64 virtual consoles, but most people use only a few. The device /dev/console is identical to tty0 and is needed for historical reasons. If your system lets you log in on consoles 1 through 6, then when you run X Windows System, X uses console 7, so you'll need /dev/tty1 through /dev/tty7 on your system. I recommend having files up through /dev/tty12. For more information on using virtual consoles, see the article Keyboards, Consoles and VT Cruising by John Fisk in the November 1996 issue of Linux Journal.
If you have a terminal connected to a serial port or if you dial in using a modem, you'll use a serial tty device. There are a number of different serial devices which are used if you have a special card with many serial ports (ttyC*, ttyD*, ttyX* and ttyR*), but most people just use ttyS* for the normal serial ports (COM1 through COM4 under DOS).
This begins to get confusing, because there are also callout devices for each serial port. For each ttyS device there is a corresponding cua device. If you're only using the serial port for one purpose, it probably doesn't matter which device you use. To be able to both dial in to your machine remotely and dial out when you're home, be sure to use the tty version of the device to listen for incoming calls and the callout version to place outgoing calls. By doing this, you won't mess up someone who has dialed in by trying to dial out on the same line. (See “Serial Port Devices”.)
Pseudo tty devices are used when a login is initiated by launching a new xterm or by connecting to your machine via TELNET. Because you can have lots of windows open and many TELNET sessions going on at one time, you may need a lot of these devices. Hence, you should have a lot of files of the form /dev/pty*. Originally, Linux limited you to 64 pseudo terminals, but some people found that too restrictive, so you can now have up to 256 of them, using different major and minor device numbers. Making it a bit more complicated, there are actually two devices for each pseudo tty: a master and a slave. Thus, for each /dev/pty* file, there's a matching /dev/tty* file. Fortunately, you don't have to worry about the distinction other than being careful to create or delete them in pairs.
If you wish to get every possible millisecond out of your system, there are a few things you should know about directories in UNIX file systems. (Though technically I'm talking about the implementation of the EXT2 file system, the points apply to almost all UNIX file systems.)
First note that though the command ls always displays the directory in a sorted order, directories are not stored with their entries sorted. Instead, each new entry is placed as close to the front of the directory as possible.
To find a file in a directory, the file name is compared with the first file in the directory. If it doesn't match, the second name is checked, and so on until either a match is found, or the end of the directory is reached. Consequently, if you have a large number of files in a directory and are frequently opening the last file in the directory, your CPU is doing a lot of comparisons. If, however, you could control the order of the entries, so as to place the most-frequently-used entries at the beginning of the directory, this would not be such an issue.
If you want to redo the internal ordering of the entries in /dev, boot from a floppy and then mount your primary file system. If your regular file system is mounted as /mnt, your regular device directory is /mnt/dev. Create a new directory called /mnt/dev2. Now you can move device files from /mnt/dev to /mnt/dev2. You will probably want to start with /mnt/dev/zero and /mnt/dev/null, as these two are opened far more frequently than any other devices.
If that sounds like a hassle, then don't bother with it. You probably won't notice any difference unless you are running on an old 386. Furthermore, the new directory cache under development in the 2.1.x kernel series will most likely make this a non-issue.
Preston Crow grew up in Boise, Idaho. He has a Master's degree in Computer Science from Dartmouth College and hopes to soon upgrade to a Ph.D. He now works for the Open Group in Cambridge, MA, where he lives with his brilliant wife. He can be reached via e-mail at Preston.F.Crow@Dartmouth.edu.