next up previous
Next: Upper-Half Tty Input Up: The Terminal Device Previous: Hardware

The Terminal Driver

We now study the device drivers for the terminal device. Together, these drivers are referred to as the terminal driver. A terminal driver implements two interfaces: an I/O interface that processes may use to interact with a user and a user interface that users may use to interact with processes. We saw the I/O interface earlier. The user interface has the following main characteristics:

characters are echoed

backspace and line-kill are supported

user is allowed to start and stop output

In the raw mode characters are passed directly to the process without any special processing. In the cbreak mode characters are echoed and special start and stop characters are supported. In the cooked mode back space and line-kill characters are supported in addition. We shall study these modes in greater detail when we look at their implementation.

Control Functions

A process may invoke the following control functions:

turn on BREAK in transmitter

turn off BREAK

look ahead 1 character

set input mode to raw

set input mode to cooked

set input mode to cbreak

return number of input chars

turn on echo

turn off echo

set `fill character'--the character to echo when input buffer is full

Device Driver Organization

Tty (and other device) driver routines can be divided into two sets: the upper-half and the lower-half. The upper half routines are called by the device-independent I/O routines such as read and write. They do not manipulate the device directly. Instead, they enqueue requests for transfer, and rely on routines in the lower-half to perform transfers later. The drivers use interrupt-driven processing to avoid the drawbacks of polling; therefore, the lower-half routines are the interrupt routines called by the input and output interrupt dispatchers.

The upper- and lower-half routines exchange data by writing to and reading from two circular buffers: the input and output buffers. These are needed because communication between the cpu and an SLU device is asynchronous. An input buffer contains characters received from the terminal by the input interrupt routine but not read by any upper-half routine. The output buffer contains data written by a upper-half routine but not transferred by the output interrupt routine.

Synchronization of the Upper and Lower Halves

Both the input and output buffers are instances of bounded buffers. For the input buffer, the (lower-half input) interrupt routine is the producer and the upper-half input routines such as ttyread and ttygetc are the consumers. For the output buffer, the upper-half output routines such as ttywrite and ttyputc are the producers and the (lower-half) output interrupt routine is the consumer.

Producers and consumers of bounded buffers need to synchronize their accesses so that a producer waits for a non-full buffer and a consumer waits for a non-empty buffer. But the lower-half interrupt routines cannot wait (recall the rules for interrupt processing). Therefore, only the higher-level input and output routines wait, on semaphores isem (input buffer non-empty) and osem ( output-buffer non-full) respectively. The input interrupt routine signals the former when it inserts a character into the input buffer and the output routine signals the latter when it removes a character from the output buffer. Thus the count field of isem (initialized to 0) indicates the number of characters in the input buffer and the count field of osem (initialized to size of output buffer) keeps the number of empty positions in the output buffer.

The inability of an input interrupt routine to wait for a non-full input buffer leads to buffer overflow problems. In the Xinu implementation, the input interrupt routine simple discards a character received when the buffer is full.

The inability of the output interrupt routine to wait for the output buffer to be non-empty, however, does not cause any problems. When the output interrupt routine finds an empty buffer, it simply disables interrupts and goes into the idle state. Higher-half output routines are responsible for enabling interrupts when they deposit characters into the output buffer. Enabling an interrupt when the device is idle causes an interrupt to be posted and the output interrupt routine to be executed.

Watermarks

The above strategy of the signalling osem whenever a character is output is inefficient. A process can deposit characters into the buffer much faster than the SLU can remove it from the buffer. Thus, when one or more processes have large amounts of data to output, the buffer is almost always full, with the processes waiting for it to be non-full. Under these conditions, a signal to osem can cause a higher-priority process waiting for a non-full buffer to unblock and produce another character, make the buffer full again, and thus block again. Thus, each character output can cause an expensive reschedule! Even when processes are of the same priority, a printing process would use (character consumption rate/character production rate) of its time quantum before being blocked.

To alleviate this problem, the output interrupt routine uses a popular technique consisting of watermarks. The output routine runs in two modes: normal and delayed. When in the normal mode, it signals osem every time it outputs a character. It goes from normal to delayed when it finds the buffer full beyond the high watermark. Under the delayed mode it does not signal osem but keeps a count of the number of times it should have done so. It goes from delayed to normal mode when the buffer has drained to the low watermark. At this point the routine msignal is called to make up for the lost signals. This technique reduces the reschedules since no signals are generated during the delayed mode.

In Xinu the high and low watermarks are defined by the constant OBMINSP. The output interrupt routine goes into delayed mode when OBMINSP + 1 space is left. It goes from delayed to normal mode after OBMINSP characters are output in the delayed mode. (What are the high and low watermarks under this scheme?)

Echo Buffer

In addition to the input/output buffer, the terminal driver maintains an echo buffer, which contains a queue of characters to be echoed. This buffer is filled by the input interrupt routine and emptied by the output interrupt routine. (When deciding on the next character to output, should the output routine service the echo or output buffer?)

Data Structures The terminal driver defines a control block for each terminal device connected to the cpu. These control blocks are kept in a control block table indexed by the minor number of the device. Each control block contains the following data:

The input and output buffers for the device, and the corresponding semaphores isem and osem.

An echo buffer which contains a queue of characters to be echoed. This buffer is filled by the input interrupt routine and emptied by the output interrupt routine.

Control Parameters, which are parameters set by the control functions and include information such as the mode of the terminal and whether characters should be echoed.

Terminal Parameters, which may vary from terminal to terminal, and include information such as whether the terminal allows erasing of characters, whether control characters are to be echoed as CTRL-X, and whether the line-kill command is to be supported.

User Parameters, which are parameters set by user commands such as the `stop output' command.

Internal Parameters, miscellaneous information such as whether the output routine is in the normal or delayed mode and the number of characters in the current line. (see pg. 163-164 of text).




next up previous
Next: Upper-Half Tty Input Up: The Terminal Device Previous: Hardware



Prasun Dewan
Thu Feb 12 11:37:50 EST 2004