Device files allow user programs to access hardware devices on the system through the kernel. They are not "files" per se, but look like files from the program's point of view: you can read from them, write to them, mmap( ) onto them, and so forth. When you access such a device "file," the kernel recognizes the I/O request and passes it a device driver, which performs some operation, such as reading data from a serial port or sending data to a sound card.
Device files (although they are inappropriately named, we will continue to use this term) provide a convenient way to access system resources without requiring the applications programmer to know how the underlying device works. Under Linux, as with most Unix systems, device drivers themselves are part of the kernel. In "Building the Kernel" in Chapter 18, we show you how to build your own kernel, including only those device drivers for the hardware on your system.
Device files are located in the directory /dev on nearly all Unix-like systems. Each device on the system should have a corresponding entry in /dev. For example, /dev/ttyS0 corresponds to the first serial port, known as COM1 under MS-DOS; /dev/hda2 corresponds to the second partition on the first IDE drive. In fact, there should be entries in /dev for devices you do not have. The device files are generally created during system installation and include every possible device driver. They don't necessarily correspond to the actual hardware on your system.
A number of pseudo-devices in /dev don't correspond to any actual peripheral. For example, /dev/null acts as a byte sink; any write request to /dev/null will succeed, but the data written will be ignored. Similarly, we've already demonstrated the use of /dev/zero to create a swap file; any read request on /dev/zero simply returns null bytes.
When using ls -l to list device files in /dev, you'll see something such as the following (if you are using a version of the ls command that supports colorized output, you should see the /dev/hda in a different color, since it's not an ordinary file):
brw-rw---- 1 root disk 3, 0 2004-04-06 15:27 /dev/hda
This is /dev/hda, which corresponds to the first IDE drive. First of all, note that the first letter of the permissions field is b, which means this is a block device file. (Normal files have a - in this first column, directories a d, and so on; we'll talk more about this in the next chapter.) Device files are denoted either by b, for block devices, or c, for character devices. A block device is usually a peripheral such as a hard drive: data is read and written to the device as entire blocks (where the block size is determined by the device; it may not be 1024 bytes as we usually call "blocks" under Linux), and the device may be accessed randomly. In contrast, character devices are usually read or written sequentially, and I/O may be done as single bytes. An example of a character device is a serial port.
Also, note that the size field in the ls -l listing is replaced by two numbers, separated by a comma. The first value is the major device number and the second is the minor device number. When a device file is accessed by a program, the kernel receives the I/O request in terms of the major and minor numbers of the device. The major number generally specifies a particular driver within the kernel, and the minor number specifies a particular device handled by that driver. For example, all serial port devices have the same major number, but different minor numbers. The kernel uses the major number to redirect an I/O request to the appropriate driver, and the driver uses the minor number to figure out which specific device to access. In some cases, minor numbers can also be used for accessing specific functions of a device.
The naming convention used by files in /dev is, to put it bluntly, a complete mess. Because the kernel itself doesn't care what filenames are used in /dev (it cares only about the major and minor numbers), the distribution maintainers, applications programmers, and device driver writers are free to choose names for a device file. Often, the person writing a device driver will suggest a name for the device, and later the name will be changed to accommodate other, similar devices. This can cause confusion and inconsistency as the system develops; hopefully, you won't encounter this problem unless you're working with newer device driversthose that are under testing. A project called udev should soon solve the problem of clashing device names.
At any rate, the device files included in your original distribution should be accurate for the kernel version and for device drivers included with that distribution. When you upgrade your kernel or add additional device drivers (see "Building a New Kernel" in Chapter 18), you may need to add a device file using the mknod command. The format of this command is:
mknod -m permissions name type major minor
For example, let's say you're adding a new device driver to the kernel, and the documentation says that you need to create the block device /dev/bogus, major number 42, minor number 0. You would use the following command:
mknod /dev/bogus b 42 0
Making devices is even easier with the command /dev/MAKEDEV that comes with many distributionsyou specify only the kind of device you want, and MAKEDEV finds out the major and minor numbers for you.
Getting back to the mknod command, if you don't specify the -m permissions argument, the new device is given the permissions for a newly created file, modified by your current umaskusually 0644. To set the permissions for /dev/bogus to 0660 instead, we use:
mknod -m 660 /dev/bogus b 42 0
You can also use chmod to set the permissions for a device file after creation.
Why are device permissions important? Like any file, the permissions for a device file control who may access the raw device, and how. As we saw in the previous example, the device file for /dev/hda has permissions 0660, which means that only the owner and users in the file's group (here, the group disk is used) may read and write directly to this device. (Permissions are introduced in "File Ownership and Permissions" in Chapter 11.)
In general, you don't want to give any user direct read and write access to certain devicesespecially those devices corresponding to disk drives and partitions. Otherwise, anyone could, say, run mkfs on a drive partition and completely destroy all data on the system.
In the case of drives and partitions, write access is required to corrupt data in this way, but read access is also a breach of security; given read access to a raw device file corresponding to a disk partition, a user could peek in on other users' files. Likewise, the device file /dev/mem corresponds to the system's physical memory (it's generally used only for extreme debugging purposes). Given read access, clever users could spy on other users' passwords, including the one belonging to root, as they are entered at login time.
Be sure that the permissions for any device you add to the system correspond to how the device can and should be accessed by users. Devices such as serial ports, sound cards, and virtual consoles are generally safe for mortals to have access to, but most other devices on the system should be limited to use by root (and to programs running setuid as root).
A technique that some distributions follow is to assign a device file to the user root, but not to use root as the group, but rather something different. For example, on SUSE, the device file /dev/video0 that is the access point to the first video hardware (such as a TV card) is owned by user root, but group video. You can thus add all users who are supposed to have access to the video hardware to the group video. Everybody else (besides root, of course) will be forbidden access to the video hardware and cannot watch TV.[*]
Many files found in /dev are actually symbolic links (created using ln -s, in the usual way) to another device file. These links make it easier to access certain devices by using a more common name. For example, if you have a serial mouse, that mouse might be accessed through one of the device files /dev/ttyS0, /dev/ttyS1, /dev/ttyS2, or /dev/ttyS3, depending on which serial port the mouse is attached to. Many people create a link named /dev/mouse to the appropriate serial device, as in the following example:
ln -s /dev/ttyS2 /dev/mouse
In this way, users can access the mouse from /dev/mouse, instead of having to remember which serial port it is on. This convention is also used for devices such as /dev/cdrom and /dev/modem. These files are usually symbolic links to a device file in /dev corresponding to the actual CD-ROM or modem device.
To remove a device file, just use rm, as in:
Removing a device file does not remove the corresponding device driver from memory or from the kernel; it simply leaves you with no means to talk to a particular device driver. Similarly, adding a device file does not add a device driver to the system; in fact, you can add device files for drivers that don't even exist. Device files simply provide a hook into a particular device driver should such a driver exist in the kernel.