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:

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:

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:

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.

There it is! It exists, and it’s a character device. So what happens if you feed it some information?

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 your dd 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:

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!

Author


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *