The article below was written in 1998 - a lot has changed since then.
Linux was barely on my radar then, so this article
doesn't mention MAKEDEV.
Perhaps more important is that dynamic device creation isn't mentioned either. Still, the basic
concepts do remain the same. Just ignore the OS specific stuff.
If you are new to unix, you may not yet understand that the
files in the /dev directory are a little different from anything
you may be used to in other operating systems.
The article SCO Device Drivers goes into this in more depth for the technically
inclined, so I'll just cover the highlights here.
This article references SCO Unix specifically, but the general
concepts of devices are the same on Linux or any Unix system.
Device numbers are likely to be different, and commands like
"idmknod" may not exist, but the rest is completely applicable.
The very first thing to understand is that these files are
NOT the drivers for the devices. Drivers are in the kernel
itself (/unix or /xenix or /stand/unix), and the files in /dev do
not actually contain anything at all: they are just pointers to
where the driver code can be found in the kernel. There is nothing
more to it than that. These aren't programs, they aren't drivers,
they are just pointers.
That also means that if the device file points at code that
isn't in the kernel, it obviously is not going to work. Existence
of a device file does not necessarily mean that the device code is
in the kernel, and creating a device file (with mknod) does NOT
create kernel code.
Unix actually even shows you what the pointer is. When you do a
long listing of a file in /dev, you may have noticed that there are
two numbers where the file size should be:
brw-rw-rw- 2 bin bin 2, 64 Dec 8 20:41 fd0
That "2,64" is a pointer into the kernel. I'll explain more
about this in a minute, but first look at some more files:
brw-rw-rw- 2 bin bin 2, 64 Dec 8 20:41 fd0
brw-rw-rw- 2 bin bin 2, 48 Sep 15 16:13 fd0135ds15
brw-rw-rw- 2 bin bin 2, 60 Feb 12 10:45 fd0135ds18
brw-rw-rw- 1 bin bin 2, 16 Sep 15 16:13 fd0135ds21
brw-rw-rw- 2 bin bin 2, 44 Sep 15 16:13 fd0135ds36
brw-rw-rw- 3 bin bin 2, 36 Sep 15 16:13 fd0135ds9
A different kind of device would have a different major number.
For example, here are the serial com ports:
crw-rw-rw- 1 bin bin 5,128 Feb 14 05:35 tty1A
crw-rw-rw- 1 root root 5, 0 Dec 9 13:13 tty1a
crw-rw-rw- 1 root sys 5,136 Nov 25 07:28 tty2A
crw-r--r-- 1 uucp sys 5, 8 Nov 25 07:16 tty2a
Notice that each of these files shares the "5" part of the
pointer, but that the other number is different. The "5" means that
the device is a serial port, and the other number tells exactly
which com port you are referring to. In Unix parlance, the 5 is the
"major number" and the other is the "minor number".
These numbers get created with a "mknod" command. For example,
you could type "mknod /dev/myfloppy b 2 60" and then
"/dev/myfloppy" would point to the same driver code that
/dev/fd0135ds18 points to, and it would work exactly the same.
This also means that if you accidentally removed
/dev/fd0135ds18, you could instantly recreate it with "mknod".
But if you didn't know that the magic numbers were "2,60", how
could you find out?
It turns out that it's not hard.
First, have a look at "man idmknod". The idmknod command wipes
out all non-required devices, and then recreates them. Sounds
scary, but this gets called every time you answer "Y" to that
"Rebuild Kernel environment?" question that follows relinking.
Actually, on 5.0.4 and on, the existing /dev files don't get wiped
out; the command simply recreates whatever it has to.
idmknod requires several arguments, and you'd need to get them
right to have success. You could make it easier by simply relinking
a new kernel and answering "Y" to the "Rebuild" question, but
that's using a fire hose to put out a candle.
A less dramatic method would be to look at the files that
idmknod uses to recreate the device nodes. These are found in
In this case, the file you want would be "fd". A quick look at
part of that shows:
fd fd0 b 64 bin bin 666
fd fd0135ds36 b 44 bin bin 666
fd fd0135ds21 b 16 bin bin 666
fd fd0135ds18 b 60 bin bin 666
fd fd0135ds15 b 48 bin bin 666
fd fd0135ds9 b 36 bin bin 666
fd fd048 b 4 bin bin 666
This gives you *almost* everything you need to know about the
device nodes in the "fd" class. The only thing it doesn't tell you
is the major number, but you can get that just by doing an "l" of
any other fd entry:
brw-rw-rw- 1 bin bin 2, 60 Feb 5 09:45 fd096ds18
this shows you that the major number is "2".
Armed with these two pieces of information, you can now do
mknod /dev/fd0135ds18 b 2 60
chown bin /dev/fd0135ds18
chgrp bin /dev/fd0135ds18
chmod 666 /dev/fd0135ds18
If you examined the node file closely, you would also notice
that /dev/rfd0135ds18 and /dev/fd0135ds18 differ only in that the
"r" version is a "c" or character device and the other is "b" or
block. If you had already known that, you wouldn't have even had to
look at the node file; you'd simply have looked at an "l" of the
/dev/rfd0135ds18 and recreated the block version appropriately.
There are other fascinating things that can be learned from the
node files. For example, fd096ds18 is also minor number 60, and can
be used in the same way with identical results. In other words, if
you z'd out (were momentarily innattentive, not CTRL-Z in a job
control shell) and dd'd an image to /dev/fd096ds18, it would write
to your hd floppy without incident.
If you have a SCSI tape drive, notice what happens when you set
it to be the "default" tape drive. It creates device files that
have different names (rct0, etc.) but that have the same major and
Knowing that it's easy to recreate missing device files also
means that you can sometimes capture the output of programs that
write directly to a device. For example, suppose some application
prints directly to /dev/lp but you need to capture this to a file.
In most situations, you can simply "rm /dev/lp" (after carefully
noting its current ownership, permissions and, of course,
major/minor numbers), and then "touch /dev/lp" to create an
ordinary file. You'll need to chmod it for appropriate permissions,
and then run your app. Unless the app has tried to do ioctl calls
on the device, the output will be there for your use. This can be
particularly useful for examining control characters that the app
What's the Difference?
One question that comes up fairly often is "what's the
difference between a block and a character device and when should I
use one rather than the other?". To answer that question fully is
hard, but I'm going to try to at least get you started here.
The real difference lies in what the kernel does when a device
file is accessed for reading or writing. If the device is a block
device, the kernel gives the driver the address of a kernel buffer
that the driver will use as the source or destination for data.
Note that the address is a "kernel" address; that's important
because that buffer will be cached by the kernel. If the device is
raw , then the address it will use is in the user space of the
process that is using the device. A block device is something you
could make a filesystem on (a disk). You can move forward and
backward, from the beginning of a block device to its end, and then
back to the beginning again. If you ask to read a block that the
kernel has buffered, then you get data from the buffer. If you ask
for a block that has not yet been buffered, the kernel reads that
block (and probably a few more following it) into the buffer cache.
If you write to a block device, it goes to the buffer cache
(eventually to the device, of course). A raw (or character) device
is often something that doesn't have a beginning or end; it just
gives a stream of characters that you read. A serial port is an
excellent example- however, it is not at all unusual to have
character (raw) drivers for things that do have a beginning and an
end- a tape drive, for example. And many times there are BOTH
character and block devices for the same physical device- disks,
for example. Nor does using a raw device absolutely mean that you
can't move forward and back, from beginning to end- you can move
wherever you want with a tape or /dev/rfd0.
And that's where the differences get confusing. It seems pretty
reasonable that you'd use the block device to mount a disk. But
which do you use for format? For fsck? For mkfs?
Well, if you try to format /dev/fd0135ds18, you'll be told that
it is not a formattable device. Does that make any sense? Well, the
format process involves sequential access- it starts at the
beginning and just keeps on going, so it seems to make sense that
it wouldn't use the block device. But you can run "mkfs" on either
the block or character device; it doesn't seem to care. The same is
true for fsck. But although that's true for those programs on SCO
OSR5, it isn't necessarily going to be true on some other UNIX, and
the "required" device may make sense to whover wrote the program,
but it may not make sense to you.
You'd use a block device when you want to take advantage of the
caching provided by the kernel. You'd use the raw device when you
don't, or for ioctl operations like "tape status" or "stty -a".