They are actually just that - interfaces. Encoded by a "major" and "minor" number they provide a hook to the kernel.
They come in two flavors (well, three, but named pipes are out of the scope of this explanation for now): Character Devices and Block Devices.
Block Devices tend to be storage devices, capable of buffering output and storing data for later retrieval.
Character Devices are things like audio or graphics cards, or input devices like keyboard and mouse.
In each case, when the kernel loads the correct driver (either at boot time, or via programs like udev) it scans the various buses to see if any devices handled by that driver are actually present on the system. If so, it sets up a device that 'listens' on the appropriate major/minor number.
(For instance, the Digital Signal Processor of the first audio card found by your system gets the major/minor number pair of 14/3; the second gets 14,35, etc.)
It's up to udev to create an entry in /dev
named dsp
as a character device marked major 14 minor 3.
(In significantly older or minimum-footprint versions of Linux, /dev/
may not be dynamically loaded but just contain all possible device files statically.)
Then, when a userspace program tries to access a file that's marked as a 'character special file' with the appropriate major/minor number (for instance, your audio player trying to send digital audio to /dev/dsp
), the kernel knows that this data needs to be transmitted via the driver that major/minor number is attached to; presumably said driver knows what to do with it in turn.
The kernel knows device numbers because it decides device numbers. Each driver registers the device numbers that it manages. The numbers are either hard-coded in the source code, or, in some cases, allocated dynamically. The sysfs filesystem allows applications such as udev to discover the devices supported by the kernel. See How does udev get device numbers for the devices it creates? for more details.
The driver's initialization code probes for the hardware, and registers devices based on what hardware it found during probing. Some types of hardware don't support probing; for example, the ISA bus (a largely outdated bus on PC-type computers) provides no way to list connected hardware, so the driver can only try to communicate and pray that there isn't a different peripheral attached at the same address. On some platforms, the bootloader includes a device tree describing the available peripherals and where they are mapped, and the Linux kernel activates drivers based on this information. There are three ways a driver may be loaded for a peripheral.
- The driver may be included as part of the kernel image.
- The driver may be compiled as a module, and loaded explicitly (e.g. by including it in
/etc/modules
or in an initramfs).
- There is a mechanism for automatically loading certain drivers based on information reported by bus types that can list connected peripherals together with a universal identification number, such as PCI (the main bus in modern PC) and USB. The kernel runs
modprobe
and passes it a symbolic name that encodes the peripheral's identification, which is an alias for the “real” name of the driver module. See Are driver modules loaded and unloaded automatically?
Best Answer
Very briefly:
The most important thing about a device driver is that it runs in kernel space, with the same permissions as the kernel, and therefore can access hardware directly. Applications are (usually) not permitted to do that.
So you can think of device drivers as the parts of the kernel that organize access to certain hardware (the "device").
An application can interact with the kernel on various levels: From higher abstractions (e.g. a file system) to middling abstractions (a block device) to really low level abstractions (some files in
/proc/
or/sys
, someioctls
in devices in/dev
). So the low level interactions will sometimes be talking to a device driver very directly, there's only a very thin layer where the kernel redirects the call to the device driver. So "an Application cannot interact directly with a Device Driver, only the Operating System can do that" is sort of true and also sort of false.Also, there are many abstraction layers in the kernel like the one you describe in your picture ("the messages the OS sends are the same, the device driver uses differnent messages to talk to the hardware). For example, the block layer receives one kind of messages, but passes them on to different block devices. The USB layer receives one kind of messages, but can use different USB host controllers. And so on.
So the picture is much more difficult, there are layers and subsystems in the kernel, and the device drivers that actually talk to the hardware are at the bottom of that hierarchy. To confuse things more, both the device drivers and other layers come in the form of modules (for Linux). If you type
lsmod
, you can see which modules are active, and which module uses which other modules.Also, printing is a very bad example; most of the printer-specific processing happens in user space, and not in a device driver.
All of Windows, Linux and MacOS follow these principles, but the details are very different.
Does that help?
Edit:
Printing on Linux is today usually done with cups. Cups has a collection of programs that can render documents for various printers. All of these programs take a file (the document as pdf/postscript/...) and convert it to another file in a format the printer understands. All of this happens outside of the kernel, because none of this needs to access actual hardware. It just reads and writes files. Only the very last part, when the converted data is sent to the printer, uses the kernel. And then it can uses various paths, even for the same type of printer: via the network, via USB, via a serial connection, etc. And this last part often isn't printer specific.
So Linux doesn't really have printer-specific device drivers for the large majority of printers. (For a few printers, you may need one).