Tanenbaum Chapter 5
The O/S at the lowest level must control access to the various hardware devices.
TeleVideo Model 925
+ 200 more
bar code reader
The majority of processes should not care what type of device I/O is done from.
For example, the ``tr'' program will translate characters whether they come
from a file on floppy or hard disk, from a keyboard, from a pipeline, or from
a network device.
The naming conventions for devices should not be dependent on the type of device.
In Unix, for example, all devices are accessed by ordinary path names within
the file system.
There may be conventions for device naming within such a system. For example,
/dev/fd* floppy fisk
/dev/mt* magnetic tape
/dev/st* streaming tape
To handle the variety of devices in an independent manner requires several
layers. These are
Device independant software
Function calls such as ``printf'', ``getchar'' etc are clearly part of the
I/O system. However, most of what these function calls do is at a software
level, divorced from the actual devices themselves.
For example, the call
printf("the value of %s is %d",
goes through these steps:
The last step is the only one that requires knowledge of the device, and this
is passed down to the next layer.
Scan the string given as, the first argument.
If there is a special sequence signalled by ``%'', determine the type of argument
expected (int, string, etc).
Find the value of the corresponding parameter for this sequence.
Convert it to a string representation.
Build a new string with all parameters substituted.
Write the string.
Frequently this layer insulates itself from the device by using buffers in
kernel space. The layer reads and writes to these buffers, and the device driver
layer is responsible for filling or emptying these.
The device itself will come with some controller card. This typically gives
a set of registers or memory and allows a set of operations to be performed
This set of operations depends on the device and cannot be generalised.
When the device does something, it will often take quite some time over it
(depending on device and operation). It will signal the end of this by generating
The process that is using the device can do one of two things: wait until the
device completes (block) or continue asynchronously (non-blocking).
In either case, something must happen when the interrupt occurs. If the process
has blocked, then it can be woken up. If the process continued then a signal
should be sent.
Note that a context switch may have occurred before the interrupt occurs. This
means that the device driver cannot leave any information on the user stack
or in user space because that may not be accessible. This makes writing device
drivers a bit tricky.
The responsibility of this layer is to convert device independent calls into
device dependent operations for the device controller.
Example device independent calls are
Devices are often categorised further:
open the device for reading/writing.
close the device.
read from the device.
write to the device.
A block device is one that stores information in fixed size blocks. Common
block sizes are 128 bytes to 1k bytes. Each block may be read independently
of the others, so the device allows random access to each block.
Typical block devices are disks.
Additional operations on block devices will include
breakup up I/O request into block-size requests.
order device requests to minimise seek time.
transfer blocks to and from buffers.
Character devices deliver or accept a stream of characters (bytes) without
regard to any other structure.
Such devices include terminals, mice, etc.
The above classification is not rigid. There may be devices that do not fit
into either. For example, in Unix System V there are streams that are devices
that implement queue-ing devices.
If a device is represented by a file in the file system, then there must be
some way of knowing that in fact it is not an ordinary file.
This information is maintained (in Unix) by information in the inode. A field
labels the device type. See this by ``ls -l'' on files in /dev
crw-rw-rw- root 22, 0 fb
brw-rw-rw- root 16, 2 fd0
``fb'' (frame buffer) is a character device, ``fd0'' is a block device. The
numbers ``22, 0'' are the major and minor device numbers respectively. These
identify which device driver it actually is.
Terminal drivers typically maintain two buffers in kernel space, one for input
and one for output. These are maintained as queues so that the correct order
When the device interrupts to say that data is ready to read, the data is placed
on the incoming queue.
The purpose of the input and output buffers is firstly to allow for different
rates of process and physical I/O. Nevertheless, if they get too out of step
data may be lost. For example, data coming in when the input buffer is full
may be lost.
If buffering was not used, then the reading process would have to be ready
to process it immediately and finish processing it before the next character
arrived. Typically, the input device would be much slower than the process.
What should the process do?
If it is blocked, then on each new arrival, it would have to be the active
process. This would involve a context switch on every character read. If it
is ``busy waiting'', then time would be wasted.
On a multi-tasking system either way wastes time. So the norm on multi-tasking
systems is to buffer character I/O. MSDOS would not need to buffer though.
when the backspace character is pressed, what should happen? The expectation
is that it should erase the currently showing character (if on a screen) and
move the cursor back.
Who should do this? If the process should do it, then it would have to be aware
of each character as it was typed. This would require unbuffered input.
The device driver could do it, because it needs to handle characters as they
arrive anyway. In this case the terminal driver does input mode processing.
If the device driver does this processing, then the process ultimately reading
from the device would not want to see the original data but the processed data.
The terminal drivers would maintain two input queues: the one containing the
raw data and the one containing the canonical data.
There are three modes that the driver can be in:
raw - the reading process gets unbuffered unprocessed input.
cooked - the reading process gets line-buffered canonical input.
rare - the reading process gets line-buffered unprocessed input.
The output side of this is that in cooked and rare modes the processing is
done, but in raw mode no output processing is done.
Unix uses the function ``ioctl'' to change terminal driver behaviour. For example
Because the set of operations is open-ended and poorly typed, this is being
replaced with a set of individual functions.
Echo or non-echo
Mode - raw, cooked, rare
Here is a program that uses the ioctl interface to the terminal driver
to control echoing of characters. Note that it will not work on a system
using the new POSIX terminal interface.
Physical terminals differ in their characteristics and what you can do with
them. Some terminals allow special sequences to be sent to them to control
their behaviour, such as clearing text to the end of line, moving around the
MSDOS often uses special device drivers such as ANSI.SYS. Unix removes this
from the device driver and places it in the device independent section. Unix
maintains a database of terminal properties in /etc/termcap. If a process wishes
to use a special sequence, it uses device independent functions to look up
this database and prepare the appropriate sequence. This is then sent to the
normal device driver.
The layered model is the current model for many Operating Systems, including
most flavours of Unix, VMS, etc. It gives a structure such as
memory and I/O devices
Client Server Model
This has become popular recently. It reduces the size of the kernel down to
a micro-kernel. For example, with the Mach system teh micro-kernel only looks
after htread scheduling, message passing, virtual memory and device drivers.
Everything else runs in user mode.