Download solution manual of operating system and more Exams Operating Systems in PDF only on Docsity! SOLUTIONS MANUAL OPERATING SYSTEMS: INTERNALS AND DESIGN PRINCIPLES SIXTH EDITION WILLIAM STALLINGS Copyright 2008: William Stallings -2- © 2008 by William Stallings All rights reserved. No part of this document may be reproduced, in any form or by any means, or posted on the Internet, without permission in writing from the author. Selected solutions may be shared with students, provided that they are not available, unsecured, on the Web. -5- CHAPTER 1 COMPUTER SYSTEM OVERVIEW A N S W E R S T O Q U E S T I O N S 1.1 A main memory, which stores both data and instructions: an arithmetic and logic unit (ALU) capable of operating on binary data; a control unit, which interprets the instructions in memory and causes them to be executed; and input and output (I/O) equipment operated by the control unit. 1.2 User-visible registers: Enable the machine- or assembly-language programmer to minimize main memory references by optimizing register use. For high-level languages, an optimizing compiler will attempt to make intelligent choices of which variables to assign to registers and which to main memory locations. Some high- level languages, such as C, allow the programmer to suggest to the compiler which variables should be held in registers. Control and status registers: Used by the processor to control the operation of the processor and by privileged, operating system routines to control the execution of programs. 1.3 These actions fall into four categories: Processor-memory: Data may be transferred from processor to memory or from memory to processor. Processor-I/O: Data may be transferred to or from a peripheral device by transferring between the processor and an I/O module. Data processing: The processor may perform some arithmetic or logic operation on data. Control: An instruction may specify that the sequence of execution be altered. 1.4 An interrupt is a mechanism by which other modules (I/O, memory) may interrupt the normal sequencing of the processor. 1.5 Two approaches can be taken to dealing with multiple interrupts. The first is to disable interrupts while an interrupt is being processed. A second approach is to define priorities for interrupts and to allow an interrupt of higher priority to cause a lower-priority interrupt handler to be interrupted. 1.6 The three key characteristics of memory are cost, capacity, and access time. 1.7 Cache memory is a memory that is smaller and faster than main memory and that is interposed between the processor and main memory. The cache acts as a buffer for recently used memory locations. 1.8 Programmed I/O: The processor issues an I/O command, on behalf of a process, to an I/O module; that process then busy-waits for the operation to be completed before proceeding. Interrupt-driven I/O: The processor issues an I/O command on behalf of a process, continues to execute subsequent instructions, and is interrupted -6- by the I/O module when the latter has completed its work. The subsequent instructions may be in the same process, if it is not necessary for that process to wait for the completion of the I/O. Otherwise, the process is suspended pending the interrupt and other work is performed. Direct memory access (DMA): A DMA module controls the exchange of data between main memory and an I/O module. The processor sends a request for the transfer of a block of data to the DMA module and is interrupted only after the entire block has been transferred. 1.9 Spatial locality refers to the tendency of execution to involve a number of memory locations that are clustered. Temporal locality refers to the tendency for a processor to access memory locations that have been used recently. 1.10 Spatial locality is generally exploited by using larger cache blocks and by incorporating prefetching mechanisms (fetching items of anticipated use) into the cache control logic. Temporal locality is exploited by keeping recently used instruction and data values in cache memory and by exploiting a cache hierarchy. A N S W E R S T O P R O B L E M S 1.1 Memory (contents in hex): 300: 3005; 301: 5940; 302: 7006 Step 1: 3005 → IR; Step 2: 3 → AC Step 3: 5940 → IR; Step 4: 3 + 2 = 5 → AC Step 5: 7006 → IR; Step 6: AC → Device 6 1.2 1. a. The PC contains 300, the address of the first instruction. This value is loaded in to the MAR. b. The value in location 300 (which is the instruction with the value 1940 in hexadecimal) is loaded into the MBR, and the PC is incremented. These two steps can be done in parallel. c. The value in the MBR is loaded into the IR. 2. a. The address portion of the IR (940) is loaded into the MAR. b. The value in location 940 is loaded into the MBR. c. The value in the MBR is loaded into the AC. 3. a. The value in the PC (301) is loaded in to the MAR. b. The value in location 301 (which is the instruction with the value 5941) is loaded into the MBR, and the PC is incremented. c. The value in the MBR is loaded into the IR. 4. a. The address portion of the IR (941) is loaded into the MAR. b. The value in location 941 is loaded into the MBR. c. The old value of the AC and the value of location MBR are added and the result is stored in the AC. 5. a. The value in the PC (302) is loaded in to the MAR. b. The value in location 302 (which is the instruction with the value 2941) is loaded into the MBR, and the PC is incremented. c. The value in the MBR is loaded into the IR. 6. a. The address portion of the IR (941) is loaded into the MAR. b. The value in the AC is loaded into the MBR. c. The value in the MBR is stored in location 941. -7- 1.3 a. 224 = 16 MBytes b. (1) If the local address bus is 32 bits, the whole address can be transferred at once and decoded in memory. However, since the data bus is only 16 bits, it will require 2 cycles to fetch a 32-bit instruction or operand. (2) The 16 bits of the address placed on the address bus can't access the whole memory. Thus a more complex memory interface control is needed to latch the first part of the address and then the second part (since the microprocessor will end in two steps). For a 32-bit address, one may assume the first half will decode to access a "row" in memory, while the second half is sent later to access a "column" in memory. In addition to the two-step address operation, the microprocessor will need 2 cycles to fetch the 32 bit instruction/operand. c. The program counter must be at least 24 bits. Typically, a 32-bit microprocessor will have a 32-bit external address bus and a 32-bit program counter, unless on- chip segment registers are used that may work with a smaller program counter. If the instruction register is to contain the whole instruction, it will have to be 32-bits long; if it will contain only the op code (called the op code register) then it will have to be 8 bits long. 1.4 In cases (a) and (b), the microprocessor will be able to access 216 = 64K bytes; the only difference is that with an 8-bit memory each access will transfer a byte, while with a 16-bit memory an access may transfer a byte or a 16-byte word. For case (c), separate input and output instructions are needed, whose execution will generate separate "I/O signals" (different from the "memory signals" generated with the execution of memory-type instructions); at a minimum, one additional output pin will be required to carry this new signal. For case (d), it can support 28 = 256 input and 28 = 256 output byte ports and the same number of input and output 16-bit ports; in either case, the distinction between an input and an output port is defined by the different signal that the executed input or output instruction generated. 1.5 Clock cycle = 1 8 MHz = 125 ns Bus cycle = 4 × 125 ns = 500 ns 2 bytes transferred every 500 ns; thus transfer rate = 4 MBytes/sec Doubling the frequency may mean adopting a new chip manufacturing technology (assuming each instructions will have the same number of clock cycles); doubling the external data bus means wider (maybe newer) on-chip data bus drivers/latches and modifications to the bus control logic. In the first case, the speed of the memory chips will also need to double (roughly) not to slow down the microprocessor; in the second case, the "word length" of the memory will have to double to be able to send/receive 32-bit quantities. 1.6 a. Input from the Teletype is stored in INPR. The INPR will only accept data from the Teletype when FGI=0. When data arrives, it is stored in INPR, and FGI is set to 1. The CPU periodically checks FGI. If FGI =1, the CPU transfers the contents of INPR to the AC and sets FGI to 0. When the CPU has data to send to the Teletype, it checks FGO. If FGO = 0, the CPU must wait. If FGO = 1, the CPU transfers the contents of the AC to OUTR and sets FGO to 0. The Teletype sets FGI to 1 after the word is printed. -10- 1.13 There are three cases to consider: Location of referenced word Probability Total time for access in ns In cache 0.9 20 Not in cache, but in main memory (0.1)(0.6) = 0.06 60 + 20 = 80 Not in cache or main memory (0.1)(0.4) = 0.04 12ms + 60 + 20 = 12,000,080 So the average access time would be: Avg = (0.9)(20) + (0.06)(80) + (0.04)(12000080) = 480026 ns 1.14 Yes, if the stack is only used to hold the return address. If the stack is also used to pass parameters, then the scheme will work only if it is the control unit that removes parameters, rather than machine instructions. In the latter case, the processor would need both a parameter and the PC on top of the stack at the same time. -11- CHAPTER 2 OPERATING SYSTEM OVERVIEW A N S W E R S T O Q U E S T I O N S 2.1 Convenience: An operating system makes a computer more convenient to use. Efficiency: An operating system allows the computer system resources to be used in an efficient manner. Ability to evolve: An operating system should be constructed in such a way as to permit the effective development, testing, and introduction of new system functions without interfering with service. 2.2 The kernel is a portion of the operating system that includes the most heavily used portions of software. Generally, the kernel is maintained permanently in main memory. The kernel runs in a privileged mode and responds to calls from processes and interrupts from devices. 2.3 Multiprogramming is a mode of operation that provides for the interleaved execution of two or more computer programs by a single processor. 2.4 A process is a program in execution. A process is controlled and scheduled by the operating system. 2.5 The execution context, or process state, is the internal data by which the operating system is able to supervise and control the process. This internal information is separated from the process, because the operating system has information not permitted to the process. The context includes all of the information that the operating system needs to manage the process and that the processor needs to execute the process properly. The context includes the contents of the various processor registers, such as the program counter and data registers. It also includes information of use to the operating system, such as the priority of the process and whether the process is waiting for the completion of a particular I/O event. 2.6 Process isolation: The operating system must prevent independent processes from interfering with each other's memory, both data and instructions. Automatic allocation and management: Programs should be dynamically allocated across the memory hierarchy as required. Allocation should be transparent to the programmer. Thus, the programmer is relieved of concerns relating to memory limitations, and the operating system can achieve efficiency by assigning memory to jobs only as needed. Support of modular programming: Programmers should be able to define program modules, and to create, destroy, and alter the size of modules dynamically. Protection and access control: Sharing of memory, at any level of the memory hierarchy, creates the potential for one program to address the memory space of another. This is desirable when sharing is needed by particular applications. At other times, it threatens the integrity of programs and even of the -12- operating system itself. The operating system must allow portions of memory to be accessible in various ways by various users. Long-term storage: Many application programs require means for storing information for extended periods of time, after the computer has been powered down. 2.7 A virtual address refers to a memory location in virtual memory. That location is on disk and at some times in main memory. A real address is an address in main memory. 2.8 Round robin is a scheduling algorithm in which processes are activated in a fixed cyclic order; that is, all processes are in a circular queue. A process that cannot proceed because it is waiting for some event (e.g. termination of a child process or an input/output operation) returns control to the scheduler. 2.9 A monolithic kernel is a large kernel containing virtually the complete operating system, including scheduling, file system, device drivers, and memory management. All the functional components of the kernel have access to all of its internal data structures and routines. Typically, a monolithic kernel is implemented as a single process, with all elements sharing the same address space. A microkernel is a small privileged operating system core that provides process scheduling, memory management, and communication services and relies on other processes to perform some of the functions traditionally associated with the operating system kernel. 2.10 Multithreading is a technique in which a process, executing an application, is divided into threads that can run concurrently. A N S W E R S T O P R O B L E M S 2.1 The answers are the same for (a) and (b). Assume that although processor operations cannot overlap, I/O operations can. 1 Job: TAT = NT Processor utilization = 50% 2 Jobs: TAT = NT Processor utilization = 100% 4 Jobs: TAT = (4N – 2)T Processor utilization = 100% 2.2 I/O-bound programs use relatively little processor time and are therefore favored by the algorithm. However, if a processor-bound process is denied processor time for a sufficiently long period of time, the same algorithm will grant the processor to that process since it has not used the processor at all in the recent past. Therefore, a processor-bound process will not be permanently denied access. 2.3 With time sharing, the concern is turnaround time. Time-slicing is preferred because it gives all processes access to the processor over a short period of time. In a batch system, the concern is with throughput, and the less context switching, the more processing time is available for the processes. Therefore, policies that minimize context switching are favored. -15- 3.9 Process identification, processor state information, and process control information. 3.10 The user mode has restrictions on the instructions that can be executed and the memory areas that can be accessed. This is to protect the operating system from damage or alteration. In kernel mode, the operating system does not have these restrictions, so that it can perform its tasks. 3.11 1. Assign a unique process identifier to the new process. 2. Allocate space for the process. 3. Initialize the process control block. 4. Set the appropriate linkages. 5. Create or expand other data structures. 3.12 An interrupt is due to some sort of event that is external to and independent of the currently running process, such as the completion of an I/O operation. A trap relates to an error or exception condition generated within the currently running process, such as an illegal file access attempt. 3.13 Clock interrupt, I/O interrupt, memory fault. 3.14 A mode switch may occur without changing the state of the process that is currently in the Running state. A process switch involves taking the currently executing process out of the Running state in favor of another process. The process switch involves saving more state information. A N S W E R S T O P R O B L E M S 3.1 •Creation and deletion of both user and system processes. The processes in the system can execute concurrently for information sharing, computation speedup, modularity, and convenience. Concurrent execution requires a mechanism for process creation and deletion. The required resources are given to the process when it is created, or allocated to it while it is running. When the process terminates, the OS needs to reclaim any reusable resources. •Suspension and resumption of processes. In process scheduling, the OS needs to change the process's state to waiting or ready state when it is waiting for some resources. When the required resources are available, OS needs to change its state to running state to resume its execution. •Provision of mechanism for process synchronization. Cooperating processes may share data. Concurrent access to shared data may result in data inconsistency. OS has to provide mechanisms for processes synchronization to ensure the orderly execution of cooperating processes, so that data consistency is maintained. •Provision of mechanism for process communication. The processes executing under the OS may be either independent processes or cooperating processes. Cooperating processes must have the means to communicate with each other. •Provision of mechanisms for deadlock handling. In a multiprogramming environment, several processes may compete for a finite number of resources. If a deadlock occurs, all waiting processes will never change their waiting state to running state again, resources are wasted and jobs will never be completed. 3.2 a. There is no limit in principle to the number of processes that can be in the Ready and Blocked states. The OS may impose a limit based on resource -16- constraints related to the amount of memory devoted to the process state for each process. At most N processes can be in the Running state, one for each processor. b. Zero is the minimum number of processes in each state. It is possible for all processes to be in either the Running state (up to N processes) or the Ready state, with no process blocked. It is possible for all processes to be blocked waiting for I/O operations, with zero processes in the Ready and Running states. 3.3 a. New → Ready or Ready/Suspend: covered in text Ready → Running or Ready/Suspend: covered in text Ready/Suspend → Ready: covered in text Blocked → Ready or Blocked/Suspend: covered in text Blocked/Suspend → Ready /Suspend or Blocked: covered in text Running → Ready, Ready/Suspend, or Blocked: covered in text Any State → Exit: covered in text b. New → Blocked, Blocked/Suspend, or Running: A newly created process remains in the new state until the processor is ready to take on an additional process, at which time it goes to one of the Ready states. Ready → Blocked or Blocked/Suspend: Typically, a process that is ready cannot subsequently be blocked until it has run. Some systems may allow the OS to block a process that is currently ready, perhaps to free up resources committed to the ready process. Ready/Suspend → Blocked or Blocked/Suspend: Same reasoning as preceding entry. Ready/Suspend → Running: The OS first brings the process into memory, which puts it into the Ready state. Blocked → Ready /Suspend: this transition would be done in 2 stages. A blocked process cannot at the same time be made ready and suspended, because these transitions are triggered by two different causes. Blocked → Running: When a process is unblocked, it is put into the Ready state. The dispatcher will only choose a process from the Ready state to run Blocked/Suspend → Ready: same reasoning as Blocked → Ready /Suspend Blocked/Suspend → Running: same reasoning as Blocked → Running Running → Blocked/Suspend: this transition would be done in 2 stages Exit → Any State: Can't turn back the clock 3.4 The following example is used in [PINK89] to clarify their definition of block and suspend: Suppose a process has been executing for a while and needs an additional magnetic tape drive so that it can write out a temporary file. Before it can initiate a write to tape, it must be given permission to use one of the drives. When it makes its request, a tape drive may not be available, and if that is the case, the process will be placed in the blocked state. At some point, we assume the system will allocate the tape drive to the process; at that time the process will be moved back to the active state. When the process is placed into the execute state again it will request a write operation to its newly acquired tape drive. At this point, the process will be move to the suspend state, where it waits for the completion of the current write on the tape drive that it now owns. -17- The distinction made between two different reasons for waiting for a device could be useful to the operating system in organizing its work. However, it is no substitute for a knowledge of which processes are swapped out and which processes are swapped in. This latter distinction is a necessity and must be reflected in some fashion in the process state. 3.5 Figure 9.3 in Chapter 9 shows the result for a single blocked queue. The figure readily generalizes to multiple blocked queues. 3.6 Penalize the Ready, suspend processes by some fixed amount, such as one or two priority levels, so that a Ready, suspend process is chosen next only if it has a higher priority than the highest-priority Ready process by several levels of priority. 3.7 a. A separate queue is associated with each wait state. The differentiation of waiting processes into queues reduces the work needed to locate a waiting process when an event occurs that affects it. For example, when a page fault completes, the scheduler know that the waiting process can be found on the Page Fault Wait queue. b. In each case, it would be less efficient to allow the process to be swapped out while in this state. For example, on a page fault wait, it makes no sense to swap out a process when we are waiting to bring in another page so that it can execute. c. The state transition diagram can be derived from the following state transition table: Next State Current State Currently Executing Computable (resident) Computable (outswapped) Variety of wait states (resident) Variety of wait states (outswapped) Currently Executing Rescheduled Wait Computable (resident) Scheduled Outswap Computable (outswapped) Inswap Variety of wait states (resident) Event satisfied Outswap Variety of wait states (outswapped) Event satisfied 3.8 a. The advantage of four modes is that there is more flexibility to control access to memory, allowing finer tuning of memory protection. The disadvantage is complexity and processing overhead. For example, procedures running at each of the access modes require separate stacks with appropriate accessibility. b. In principle, the more modes, the more flexibility, but it seems difficult to justify going beyond four. -20- to only one processor at a time. Therefore, only a single thread within a process can execute at a time. 4.8 Jacketing converts a blocking system call into a nonblocking system call by using an application-level I/O routine which checks the status of the I/O device. 4.9 SIMD: A single machine instruction controls the simultaneous execution of a number of processing elements on a lockstep basis. Each processing element has an associated data memory, so that each instruction is executed on a different set of data by the different processors.. MIMD: A set of processors simultaneously execute different instruction sequences on different data sets. Master/slave: The operating system kernel always runs on a particular processor. The other processors may only execute user programs and perhaps operating system utilities. SMP: the kernel can execute on any processor, and typically each processor does self-scheduling from the pool of available processes or threads. Cluster: Each processor has a dedicated memory, and is a self-contained computer. 4.10 Simultaneous concurrent processes or threads; scheduling; synchronization; memory management; reliability and fault tolerance. 4.11 Device drivers, file systems, virtual memory manager, windowing system, and security services. 4.12 Uniform interfaces: Processes need not distinguish between kernel-level and user- level services because all such services are provided by means of message passing. Extensibility: facilitates the addition of new services as well as the provision of multiple services in the same functional area. Flexibility: not only can new features be added to the operating system, but existing features can be subtracted to produce a smaller, more efficient implementation. Portability: all or at least much of the processor-specific code is in the microkernel; thus, changes needed to port the system to a new processor are fewer and tend to be arranged in logical groupings. Reliability: A small microkernel can be rigorously tested. Its use of a small number of application programming interfaces (APIs) improves the chance of producing quality code for the operating-system services outside the kernel. Distributed system support: the message orientation of microkernal communication lends itself to extension to distributed systems. Support for object-oriented operating system (OOOS): An object-oriented approach can lend discipline to the design of the microkernel and to the development of modular extensions to the operating system. 4.13 It takes longer to build and send a message via the microkernel, and accept and decode the reply, than to make a single service call. 4.14 These functions fall into the general categories of low-level memory management, inter-process communication (IPC), and I/O and interrupt management. 4.15 Messages. A N S W E R S T O P R O B L E M S -21- 4.1 Yes, because more state information must be saved to switch from one process to another. 4.2 Because, with ULTs, the thread structure of a process is not visible to the operating system, which only schedules on the basis of processes. 4.3 a. The use of sessions is well suited to the needs of an interactive graphics interface for personal computer and workstation use. It provides a uniform mechanism for keeping track of where graphics output and keyboard/mouse input should be directed, easing the task of the operating system. b. The split would be the same as any other process/thread scheme, with address space and files assigned at the process level. 4.4 The issue here is that a machine spends a considerable amount of its waking hours waiting for I/O to complete. In a multithreaded program, one KLT can make the blocking system call, while the other KLTs can continue to run. On uniprocessors, a process that would otherwise have to block for all these calls can continue to run its other threads. Source: [LEWI96] 4.5 No. When a process exits, it takes everything with it—the KLTs, the process structure, the memory space, everything—including threads. Source: [LEWI96] 4.6 As much information as possible about an address space can be swapped out with the address space, thus conserving main memory. 4.7 a. If a conservative policy is used, at most 20/4 = 5 processes can be active simultaneously. Because one of the drives allocated to each process can be idle most of the time, at most 5 drives will be idle at a time. In the best case, none of the drives will be idle. b. To improve drive utilization, each process can be initially allocated with three tape drives. The fourth one will be allocated on demand. In this policy, at most 20/3 = 6 processes can be active simultaneously. The minimum number of idle drives is 0 and the maximum number is 2. Source: Advanced Computer Architecture, K. Hwang, 1993. 4.8 a. The function counts the number of positive elements in a list. b. This should work correctly, because count_positives in this specific case does not update global_positives, and hence the two threads operate on distinct global data and require no locking. Source: Boehn, H. et al. "Multithreading in C and C++." ;login, February 2007. 4.9 This transformation is clearly consistent with the C language specification, which addresses only single-threaded execution. In a single-threaded environment, it is indistinguishable from the original. The pthread specification also contains no clear prohibition against this kind of transformation. And since it is a library and not a language specification, it is not clear that it could. However, in a multithreaded environment, the transformed version is quite different, in that it assigns to global_positives, even if the list contains only negative elements. The original program is now broken, because the update of global_positives by thread B may be lost, as a result of thread A writing back an earlier value of global_positives. By pthread rules, a thread-unaware compiler has turned a -22- perfectly legitimate program into one with undefined semantics. Source: Boehn, H. et al. "Multithreading in C and C++." ;login, February 2007. 4.10 a. This program creates a new thread. Both the main thread and the new thread then increment the global variable myglobal 20 times. b. Quite unexpected! Because myglobal starts at zero, and both the main thread and the new thread each increment it by 20, we should see myglobal equaling 40 at the end of the program. But myglobal equals 21, so we know that something fishy is going on here. In thread_function(), notice that we copy myglobal into a local variable called j. The program increments j, then sleeps for one second, and only then copies the new j value into myglobal. That's the key. Imagine what happens if our main thread increments myglobal just after our new thread copies the value of myglobal into j. When thread_function() writes the value of j back to myglobal, it will overwrite the modification that the main thread made. Source: Robbins, D. "POSIX Threads Explained." IBM Developer Works, July 2000. ibm.com/developerworks/library/l-posix1.html 4.11 Every call that can possibly change the priority of a thread or make a higher- priority thread runnable will also call the scheduler, and it in turn will preempt the lower-priority active thread. So there will never be a runnable, higher- priority thread. Source: [LEWI96] 4.12 a. Some programs have logical parallelism that can be exploited to simplify and structure the code but do not need hardware parallelism. For example, an application that employs multiple windows, only one of which is active at a time, could with advantage be implemented as a set of ULTs on a single LWP. The advantage of restricting such applications to ULTs is efficiency. ULTs may be created, destroyed, blocked, activated, and so on. without involving the kernel. If each ULT were known to the kernel, the kernel would have to allocate kernel data structures for each one and perform thread switching. Kernel-level thread switching is more expensive than user-level thread switching. b. The execution of user-level threads is managed by the threads library whereas the LWP is managed by the kernel. c. An unbound thread can be in one of four states: runnable, active, sleeping, or stopped. These states are managed by the threads library. A ULT in the active state is currently assigned to a LWP and executes while the underlying kernel thread executes. We can view the LWP state diagram as a detailed description of the ULT active state, because an thread only has an LWP assigned to it when it is in the Active state. The LWP state diagram is reasonably self- explanatory. An active thread is only executing when its LWP is in the Running state. When an active thread executes a blocking system call, the LWP enters the Blocked state. However, the ULT remains bound to that LWP and, as far as the threads library is concerned, that ULT remains active. 4.13 As the text describes, the Uninterruptible state is another blocked state. The difference between this and the Interruptible state is that in an uninterruptible state, a process is waiting directly on hardware conditions and therefore will not handle any signals. This is used in situations where the process must wait without interruption or when the event is expected to occur quite quickly. For -25- 5.8 1. A semaphore may be initialized to a nonnegative value. 2. The wait operation decrements the semaphore value. If the value becomes negative, then the process executing the wait is blocked. 3. The signal operation increments the semaphore value. If the value is not positive, then a process blocked by a wait operation is unblocked. 5.9 A binary semaphore may only take on the values 0 and 1. A general semaphore may take on any integer value. 5.10 A strong semaphore requires that processes that are blocked on that semaphore are unblocked using a first-in-first-out policy. A weak semaphore does not dictate the order in which blocked processes are unblocked. 5.11 A monitor is a programming language construct providing abstract data types and mutually exclusive access to a set of procedures 5.12 There are two aspects, the send and receive primitives. When a send primitive is executed in a process, there are two possibilities: either the sending process is blocked until the message is received, or it is not. Similarly, when a process issues a receive primitive, there are two possibilities: If a message has previously been sent, the message is received and execution continues. If there is no waiting message, then either (a) the process is blocked until a message arrives, or (b) the process continues to execute, abandoning the attempt to receive. 5.13 1. Any number of readers may simultaneously read the file. 2. Only one writer at a time may write to the file. 3. If a writer is writing to the file, no reader may read it. A N S W E R S T O P R O B L E M S 5.1 On uniprocessors you can avoid interruption and thus concurrency by disabling interrupts. Also on multiprocessor machines another problem arises: memory ordering (multiple processors accessing the memory unit). 5.2 b. The read coroutine reads the cards and passes characters through a one- character buffer, rs, to the squash coroutine. The read coroutine also passes the extra blank at the end of every card image. The squash coroutine need known nothing about the 80-character structure of the input; it simply looks for double asterisks and passes a stream of modified characters to the print coroutine via a one-character buffer, sp. Finally, print simply accepts an incoming stream of characters and prints it as a sequence of 125-character lines. d. This can be accomplished using three concurrent processes. One of these, Input, reads the cards and simply passes the characters (with the additional trailing space) through a finite buffer, say InBuffer, to a process Squash which simply looks for double asterisks and passes a stream of modified characters through a second finite buffer, say OutBuffer, to a process Output, which extracts the characters from the second buffer and prints them in 15 column lines. A producer/consumer semaphore approach can be used. -26- 5.3 ABCDE; ABDCE; ABDEC; ADBCE; ADBEC; ADEBC; DEABC; DAEBC; DABEC; DABCE 5.4 a. On casual inspection, it appears that tally will fall in the range 50 ≤ tally ≤ 100 since from 0 to 50 increments could go unrecorded due to the lack of mutual exclusion. The basic argument contends that by running these two processes concurrently we should not be able to derive a result lower than the result produced by executing just one of these processes sequentially. But consider the following interleaved sequence of the load, increment, and store operations performed by these two processes when altering the value of the shared variable: 1. Process A loads the value of tally, increments tally, but then loses the processor (it has incremented its register to 1, but has not yet stored this value. 2. Process B loads the value of tally (still zero) and performs forty-nine complete increment operations, losing the processor after it has stored the value 49 into the shared variable tally. 3. Process A regains control long enough to perform its first store operation (replacing the previous tally value of 49 with 1) but is then immediately forced to relinquish the processor. 4. Process B resumes long enough to load 1 (the current value of tally) into its register, but then it too is forced to give up the processor (note that this was B's final load). 5. Process A is rescheduled, but this time it is not interrupted and runs to completion, performing its remaining 49 load, increment, and store operations, which results in setting the value of tally to 50. 6. Process B is reactivated with only one increment and store operation to perform before it terminates. It increments its register value to 2 and stores this value as the final value of the shared variable. Some thought will reveal that a value lower than 2 cannot occur. Thus, the proper range of final values is 2 ≤ tally ≤ 100. b. For the generalized case of N processes, the range of final values is 2 ≤ tally ≤ (N × 50), since it is possible for all other processes to be initially scheduled and run to completion in step (5) before Process B would finally destroy their work by finishing last. Source: [RUDO90]. A slightly different formulation of the same problem appears in [BEN98] 5.5 On average, yes, because busy-waiting consumes useless instruction cycles. However, in a particular case, if a process comes to a point in the program where it must wait for a condition to be satisfied, and if that condition is already satisfied, then the busy-wait will find that out immediately, whereas, the blocking wait will consume OS resources switching out of and back into the process. 5.6 Consider the case in which turn equals 0 and P(1) sets blocked[1] to true and then finds blocked[0] set to false. P(0) will then set blocked[0] to true, find turn = 0, and enter its critical section. P(1) will then assign 1 to turn and will also enter its critical section. The error was pointed out in [RAYN86]. -27- 5.7 a. When a process wishes to enter its critical section, it is assigned a ticket number. The ticket number assigned is calculated by adding one to the largest of the ticket numbers currently held by the processes waiting to enter their critical section and the process already in its critical section. The process with the smallest ticket number has the highest precedence for entering its critical section. In case more than one process receives the same ticket number, the process with the smallest numerical name enters its critical section. When a process exits its critical section, it resets its ticket number to zero. b. If each process is assigned a unique process number, then there is a unique, strict ordering of processes at all times. Therefore, deadlock cannot occur. c. To demonstrate mutual exclusion, we first need to prove the following lemma: if Pi is in its critical section, and Pk has calculated its number[k] and is attempting to enter its critical section, then the following relationship holds: ( number[i], i ) < ( number[k], k ) To prove the lemma, define the following times: Tw1 Pi reads choosing[k] for the last time, for j = k, in its first wait, so we have choosing[k] = false at Tw1. Tw2 Pi begins its final execution, for j = k, of the second while loop. We therefore have Tw1 < Tw2. Tk1 Pk enters the beginning of the repeat loop. Tk2 Pk finishes calculating number[k]. Tk3 Pk sets choosing[k] to false. We have Tk1 < Tk2 < Tk3. Since at Tw1, choosing[k] = false, we have either Tw1 < Tk1 or Tk3 < Tw1. In the first case, we have number[i] < number[k], since Pi was assigned its number prior to Pk; this satisfies the condition of the lemma. In the second case, we have Tk2 < Tk3 < Tw1 < Tw2, and therefore Tk2 < Tw2. This means that at Tw2, Pi has read the current value of number[k]. Moreover, as Tw2 is the moment at which the final execution of the second while for j = k takes place, we have (number[i], i ) < ( number[k], k), which completes the proof of the lemma. It is now easy to show the mutual exclusion is enforced. Assume that Pi is in its critical section and Pk is attempting to enter its critical section. Pk will be unable to enter its critical section, as it will find number[i] ≠ 0 and ( number[i], i ) < ( number[k], k ). 5.8 Suppose we have two processes just beginning; call them p0 and p1. Both reach line 3 at the same time. Now, we'll assume both read number[0] and number[1] before either addition takes place. Let p1 complete the line, assigning 1 to number[1], but p0 block before the assignment. Then p1 gets through the while loop at line 5 and enters the critical section. While in the critical section, it blocks; p0 unblocks, and assigns 1 to number[0] at line 3. It proceeds to the while loop at line 5. When it goes through that loop for j = 1, the first condition on line 5 is true. Further, the second condition on line 5 is false, so p0 enters the critical section. Now p0 and p1 are both in the critical section, violating mutual exclusion. The -30- 5.14 This problem and the next two are based on examples in; Reek, K. "Design Patterns for Semaphores." ACM SIGCSE’04, March 2004. a. We quote the explanation in Reek;s paper. There are two problems. First, because unblocked processes must reenter the mutual exclusion (line 10) there is a chance that newly arriving processes (at line 5) will beat them into the critical section. Second, there is a time delay between when the waiting processes are unblocked and when they resume execution and update the counters. The waiting processes must be accounted for as soon as they are unblocked (because they might resume execution at any time), but it may be some time before the processes actually do resume and update the counters to reflect this. To illustrate, consider the case where three processes are blocked at line 9. The last active process will unblock them (lines 25-28) as it departs. But there is no way to predict when these processes will resume executing and update the counters to reflect the fact that they have become active. If a new process reaches line 6 before the unblocked ones resume, the new one should be blocked. But the status variables have not yet been updated so the new process will gain access to the resource. When the unblocked ones eventually resume execution, they will also begin accessing the resource. The solution has failed because it has allowed four processes to access the resource together. b. This forces unblocked processes to recheck whether they can begin using the resource. But this solution is more prone to starvation because it encourages new arrivals to “cut in line” ahead of those that were already waiting. 5.15 a. This approach is to eliminate the time delay. If the departing process updates the waiting and active counters as it unblocks waiting processes, the counters will accurately reflect the new state of the system before any new processes can get into the mutual exclusion. Because the updating is already done, the unblocked processes need not reenter the critical section at all. Implementing this pattern is easy. Identify all of the work that would have been done by an unblocked process and make the unblocking process do it instead. b. Suppose three processes arrived when the resource was busy, but one of them lost its quantum just before blocking itself at line 9 (which is unlikely, but certainly possible). When the last active process departs, it will do three semSignal operations and set must_wait to true. If a new process arrives before the older ones resume, the new one will decide to block itself. However, it will breeze past the semWait in line 9 without blocking, and when the process that lost its quantum earlier runs it will block itself instead. This is not an error—the problem doesn’t dictate which processes access the resource, only how many are allowed to access it. Indeed, because the unblocking order of semaphores is implementation dependent, the only portable way to ensure that processes proceed in a particular order is to block each on its own semaphore. c. The departing process updates the system state on behalf of the processes it unblocks. 5.16 a. After you unblock a waiting process, you leave the critical section (or block yourself) without opening the mutual exclusion. The unblocked process doesn’t reenter the mutual exclusion—it takes over your ownership of it. The process can therefore safely update the system state on its own. When it is finished, it reopens the mutual exclusion. Newly arriving processes can no longer cut in line because they cannot enter the mutual exclusion until the unblocked process has finished. Because the unblocked process takes care of its -31- own updating, the cohesion of this solution is better. However, once you have unblocked a process, you must immediately stop accessing the variables protected by the mutual exclusion. The safest approach is to immediately leave (after line 26, the process leaves without opening the mutex) or block yourself. b. Only one waiting process can be unblocked even if several are waiting—to unblock more would violate the mutual exclusion of the status variables. This problem is solved by having the newly unblocked process check whether more processes should be unblocked (line 14). If so, it passes the baton to one of them (line 15); if not, it opens up the mutual exclusion for new arrivals (line 17). c. This pattern synchronizes processes like runners in a relay race. As each runner finishes her laps, she passes the baton to the next runner. “Having the baton” is like having permission to be on the track. In the synchronization world, being in the mutual exclusion is analogous to having the baton—only one person can have it.. 5.17 Suppose two processes each call semWait(s) when s is initially 0, and after the first has just done semSignalB(mutex) but not done semWaitB(delay), the second call to semWait(s) proceeds to the same point. Because s = –2 and mutex is unlocked, if two other processes then successively execute their calls to semSignal(s) at that moment, they will each do semSignalB(delay), but the effect of the second semSignalB is not defined. The solution is to move the else line, which appears just before the end line in semWait to just before the end line in semSignal. Thus, the last semSignalB(mutex) in semWait becomes unconditional and the semSignalB(mutex) in semSignal becomes conditional. For a discussion, see "A Correct Implementation of General Semaphores," by Hemmendinger, Operating Systems Review, July 1988. 5.18 The program is found in [RAYN86]: var a, b, m: semaphore; na, nm: 0 … +∞; a := 1; b := 1; m := 0; na := 0; nm := 0; semWait(b); na ← na + 1; semSignal(b); semWait(a); nm ← nm + 1; semWait(b); na ← na – 1; if na = 0 then semSignal(b); semSignal(m) else semSignal(b); semSignal(a) endif; semWait(m); nm ← nm – 1; <critical section>; if nm = 0 then semSignal(a) else semSignal(m) endif; 5.19 The code has a major problem. The V(passenger_released) in the car code can unblock a passenger blocked on P(passenger_released) that is NOT the one riding in the car that did the V(). -32- 5.20 Producer Consumer s n delay 1 1 0 0 2 semWaitB(s) 0 0 0 3 n++ 0 1 0 4 if (n==1) (semSignalB(delay)) 0 1 1 5 semSignalB(s) 1 1 1 6 semWaitB(delay) 1 1 0 7 semWaitB(s) 0 1 0 8 n-- 0 0 0 9 if (n==0) (semWaitB(delay)) 10 semWaitB(s) Both producer and consumer are blocked. -35- 5.24 #define REINDEER 9 /* max # of reindeer */ #define ELVES 3 /* size of elf group */ /* Semaphores */ only_elves = 3, /* 3 go to Santa */ emutex = 1, /* update elf_cnt */ rmutex = 1, /* update rein_ct */ rein_semWait = 0, /* block early arrivals back from islands */ sleigh = 0, /*all reindeer semWait around the sleigh */ done = 0, /* toys all delivered */ santa_semSignal = 0, /* 1st 2 elves semWait on this outside Santa's shop */ santa = 0, /* Santa sleeps on this blocked semaphore */ problem = 0, /* semWait to pose the question to Santa */ elf_done = 0; /* receive reply */ /* Shared Integers */ rein_ct = 0; /* # of reindeer back */ elf_ct = 0; /* # of elves with problem */ /* Reindeer Process */ for (;;) { tan on the beaches in the Pacific until Christmas is close semWait (rmutex) rein_ct++ if (rein_ct == REINDEER) { semSignal (rmutex) semSignal (santa) } else { semSignal (rmutex) semWait (rein_semWait) } /* all reindeer semWaiting to be attached to sleigh */ semWait (sleigh) fly off to deliver toys semWait (done) /* Elf Process */ for (;;) { semWait (only_elves) /* only 3 elves "in" */ semWait (emutex) elf_ct++ if (elf_ct == ELVES) { semSignal (emutex) semSignal (santa) /* 3rd elf wakes Santa */ } else { semSignal (emutex) semWait (santa _semSignal) /* semWait outside Santa's shop door */ } semWait (problem) ask question /* Santa woke elf up */ semWait (elf_done) semSignal (only_elves) } /* end "forever" loop */ /* Santa Process */ for (;;) { semWait (santa) /* Santa "rests" */ /* mutual exclusion is not needed on rein_ct because if it is not equal to REINDEER, then elves woke up Santa */ if (rein_ct == REINDEER) { semWait (rmutex) rein_ct = 0 /* reset while blocked */ semSignal (rmutex) for (i = 0; i < REINDEER – 1; i++) semSignal (rein_semWait) for (i = 0; i < REINDEER; i++) semSignal (sleigh) deliver all the toys and return for (i = 0; i < REINDEER; i++) semSignal (done) } else { /* 3 elves have arrive */ for (i = 0; i < ELVES – 1; i++) semSignal (santa_semSignal) semWait (emutex) elf_ct = 0 semSignal (emutex) for (i = 0; i < ELVES; i++) { semSignal (problem) -36- head back to the Pacific islands } /* end "forever" loop */ answer that question semSignal (elf_done) } } } /* end "forever" loop */ 5.25 a. There is an array of message slots that constitutes the buffer. Each process maintains a linked list of slots in the buffer that constitute the mailbox for that process. The message operations can implemented as: send (message, dest) semWait (mbuf) semWait for message buffer available semWait (mutex) mutual exclusion on message queue acquire free buffer slog copy message to slot link slot to other messages semSignal (dest.sem) wake destination process semSignal (mutex) release mutual exclusion receive (message) semWait (own.sem) semWait for message to arrive semWait (mutex) mutual exclusion on message queue unlink slot from own.queue copy buffer slot to message add buffer slot to freelist semSignal (mbuf) indicate message slot freed semSignal (mutex) release mutual exclusion where mbuf is initialized to the total number of message slots available; own and dest refer to the queue of messages for each process, and are initially zero. b. This solution is taken from [TANE97]. The synchronization process maintains a counter and a linked list of waiting processes for each semaphore. To do a WAIT or SIGNAL, a process calls the corresponding library procedure, wait or signal, which sends a message to the synchronization process specifying both the operation desired and the semaphore to be used. The library procedure then does a RECEIVE to get the reply from the synchronization process. When the message arrives, the synchronization process checks the counter to see if the required operation can be completed. SIGNALs can always complete, but WAITs will block if the value of the semaphore is 0. If the operation is allowed, the synchronization process sends back an empty message, thus unblocking the caller. If, however, the operation is a WAIT and the semaphore is 0, the synchronization process enters the caller onto the queue and does not send a reply. The result is that the process doing the WAIT is blocked, just as it should be. Later, when a SIGNAL is done, the synchronization process picks one of the processes blocked on the semaphore, either in FIFO order, priority order, or some other order, and sends a reply. Race conditions are avoided here because the synchronization process handles only one request at a time. -37- CHAPTER 6 CONCURRENCY: DEADLOCK AND STARVATION A N S W E R S T O Q U E S T I O N S 6.1 Examples of reusable resources are processors, I/O channels, main and secondary memory, devices, and data structures such as files, databases, and semaphores. Examples of consumable resources are interrupts, signals, messages, and information in I/O buffers. 6.2 Mutual exclusion. Only one process may use a resource at a time. Hold and wait. A process may hold allocated resources while awaiting assignment of others. No preemption. No resource can be forcibly removed from a process holding it. 6.3 The above three conditions, plus: Circular wait. A closed chain of processes exists, such that each process holds at least one resource needed by the next process in the chain. 6.4 The hold-and-wait condition can be prevented by requiring that a process request all of its required resources at one time, and blocking the process until all requests can be granted simultaneously. 6.5 First, if a process holding certain resources is denied a further request, that process must release its original resources and, if necessary, request them again together with the additional resource. Alternatively, if a process requests a resource that is currently held by another process, the operating system may preempt the second process and require it to release its resources. 6.6 The circular-wait condition can be prevented by defining a linear ordering of resource types. If a process has been allocated resources of type R, then it may subsequently request only those resources of types following R in the ordering. 6.7 Deadlock prevention constrains resource requests to prevent at least one of the four conditions of deadlock; this is either done indirectly, by preventing one of the three necessary policy conditions (mutual exclusion, hold and wait, no preemption), or directly, by preventing circular wait. Deadlock avoidance allows the three necessary conditions, but makes judicious choices to assure that the deadlock point is never reached. With deadlock detection, requested resources are granted to processes whenever possible.; periodically, the operating system performs an algorithm that allows it to detect the circular wait condition. -40- input can be produced provided the user processes have not exhausted the disk ( p < max – reso). Conclusion: the uncontrolled amount of storage assigned to the user processes is the only possible source of a storage deadlock. Source: [BRIN73]. 6.11 a. Creating the process would result in the state: Process Max Hold Claim Free 1 70 45 25 25 2 60 40 20 3 60 15 45 4 60 25 35 There is sufficient free memory to guarantee the termination of either P1 or P2. After that, the remaining three jobs can be completed in any order. b. Creating the process would result in the trivially unsafe state: Process Max Hold Claim Free 1 70 45 25 15 2 60 40 20 3 60 15 45 4 60 35 25 6.12 It is unrealistic: don't know max demands in advance, number of processes can change over time, number of resources can change over time (something can break). Most OS's ignore deadlock. But Solaris only lets the superuser use the last process table slot. -41- 6.13 a. The buffer is declared to be an array of shared elements of type T. Another array defines the number of input elements available to each process. Each process keeps track of the index j of the buffer element it is referring to at the moment. var buffer: array 0..max-1 of shared T; available: shared array 0..n-1 of 0..max; "Initialization" var K: 1..n-1; region available do begin available(0) := max; for every k do available (k) := 0; end "Process i" var j: 0..max-1; succ: 0..n-1; begin j := 0; succ := (i+1) mod n; repeat region available do await available (i) > 0; region buffer(j) do consume element; region available do begin available (i) := available(i) – 1; available (succ) := available (succ) + 1; end j := (j+1) mod max; forever end In the above program, the construct region defines a critical region using some appropriate mutual-exclusion mechanism. The notation region v do S means that at most one process at a time can enter the critical region associated with variable v to perform statement S. -42- b. A deadlock is a situation in which: P0 waits for Pn-1 AND P1 waits for P0 AND . . . . . Pn-1 waits for Pn-2 because (available (0) = 0) AND (available (1) = 0) AND . . . . . (available (n-1) = 0) But if max > 0, this condition cannot hold because the critical regions satisfy the following invariant: claim(i) i=1 N ∑ < N available( i) i= 0 n−1 ∑ = max Source: [BRIN73]. 6.14 a. Deadlock occurs if all resource units are reserved while one or more processes are waiting indefinitely for more units. But, if all 4 units are reserved, at least one process has acquired 2 units. Therefore, that process will be able to complete its work and release both units, thus enabling another process to continue. b. Using terminology similar to that used for the banker's algorithm, define claim[i] = total amount of resource units needed by process i; allocation[i] = current number of resource units allocated to process i; and deficit[i] = amount of resource units still needed by i. Then we have: claim[i] i N ∑ = deficit[i] i N ∑ + allocation[i] i N ∑ < M +N In a deadlock situation, all resource units are reserved: allocation[i] i N ∑ = M and some processes are waiting for more units indefinitely. But from the two preceding equations, we find deficit[i] i N ∑ <N -45- Source: [GING90]. 6.19 One solution (6.14) waits on available forks; the other solutions (6.17) waits for the neighboring philosophers to be free. The logic is essentially the same. The solution of Figure 6.17 is slightly more compact. 6.20 Atomic operations operate on atomic data types, which have their own internal format. Therefore, a simple read operation cannot be used, but a special read operation for the atomic data type is needed. 6.21 This code causes a deadlock, because the writer lock will spin, waiting for all readers to release the lock, including this thread. 6.22 Without using the memory barriers, on some processors it is possible that c receives the new value of b, while d receives the old value of a. For example, c could equal 4 (what we expect), yet d could equal 1 (not what we expect). Using the mb() insures a and b are written in the intended order, while the rmb() insures c and d are read in the intended order. This example is from [LOVE04]. -46- CHAPTER 7 MEMORY MANAGEMENT A N S W E R S T O Q U E S T I O N S 7.1 Relocation, protection, sharing, logical organization, physical organization. 7.2 Typically, it is not possible for the programmer to know in advance which other programs will be resident in main memory at the time of execution of his or her program. In addition, we would like to be able to swap active processes in and out of main memory to maximize processor utilization by providing a large pool of ready processes to execute. In both these cases, the specific location of the process in main memory is unpredictable. 7.3 Because the location of a program in main memory is unpredictable, it is impossible to check absolute addresses at compile time to assure protection. Furthermore, most programming languages allow the dynamic calculation of addresses at run time, for example by computing an array subscript or a pointer into a data structure. Hence all memory references generated by a process must be checked at run time to ensure that they refer only to the memory space allocated to that process. 7.4 If a number of processes are executing the same program, it is advantageous to allow each process to access the same copy of the program rather than have its own separate copy. Also, processes that are cooperating on some task may need to share access to the same data structure. 7.5 By using unequal-size fixed partitions: 1. It is possible to provide one or two quite large partitions and still have a large number of partitions. The large partitions can allow the entire loading of large programs. 2. Internal fragmentation is reduced because a small program can be put into a small partition. 7.6 Internal fragmentation refers to the wasted space internal to a partition due to the fact that the block of data loaded is smaller than the partition. External fragmentation is a phenomenon associated with dynamic partitioning, and refers to the fact that a large number of small areas of main memory external to any partition accumulates. 7.7 A logical address is a reference to a memory location independent of the current assignment of data to memory; a translation must be made to a physical address before the memory access can be achieved. A relative address is a particular example of logical address, in which the address is expressed as a location relative to some known point, usually the beginning of the program. A physical address, or absolute address, is an actual location in main memory. -47- 7.8 In a paging system, programs and data stored on disk or divided into equal, fixed- sized blocks called pages, and main memory is divided into blocks of the same size called frames. Exactly one page can fit in one frame. 7.9 An alternative way in which the user program can be subdivided is segmentation. In this case, the program and its associated data are divided into a number of segments. It is not required that all segments of all programs be of the same length, although there is a maximum segment length. A N S W E R S T O P R O B L E M S 7.1 Here is a rough equivalence: Relocation ≈ support modular programming Protection ≈ process isolation; protection and access control Sharing ≈ protection and access control Logical Organization ≈ support of modular programming Physical Organization ≈ long-term storage; automatic allocation and management 7.2 The number of partitions equals the number of bytes of main memory divided by the number of bytes in each partition: 224/216 = 28. Eight bits are needed to identify one of the 28 partitions. 7.3 Let s and h denote the average number of segments and holes, respectively. The probability that a given segment is followed by a hole in memory (and not by another segment) is 0.5, because deletions and creations are equally probable in equilibrium. so with s segments in memory, the average number of holes must be s/2. It is intuitively reasonable that the number of holes must be less than the number of segments because neighboring segments can be combined into a single hole on deletion. 7.4 By problem 7.3, we know that the average number of holes is s/2, where s is the number of resident segments. Regardless of fit strategy, in equilibrium, the average search length is s/4. 7.5 A criticism of the best fit algorithm is that the space remaining after allocating a block of the required size is so small that in general it is of no real use. The worst fit algorithm maximizes the chance that the free space left after a placement will be large enough to satisfy another request, thus minimizing the frequency of compaction. The disadvantage of this approach is that the largest blocks are allocated first; therefore a request for a large area is more likely to fail. 7.6 a. The 40 M block fits into the second hole, with a starting address of 80M. The 20M block fits into the first hole, with a starting address of 20M. The 10M block is placed at location 120M. -50- d. There is one entry for each page in the logical address space. Therefore there are 216 entries. e. In addition to the valid/invalid bit, 22 bits are needed to specify the frame location in main memory, for a total of 23 bits. 7.13 The relationship is a = pz + w, 0 ≤ w < z, which can be stated as: p = a/z, the integer part of a/z. w = Rz(a), the remainder obtained in dividing a by z. 7.14 a. Segment 0 starts at location 660. With the offset, we have a physical address of 660 + 198 = 858 b. 222 + 156 = 378 c. Segment 1 has a length of 422 bytes, so this address triggers a segment fault. d. 996 + 444 = 1440 e. 660 + 222 = 882 7.15 a. Observe that a reference occurs to some segment in memory each time unit, and that one segment is deleted every t references. Because the system is in equilibrium, a new segment must be inserted every t references; therefore, the rate of the boundary's movement is s/t words per unit time. The system's operation time t0 is then the time required for the boundary to cross the hole, i.e., t0 = fmr/s, where m = size of memory. The compaction operation requires two memory references—a fetch and a store—plus overhead for each of the (1 – f)m words to be moved, i.e., the compaction time tc is at least 2(1 – f)m. The fraction F of the time spent compacting is F = 1 – t0/(t0 + tc), which reduces to the expression given. b. k = (t/2s) – 1 = 9; F ≥ (1 – 0.2)/(1 + 1.8) = 0.29 -51- CHAPTER 8 VIRTUAL MEMORY A N S W E R S T O Q U E S T I O N S 8.1 Simple paging: all the pages of a process must be in main memory for process to run, unless overlays are used. Virtual memory paging: not all pages of a process need be in main memory frames for the process to run.; pages may be read in as needed 8.2 Thrashing is a phenomenon in virtual memory schemes, in which the processor spends most of its time swapping pieces rather than executing instructions. 8.3 Algorithms can be designed to exploit the principle of locality to avoid thrashing. In general, the principle of locality allows the algorithm to predict which resident pages are least likely to be referenced in the near future and are therefore good candidates for being swapped out. 8.4 Frame number: the sequential number that identifies a page in main memory; present bit: indicates whether this page is currently in main memory; modify bit: indicates whether this page has been modified since being brought into main memory. 8.5 The TLB is a cache that contains those page table entries that have been most recently used. Its purpose is to avoid, most of the time, having to go to disk to retrieve a page table entry. 8.6 With demand paging, a page is brought into main memory only when a reference is made to a location on that page. With prepaging, pages other than the one demanded by a page fault are brought in. 8.7 Resident set management deals with the following two issues: (1) how many page frames are to be allocated to each active process; and (2) whether the set of pages to be considered for replacement should be limited to those of the process that caused the page fault or encompass all the page frames in main memory. Page replacement policy deals with the following issue: among the set of pages considered, which particular page should be selected for replacement. 8.8 The clock policy is similar to FIFO, except that in the clock policy, any frame with a use bit of 1 is passed over by the algorithm. 8.9 (1) If a page is taken out of a resident set but is soon needed, it is still in main memory, saving a disk read. (2) Modified page can be written out in clusters rather -52- than one at a time, significantly reducing the number of I/O operations and therefore the amount of disk access time. 8.10 Because a fixed allocation policy requires that the number of frames allocated to a process is fixed, when it comes time to bring in a new page for a process, one of the resident pages for that process must be swapped out (to maintain the number of frames allocated at the same amount), which is a local replacement policy. 8.11 The resident set of a process is the current number of pages of that process in main memory. The working set of a process is the number of pages of that process that have been referenced recently. 8.12 With demand cleaning, a page is written out to secondary memory only when it has been selected for replacement. A precleaning policy writes modified pages before their page frames are needed so that pages can be written out in batches. A N S W E R S T O P R O B L E M S 8.1 a. Split binary address into virtual page number and offset; use VPN as index into page table; extract page frame number; concatenate offset to get physical memory address b. (i) 1052 = 1024 + 28 maps to VPN 1 in PFN 7, (7 × 1024+28 = 7196) (ii) 2221 = 2 × 1024 + 173 maps to VPN 2, page fault (iii) 5499 = 5 × 1024 + 379 maps to VPN 5 in PFN 0, (0 × 1024+379 = 379) 8.2 a. Virtual memory can hold (232 bytes of main memory)/( 210 bytes/page) = 222 pages, so 22 bits are needed to specify a page in virtual memory. Each page table contains (210 bytes per page table)/(4 bytes/entry) = 28 entries. Thus, each page table can handle 8 of the required 22 bits. Therefore, 3 levels of page tables are needed. b. Tables at two of the levels have 28 entries; tables at one level have 26 entries. (8 + 8 + 6 = 22). c. Less space is consumed if the top level has 26 entries. In that case, the second level has 26 pages with 28 entries each, and the bottom level has 214 pages with 28 entries each, for a total of 1 + 26 + 214 pages = 16,449 pages. If the middle level has 26 entries, then the number of pages is 1 + 28 + 214 pages = 16,641 pages. If the bottom level has 26 entries, then the number of tables is 1 + 28 + 216 pages = 65,973 pages. 8.3 a. 4 Mbyte b. Number of rows: 26+2=128 entries. Each entry consist of: 20 (page number) + 20 (frame number) + 8 bits (chain index) = 48 bits = 6 bytes. Total: 128 × 6= 768 bytes 8.4 a. PFN 3 since loaded longest ago at time 20 b. PFN 1 since referenced longest ago at time 160 c. Clear R in PFN 3 (oldest loaded), clear R in PFN 2 (next oldest loaded), victim PFN is 0 since R=0 d. Replace the page in PFN 3 since VPN 3 (in PFN 3) is used furthest in the future -55- address. Your top level page table then would have only 4 entries. Yet another option is to revise the criteria that the top level page table fit into a single physical page and instead make it fit into 4 pages. This would save a physical page, which is not much. 8.11 a. 400 nanoseconds. 200 to get the page table entry, and 200 to access the memory location. b. This is a familiar effective time calculation: (220 × 0.85) + (420 × 0.15) = 250 Two cases: First, when the TLB contains the entry required. In that case we pay the 20 ns overhead on top of the 200 ns memory access time. Second, when the TLB does not contain the item. Then we pay an additional 200 ns to get the required entry into the TLB. c. The higher the TLB hit rate is, the smaller the EMAT is, because the additional 200 ns penalty to get the entry into the TLB contributes less to the EMAT. 8.12 a. N b. P 8.13 a. This is a good analogy to the CLOCK algorithm. Snow falling on the track is analogous to page hits on the circular clock buffer. The movement of the CLOCK pointer is analogous to the movement of the plow. b. Note that the density of replaceable pages is highest immediately in front of the clock pointer, just as the density of snow is highest immediately in front of the plow. Thus, we can expect the CLOCK algorithm to be quite efficient in finding pages to replace. In fact, it can be shown that the depth of the snow in front of the plow is twice the average depth on the track as a whole. By this analogy, the number of pages replaced by the CLOCK policy on a single circuit should be twice the number that are replaceable at a random time. The analogy is imperfect because the CLOCK pointer does not move at a constant rate, but the intuitive idea remains. The snowplow analogy to the CLOCK algorithm comes from [CARR84]; the depth analysis comes from Knuth, D. The Art of Computer Programming, Volume 2: Sorting and Searching. Reading, MA: Addison-Wesley, 1997 (page 256). 8.14 The processor hardware sets the reference bit to 0 when a new page is loaded into the frame, and to 1 when a location within the frame is referenced. The operating system can maintain a number of queues of page-frame tables. A page-frame table entry moves from one queue to another according to how long the reference bit from that page frame stays set to zero. When pages must be replaced, the pages to be replaced are chosen from the queue of the longest-life nonreferenced frames. -56- 8.15 a. Seq of page refs Window Size, ∆ 1 2 3 4 5 6 1 1 1 1 1 1 1 2 2 1 2 1 2 1 2 1 2 1 2 3 3 2 3 1 2 3 1 2 3 1 2 3 1 2 3 4 4 3 4 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 5 5 4 5 3 4 5 2 3 4 5 1 2 3 4 5 1 2 3 4 5 2 2 5 2 4 5 2 3 4 5 2 3 4 5 2 1 3 4 5 2 1 1 2 1 5 2 1 4 5 2 1 3 4 5 2 1 3 4 5 2 1 3 3 1 3 2 1 3 5 2 1 3 4 5 2 1 3 4 5 2 1 3 3 3 3 1 3 2 1 3 5 2 1 3 4 5 2 1 3 2 2 3 2 3 2 1 3 2 1 3 2 5 1 3 2 3 3 2 3 2 3 2 3 1 2 3 1 2 3 4 4 3 4 2 3 4 2 3 4 2 3 4 1 2 3 4 5 5 4 5 3 4 5 2 3 4 5 2 3 4 5 2 3 4 5 4 4 5 4 5 4 3 5 4 2 3 5 4 2 3 5 4 5 5 4 5 4 5 4 5 3 4 5 2 3 4 5 1 1 5 1 4 5 1 4 5 1 4 5 1 3 4 5 1 1 1 1 5 1 4 5 1 4 5 1 4 5 1 3 3 1 3 1 3 5 1 3 4 5 1 3 4 5 1 3 2 2 3 2 1 3 2 1 3 2 5 1 3 2 4 5 1 3 2 5 5 2 5 3 2 5 1 3 2 5 1 3 2 5 1 3 2 5 b., c. ∆ 1 2 3 4 5 6 s20(∆) 1 1.85 2.5 3.1 3.55 3.9 m20(∆) 0.9 0.75 0.75 0.65 0.55 0.5 s20(∆) is an increasing function of ∆. m20(∆) is a nonincreasing function of ∆. 8.16 [PIZZ89] suggests the following strategy. Use a mechanism that adjusts the value of Q at each window time as a function of the actual page fault rate experienced during the window. The page fault rate is computed and compared with a system- wide value for "desirable" page fault rate for a job. The value of Q is adjusted upward (downward) whenever the actual page fault rate of a job is higher (lower) than the desirable value. Experimentation using this adjustment mechanism showed that execution of the test jobs with dynamic adjustment of Q consistently produced a lower number of page faults per execution and a decreased average resident set size than the execution with a constant value of Q (within a very broad range). The memory time product (MT) versus Q using the adjustment mechanism also produced a consistent and considerable improvement over the previous test results using a constant value of Q. -57- 8.17 232 memory 211 page size = 221 page frames Segment: 0 1 2 3 0 7 Page descriptor table 00021ABC Main memory (232 bytes) 232 memory 211 page size = 221 page frames a. 8 × 2K = 16K b. 16K × 4 = 64K c. 232 = 4 GBytes -60- 9.10 When the current process completes or is blocked, choose the ready process with the greatest value of R, where R = (w + s)/s, with w = time spent waiting for the processor and s = expected service time. 9.11 Scheduling is done on a preemptive (at time quantum) basis, and a dynamic priority mechanism is used. When a process first enters the system, it is placed in RQ0 (see Figure 9.4). After its first execution, when it returns to the Ready state, it is placed in RQ1. Each subsequent time that it is preempted, it is demoted to the next lower-priority queue. A shorter process will complete quickly, without migrating very far down the hierarchy of ready queues. A longer process will gradually drift downward. Thus, newer, shorter processes are favored over older, longer processes. Within each queue, except the lowest-priority queue, a simple FCFS mechanism is used. Once in the lowest-priority queue, a process cannot go lower, but is returned to this queue repeatedly until it completes execution. A N S W E R S T O P R O B L E M S 9.1 Each square represents one time unit; the number in the square refers to the currently-running process. FCFS A A A B B B B B C C D D D D D E E E E E RR, q = 1 A B A B C A B C B D B D E D E D E D E E RR, q = 4 A A A B B B B C C B D D D D E E E E D E SPN A A A C C B B B B B D D D D D E E E E E SRT A A A C C B B B B B D D D D D E E E E E HRRN A A A B B B B B C C D D D D D E E E E E Feedback, q = 1 A B A C B C A B B D B D E D E D E D E E Feedback, q = 2i A B A A C B B C B B D D E D D E E D E E -61- A B C D E Ta 0 1 3 9 12 Ts 3 5 2 5 5 FCFS Tf 3 8 10 15 20 Tr 3.00 7.00 7.00 6.00 8.00 6.20 Tr/Ts 1.00 1.40 3.50 1.20 1.60 1.74 RR q = 1 Tf 6.00 11.00 8.00 18.00 20.00 Tr 6.00 10.00 5.00 9.00 8.00 7.60 Tr/Ts 2.00 2.00 2.50 1.80 1.60 1.98 RR q = 4 Tf 3.00 10.00 9.00 19.00 20.00 Tr 3.00 9.00 6.00 10.00 8.00 7.20 Tr/Ts 1.00 1.80 3.00 2.00 1.60 1.88 SPN Tf 3.00 10.00 5.00 15.00 20.00 Tr 3.00 9.00 2.00 6.00 8.00 5.60 Tr/Ts 1.00 1.80 1.00 1.20 1.60 1.32 SRT Tf 3.00 10.00 5.00 15.00 20.00 Tr 3.00 9.00 2.00 6.00 8.00 5.60 Tr/Ts 1.00 1.80 1.00 1.20 1.60 1.32 HRRN Tf 3.00 8.00 10.00 15.00 20.00 Tr 3.00 7.00 7.00 6.00 8.00 6.20 Tr/Ts 1.00 1.40 3.50 1.20 1.60 1.74 FB q = 1 Tf 7.00 11.00 6.00 18.00 20.00 Tr 7.00 10.00 3.00 9.00 8.00 7.40 Tr/Ts 2.33 2.00 1.50 1.80 1.60 1.85 FB Tf 4.00 10.00 8.00 18.00 20.00 q = 2i Tr 4.00 9.00 5.00 9.00 8.00 7.00 Tr/Ts 1.33 1.80 2.50 1.80 1.60 1.81 -62- 9.2 FCFS A B B B B B B B B B C D D D D D D D D D RR, q = 1 A B C B D B D B D B D B D B D B D B D D RR, q = 4 A B B B B C B B B B D D D D B D D D D D SPN A B B B B B B B B B C D D D D D D D D D SRT A B C B B B B B B B B D D D D D D D D D HRRN A B B B B B B B B B C D D D D D D D D D Feedback, q = 1 A B C D B D B D B D B D B D B D B D B D Feedback, q = 2i A B C D B B D D B B B B D D D D B B D D -65- 9.8 This proof, due to P. Mondrup, is reported in [BRIN73]. Consider the queue at time t immediately after a departure and ignore further arrivals. The waiting jobs are numbered 1 to n in the order in which they will be scheduled: job: 1 2 . . . i . . . n arrival time: t1 t2 . . . ti . . . tn service time: r1 r2 . . . ri . . . rn Among these we assume that job i will reach the highest response ratio before its departure. When the jobs 1 to i have been executed, time becomes Ti = t + r1 + r2 + . . . + ri and job i has the response ratio Ri Ti( ) + Ti − ti ri The reason for executing job i last in the sequence 1 to i is that its response ratio will be the lowest one among these jobs at time Ti: Ri(Ti) = min [ R1(Ti), R2(Ti), . . . , Ri(Ti) ] Consider now the consequences of scheduling the same n jobs in any other sequence: -66- job: a b . . . j . . . z arrival time: ta tb . . . tj . . . tz service time: ra rb . . . rj . . . rz In the new sequence, we select the smallest subsequence of jobs, a to j, that contains all the jobs, 1 to i, of the original subsequence (This implies that job j is itself one of the jobs 1 to i). When the jobs a to j have been served, time becomes Tj = t + ra + rb + . . . + rj and job j reaches the response ratio Rj T j( ) + T j − tj rj Since the jobs 1 to i are a subset of the jobs a to j, the sum of their service times Ti – t must be less than or equal to the sum of service time Tj – t. And since response ratios increase with time, Ti ≤ Tj implies Rj(Tj) ≥ Rj(Ti) It is also known that job j is one of the jobs 1 to i, of which job j has the smallest response ratio at time Ti. The above inequality can therefore be extended as follows: Rj(Tj) ≥ Rj(Ti) ≥ Ri(Ti) In other words, when the scheduling algorithm is changed, there will always be a job j that reaches response ratio Rj(Tj), which is greater than or equal to the highest response ratio Ri(Ti) obtained with the original algorithm. Notice that this proof is valid in general for priorities that are non- decreasing functions of time. For example, in a FIFO system, priorities increase linearly with waiting time at the same rate for all jobs. Therefore, the present proof shows that the FIFO algorithm minimizes the maximum waiting time for a given batch of jobs. 9.9 Before we begin, there is one result that is needed, as follows. Assume that an item with service time Ts has been in service for a time h. Then, the expected remaining service time E [T/T > h] = Ts. That is, no matter how long an item has been in service, the expected remaining service time is just the average service time for the item. This result, though counter to intuition, is correct, as we now show. Consider the exponential probability distribution function: F(x) = Pr[X ≤ x] = 1 – e–µx Then, we have Pr[X > x] = e-µx. Now let us look at the conditional probability that X is greater than x + h given that X is greater than x: -67- Pr X > x + h X > x[ ] = Pr X > x + h( ), X > x( )[ ]Pr X > x[ ] = Pr X > x + h[ ] Pr X > x[ ] Pr X > x + h X > x[ ] = e -µ x+h( ) e-µx = e-µh So, Pr[X ≤ x + h/X > x] = 1 – e–µh = Pr[X ≤ h] Thus the probability distribution for service time given that there has been service of duration x is the same as the probability distribution of total service time. Therefore the expected value of the remaining service time is the same as the original expected value of service time. With this result, we can now proceed to the original problem. When an item arrives for service, the total response time for that item will consist of its own service time plus the service time of all items ahead of it in the queue. The total expected response time has three components. •Expected service time of arriving process = Ts •Expected service time of all processes currently waiting to be served. This value is simply w × Ts, where w is the mean number of items waiting to be served. •Remaining service time for the item currently in service, if there is an item currently in service. This value can be expressed as ρ × Ts, where ρ is the utilization and therefore the probability that an item is currently in service and Ts, as we have demonstrated, is the expected remaining service time. Thus, we have R = Ts × 1+ w + ρ( ) = Ts × 1+ ρ 2 1− ρ + ρ = Ts 1− ρ 9.10 Let us denote the time slice, or quantum, used in round robin scheduling as δ. In this problem, δ is assumed to be very small compared to the service time of a process. Now, consider a newly arrived process, which is placed at the end of the ready queue for service. We are assuming that this particular process has a service time of x, which is some multiple of δ: x = mδ To begin, let us ask the question, how much time does the process spend in the queue before it receives its first quantum of service. It must wait until all q processes waiting in line ahead of it have been serviced. Thus the initial wait time = qδ, where q is the average number of items in the system (waiting and being served). We can now calculate the total time this process will spend waiting before -70- cannot set flag [1] to true, being prevented from doing so by P0's reading of the variable (remember that access to the variable takes place under mutual exclusion). 9.16 a. Sequence with which processes will get 1 min of processor time: 1 2 3 4 5 Elapsed time A A A A A A A A A A A A A A A B B B B B B B B B C C C D D D D D D E E E E E E E E E E E E 5 10 15 19 23 27 30 33 36 38 40 42 43 44 45 The turnaround time for each process: A = 45 min, B = 35 min, C = 13 min, D = 26 min, E = 42 min The average turnaround time is = (45+35+13+26+42) / 5 = 32.2 min b. Priority Job Turnaround Time 3 4 6 7 9 B E A C D 9 9 + 12 = 21 21 + 15 = 36 36 + 3 = 39 39 + 6 = 45 The average turnaround time is: (9+21+36+39+45) / 5 = 30 min c. Job Turnaround Time A B C D E 15 15 + 9 = 24 24 + 3 = 27 27 + 6 = 33 33 + 12 = 45 The average turnaround time is: (15+24+27+33+45) / 5 = 28.8 min -71- d. Running Time Job Turnaround Time 3 6 9 12 15 C D B E A 3 3 + 6 = 9 9 + 9 = 18 18 + 12 = 30 30 + 15 = 45 The average turnaround time is: (3+9+18+30+45) / 5 = 21 min -72- CHAPTER 10 MULTIPROCESSOR AND REAL-TIME SCHEDULING A N S W E R S T O Q U E S T I O N S 10.1 Fine: Parallelism inherent in a single instruction stream. Medium: Parallel processing or multitasking within a single application. Coarse: Multiprocessing of concurrent processes in a multiprogramming environment. Very Coarse: Distributed processing across network nodes to form a single computing environment. Independent: Multiple unrelated processes. 10.2 Load sharing: Processes are not assigned to a particular processor. A global queue of ready threads is maintained, and each processor, when idle, selects a thread from the queue. The term load sharing is used to distinguish this strategy from load-balancing schemes in which work is allocated on a more permanent basis. Gang scheduling: A set of related threads is scheduled to run on a set of processors at the same time, on a one-to-one basis. Dedicated processor assignment: Each program is allocated a number of processors equal to the number of threads in the program, for the duration of the program execution. When the program terminates, the processors return to the general pool for possible allocation to another program. Dynamic scheduling: The number of threads in a program can be altered during the course of execution. 10.3 First Come First Served (FCFS): When a job arrives, each of its threads is placed consecutively at the end of the shared queue. When a processor becomes idle, it picks the next ready thread, which it executes until completion or blocking. Smallest Number of Threads First: The shared ready queue is organized as a priority queue, with highest priority given to threads from jobs with the smallest number of unscheduled threads. Jobs of equal priority are ordered according to which job arrives first. As with FCFS, a scheduled thread is run to completion or blocking. Preemptive Smallest Number of Threads First: Highest priority is given to jobs with the smallest number of unscheduled threads. An arriving job with a smaller number of threads than an executing job will preempt threads belonging to the scheduled job. 10.4 A hard real-time task is one that must meet its deadline; otherwise it will cause undesirable damage or a fatal error to the system. A soft real-time task has an associated deadline that is desirable but not mandatory; it still makes sense to schedule and complete the task even if it has passed its deadline. -75- 10.4 The total load now exceeds 100%. None of the methods can handle the load. The tasks that fail to complete vary for the various methods. ARM EDF LLF B 5 B1 C1 C2 A B A B C CB A A BB 10 15 20 25 A B 5 A C B A AB B 10 15 20 25 B 5 A C A B A B 10 15 20 25 B B C B2 B3, C2 C1 A4, B3 A 10.5 On this task profile, MUF behavior is identical to RM. Note that EDF has fewer context switches (11) than the other methods, all of which have 13. ARM EDF LLF MUF B 5 C A B C A C B A C A B 10 15 20 25 A B 5 C A B A C B A A B 10 15 20 25 A B 5 C A C B A BC A C A B 10 15 20 25 A B 5 C A CB A BC A C A B 10 15 20 25 10.6 The critical task set consists of A and B. Note that only MUF is the only algorithm that gets the critical set (A and B) scheduled in time. ARM EDF LLF MUF B 5 B1 C1 C2 A B A B C CB A A BB 10 15 20 25 A B 5 A C B A AB B 10 15 20 25 B 5 A C A B A B 10 15 20 25 B A 5 BA A ABB 10 15 20 25 B B C B BA B2 B3, C2 C1 C1 C2 A4, B3 A -76- 10.7 a. The total utilization of P1 and P2 is 0.41, which is less than 0.828, the bound given for two tasks by Equation 10.2. Therefore, these two tasks are schedulable. b. The utilization of all the tasks is 0.86, which exceeds the bound of 0.779. c. Observe that P1 and P2 must execute at least once before P3 can begin executing. Therefore, the completion time of the first instance of P3 can be no less than 20 + 30 + 68 = 118. However, P1 is initiated one additional time in the interval (0, 118). Therefore, P3 does not complete its first execution until 118 + 20 = 138. This is within the deadline for P3. By continuing this reasoning, we can see that all deadlines of all three tasks can be met. 10.8 normal execution execution in critical section T1 T2 T3 s locked by T3 s unlocked s unlocked s locked by T1 Once T3 enters its critical section, it is assigned a priority higher than T1. When T3 leaves its critical section, it is preempted by T1. -77- CHAPTER 11 I/O MANAGEMENT AND DISK SCHEDULING A N S W E R S T O Q U E S T I O N S 11.1 Programmed I/O: The processor issues an I/O command, on behalf of a process, to an I/O module; that process then busy-waits for the operation to be completed before proceeding. Interrupt-driven I/O: The processor issues an I/O command on behalf of a process, continues to execute subsequent instructions, and is interrupted by the I/O module when the latter has completed its work. The subsequent instructions may be in the same process, if it is not necessary for that process to wait for the completion of the I/O. Otherwise, the process is suspended pending the interrupt and other work is performed. Direct memory access (DMA): A DMA module controls the exchange of data between main memory and an I/O module. The processor sends a request for the transfer of a block of data to the DMA module and is interrupted only after the entire block has been transferred. 11.2 Logical I/O: The logical I/O module deals with the device as a logical resource and is not concerned with the details of actually controlling the device. The logical I/O module is concerned with managing general I/O functions on behalf of user processes, allowing them to deal with the device in terms of a device identifier and simple commands such as open, close, read, write. Device I/O: The requested operations and data (buffered characters, records, etc.) are converted into appropriate sequences of I/O instructions, channel commands, and controller orders. Buffering techniques may be used to improve utilization. 11.3 Block-oriented devices stores information in blocks that are usually of fixed size, and transfers are made one block at a time. Generally, it is possible to reference data by its block number. Disks and tapes are examples of block-oriented devices. Stream-oriented devices transfer data in and out as a stream of bytes, with no block structure. Terminals, printers, communications ports, mouse and other pointing devices, and most other devices that are not secondary storage are stream oriented. 11.4 Double buffering allows two operations to proceed in parallel rather than in sequence. Specifically, a process can transfer data to (or from) one buffer while the operating system empties (or fills) the other. 11.5 Seek time, rotational delay, access time. -80- Ps[ j /t] = 1 N if t ≤ j – 1 OR t ≥ N – j Ps[ j /t] = 2 N if j – 1 < t < N – j In the former case, the current track is so close to one end of the disk (track 0 or track N – 1) that only one track is exactly j tracks away. In the second case, there are two tracks that are exactly j tracks away from track t, and therefore the probability of a seek of length j is the probability that either of these two tracks is selected, which is just 2/N. b. Let Ps [K] = Pr [seek of length K, independent of current track position]. Then: € Ps K[ ] = Ps K / t[ ] t=0 N−1 ∑ ×Pr current track is track t[ ] = 1 N Ps K / t[ ] t=0 N−1 ∑ From part (a), we know that Ps[K/t] takes on the value 1/N for 2K of the tracks, and the value 2/N for (N – 2K) of the tracks. So € Ps K[ ] = 1 N 2K N + 2 N −K( ) N = 2 N 2 N −K( ) c. E K[ ] = K × Ps K[ ] K=0 N−1 ∑ = 2K N − K( ) N 2K=0 N−1 ∑ = 2 N K K=0 N−1 ∑ − 2N2 K2 K=0 N−1 ∑ = N +1( ) − 2 N + 1( ) 2N + 1( ) 6N = N 2 − 1 3N d. This follows directly from the last equation. 11.5 Define Ai = Time to retrieve a word from memory level i Hi = Probability that a word is in memory level i and no higher-level memory Bi = Time to transfer a block of data from memory level (i + 1) to memory level i -81- Let cache be memory level 1; main memory, memory level 2, and so on, for a total of N levels of memory. Then, we can write Ts = AiHi i=1 N ∑ Now, recognize that if a word is in M1 (cache), it is read immediately. If it is in M2 but not in M1, then a block of data is transferred from M2 to M1 and then read. Thus A2 = B1 + A1 Further A3 = B2 + A2 = B1 + B2 + A1 Generalizing, Ai = A1 + Bj j=1 i−1 ∑ So Ts = T1 Hi i=1 N ∑ + BjHi J =1 L−1 ∑ L= 2 N ∑ But Hi i=1 N ∑ = 1 Finally Ts = T1 + BjHi J =1 L−1 ∑ L= 2 N ∑ 11.6 a. The middle section in Figure 11.11b is empty. Thus, this reduces to the strategy of Figure 11.11a. b. The old section consists of one block, and we have the LRU replacement policy. 11.7 Each sector can hold 4 logical records. The required number of sectors is 303,150/4 = 75,788 sectors. This requires 75,788/96 = 790 tracks, which in turn requires 790/110 = 8 surfaces. 11.8 There are 512 bytes/sector. Since each byte generates an interrupt, there are 512 interrupts. Total interrupt processing time = 2.5 × 512 = 1280 µs. The time to read one sector is: ((60 sec/min) / (360 rev/min)) / (96 sectors/track) = 0.001736 sec = 1736 µs Percentage of time processor spends handling I/O: (100) × (1280/1736) = 74% 11.9 With DMA, there is only one interrupt of 2.5 µs. Therefore, the percentage of time the processor spends handling I/O is (100) × (2.5/1736) = 0.14% -82- 11.10 Only one device at a time can be serviced on a selector channel. Thus, Maximum rate = 800 + 800 + 2 × 6.6 + 2 × 1.2 + 10 × 1 = 1625.6 KBytes/sec 11.11 It depends on the nature of the I/O request pattern. On one extreme, if only a single process is doing I/O and is only doing one large I/O at a time, then disk striping improves performance. If there are many processes making many small I/O requests, then a nonstriped array of disks should give comparable performance to RAID 0. 11.12 RAID 0: 800 GB RAID 1: 400 GB RAID 3: 600 GB RAID 4: 600 GB RAID 5: 600 GB RAID 6: 400 GB -85- 12.4 File size 41,600 bytes 640,000 bytes 4,064,000 bytes No. of blocks 3 (rounded up to whole number) 40 249 Total capacity 3 × 16K = 48K = 49,152 bytes 40 × 16K = 640K = 655,360 bytes 249 × 16K = 3984K = 4,079,616 bytes Wasted space 7,552 bytes 15,360 bytes 15,616 bytes % of wasted space 15.36% 2.34% 0.38% 12.5 Directories enable files to be organized and clearly separated on the basis of ownership, application, or some other criterion. This improves security, integrity (organizing backups), and avoids the problem of name clashes. 12.6 Clearly, security is more easily enforced if directories are easily recognized as special files by the operating system. Treating a directory as an ordinary file with certain assigned access restrictions provides a more uniform set of objects to be managed by the operating system and may make it easier to create and manage user-owned directories. 12.7 This is a rare feature. If the operating system structures the file system so that subdirectories are allowed underneath a master directory, there is little or no additional logic required to allow arbitrary depth of subdirectories. Limiting the depth of the subdirectory tree places an unnecessary limitation on the user's organization of file space. 12.8 a. Yes. the method employed is very similar to that used by many LISP systems for garbage collection. First we would establish a data structure representing every block on a disk supporting a file system. A bit map would be appropriate here. Then, we would start at the root of the file system (the "/" directory), and mark every block used by every file we could find through a recursive descent through the file system. When finished, we would create a free list from the blocks remaining as unused. This is essentially what the UNIX utility fsck does. b. Keep a "backup" of the free-space list pointer at one or more places on the disk. Whenever this beginning of the list changes, the "backup" pointers are also updated. This will ensure you can always find a valid pointer value even if there is a memory or disk block failure. 12.9 Level Number of Blocks Number of Bytes Direct 10 10K Single indirect 256 256K Double indirect 256 × 256 = 65K 65M Triple indirect 256 × 65K = 64M 16G The maximum file size is over 16G bytes. 12.10 a. Find the number of disk block pointers that fit in one block by dividing the block size by the pointer size: -86- 8K/4 = 2K pointers per block The maximum file size supported by the inode is thus: 12 Direct 12 + 2K Indirect – 1 + 2K + (2K × 2K) Indirect – 2 + 4M + (2K × 2K × 2K) Indirect – 3 + 8G blocks Which, when multiplied by the block size (8K), is 96KB + 16MB + 32GB + 64TB Which is HUGE. b. There are 24 bits for identifying blocks within a partition, so that leads to: 224 × 8K = 16M × 8K = 128 GB c. Using the information from (a), we see that the direct blocks only cover the first 96KB, while the first indirect block covers the next 16MB. the requested file position is 13M and change, which clearly falls within the range of the first indirect block. There will thus be two disk accesses. One for the first indirect block, and one for the block containing the required data. -87- CHAPTER 13 EMBEDDED OPERATING SYSTEMS A N S W E R S T O Q U E S T I O N S 13.1 A combination of computer hardware and software, and perhaps additional mechanical or other parts, designed to perform a dedicated function. In many cases, embedded systems are part of a larger system or product, as in the case of an antilock braking system in a car. 13.2 • Small to large systems, implying very different cost constraints, thus different needs for optimization and reuse • Relaxed to very strict requirements and combinations of different quality requirements, for example, with respect to safety, reliability, real-time, flexibility, and legislation • Short to long life times • Different environmental conditions in terms of, for example, radiation, vibrations, and humidity • Different application characteristics resulting in static versus dynamic loads, slow to fast speed, compute versus interface intensive tasks, and/or combinations thereof • Different models of computation ranging from discrete-event systems to those involving continuous time dynamics (usually referred to as hybrid systems) 13.3 An embedded OS is an OS designed for the embedded system environment. 13.4 • Real time operation: In many embedded systems, the correctness of a computation depends, in part, on the time at which it is delivered. Often, real- time constraints are dictated by external I/O and control stability requirements. • Reactive operation: Embedded software may execute in response to external events. If these events do not occur periodically or at predictable intervals, the embedded software may need to take into account worst-case conditions and set priorities for execution of routines. • Configurability: Because of the large variety of embedded systems, there is a large variation in the requirements, both qualitative and quantitative, for embedded OS functionality. Thus, and embedded OS intended for use on a variety of embedded systems lend itself to flexible configuration so that only the functionality needed for a specific application and hardware suite is provided. • I/O device flexibility: There is virtually no device that needs to be supported by all versions of the OS, and the range of I/O devices is large. • Streamlined protection mechanisms: Embedded systems are typical designed for a limited, well-defined functionality. Untested programs are rarely added to the software. After the software has been configured and tested, it can be assumed to be reliable. Thus, apart from security measures, embedded systems -90- 13.6 If a thread has locked a mutex and then attempts to lock the mutex again, typically as a result of some recursive call in a complicated call graph, then either an assertion failure will be reported or the thread will deadlock. This behavior is deliberate. When a thread has just locked a mutex associated with some data structure, it can assume that that data structure is in a consistent state. Before unlocking the mutex again it must ensure that the data structure is again in a consistent state. Recursive mutexes allow a thread to make arbitrary changes to a data structure, then in a recursive call lock the mutex again while the data structure is still inconsistent. The net result is that code can no longer make any assumptions about data structure consistency, which defeats the purpose of using mutexes. 13.7 a. This listing is an example using a condition variable. Thread A is acquiring data that are processed by Thread B. First, B executes. On line 24, B acquires the mutex associated with the condition variable. There is no data in the buffer and buffer_empty is initialized so true, so B calls cyg_cond_wait on line 26. This call suspends B waiting for the condition variable to be set and it unlocks the mutex mut_cond_var. Once A has acquired data, it sets buffer_empty to false (line 11). A then locks the mutex (line 13), signals the condition variable (line 15), and then unlocks the mutex (line 17). B can now run. Before returning from cyg_cond_wait, the mutex mut_cond_var is locked and owned by B. B can now get the data buffer (line 28) and set buffer_empty to true (line 31). Finally, the mutex is released by B (line 33) and the data in the buffer is processed (line 35) b. If this code were not atomic, it would be possible for B to miss the signal call from A even though the buffer contained data. This is because cyg_cond_wait first checks to see if the condition variable is set and, in this case, it is not. Next, the mutex is released in the cyg_cond_wait call. Now, A executes, putting data into the buffer and then signals the condition variable (line 15). Then, B returns to waiting. However, the condition variable has been set. c. This ensures that the condition that B is waiting on is still true after returning from the condition wait call. This is needed in case there are other threads waiting on the same condition. Source: [MASS03] 13.8 Even if the two threads were running at the same priority, the one attempting to claim the spinlock would spin until it was timesliced and a lot of processor time would be wasted. If an interrupt handler tried to claim a spinlock owned by a thread, the interrupt handler would loop forever. 13.9 The basic reason is that the system is almost completely idle. You only need intelligent scheduling when a resource is under contention. -91- 13.10 a. Clients are not able to monopolize the resource queue by making multiple requests. b. The basic reason is that a component may still hold a lock after it calls release. It may still hold the lock because transferring control to the next holder requires reconfiguring the hardware in a split-phase operation. The lock can be granted to the next holder only after this reconfiguration occurs. This means that technically, a snippet of code such as release() request() can be seen as the lock as re-request. There are other solutions to the problem (e.g., give the lock a special "owner" who holds it while reconfiguring), but this raises issue when accounting for CPU cycles or energy. -92- CHAPTER 14 COMPUTER SECURITY THREATS A N S W E R S T O Q U E S T I O N S 14.1 The protection afforded to an automated information system in order to attain the applicable objectives of preserving the integrity, availability and confidentiality of information system resources (includes hardware, software, firmware, information/data, and telecommunications). 14.2 Confidentiality, integrity, and availability. 14.3 Passive attacks have to do with eavesdropping on, or monitoring, transmissions. Electronic mail, file transfers, and client/server exchanges are examples of transmissions that can be monitored. Active attacks include the modification of transmitted data and attempts to gain unauthorized access to computer systems. 14.4 Masquerader: An individual who is not authorized to use the computer and who penetrates a system's access controls to exploit a legitimate user's account. Misfeasor: A legitimate user who accesses data, programs, or resources for which such access is not authorized, or who is authorized for such access but misuses his or her privileges. Clandestine user: An individual who seizes supervisory control of the system and uses this control to evade auditing and access controls or to suppress audit collection. 14.5 Hackers: those who hack into computers do so for the thrill of it or for status. The hacking community is a strong meritocracy in which status is determined by level of competence. Thus, attackers often look for targets of opportunity and then share the information with others. Criminals: organized groups of hackers, typically with specific target or target classes in mind. Insider attacks: carried out by those within an organization. 14.6 A virus may use compression so that the infected program is exactly the same length as an uninfected version. 14.7 A portion of the virus, generally called a mutation engine, creates a random encryption key to encrypt the remainder of the virus. The key is stored with the virus, and the mutation engine itself is altered. When an infected program is invoked, the virus uses the stored random key to decrypt the virus. When the virus replicates, a different random key is selected. 14.8 A dormant phase, a propagation phase, a triggering phase, and an execution phase -95- However, multiple signature samples from a single individual will not be identical. This complicates the task of developing a computer representation of the signature that can be matched to future samples. Voice: Whereas the signature style of an individual reflects not only the unique physical attributes of the writer but also the writing habit that has developed, voice patterns are more closely tied to the physical and anatomical characteristics of the speaker. Nevertheless, there is still a variation from sample to sample over time from the same speaker, complicating the biometric recognition task. 15.5 Discretionary access control (DAC) controls access based on the identity of the requestor and on access rules (authorizations) stating what requestors are (or are not) allowed to do. This policy is termed discretionary because an entity might have access rights that permit the entity, by its own volition, to enable another entity to access some resource. Role-based access control (RBAC) controls access based on the roles that users have within the system and on rules stating what accesses are allowed to users in given roles. RBAC may have a discretionary or mandatory mechanism. 15.6 Statistical anomaly detection involves the collection of data relating to the behavior of legitimate users over a period of time. Then statistical tests are applied to observed behavior to determine with a high level of confidence whether that behavior is not legitimate user behavior. Signature intrusion detection involves an attempt to define a set of rules that can be used to decide that a given behavior is that of an intruder. 15.7 A digital immune system provides a general-purpose emulation and virus- detection system. The objective is to provide rapid response time so that viruses can be stamped out almost as soon as they are introduced. When a new virus enters an organization, the immune system automatically captures it, analyzes it, adds detection and shielding for it, removes it, and passes information about that virus to systems running a general antivirus program so that it can be detected before it is allowed to run elsewhere. 15.8 Behavior-blocking software integrates with the operating system of a host computer and monitors program behavior in real-time for malicious actions. The behavior blocking software then blocks potentially malicious actions before they have a chance to affect the system. 15.9 Signature-based worm scan filtering: This type of approach generates a worm signature, which is then used to prevent worm scans from entering/leaving a network/host. Typically, this approach involves identifying suspicious flows and generating a worm signature. This approach is vulnerable to the use of polymorphic worms: Either the detection software misses the worm or, if it is sufficiently sophisticated to deal with polymorphic worms, the scheme may take a long time to react. Filter-based worm containment: This approach is similar to class A but focuses on worm content rather than a scan signature. The filter checks a message to determine if it contains worm code. Payload-classification-based worm containment: These network-based techniques examine packets to see if they contain a worm. Various anomaly -96- detection techniques can be used, but care is needed to avoid high levels of false positives or negatives. Threshold random walk (TRW) scan detection: TRW exploits randomness in picking destinations to connect to as a way of detecting if a scanner is in operation [JUNG04]. TRW is suitable for deployment in high-speed, low-cost network devices. It is effective against the common behavior seen in worm scans. Rate limiting: This class limits the rate of scanlike traffic from an infected host. Various strategies can be used, including limiting the number of new machines a host can connect to in a window of time, detecting a high connection failure rate, and limiting the number of unique IP addresses a host can scan in a window of time. Rate halting: This approach immediately blocks outgoing traffic when a threshold is exceeded either in outgoing connection rate or diversity of connection attempts. 15.10 The programming languages vulnerable to buffer overflows are those without a very strong notion of the type of variables, and what constitutes permissible operations on them. They include assembly language, and C and similar languages. Strongly typed languages such as Java, ADA, Python, and many others are not vulnerable to these attacks. 15.11 Two broad categories of defenses against buffer overflows are: compile-time defenses which aim to harden programs to resist attacks in new programs; and run-time defenses which aim to detect and abort attacks in existing programs. 15.12 Compile-time defenses include: writing programs using a modern high-level programming language that is not vulnerable to buffer overflow attacks; using safe coding techniques to validate buffer use; using language safety extensions and/or safe library implementations; or using stack protection mechanisms. 15.13 Run-time defenses that provide some protection for existing vulnerable programs include: using “Executable Address Space Protection” that blocks execution of code on the stack, heap, or in global data; using “Address Space Randomization” to manipulate the location of key data structures such as the stack and heap in the processes address space; or by placing guard pages between critical regions of memory in a processes address space. A N S W E R S T O P R O B L E M S 15.1 a. If this is a license plate number, that is easily guessable. b. suitable c. easily guessable d. easily guessable e. easily guessable f. suitable g. very unsuitable h. This is bigbird in reverse; not suitable. 15.2 The number of possible character strings of length 8 using a 36-character alphabet is 368 ≈ 241. However, only 215 of them need be looked at, because that is the -97- number of possible outputs of the random number generator. This scheme is discussed in [MORR79]. 15.3 a. T = 264 2 seconds = 63.5 hours b. Expect 13 tries for each digit. T = 13 × 4 = 52 seconds. 15.4 a. p = rk b. p = r k − rp rk+p c. p = rp 15.5 There are 9510 ≈ 6 × 1019 possible passwords. The time required is: 6 × 1019passwords 6.4 ×106 passwords/ second = 9.4 × 1012seconds = 300, 000 years 15.6 a. Since PUa and PRa are inverses, the value PRa can be checked to validate that Pa was correctly supplied: Simply take some arbitrary block X and verify that X = D(PRa, E[PUa, X]). b. Since the file /etc/publickey is publicly readable, an attacker can guess P (say P') and compute PRa' = D(P', E[P, PRa]). now he can choose an arbitrary block Y and check to see if Y = D(PRa, E[PUa, Y]). If so, it is highly probable that P' = P. Additional blocks can be used to verify the equality. 15.7 Without the salt, the attacker can guess a password and encrypt it. If ANY of the users on a system use that password, then there will be a match. With the salt, the attacker must guess a password and then encrypt it once for each user, using the particular salt for each user. 15.8 It depends on the size of the user population, not the size of the salt, since the attacker presumably has access to the salt for each user. The benefit of larger salts is that the larger the salt, the less likely it is that two users will have the same salt. If multiple users have the same salt, then the attacker can do one encryption per password guess to test all of those users.