Operating Systems Lecture Notes-11
Operating Systems Lecture Notes-11
151
UNIT V
152
Fig: A Typical PCI Bus Structure
One way of communicating with devices is through registers associated with each port.
Registers may be one to four bytes in size, and may typically include ( a subset of ) the
following four:
1. The data-in register is read by the host to get input from the device.
2. The data-out register is written by the host to send output.
3. The status register has bits read by the host to ascertain the status of the device,
such as idle, ready for input, busy, error, transaction complete, etc.
4. The control register has bits written by the host to issue commands or to change
settings of the device such as parity checking, word length, or full- versus half-
duplex operation.
Above Figure shows some of the most common I/O port address ranges.
153
Another technique for communicating with devices is memory-mapped I/O.
o In this case a certain portion of the processor's address space is mapped to the
device, and communications occur by reading and writing directly to/from those
memory areas.
o Memory-mapped I/O is suitable for devices which must move large quantities of
data quickly, such as graphics cards.
o Memory-mapped I/O can be used either instead of or more often in combination
with traditional registers. For example, graphics cards still use registers for
control information such as setting the video mode.
o A potential problem exists with memory-mapped I/O, if a process is allowed to
write directly to the address space used by a memory-mapped I/O device.
o ( Note: Memory-mapped I/O is not the same thing as direct memory access,
DMA. See section 13.2.3 below. )
5.1.2.1 Polling
5.1.2.2 Interrupts
Interrupts allow devices to notify the CPU when they have data to transfer or when an
operation is complete, allowing the CPU to perform other duties when no I/O transfers
need its immediate attention.
The CPU has an interrupt-request line that is sensed after every instruction.
o A device's controller raises an interrupt by asserting a signal on the
interrupt request line.
o The CPU then performs a state save, and transfers control to
the interrupt handler routine at a fixed address in memory. ( The
CPU catches the interrupt and dispatches the interrupt handler. )
o The interrupt handler determines the cause of the interrupt, performs
the necessary processing, performs a state restore, and executes
154
a return from interrupt instruction to return control to the CPU. ( The
interrupt handler clears the interrupt by servicing the device. )
( Note that the state restored does not need to be the same state
as the one that was saved when the interrupt went off. See
below for an example involving time-slicing. )
Below Figure illustrates the interrupt-driven I/O procedure:
The above description is adequate for simple interrupt-driven I/O, but there are three
needs in modern computing which complicate the picture:
2. The need to determine which interrupt handler to invoke, without having to poll all
devices to see which one needs attention, and
155
3. The need for multi-level interrupts, so the system can differentiate between high- and
low-priority interrupts for proper response.
o Most CPUs now have two interrupt-request lines: One that is non-maskable for critical
error conditions and one that is maskable, that the CPU can temporarily ignore during
critical processing.
o The interrupt mechanism accepts an address, which is usually one of a small set of
numbers for an offset into a table called the interrupt vector. This table ( usually located
at physical address zero ? ) holds the addresses of routines prepared to process specific
interrupts.
o The number of possible interrupt handlers still exceeds the range of defined interrupt
numbers, so multiple handlers can be interrupt chained. Effectively the addresses held in
the interrupt vectors are the head pointers for linked-lists of interrupt handlers.
o Figure below shows the Intel Pentium interrupt vector. Interrupts 0 to 31 are non-
maskable and reserved for serious hardware and other errors. Maskable interrupts,
including normal device I/O interrupts begin at interrupt 32.
o Modern interrupt hardware also supports interrupt priority levels, allowing systems to
mask off only lower-priority interrupts while servicing a high-priority interrupt, or
conversely to allow a high-priority signal to interrupt the processing of a low-priority
one.
156
At boot time the system determines which devices are present, and loads the appropriate
handler addresses into the interrupt table.
During operation, devices signal errors or the completion of commands via interrupts.
Time slicing and context switches can also be implemented using the interrupt
mechanism.
o The scheduler sets a hardware timer before transferring control over to a user process.
o When the timer raises the interrupt request line, the CPU performs a state-save, and
transfers control over to the proper interrupt handler, which in turn runs the scheduler.
o The scheduler does a state-restore of a different process before resetting the timer and
issuing the return-from-interrupt instruction.
A similar example involves the paging system for virtual memory - A page fault
causes an interrupt, which in turn issues an I/O request and a context switch as
described above, moving the interrupted process into the wait queue and selecting a
different process to run. When the I/O request has completed ( i.e. when the requested
page has been loaded up into physical memory ), then the device interrupts, and the
interrupt handler moves the process from the wait queue into the ready queue, ( or
depending on scheduling algorithms and policies, may go ahead and context switch it
back onto the CPU. )
System calls are implemented via software interrupts, a.k.a. traps. When a ( library )
program needs work performed in kernel mode, it sets command information and
possibly data addresses in certain registers, and then raises a software interrupt. ( E.g.
21 hex in DOS. ) The system does a state save and then calls on the proper interrupt
handler to process the request in kernel mode. Software interrupts generally have low
priority, as they are not as urgent as devices with limited buffering space.
Interrupts are also used to control kernel operations, and to schedule activities for
optimal performance. For example, the completion of a disk read operation involves
two interrupts:
o A lower-priority interrupt transfers the data from the kernel memory space to
the user space, and then transfers the process from the waiting queue to the
ready queue.
157
o The Solaris OS uses a multi-threaded kernel and priority threads to assign
different threads to different interrupt handlers. This allows for the
"simultaneous" handling of multiple interrupts, and the assurance that high-
priority interrupts will take precedence over low-priority ones and over user
processes.
For devices that transfer large quantities of data ( such as disk controllers ), it is wasteful
to tie up the CPU transferring data in and out of registers one byte at a time.
Instead this work can be off-loaded to a special processor, known as the Direct Memory
Access, DMA, Controller.
The host issues a command to the DMA controller, indicating the location where the data
is located, the location where the data is to be transferred to, and the number of bytes of
data to transfer. The DMA controller handles the data transfer, and then interrupts the
CPU when the transfer is complete.
A simple DMA controller is a standard component in modern PCs, and many bus-
mastering I/O cards contain their own DMA hardware.
Handshaking between DMA controllers and their devices is accomplished through two
wires called the DMA-request and DMA-acknowledge wires.
While the DMA transfer is going on the CPU does not have access to the PCI bus (
including main memory ), but it does have access to its internal registers and primary and
secondary caches.
DMA can be done in terms of either physical addresses or virtual addresses that are
mapped to physical addresses. The latter approach is known as Direct Virtual Memory
Access, DVMA, and allows direct data transfer from one memory-mapped device to
another without using the main memory chips.
Direct DMA access by user processes can speed up operations, but is generally forbidden
by modern systems for security and protection reasons. ( I.e. DMA is a kernel-mode
operation. )
158
Fig: Steps in a DMA Transfer
159
Devices differ on many different dimensions, as outlined in Figure below:
Most devices can be characterized as either block I/O, character I/O, memory mapped file
access, or network sockets. A few devices are special, such as time-of-day clock and the
system timer.
Most OSes also have an escape, or back door, which allows applications to send
commands directly to device drivers if needed. In UNIX this is the ioctl( ) system call (
I/O Control ). Ioctl( ) takes three arguments - The file descriptor for the device driver
being accessed, an integer indicating the desired function to be performed, and an address
used for communicating or transferring additional information.
Block devices are accessed a block at a time, and are indicated by a "b" as the first
character in a long listing on UNIX systems. Operations supported include read( ), write(
), and seek( ).
o Accessing blocks on a hard drive directly ( without going through the file system
structure ) is called raw I/O, and can speed up certain operations by bypassing the
buffering and locking normally conducted by the OS. ( It then becomes the
application's responsibility to manage those issues. )
o A new alternative is direct I/O, which uses the normal file system access, but
which disables buffering and locking operations.
Memory-mapped file I/O can be layered on top of block-device drivers.
o Rather than reading in the entire file, it is mapped to a range of memory
addresses, and then paged into memory as needed using the virtual memory
system.
o Access to the file is then accomplished through normal memory accesses, rather
than through read( ) and write( ) system calls. This approach is commonly used
for executable program code.
160
Character devices are accessed one byte at a time, and are indicated by a "c" in UNIX
long listings. Supported operations include get( ) and put( ), with more advanced
functionality such as reading an entire line supported by higher-level library routines.
Because network access is inherently different from local disk access, most systems
provide a separate interface for network devices.
One common and popular interface is the socket interface, which acts like a cable or
pipeline connecting two networked entities. Data can be put into the socket at one end,
and read out sequentially at the other end. Sockets are normally full-duplex, allowing for
bi-directional data transfer.
The select( ) system call allows servers ( or other applications ) to identify sockets which
have data waiting, without having to poll all available sockets.
With blocking I/O a process is moved to the wait queue when an I/O request is made, and
moved back to the ready queue when the request completes, allowing other processes to
run in the meantime.
With non-blocking I/O the I/O request returns immediately, whether the requested I/O
operation has ( completely ) occurred or not. This allows the process to check for
available data without getting hung completely if it is not there.
161
One approach for programmers to implement non-blocking I/O is to have a multi-
threaded application, in which one thread makes blocking I/O calls ( say to read a
keyboard or mouse ), while other threads continue to update the screen or perform other
tasks.
A subtle variation of the non-blocking I/O is the asynchronous I/O, in which the I/O
request returns immediately allowing the process to continue on with other tasks, and
then the process is notified ( via changing a process variable, or a software interrupt, or a
callback function ) when the I/O operation has completed and the data is available for
use. ( The regular non-blocking I/O returns immediately with whatever results are
available, but does not complete the operation and notify the process later. )
Scheduling I/O requests can greatly improve overall efficiency. Priorities can also play a
part in request scheduling.
The classic example is the scheduling of disk accesses, as discussed in detail in chapter
12.
Buffering and caching can also help, and can allow for more flexible scheduling options.
On systems with many devices, separate request queues are often kept for each device:
162
5.1.4.2
Buffering
1. Speed differences between two devices. ( See Figure 13.10 below. ) A slow device may
write data into a buffer, and when the buffer is full, the entire buffer is sent to the fast
device all at once. So that the slow device still has somewhere to write while this is going
on, a second buffer is used, and the two buffers alternate as each becomes full. This is
known as double buffering. ( Double buffering is often used in ( animated ) graphics, so
that one screen image can be generated in a buffer while the other ( completed ) buffer is
displayed on the screen. This prevents the user from ever seeing any half-finished screen
images. )
2. Data transfer size differences. Buffers are used in particular in networking systems to
break messages up into smaller packets for transfer, and then for re-assembly at the
receiving side.
3. To support copy semantics. For example, when an application makes a request for a disk
write, the data is copied from the user's memory area into a kernel buffer. Now the
application can change their copy of the data, but the data which eventually gets written
out to
disk is
the
version
of the
data at
the time
the write
request
was
made.
163
Fig: Sun Enterprise 6000 device-transfer rates ( logarithmic )
5.1.4.3 Caching
Caching involves keeping a copy of data in a faster-access location than where the data is
normally stored.
Buffering and caching are very similar, except that a buffer may hold the only copy of a
given data item, whereas a cache is just a duplicate copy of some other data stored
elsewhere.
Buffering and caching go hand-in-hand, and often the same storage space may be used
for both purposes. For example, after a buffer is written to disk, then the copy in memory
can be used as a cached copy, (until that buffer is needed for other purposes. )
If multiple processes want to print at the same time, they each send their print data to files
stored in the spool directory. When each file is closed, then the application sees that print
job as complete, and the print scheduler sends each file to the appropriate printer one at a
time.
Support is provided for viewing the spool queues, removing jobs from the queues,
moving jobs from one queue to another queue, and in some cases changing the priorities
of jobs in the queues.
Spool queues can be general ( any laser printer ) or specific ( printer number 42. )
164
OSes can also provide support for processes to request / get exclusive access to a
particular device, and/or to wait until a device becomes available.
I/O requests can fail for many reasons, either transient ( buffers overflow ) or permanent (
disk crash ).
I/O requests usually return an error bit ( or more ) indicating the problem. UNIX systems
also set the global variable errno to one of a hundred or so well-defined values to indicate
the specific error that has occurred. ( See errno.h for a complete listing, or man errno. )
Some devices, such as SCSI devices, are capable of providing much more detailed
information about errors, and even keep an on-board error log that can be requested by
the host.
The I/O system must protect against either accidental or deliberate erroneous I/O.
User applications are not allowed to perform I/O in user mode - All I/O requests are
handled through system calls that must be performed in kernel mode.
Memory mapped areas and I/O ports must be protected by the memory management
system, but access to these areas cannot be totally denied to user programs. ( Video
games and some other applications need to be able to write directly to video memory for
optimal performance for example. ) Instead the memory protection system restricts access
so that only one process at a time can access particular parts of memory, such as the
portion of the screen memory corresponding to a particular window.
165