5-INPUT OUTPUT

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

5.1 PRINCIPLES OF I/O HARDWARE

5.1.1 I/O Devices

  • I/O devices can be divided into two categories: block devices and character devices.
    1. A block device is one that stores information in fixed-size blocks, each one with its own address. Common block sizes range from 512 to 65536 bytes. All transfers are in units of one or more entire(consecutive) blocks. The essential property of a block device is that it is possible to read or write each block independently of all the other ones. Hard disks and USB sticks are block devices.
    2. A character device delivers or accepts a stream of characters without regard to any block structure. It isn’t addressable and doesn’t have any seek operation. Printers and network interfaces can be seen as character devices.
  • However, some devices do not fit in this classification scheme. Clocks are neither block addressable nor do they generate or accept character streams. All they do is cause interrupts at well-defined intervals.
  • I/O devices cover a huge range in speeds(Figure 5-1).

5.1.2 Device Controllers

  • I/O units often consist of a mechanical component(the device itself) and an electronic component(called the device controller or adapter). Fig. 1-6.

  • The controller card usually has a connector on it, into which a cable leading to the device itself can be plugged. The interface between the controller and the device is often a low-level one.
  • The controller’s job is to convert the serial bit stream into a block of bytes and perform any error correction necessary. The block of bytes is typically first assembled, bit by bit, in a buffer inside the controller. After its checksum has been verified and the block has been declared to be error free, it can then be copied to main memory.

5.1.3 Memory-Mapped I/O

  • Each controller has a few registers that are used for communicating with the CPU.
    1. By writing into these registers, the OS can command the device to deliver data, accept data, switch itself on or off, or otherwise perform some action.
    2. By reading from these registers, the OS can learn what the device’s state is, whether it is prepared to accept a new command, and so on.
  • In addition to the control registers, many devices have a data buffer that the OS can read and write. E.g., a common way for computers to display pixels on the screen is to have a video RAM, which is a data buffer, available for programs or the OS to write into.

  • Two alternatives for CPU to communicates with the control registers and the device data buffers.
    1. Each control register is assigned an I/O port number, an 8- or 16-bit integer. The set of all the I/O ports form the I/O port space which is protected so that only OS can access it. The address spaces for memory and I/O are different(Fig. 5-2(a)).
      E.g., using IN REG, PORT, the CPU can read in control register PORT and store the result in CPU register REG.
    2. Map all the control registers into the memory space(Fig. 5-2(b)). Each control register is assigned a unique memory address to which no memory is assigned. This system is called memory-mapped I/O.
  • The x86 uses a hybrid scheme, with memory-mapped I/O data buffers and separate I/O ports for the control registers(Fig. 5-2(c)).
  • How do these schemes work in practice?
    1. When the CPU wants to read a word, either from memory or from an I/O port, it puts the address it needs on the bus’ address lines and then asserts a READ signal on a bus’ control line.
    2. A second signal line is used to tell whether I/O space or memory space is needed.
      -1- If it is memory space, the memory responds to the request.
      -2- If it is I/O space, the I/O device responds to the request.
      -3- If there is only memory space, every memory module and every I/O device compares the address lines to the range of addresses that it services. If the address falls in its range, it responds to the request. Since no address is assigned to both memory and an I/O device, there is no conflict.

Advantages of memory-mapped I/O.

  1. If special I/O instructions are needed to read and write the device control registers, access to them requires the use of assembly code since there is no way to execute an IN or OUT instruction in C. Calling such a procedure adds overhead to controlling I/O. With memory-mapped I/O, device control registers are just variables in memory and can be addressed in C the same way as any other variables.
    Summary: An I/O device driver can be written entirely in C with memory-mapped I/O, otherwise assembly code is needed.
  2. No special protection mechanism is needed to keep user processes from performing I/O. All the OS has to do is refrain from putting that portion of the address space containing the control registers in any user’s virtual address space.
  3. Every instruction that can reference memory can also reference control registers. E.g., The instruction TEST that tests a memory word for 0 can also be used to test a control register for 0. Without memory-mapped I/O, the control register must first be read into the CPU, then tested, requiring two instructions instead of one, thus slowing down the responsiveness of detecting an device.
LOOP:   TEST PORT 4     // check if port 4 is 0
        BEQ READY       // if it is 0, go to ready
        BRANCH LOOP // otherwise, continue testing
READY:

Disadvantages of memory-mapped I/O.

  1. Since most computers cache memory words, caching a device control register would be disastrous. For the above codes: The first reference to PORT 4 would cause it to be cached. Subsequent references would take the value from the cache and not ask the device. When the device became ready, the software would have no way of finding out. So, the loop would go on forever.
    Solution: the hardware has to be able to selectively disable caching on a per-page basis. This feature adds extra complexity to both the hardware and the OS.

  1. If there is only one address space, then all memory modules and all I/O devices must examine all memory references to see which ones to respond to.
    -1- If the computer has a single bus(Fig. 5-3(a)), having everyone look at every address is straightforward.
    -2- But modern computers have a dedicated high-speed memory bus(Fig. 5-3(b)). The trouble is that the I/O devices have no way of seeing memory addresses as they go by on the memory bus, so they can’t respond to them.
    Solution: First send all memory references to the memory, if the memory fails to respond, then the CPU tries the other buses. This design requires additional hardware complexity.

5.1.4 Direct Memory Access

  • A DMA controller has access to the system bus independent of the CPU. It contains several registers that can be written and read by the CPU. These include a memory address register, a byte count register, and one or more control registers that specify the I/O port to use, the direction of the transfer(reading from or writing to the I/O device), the transfer unit(byte at a time or word at a time), and the number of bytes to transfer in one burst.
  • How disk reads occur when DMA isn’t used.
    1. The disk controller reads the block from the drive serially, bit by bit, until the entire block is in the controller’s internal buffer.
    2. It computes the checksum to verify that no read errors have occurred.
    3. The controller causes an interrupt.
    4. The OS starts running, it can read the disk block from the controller’s buffer a byte or a word at a time by executing a loop, with each iteration reading one byte or word from a controller device register and storing it in main memory.

  • When DMA is used:
    1. The CPU programs the DMA controller by setting its registers so it knows what to transfer where(step 1). It also issues a command to the disk controller telling it to read data from the disk into its internal buffer and verify the checksum. When valid data are in the disk controller’s buffer, DMA can begin.
    2. The DMA controller initiates the transfer by issuing a read request over the bus to the disk controller(step 2). The memory address to write to is on the bus’ address lines, so when the disk controller fetches the next word from its internal buffer, it knows where to write it.
    3. The write to memory is another standard bus cycle(step 3).
    4. When the write is complete, the disk controller sends an acknowledgement signal to the DMA controller over the bus(step 4). The DMA controller then increments the memory address to use and decrements the byte count.
  • If the byte count is still greater than 0, steps 2 through 4 are repeated until the count reaches 0. At that time, the DMA controller interrupts the CPU to let it know that the transfer is now complete. When the OS starts up, it doesn’t have to copy the disk block to memory; it is already there.
  • Many buses can operate in two modes: word-at-a-time mode and block mode. Some DMA controllers can operate in either mode.
    1. In word-at-a-time mode, the operation is as described above: the DMA controller requests the transfer of one word and gets it. If the CPU also wants the bus, it has to wait. The mechanism is called cycle stealing because the device controller steals an occasional bus cycle from the CPU once in a while, delaying it slightly.
    2. In block mode, the DMA controller tells the device to acquire the bus, issue a series of transfers, then release the bus. This is called burst mode. It is more efficient than cycle stealing because acquiring the bus takes time and multiple words can be transferred for the price of one bus acquisition. The down side to burst mode is that it can block the CPU and other devices for a substantial period if a long burst is being transferred.
    1. Most DMA controllers use physical memory addresses for their transfers. Using physical addresses requires the OS to convert the virtual address of the intended memory buffer into a physical address and write this physical address into the DMA controller’s address register.
    2. The other scheme is to write virtual addresses into the DMA controller. Then the DMA controller must use the MMU to have the virtual-to-physical translation done. Only if the MMU is part of the memory rather than part of the CPU, can virtual addresses be put on the bus.
  • Recall that the disk first reads data into its internal buffer before DMA can start. Why does it need an internal buffer?
    1. By doing internal buffering, the disk controller can verify the checksum before starting a transfer. If the checksum is incorrect, an error is signaled and no transfer is done.
    2. Once a disk transfer has started, the bits keep arriving from the disk at a constant rate, whether the controller is ready for them or not. If the controller tried to write data directly to memory, it would have to go over the system bus for each word transferred.
      -1- If the bus were busy, the controller would have to wait. If the next disk word arrived before the previous one had been stored, the controller would have to store it somewhere.
      -2- If the bus were very busy, the controller might end up storing only a few words and having a lot of administration to do as well.
      When the block is buffered internally, the bus isn’t needed until the DMA begins, so the design of the controller is simpler because the DMA transfer to memory isn’t time critical.
  • Some computers don’t use DMA since the CPU is often far faster than the DMA controller and can do the job much faster(when the limiting factor isn’t the speed of the I/O device). If there is no other work for it to do, having the fast CPU wait for the slow DMA controller to finish is pointless.

5.1.5 Interrupts Revisited

  • Fig. 5-5. At the hardware level, interrupts work as follows:
    1. When an I/O device has finished the work given to it, it causes an interrupt (assuming that interrupts have been enabled by the OS). It does this by asserting a signal on a bus line that it has been assigned.
    2. This signal is detected by the interrupt controller chip on the parentboard, which then decides what to do.
      -1- If no other interrupts are pending, the interrupt controller handles the interrupt immediately.
      -2- If another interrupt is in progress, or another device has made a simultaneous request on a higher-priority interrupt request line on the bus, the device is ignored for the moment. It continues to assert an interrupt signal on the bus until it is serviced by the CPU.
    3. To handle the interrupt, the controller puts a number on the address lines specifying which device wants attention and asserts a signal to interrupt the CPU.
    4. The interrupt signal causes the CPU to stop what it is doing and start doing something else. The number on the address lines is used as an index into a table called the interrupt vector to fetch a new program counter. This program counter points to the start of the corresponding interrupt-service procedure.(Typically traps and interrupts use the same mechanism from this point on, sharing the same interrupt vector.)
    5. After it starts running, the interrupt-service procedure acknowledges the interrupt by writing a certain value to one of the interrupt controller’s I/O ports. This acknowledgement tells the controller that it is free to issue another interrupt. By having the CPU delay this acknowledgement until it is ready to handle the next interrupt, race conditions involving multiple interrupts can be avoided.
  • The hardware always saves certain information before starting the service procedure. The program counter must be saved, so the interrupted process can be restarted.
  • Most CPUs save the information on the stack.
    1. If the current stack is used, it may well be a user process stack. The stack pointer may not even be legal, which would cause a fatal error when the hardware tried to write some words at the address pointed to.
    2. If the kernel stack is used, there is a better chance of the stack pointer being legal. But switching into kernel mode requires changing MMU contexts and will invalidate most or all of the cache and TLB. Reloading all of these will increase the time to process an interrupt and thus waste CPU time.
  • An interrupt that leaves the machine in a well-defined state is called a precise interrupt. Such an interrupt has four properties:
    1. The PC(Program Counter) is saved in a known place.
    2. All instructions before the one pointed to by the PC have completed.
    3. No instruction beyond the one pointed to by the PC has finished.
    4. The execution state of the instruction pointed to by the PC is known.

  • An interrupt that doesn’t meet these requirements is called an imprecise interrupt. Fig. 5-6(b) illustrates an imprecise interrupt, where different instructions near the program counter are in different stages of completion, with older ones not necessarily more complete than younger ones.

5.2 PRINCIPLES OF I/O SOFTWARE

5.2.1 Goals of the I/O Software

  • Device independence. We should be able to write programs that can access any I/O device without having to specify the device in advance. It is up to the OS to take care of the problems caused by the fact that devices are different and require different command sequences to read or write.
  • Uniform naming. The name of a file or a device should be a string or an integer and not depend on the device. So, all files and devices are addressed the same way: by a path name.
  • Error handling. Errors should be handled as close to the hardware as possible. If the controller discovers an error, it should try to correct the error itself if it can. If it cannot, then the device driver should handle it. Only if the lower layers are not able to deal with the problem should the upper layers be told about it.
  • Synchronous(blocking) VS Asynchronous(interrupt-driven) transfers. Most physical I/O is asynchronous(the CPU starts the transfer and goes off to do something else until the interrupt arrives), but user programs are easier to write if the I/O operations are blocking. It is up to the OS to make operations that are actually interrupt-driven look blocking to the user programs. Some high-performance applications need to control all the details of the I/O, so OS make asynchronous I/O available to them.
  • Buffering. Often data that come off a device can’t be stored directly in their final destination. E.g., when a packet comes in off the network, the OS doesn’t know where to put it until it has stored the packet somewhere and examined it. Also, some devices have severe real-time constraints, so the data must be put into an output buffer in advance to decouple the rate at which the buffer is filled from the rate at which it is emptied, in order to avoid buffer under runs.
  • Sharable vs. Dedicated devices. Some I/O devices(disks…) can be used by many users at the same time, other devices(printers…) have to be dedicated to a single user until that user is finished.

5.2.2 Programmed I/O

  • There are three fundamentally different ways that I/O can be performed: programmed I/O(have the CPU do all the work), interrupt-driven I/O and DMA I/O.

  • Consider a process that wants to print the string ‘‘ABCDEFGH” on the printer via a serial interface.
    1. The software assembles the string in a buffer in user space(Fig. 5-7(a)).
    2. The user process acquires the printer for writing by making a system call to open it. If the printer is currently in use by another process, this call will fail and return an error code or will block until the printer is available. Once it has the printer, the user process makes a system call telling the OS to print the string on the printer.
    3. The OS then copies the buffer with the string to an array in kernel space. It then checks to see if the printer is currently available. If not, it waits until it is.
    4. As soon as the printer is available, the OS copies the first character to the printer’s data register, in this example using memory-mapped I/O. This action activates the printer. The character may not appear yet because some printers buffer a line or a page before printing anything.
    5. As soon as it has copied the first character to the printer(Fig. 5-7(b)), the OS checks to see if the printer is ready to accept another one. Generally, the printer has a second register, which gives its status. The act of writing to the data register causes the status to become not ready. When the printer controller has processed the current character, it indicates its availability by setting some bit in its status register or putting some value in it.
    6. At this point the OS waits for the printer to become ready again. When that happens, it prints the next character(Fig. 5-7(c)). This loop continues until the entire string has been printed. Then control returns to the user process.

  • Fig. 5-8. First the data are copied to the kernel. Then the OS enters a tight loop, outputting the characters one at a time.
    The essential aspect of programmed I/O is that after outputting a character, the CPU continuously polls the device to see if it is ready to accept another one. This behavior is often called polling or busy waiting.
  • Disadvantage: tying up the CPU full time until all the I/O is done.
    If the time to print a character is very short, then busy waiting is fine. But if the CPU has other work to do, busy waiting is inefficient.

5.2.3 Interrupt-Driven I/O

  • Fig. 5-9(a). When the system call to print the string is made, the buffer is copied to kernel space, and the first character is copied to the printer as soon as it is willing to accept a character. At that point the CPU calls the scheduler and some other process is run. The process that asked for the string to be printed is blocked until the entire string has printed.
  • Fig. 5-9(b). When the printer has printed the character and is prepared to accept the next one, it generates an interrupt. This interrupt stops the current process and saves its state. Then the printer interrupt-service procedure is run. If there are no more characters to print, the interrupt handler takes some action to unblock the user. Otherwise, it outputs the next character, acknowledges the interrupt, and returns to the process that was running just before the interrupt, which continues from where it left off.

5.2.4 I/O Using DMA

  • Disadvantage of interrupt-driven I/O: An interrupt occurs on every character, so wasting CPU time.
    Solution: let the DMA controller feed the characters to the printer one at time, without the CPU being bothered.
  • In essence, DMA is programmed I/O with the DMA controller doing all the work instead of the main CPU. This strategy requires special hardware(the DMA controller) but frees up the CPU during the I/O to do other work. Fig. 5-10.

  • Pro: there is one interrupt per buffer printed instead of one per character.
    Con: the DMA controller is much slower than the main CPU. If the DMA controller isn’t capable of driving the device at full speed, or the CPU usually has nothing to do while waiting for the DMA interrupt, then interrupt-driven I/O or even programmed I/O may be better.

5.3 I/O SOFTWARE LAYERS

  • I/O software is organized in four layers(Fig. 5-11). Each layer has a function to perform and an interface to the adjacent layers.

5.3.1 Interrupt Handlers

  • The best way to hide interrupts is to have the driver starting an I/O operation block until the I/O has completed and the interrupt occurs. E.g., the driver can block itself by doing a down on a semaphore, a wait on a condition variable, a receive on a message, or something similar.
  • When the interrupt happens, the interrupt procedure does whatever it has to in order to handle the interrupt. Then it can unblock the driver that was waiting for it. This model works best if drivers are structured as kernel processes with their own states, stacks, and program counters.
  • Steps that must be performed in software after the hardware interrupt has completed.
    1. Save any registers(including the PSW) that have not been saved by the interrupt hardware.
    2. Set up a context for the interrupt-service procedure. Doing this may involve setting up the TLB, MMU and a page table.
    3. Set up a stack for the interrupt service-procedure.
    4. Acknowledge the interrupt controller. If there is no centralized interrupt controller, reenable interrupts.
    5. Copy the registers from where they were saved(possibly some stack) to the process table.
    6. Run the interrupt-service procedure. It will extract information from the interrupting device controller’s registers.
    7. Choose which process to run next. If the interrupt has caused some high-priority process that was blocked to become ready, it may be chosen to run now.
    8. Set up the MMU context for the process to run next. Some TLB set-up may also be needed.
    9. Load the new process’ registers, including its PSW.
    10. Start running the new process.

5.3.2 Device Drivers

  • Each I/O device attached to a computer needs the device driver for controlling it. Each device driver handles one device type, or one class of closely related devices.
  • In order to access the device’s hardware(i.e., the controller’s registers), the device driver has to be part of the OS kernel.
  • OS needs to have an architecture that allows drivers written by outsiders be installed in it. Device drivers are normally positioned below the rest of the OS. Figure 5-12.

  • OS classify drivers into several categories. Common categories are the block devices (such as disks, which contain data blocks that can be addressed independently) and the character devices(such as keyboards and printers, which generate or accept a stream of characters).
  • Most OS define a standard interface that all block drivers must support and a second standard interface that all character drivers must support. These interfaces consist of a number of procedures that the rest of the OS can call to get the driver to do work for it.
  • Modern OS can dynamically load drivers into the system during execution. Different systems handle loading drivers in different ways.
  • A device driver has several functions. One is to accept abstract read and write requests from the device-independent software above it and see that they are carried out.
  • Many device drivers have a similar general structure. A typical driver starts out by checking the input parameters to see if they are valid. If not, an error is returned. If they are valid, a translation from abstract to concrete terms may be needed. For a disk driver, this may mean converting a linear block number into the head, track, sector, and cylinder numbers for the disk’s geometry.
  • Next the driver may check if the device is currently in use. If it is, the request will be queued for later processing. If the device is idle, the hardware status will be examined to see if the request can be handled now. It may be necessary to switch the device on or start a motor before transfers can be begun. Once the device is on and ready to go, the actual control can begin.
  • Controlling the device means issuing a sequence of commands to it. The driver is the place where the command sequence is determined, depending on what has to be done. After the driver knows which commands it is going to issue, it starts writing them into the controller’s device registers. After each command is written to the controller, it may be necessary to check to see if the controller accepted the command and is prepared to accept the next one. This sequence continues until all the commands have been issued. Some controllers can be given a linked list of commands and told to read and process them all by itself without further help from the OS.
  • After the commands have been issued, one of two situations will apply. In many cases the device driver must wait until the controller does some work for it, so it blocks itself until the interrupt comes in to unblock it. In other cases, the operation finishes without delay, so the driver need not block. In the former case, the blocked driver will be awakened by the interrupt. In the latter case, it will never go to sleep. Either way, after the operation has been completed, the driver must check for errors. If everything is all right, the driver may have some data to pass to the device-independent software(e.g., a block just read).
  • Finally, it returns some status information for error reporting back to its caller. If any other requests are queued, one of them can now be selected and started. If nothing is queued, the driver blocks waiting for the next request.
  • In a hot-pluggable system, devices can be added or removed while the computer is running. As a result, while a driver is busy reading from some device, the system may inform it that the user has suddenly removed that device from the system. Not only must the current I/O transfer be aborted without damaging any kernel data structures, but any pending requests for the now-vanished device must also be gracefully removed from the system and their callers given the bad news. Furthermore, the unexpected addition of new devices may cause the kernel to juggle resources(e.g., interrupt request lines), taking old ones away from the driver and giving it new ones in their place.
  • Drivers are not allowed to make system calls, but they often need to interact with the rest of the kernel. Usually, calls to certain kernel procedures are permitted. E.g., there are usually calls to allocate and deallocate hardwired pages of memory for use as buffers. Other useful calls are needed to manage the MMU, timers, the DMA controller, the interrupt controller, and so on.

5.3.3 Device-Independent I/O Software

  • The functions shown in Fig. 5-13 are typically done in the device-independent software.

  • The basic function of the device-independent software is to perform the I/O functions that are common to all devices and to provide a uniform interface to the user-level software.

Uniform Interfacing for Device Drivers

  • Fig. 5-14(a). Each device driver has a different interface to the OS, that is, the driver functions available for the system to call and the kernel functions that the driver needs differ from driver to driver.
  • Fig. 5-14(b). All drivers have the same interface. The way this works is as follows.
    1. For each class of devices(disks, printers…), the OS defines a set of functions that the driver must supply.
    2. Often the driver holds a table with pointers into itself for these functions.
    3. When the driver is loaded, the OS records the address of this table of function pointers, so when it needs to call one of the functions, it can make an indirect call via this table. This table of function pointers defines the interface between the driver and the rest of the OS.
  • How I/O devices are named. The device-independent software takes care of mapping symbolic device names onto the proper driver. In UNIX a device name, such as /dev/disk0, uniquely specifies the i-node for a special file, and this i-node contains the major device number, which is used to locate the appropriate driver. The i-node also contains the minor device number, which is passed as a parameter to the driver in order to specify the unit to be read or written. All devices have major and minor numbers, and all drivers are accessed by using the major device number to select the driver.
  • How does the system prevent users from accessing devices that they are not entitled to access? In UNIX, devices appear in the file system as named objects, which means that the usual protection rules for files also apply to I/O devices. The system administrator can then set the proper permissions for each device.

Buffering

  • Consider a process that wants to read data from a modem.

  • Fig. 5-15(a).
    1. The user process do a read system call and block waiting for one character.
    2. Each arriving character causes an interrupt.
    3. The interrupt-service procedure hands the character to the user process and unblocks it.
    4. After putting the character somewhere, the process reads another character and blocks again.
  • Cons: The user process has to be started up for every incoming character which is inefficient.
  • Fig. 5-15(b).
    1. The user process provides an n-character buffer in user space and does a read of n characters.
    2. The interrupt-service procedure puts incoming characters in this buffer until it is completely full. Only then does it wakes up the user process.
  • Cons: What happens if the buffer is paged out when a character arrives? The buffer could be locked in memory, but if many processes start locking pages in memory, the pool of available pages will shrink and performance will degrade.
  • Fig. 5-15(c).
    1. Create a buffer inside the kernel and have the interrupt handler put the characters there.
    2. When this buffer is full, the page with the user buffer is brought in, if needed, and the buffer copied there in one operation.
  • Cons: What happens to characters that arrive while the page with the user buffer is being brought in from the disk? Since the buffer is full, there is no place to put them.
  • Fig. 5-15(d). Solution is double buffering.
    1. After the first buffer fills up, but before it has been emptied, the second one is used.
    2. When the second buffer fills up, it is available to be copied to the user.
    3. While the second buffer is being copied to user space, the first one can be used for new characters. The two buffers take turns: while one is being copied to user space, the other is accumulating new input.
  • Circular buffer. It consists of a region of memory and two pointers: one points to the next free word where new data can be placed; the other points to the first word of data in the buffer that has not been removed yet. The hardware advances the first pointer as it adds new data and the OS advances the second pointer as it removes and processes data. Both pointers wrap around, going back to the bottom when they hit the top.
  • Buffering is also important on output. Consider how output is done to the modem without buffering using the model of Fig. 5-15(b). The user process executes a write system call to output n characters. The system has two choices.
    1. It can block the user until all the characters have been written, but this could take a very long time over a slow telephone line.
    2. It can release the user immediately and do the I/O while the user computes some more, but how does the user process know that the output has been completed and it can reuse the buffer? The system could generate a signal or software interrupt, but that style of programming is difficult and prone to race conditions.
  • Solution is for the kernel to copy the data to a kernel buffer(Fig. 5-15(c)), and unblock the caller immediately. Now it doesn’t matter when the actual I/O has been completed. The user is free to reuse the buffer the instant it is unblocked.

  • If data get buffered too many times, performance suffers. Consider the network of Fig. 5-16. Here a user does a system call to write to the network.
    1. The kernel copies the packet to a kernel buffer to allow the user to proceed immediately(step 1). At this point the user program can reuse the buffer.
    2. When the driver is called, it copies the packet to the controller for output(step 2). The reason it doesn’t output to the wire directly from kernel memory is that once a packet transmission has been started, it must continue at a uniform speed. The driver can’t guarantee that it can get to memory at a uniform speed because DMA channels and other I/O devices may steal many cycles. Failing to get a word on time would ruin the packet. By buffering the packet inside the controller, this problem is avoided.
    3. After the packet has been copied to the controller’s internal buffer, it is copied out onto the network(step 3).
    4. Next the packet is copied to the receiver’s kernel buffer(step 4).
    5. Finally, it is copied to the receiving process’ buffer(step 5). Usually, the receiver then sends back an acknowledgement. When the sender gets the acknowledgement, it is free to send the next packet.
  • All this copying is going to slow down the transmission rate considerably because all the steps must happen sequentially.

Error Reporting

  • One class of I/O errors is programming errors. These occur when a process asks for something impossible: writing to an input device, reading from an output device and so on. The action to take on these errors is report back an error code to the caller.
  • Another class of errors is actual I/O errors, e.g., trying to write a disk block that has been damaged or trying to read from a camcorder that has been switched off. It is up to the driver to determine what to do. If the driver doesn’t know what to do, it may pass the problem back up to device-independent software. What this software does depends on the environment and the nature of the error.
  • Some errors can’t be handled this way. E.g., a critical data structure, such as the root directory, may have been destroyed. In this case, the system have to display an error message and terminate. There isn’t much else it can do.

Allocating and Releasing Dedicated Devices

  • Some devices(printers…) can be used only by a single process at any given moment. It is up to the OS to examine requests for device usage and accept or reject them, depending on whether the requested device is available or not.
  • One way to handle these requests is to require processes to perform opens on the special files for devices directly. If the device is unavailable, the open fails. Closing such a dedicated device then releases it.
  • Another way is to have special mechanisms for requesting and releasing dedicated devices. An attempt to acquire a device that isn’t available blocks the caller. Blocked processes are put on a queue. Sooner or later, the requested device becomes available and the first process on the queue is allowed to acquire it and continue execution.

5.3.4 User-Space I/O Software

  • Although most of the I/O software is within the OS, a small portion of it consists of libraries linked together with user programs. In other systems, libraries can be loaded during program execution. Either way, the collection of all these library procedures is part of the I/O system.
  • Spooling is a way of dealing with dedicated I/O devices in a multiprogramming system. Consider a spooled device: a printer. Although it would be easy to let any user process open the character special file for the printer, suppose a process opened it and then did nothing for hours. No other process could print anything.
  • Instead what is done is to create a daemon and a spooling directory. To print a file, a process first generates the entire file to be printed and puts it in the spooling directory. It is up to the daemon, which is the only process having permission to use the printer’s special file, to print the files in the directory. By protecting the special file against direct use by users, the problem of having someone keeping it open unnecessarily long is eliminated.

  • Figure 5-17 summarizes the I/O system, showing all the layers and the principal functions of each layer.
    The arrows show the flow of control. When a user program tries to read a block from a file, the OS is invoked to carry out the call. The device-independent software looks for it in the buffer cache. If the needed block isn’t there, it calls the device driver to issue the request to the hardware to go get it from the disk. The process is then blocked until the disk operation has been completed and the data are safely available in the caller’s buffer.
  • When the disk is finished, the hardware generates an interrupt. The interrupt handler is run to discover what has happened, that is, which device wants attention right now. It then extracts the status from the device and wakes up the sleeping process to finish off the I/O request and let the user process continue.

5.4 DISKS

5.4.3 Disk Arm Scheduling Algorithms

  • The time required to read or write a disk block is determined by three factors:
    1. Seek time(the time to move the arm to the proper cylinder).
    2. Rotational delay(how long for the proper sector to appear under the reading head).
    3. Actual data transfer time.
      For most disks, the seek time dominates the other two times, so reducing the mean seek time can improve system performance substantially.
  • When the disk is heavily loaded, while the arm is seeking on behalf of one request, other disk requests may be generated by other processes. Many disk drivers maintain a table, indexed by cylinder number, with all the pending requests for each cylinder chained together in a linked list headed by the table entries.
  • Consider a disk with 40 cylinders. A request comes in to read a block on cylinder 11. While the seek to cylinder 11 is in progress, new requests come in for cylinders 1, 36, 16, 34, 9, and 12, in that order. They are entered into the table of pending requests, with a separate linked list for each cylinder. When the current request(for cylinder 11) is finished, the disk driver has a choice of which request to handle next.
  • Using FCFS, it would go next to cylinder 1, then to 36, and so on. This algorithm would require arm motions of 10, 35, 20, 18, 25, and 3, respectively, for a total of 111 cylinders.

  • SSF(Shortest Seek First) always handle the closest request next to minimize seek time.
  • Problem: With a heavily loaded disk, the arm will tend to stay in the middle of the disk most of the time, so requests at either extreme will have to wait until a statistical fluctuation in the load causes there to be no requests near the middle. Requests far from the middle may get poor service. The goals of minimal response time and fairness are in conflict here.
  • Elevator Algorithm keep moving in the same direction until there are no more outstanding requests in that direction, then they switch directions. This algorithm requires the software to maintain the current direction bit, UP or DOWN. When a request finishes, the disk driver checks the bit. If it is UP, the arm is moved to the next highest pending request. If no requests are pending at higher positions, the direction bit is reversed. When the bit is set to DOWN, the move is to the next lowest requested position, if any. If no request is pending, it just stops and waits.

  • Figure 5-25 shows the elevator algorithm using the same seven requests as Fig. 5-24, assuming the direction bit was initially UP. One nice property the elevator algorithm has is that given any collection of requests, the upper bound on the total motion is fixed: twice the number of cylinders.
  • Modification of this algorithm that has a smaller variance in response times is to always scan in the same direction. When the highest-numbered cylinder with a pending request has been serviced, the arm goes to the lowest-numbered cylinder with a pending request and then continues moving in an upward direction.
  • Some disk controllers provide a way for the software to inspect the current sector number under the head. With such a controller, another optimization is possible.
  • If two or more requests for the same cylinder are pending, the driver can issue a request for the sector that will pass under the head next. When multiple tracks are present in a cylinder, consecutive requests can be for different tracks with no penalty. The controller can select any of its heads almost instantaneously(head selection involves neither arm motion nor rotational delay).
  • If the disk has the property that seek time is much faster than the rotational delay, then a different optimization should be used. Pending requests should be sorted by sector number, and as soon as the next sector is about to pass under the head, the arm should be zipped over to the right track to read or write it.
  • With a modern hard disk, the seek and rotational delays so dominate performance that reading one or two sectors at a time is very inefficient. So, many disk controllers always read and cache multiple sectors, even when only one is requested. Typically any request to read a sector will cause that sector and much or all the rest of the current track to be read, depending upon how much space is available in the controller’s cache memory.
  • The use of the cache is determined dynamically by the controller. In its simplest mode, the cache is divided into two sections, one for reads and one for writes. If a subsequent read can be satisfied out of the controller’s cache, it can return the requested data immediately.
  • The disk controller’s cache is independent of the OS’s cache.
    1. The controller’s cache usually holds blocks that have not actually been requested, but which were convenient to read because they happened to pass under the head as a side effect of some other read.
    2. Any cache maintained by the OS will consist of blocks that were explicitly read and which the OS thinks might be needed again in the near future(e.g., a disk block holding a directory block).
  • When several drives are present on the same controller, the OS should maintain a pending request table for each drive separately. Whenever any drive is idle, a seek should be issued to move its arm to the cylinder where it will be needed next (assuming the controller allows overlapped seeks). When the current transfer finishes, a check can be made to see if any drives are positioned on the correct cylinder. If one or more are, the next transfer can be started on a drive that is already on the right cylinder. If none of the arms is in the right place, the driver should issue a new seek on the drive that just completed a transfer and wait until the next interrupt to see which arm gets to its destination first.
  • It is important to realize that all of the above disk-scheduling algorithms tacitly assume that the real disk geometry is the same as the virtual geometry. If it is not, then scheduling disk requests makes no sense because the OS can’t really tell whether cylinder 40 or cylinder 200 is closer to cylinder 39. On the other hand, if the disk controller can accept multiple outstanding requests, it can use these scheduling algorithms internally. In that case, the algorithms are still valid, but one level down, inside the controller.

5.5 CLOCKS

  • Clocks(also called timers) are essential to the operation of any multiprogrammed system. They maintain the time of day and prevent one process from monopolizing the CPU, among other things. The clock software can take the form of a device driver.

5.5.1 Clock Hardware

  • Two types of clocks are used in computers.
    1. One kind of clock are tied to the 110- or 220-volt power line and cause an interrupt on every voltage cycle, at 50 or 60 Hz. These clocks used to dominate, but are rare nowadays.
    2. The other kind is built out of three components: a crystal oscillator, a counter, and a holding register(Fig. 5-28). Crystal oscillator can be made to generate a periodic signal of great accuracy. At least one such circuit is found in any computer, providing a synchronizing signal to the computer’s various circuits. This signal is fed into the counter to make it count down to zero. When the counter gets to zero, it causes a CPU interrupt.
  • Programmable clocks typically have several modes of operation.
    1. In one-shot mode, when the clock is started, it copies the value of the holding register into the counter and then decrements the counter at each pulse from the crystal. When the counter gets to zero, it causes an interrupt and stops until it is explicitly started again by the software.
    2. In square-wave mode, after getting to zero and causing the interrupt, the holding register is automatically copied into the counter, and the whole process is repeated again indefinitely. These periodic interrupts are called clock ticks.
  • The advantage of the programmable clock is that its interrupt frequency can be controlled by software. Programmable clock chips usually contain two or three independently programmable clocks and have many other options as well(e.g., counting up instead of down, interrupts disabled, and more).
  • To prevent the current time from being lost when the computer’s power is turned off, computers have a battery-powered backup clock that can be read at startup. There is also a standard way for a networked system to get the current time from a remote host.
  • In any case the time is then translated into the number of clock ticks since 12 A.M. UTC(Universal Coordinated Time, known as Greenwich Mean Time) on Jan. 1, 1970, as UNIX does. At every clock tick, the real time is incremented by one count. Usually utility programs are provided to manually set the system clock and the backup clock and to synchronize the two clocks.

5.5.2 Clock Software

  • All the clock hardware does is generate interrupts at known intervals. Everything else involving time must be done by the software, the clock driver. The duties of the clock driver usually include the following:
    1. Maintaining the time of day(also called the real time) requires incrementing a counter at each clock tick.
    2. Preventing processes from running longer than they are allowed to. Whenever a process is started, the scheduler initializes a counter to the value of that process’ quantum in clock ticks. At every clock interrupt, the clock driver decrements the quantum counter by 1. When it gets to zero, the clock driver calls the scheduler to set up another process.
    3. Accounting for CPU usage.
      -1- The most accurate way is to start a second timer, distinct from the main system timer, whenever a process is started up. When that process is stopped, the timer can be read out to tell how long the process has run. To do things right, the second timer should be saved when an interrupt occurs and restored afterward.
      -2- A less accurate, but simpler, way to do accounting is to maintain a pointer to the process table entry for the currently running process in a global variable. At every clock tick, a field in the current process’ entry is incremented. In this way, every clock tick is charged to the process running at the time of the tick. Problem with this strategy is that if many interrupts occur during a process’ run, it is still charged for a full tick, even though it did not get much work done.
    4. Handling the alarm system call made by user processes.
    5. Providing watchdog timers for parts of the system itself.
    6. Doing profiling, monitoring, and statistics gathering.
  • In many systems, a process can request that the OS give it a warning after a certain interval. The warning is usually a signal, interrupt, message, or something similar. One application requiring such warnings is networking, in which a packet not acknowledged within a certain time interval must be retransmitted.
  • If the clock driver had enough clocks, it could set a separate clock for each request. This not being the case, it must simulate multiple virtual clocks with a single physical clock. One way is to maintain a table in which the signal time for all pending timers is kept, as well as a variable giving the time of the next one. Whenever the time of day is updated, the driver checks to see if the closest signal has occurred. If it has, the table is searched for the next one to occur.

  • If many signals are expected, it is more efficient to simulate multiple clocks by chaining all the pending clock requests together, sorted on time, in a linked list, as shown in Fig. 5-30. Each entry on the list tells how many clock ticks following the previous one to wait before causing a signal. In this example, signals are pending for 4203, 4207, 4213, 4215, and 4216.
  • In Fig. 5-30, the next interrupt occurs in 3 ticks. On each tick, Next signal is decremented. When it gets to 0, the signal corresponding to the first item on the list is caused, and that item is removed from the list. Then Next signal is set to the value in the entry now at the head of the list, in this example, 4.
  • Note that during a clock interrupt, the clock driver has several things to do: increment the real time, decrement the quantum and check for 0, do CPU ac-counting, and decrement the alarm counter. However, each of these operations has been carefully arranged to be very fast because they have to be repeated many times a second.
  • Parts of the OS also need to set timers. These are called watch-dog timers and are frequently used(especially in embedded devices) to detect problems such as hangs. For instance, a watchdog timer may reset a system that stops running. While the system is running, it regularly resets the timer, so that it never expires. In that case, expiration of the timer proves that the system has not run for a long time, and leads to corrective action—such as a full-system reset.
  • The mechanism used by the clock driver to handle watchdog timers is the same as for user signals. The only difference is that when a timer goes off, instead of causing a signal, the clock driver calls a procedure supplied by the caller. The procedure is part of the caller’s code. The called procedure can do whatever is necessary, even causing an interrupt, although within the kernel interrupts are often inconvenient and signals do not exist. That is why the watchdog mechanism is provided. It is worth nothing that the watchdog mechanism works only when the clock driver and the procedure to be called are in the same address space.
  • The last thing in our list is profiling. Some OS provide a mechanism by which a user program can have the system build up a histogram of its program counter, so it can see where it is spending its time. When profiling is a possibility, at every tick the driver checks to see if the current process is being pro-filed, and if so, computes the bin number(a range of addresses) corresponding to the current program counter. It then increments that bin by one. This mechanism can also be used to profile the system itself.

5.5.3 Soft Timers

  • Most computers have a second programmable clock that can be set to cause timer interrupts at whatever rate a program needs. This timer is in addition to the main system timer whose functions were described above. As long as the interrupt frequency is low, there is no problem using this second timer for application-specific purposes. The trouble arrives when the frequency of the application-specific timer is very high. Below we will briefly describe a software-based timer scheme that works well under many circumstances, even at fairly high frequencies.
  • Generally, there are two ways to manage I/O: interrupts and polling. Interrupts have low latency, that is, they happen immediately after the event itself with little or no delay. On the other hand, with modern CPUs, interrupts have a substantial overhead due to the need for context switching and their influence on the pipeline, TLB, and cache.
  • The alternative to interrupts is to have the application poll for the event expect-ed itself. Doing this avoids interrupts, but there may be substantial latency because an event may happen directly after a poll, in which case it waits almost a whole polling interval. On the average, the latency is half the polling interval.
  • Interrupt latency today is barely better than that of computers in the 1970s. On most minicomputers, e.g., an interrupt took four bus cycles: to stack the program counter and PSW and to load a new program counter and PSW. Nowadays dealing with the pipeline, MMU, TLB, and cache adds a great deal to the overhead. These effects are likely to get worse rather than better in time, thus canceling out faster clock rates. Unfortunately, for certain applications, we want neither the overhead of interrupts nor the latency of polling.
  • Soft timers avoid interrupts. Instead, whenever the kernel is running for some other reason, just before it returns to user mode it checks the real-time clock to see if a soft timer has expired. If it has expired, the scheduled event(e.g., packet trans-mission or checking for an incoming packet) is performed, with no need to switch into kernel mode since the system is already there. After the work has been per-formed, the soft timer is reset to go off again. All that has to be done is copy the current clock value to the timer and add the timeout interval to it.
  • Soft timers stand or fall with the rate at which kernel entries are made for other reasons. These reasons include: System calls. TLB misses. Page faults. I/O interrupts. The CPU going idle.
  • Of course, there will be periods when there are no system calls, TLB misses, or page faults, in which case no soft timers will go off. To put an upper bound on these intervals, the second hardware timer can be set to go off, say, every 1 msec.
  • If the application can live with only 1000 activations per second for occasional intervals, then the combination of soft timers and a low-frequency hardware timer may be better than either pure interrupt-driven I/O or pure polling.

5.6 USER INTERFACES: KEYBOARD, MOUSE, MONITOR

5.7 THIN CLIENTS

5.8 POWER MANAGEMENT

5.9 RESEARCH ON INPUT/OUTPUT

5.10 SUMMARY

  • I/O can be structured in four levels: the interrupt-service procedures, the device drivers, the device-independent I/O software, and the I/O libraries and spoolers that run in user space. The device drivers handle the details of running the devices and providing uniform interfaces to the rest of the OS. The device-independent I/O software does things like buffering and error reporting.

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

你可能感兴趣的:(github,IO,硬件)