Almost all the files under /dev
are device files. Whereas reading and writing to a regular file stores data on a disk or other filesystem, accessing a device file communicates with a driver in the kernel, which generally in turn communicates with a piece of hardware (a hardware device, hence the name).
There are two types of device files: block devices (indicated by b
as the first character in the output of ls -l
), and character devices (indicated by c
). The distinction between block and character devices is not completely universal. Block devices are things like disks, which behave like large, fixed-size files: if you write a byte at a certain offset, and later read from the device at that offset, you get that byte back. Character devices are just about anything else, where writing a byte has some immediate effect (e.g. it's emitted on a serial line) and reading a byte also has some immediate effect (e.g. it's read from the serial port).
The meaning of a device file is determined by its number, not by its name (the name matters to applications, but not to the kernel). The number is actually two numbers: the major number indicates which driver is responsible for this device, and the minor number allows a driver to drive several devices¹. These numbers appear in the ls -l
listing, where you would normally find the file size. E.g. brw-rw---- 1 root disk 8, 0 Jul 12 15:54 /dev/sda
→ this device is major 8, minor 0.
Some device files under /dev
don't correspond to hardware devices. One that exists on every unix system is /dev/null
; writing to it has no effect, and reading from it never returns any data. It's often convenient in shell scripts, when you want to ignore the output from a command (>/dev/null
) or run a command with no input (</dev/null
). Other common examples are /dev/zero
(which returns null bytes ad infinitum) /dev/urandom
(which returns random bytes ad infinitum).
A few device files have a meaning that depends on the process that accesses it. For example, /dev/stdin
designates the standard input of the current process; opening from has approximately the same effect as opening the original file that was opened as the process's standard input. Somewhat similarly, /dev/tty
designates the terminal to which the process is connected. Under Linux, nowadays, /dev/stdin
and friends are not implemented as character devices, but instead as symbolic links to a more general mechanism that allows every file descriptor to be referenced (as opposed to only 0, 1 and 2 under the traditional method); for example /dev/stdin
is a symbolic link to /proc/self/fd/0
. See How does /dev/fd relate to /proc/self/fd/?.
You'll find a number of symbolic links under /dev
. This can occur for historical reasons: a device file was moved from one name to another, but some applications still use the old name. For example, /dev/scd0
is a symbolic link to /dev/sr0
under Linux; both designate the first CD device. Another reason for symbolic links is organization: under Linux, you'll find your hard disks and partitions in several places: /dev/sda
and /dev/sda1
and friends (each disk designated by an arbitrary letter, and partitions according to the partition layout), /dev/disk/by-id/*
(disks designated by a unique serial number), /dev/disk/by-label/*
(partitions with a filesystem, designated by a human-chosen label); and more. Symbolic links are also used when a generic device name could be one of several; for example /dev/dvd
might be a symbolic link to /dev/sr0
, or it might be a link to /dev/sr1
if you have two CD readers and the second one is to be the default DVD reader.
Finally, there are a few other files that you might find under /dev
, for traditional reasons. You won't find the same on every system. On most unices, /dev/log
is a socket that programs use to emit log messages. /dev/MAKEDEV
is a script that creates entries in /dev
. On modern Linux systems, entries in /dev/
are created automatically by udev, obsoleting MAKEDEV
.
¹ This is actually no longer true under Linux, but this detail only matters to device driver writers.