Recently, on a sysadmin forum I read, there was a post complaining about /dev/
files not copying over properly. The old adage “Under UNIX and Linux, everything is a file”, while true, can lead to misunderstandings. Unless we figured out quantum teleportation, the copying of those files wouldn’t work, or at the very least be productive. The truth about /dev/
is that it’s not populated with “tangible” files, but representations (that can be treated as files) of hardware.
/dev/ and “everything is a file”
To paraphrase Linus Torvalds, the whole point of “everything is a file” is that you can use common tools to operate on different things. The devices you attach to your system, such as hard disks, are going to be accessed by user applications and system tools. For that to happen, they need to be exposed to those applications in some form, and /dev/
is a filesystem that does exactly that. It represents devices that are attached to the local system as files.
(There are similar exceptions for /proc
and /sys
. /sys
is a virtual filesystem of the type sysfs, rebuilt upon boot and allowing you to get information about the system and its components. /proc
is a procfs virtual filesystem that provides information about the kernel and its processes, and gets generated dynamically.)
Device numbers
In order for access to happen, there needs to be a way for a program to identify the device it wants to interact with. This is where device numbers come in. Look at this simple example:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ ls -l /dev | tail brw-rw----. 1 root disk 253, 2 Jun 12 22:44 vda2 brw-rw----. 1 root disk 253, 3 Jun 12 22:44 vda3 drwxr-xr-x. 2 root root 60 Jun 12 22:44 vfio crw-------. 1 root root 10, 63 Jun 12 22:44 vga_arbiter crw-------. 1 root root 10, 137 Jun 12 22:44 vhci crw-------. 1 root root 10, 238 Jun 12 22:44 vhost-net crw-------. 1 root root 10, 241 Jun 12 22:44 vhost-vsock drwxr-xr-x. 2 root root 60 Jun 12 22:44 virtio-ports crw-------. 1 root root 244, 1 Jun 12 22:44 vport1p1 crw-rw-rw-. 1 root root 1, 5 Jun 12 22:44 zero |
There are two things to notice. First, the c
and d
letters in front of files. Second, in the fifth column, where file size usually resides, there are not one, but two numbers. Those two numbers are device numbers. Each device you connect to your machine, in order to work properly, is controlled by a driver. Every driver and hardware component it controls is assigned a device number, with which programs that need to interact with it identify it.
The device number is split in two. There’s a major and minor device number. The first (major) number indicates the driver responsible for the device. The minor number allows the driver to determine which particular hardware instance is being accessed. See the output of the ls
command? Take a closer look at what the filesystems vda*
reports:
1 2 3 4 5 6 |
$ ls -l /dev | grep vda brw-rw----. 1 root disk 253, 0 Jun 12 22:44 vda brw-rw----. 1 root disk 253, 1 Jun 12 22:44 vda1 brw-rw----. 1 root disk 253, 2 Jun 12 22:44 vda2 brw-rw----. 1 root disk 253, 3 Jun 12 22:44 vda3 |
It’s pretty clear 253 is the number of the driver “driving” all the filesystems, while the minor number is a unique identifier, increasing by 1 for each filesystem. So what about the letter b
in front of all of them?
Block and character devices.
The b
and c
in front of the “files” under /dev
stand for block or character devices. Block devices are things like drives, disks, flash memory. The expected behavior of these is that when a byte is written at a certain offset, a read from the same device later on would read that same byte written at that offset. As you saw before, your hard drive is a block device, so interacting with it writes and reads bytes from it.
A character device, on the other hand, takes a byte written to it and does something with it. It might display it on a terminal or a serial line, print it, or send it as audio through a speaker. To understand how a character device works, try sending yourself a message! Run the w
command:
1 2 3 4 5 |
$ w 20:06:16 up a long time, 1 user, load average: 0.06, 0.09, 0.09 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT author pts/1 18:19 0.00s 0.08s 0.01s w |
You can see yourself and the terminal (tty, or teletype) you’re running. In my example, I’m the user author
and I’m on pts/1
, which is a pseudo-terminal (hardware terminals are connected over something like a serial port, but pseudo-terminals are provided by a terminal emulator such as Bash or SSH.)
See if you can find your pseudo-terminal in /dev
.
1 2 3 |
$ ls -l /dev/pts/1 crw--w----. 1 author tty 136, 1 Jun 28 20:09 /dev/pts/1 |
There it is! It exists, and it’s a character device. So what happens if you feed it some information?
1 2 3 |
$ echo "Hello!" > /dev/pts/1 Hello! |
The bytes that were written to the character device /dev/pts/1
were directly printed out on my terminal. That makes sense, considering the device is your terminal.
You might notice a “device” called /dev/null
? You know the hardware you have connected to your system, and null
isn’t one of them, so why is it in /dev
?
Block and character and others
There are files under /dev
that don’t correspond to hardware (or pseudo-hardware) devices. The most commonly thought of one is /dev/null
, which discards anything written to it and returns EOF
on reading. /dev/null
is write-able and read-able by anyone, but not executable, so piping does not work. The only way to write to /dev/null
is by shell redirection. /dev/null
is also a special file, not a directory, so throwing files in /dev/null
doesn’t work.
Other such devices are:
/dev/zero
– infinite zero numbers for yourdd
overwrite needs/dev/urandom
– infinite random bytes using the entropy pool or, if empty pool, using data generated using SHA/MD5/ other algorithms/dev/random
– infinite random bytes from the entropy pool or, if empty entropy pool, reads to/dev/random
will be blocked/dev/log
– a socket that programs use to emit log messages
There are other files, like symlinks – be it for historic reasons, or in the case of hard drives, for identification reasons (/dev/disk/by-id/
and /dev/disk/by-label/
all hold the same devices – your disks, identified differently), or like /dev/stdin
, which designates the standard input of the current process:
1 2 3 |
$ echo "Hello!" > /dev/stdin Hello! |
devfs, devtmpfs, and udev
Finally, there’s an entry in /dev
called devtmpfs
.
It wasn’t always devtmpfs
. It started off as devfs
. The history of devfs
, its successor udev
and udev
‘s successor devtmpfs
could easily be a series of articles of its own, so I won’t delve deep into it. devfs
, introduced in Kernel 2.3.46, registered devices by name and was executed in kernel space. Udev
was a replacement aimed to improve a lot of aspects, identify devices based on properties like vendor ID and device ID and ran in userspace as opposed to kernel space. The idea was to create a tmpfs
early in the kernel initialization, before the driver core’s initialization. Then, as each device registers with the driver core, it dynamically fills /dev
with the devices, each entry represented by a device’s major and minor number and device name. That eliminated the need for user-space support and improved boot times. As of the writing of this article, devtmpfs
is still the standard in the Linux kernel.
Conclusion
Understanding the intricacies of the /dev
directory in Linux provides valuable insights into the way devices are accessed and managed within the operating system. From the concept of “everything is a file” to the use of device numbers, block and character devices, and various special files, /dev
plays a critical role in facilitating communication between user applications, system tools, and hardware components. /dev
is not only one of a couple of unique filesystems in the Linux system, but probably the most unique of them all in the way it allows even the user to interact directly with their hardware devices. And it shouldn’t be copied from system to system – but feel free to try and report what happens!
0 Comments