/dev/tty* terminals /dev/fd* floppy fisk /dev/mt* magnetic tape /dev/st* streaming tape /dev/mouse mouse
printf("the value of %s is %d", str, n)goes through these steps:
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.
This set of operations depends on the device and cannot be generalised.
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.
Typical block devices are disks.
Additional operations on block devices will include
Such devices include terminals, mice, etc.
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.
When the device interrupts to say that data is ready to read, the data is placed on the incoming queue.
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.
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:
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.
Here is a program that uses the ioctl interface to the terminal driver to control echoing of characters.
#include <stdio.h> #include <unistd.h> #include <stropts.h> #include <termio.h> const int SIZE = 128; int main(void) { struct termio tty_params; short flags; char buff[SIZE]; ioctl(0, TCGETA, &tty_params); /* save echo and other state */ flags = tty_params.c_lflag; /* turn off echoing */ tty_params.c_lflag &= ~ECHO; ioctl(0, TCSETA, &tty_params); printf("Enter password\n"); fgets(buff, SIZE, stdin); printf("Secret password was %s\n", buff); /* restore previous state */ tty_params.c_lflag = flags; ioctl(0, TCSETA, &tty_params); exit(0); }
There are so many different devices with so many different control features
that ioctl
has ended up as a mess. The parameters can be lots of
different types, and even the number of arguments can change.
For terminals, a new interface based on the termios
structure
has been devised. Functions using this are
tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow,
cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed, tcgetp-
grp, tcsetpgrp
These basically are used in the same way: tcgetattr()
instead
of ioctl(..., TCGETA, ...)
, tcsetattr()
instead
of ioctl(..., TCSETA, ...)
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 the termcap
and terminfo
databases. 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 functions tgetent, tgetflag, tgetnum, tgetstr, tgoto, tputs
are used to access the terminfo
database.