Computers are not useful unless we can put data into them, and get results out.
input -- data to computer
output -- data from computer
Here is the standard computer model in a diagram:
----- -------- |CPU| <----> |memory| ----- ^ -------- | | \ / ----- |i/o| -----
Examples of input devices:
keyboard, mouse, network, disk, scanner, camera, microphone, ??
Examples of output devices:
printer, (terminal) display, network, disk, speakers, ??
Note: We have seen only 2 of our simulator's I/O devices: the keyboard (for input), and the display (for output)
getc
, putc
, puts
These are actually OS implemented procedures. (The OS is the program that interfaces between the programmer or application user and the actual hardware. )
Should one user be able to display characters on another's display? Lock up another's keyboard? Send a file of infinite length to the printer, effectively shutting all others out?
In practice, the Operating System's (OS's) job is "resource management," allocating all portions of the computer system. Examples of resources are the processor itself, as well as all I/O devices.
Vocabulary, to form a picture of a disk:
Platter -- sort of like a phonograph record, or a CD.
Data is stored on a surface of a platter. Each platter can have one or two surfaces.
All platters are tied together and rotate around the spindle at a fixed speed.
Each surface has one or more read/write heads.
Platters are broken down into tracks. A single track is one of many concentric circles on the platter.
All the corresponding tracks on all surfaces, taken together, form a cylinder.
Each track is broken down into sectors.
Given: the sector position on the cylinder. (looked up in a table, or calculated from the disk address).
Therefore, the time to read a sector = seek time + rotate time + read time.
There are 2 possibilities.
input
output
How does the CPU know that the instruction has completed? (Is there any need to wait?)
What happens if the device encounters an error? (Does this halt the computer?)
Overload memory locations to use as communication channels.
For example,
address 0x0000 0000 -| . | real memory . | . | 0xffff 0000 -| 0xffff 0008 - data from keyboard (Keyboard_Data) 0xffff 0010 - data to display (Display_Data)
Then, by reading (loading) from location 0xffff0008
, data
is requested from the keyboard.
Then, by writing (storing) to location 0xffff0010
, data
is sent to the display.
The syscall code within the OS must be (in essence)
lw $2, Keyboard_Data # getc syscall return from syscalland
sw $2, Display_Data # putc syscall return from syscall
This method of using overloaded memory locations for communication with I/O devices is called memory mapped I/O.
Problems with memory-mapped I/O as currently given:
getc
presumably returns once a character has been typed.
What happens if the user does not type a character?
Types it on the wrong keyboard? Goes to get a drink
of water?
What happens to the data if the user types 2 characters
before getc
has been called?
How does the processor know if a character has been typed?
putc
and puts
:
how does the processor know that the device
is ready to print out a second character? What if the
printer jams? (printers and terminals are slow!)
What is needed is a way to convey information about the status of I/O devices. This status information is used to coordinate and synchronize the useage of devices.
address 0x0000 0000 -| . | real memory . | . | 0xffff 0000 -| 0xffff 0008 - data from keyboard (Keyboard_Data) 0xffff 000c - STATUS from keyboard (Keyboard_Status) 0xffff 0010 - data to display (Display_Data) 0xffff 0014 - STATUS from display (Display_Status)
Assume that the MSB is used to tell the status of a device.
MSB = 1 means device ready
MSB = 0 means device is busy (not ready)
Note that we can check for device ready/busy by looking to see if the Status word is negative (2's comp) or not.
For the keyboard:
a 1 means that a character has been typed
a 0 means that no character is available
For the display:
a 1 means that a new character may be sent
a 0 means that the device is still disposing
of a previous character
Then, the syscall code in the OS must be more like
getc_loop: lw $8, Keyboard_Status # getc syscall bgez $8, getc_loop lw $2, Keyboard_Data return from syscall # back to the user-level application putc_loop: lw $8, Display_Status # putc syscall bgez $8, putc_loop sw $4, Display_Data return from syscall # back to the user-level application
This scheme is known as busy waiting, or spin waiting. The little loop is called a spin wait loop.
Something that is not well explained (at this level) is how these status bits get set and cleared. The spin wait loop reads the status word, but does not change it.
The device (its controller) sets and clears the bit. An implied function is that the device sets the bit when it becomes ready to work on another character.
And, a load from Keyboard_Data
also clears the MSB of Keyboard_Status
And, a store to Display_Data
also clears the MSB of Display_Status
------------- ------------- |processor | <---------------------> | memory | ------------- | ------------- | | controller | ----------- ---------- | | display | <--> | Status |<---->| ----------- | Data | | ---------- | | | controller | ------------ ---------- | | keyboard | <--> | Status |<---->| ------------ | Data | | ---------- | |
Note that each device is "hooked up" and operates the same way with respect to the interaction between processor and memory and the device. In fact, any new device that can operate in this manner can be added to this computer system. This is an important feature.
If it takes 100 instructions to program this, and each instruction takes 20ns to execute, then it takes
100 * 20nsec = 2000nsec = 2 usec to execute this code
If a device takes 2msec (=2000usec) to deal with one character, then the percent of time spent waiting is
time waiting 2000us ------------ = --------------- = .999 = 99.9% total time 2000us + 2usec
We'd like a solution that spent less time "doing nothing."
getc
, the first key pressed is lost. There is only one
character's worth of storage.
This problem is actually a "Catch-22." The getc
code has
to be run often enough that no characters are lost, but
executing this code spin waits until a character is pressed.
The system could do nothing but getc
calls!
Some problems are solved by the use of queues (buffers). The check for device ready is separated from the sending and receiving of characters.
For the display:
putnextchar
:
Print a character if there is one in the
queue, and the device is ready.
This routine must be called at regular intervals,
so that there is progress toward emptying the queue.
printstring
:
put character(s) in queue and return. This is called
by the user level program when it desires to do
a putc
or puts
.
getnextchar
:
Get a character from the keyboard, if one is available,
and put it in a queue.
This routine must be called at regular intervals,
so that characters typed on the keyboard are not
missed.
getstring
:
get character from queue (if available) and return.
Some of the many difficulties caused by this situation:
getnextchar
regularly and
often so as not to lose characters.
putnextchar
regularly to empty
out the queue.
Copyright © Karen Miller, 2006 |