UNIVERSITY OF WISCONSIN-MADISON
Computer Sciences Department | ||
CS 537-2
Spring 2003 | A. Arpaci-Dusseau | |
Midterm Exam |
Name: Solutions | Student ID #: |
Problem | Points | Total |
1 | 14 | |
2 | 15 | |
3 | 14 | |
4 | 14 | |
5 | 14 | |
6 | 14 | |
7 | 15 | |
Total | 100 |
This exam contains seven in-depth questions, each with multiple parts. Please be sure to pace yourself. Remember you have two hours, so you should spend about 15 minutes on each question. You do not need to write your answers in complete sentences, but please write clearly and as neatly as possible!
A) What are the advantages of disabling interrupts to implement a
critical section (as opposed to other approaches)?
Worth 3 points:
- Very fast, low overhead
- Works for any number of processes
- Does not require any special hardware instructions (e.g., TestAndSet)
- Does not induce any busy-waiting
- Relatively simple to implement
- Does not require any shared memory between cooperative processes
B) What are the disadvantages of disabling interrupts to implement a
critical section?
Worth 6 points:
- Prevents all other processes from running, even those not involved
with critical section
- Malicious or buggy processes can hold on to CPU for arbitrary amount
of time
- Cannot guarantee correctness if process performs a system call, any
I/O, or a page fault
- Does not work on a multiprocessor
- Must track nested critical sections carefully
C) Under what circumstances is it most appropriate to disable interrupts?
Give a concrete example.
Worth 5 points: (Note: Very few people got what I was looking for
here)
The critical sections that are most appropriate for guarding with
interrupts are those that are:
- short pieces of code
- running within the OS
- for performance improvements and not correctness (so okay if run on a
multiprocessor)
The example we discussed in lecture was for the critical section code
within the OS to atomically add/remove one process to/from a list of
processes waiting for a lock or semaphore.
monitor { Condition mayEat[5]; int state[5]; // Initialized to THINKING void take_chopsticks(int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) mayEat[i].wait(); } void put_chopsticks(int i) { state[i] = THINKING; test(i+1%5); test(i+4%5); } void test(int i) { if (state[i]==HUNGRY && state[i+1%5]!=EATING && state[i+4%5]!=EATING) { state[i] = EATING; mayEat[i].signal(); } } } |
A) Throughout this example, we will consider the case where
this code is run on a system with Mesa semantics. What is the difference
between Mesa and Hoare semantics?
Worth 3 points:
- Hoare: The signaling process hands off the CPU and the monitor lock
immediately and directly to the waiting process
- Mesa: The waiting process is awoken from the CV, but must compete
with all other processes for the monitor lock
B) Often when Mesa semantics are used, after being woken by a
corresponding signal, the process must recheck the condition it is
waiting on. In this example, this corresponds to changing:
if (state[i] != EATING) mayEat[i].wait();
to
while (state[i] != EATING) mayEat[i].wait();
Why isn't a while() loop needed in this code?
Worth 2 points: The main issue that must be addressed is "why is it
guaranteed that state[i] can't have changed, even with Mesa semantics,
given that another process could have been scheduled after this
process was signaled?"
The key is that each process is waiting on its own mayEat[i] CV; no
other process will be woken and no other process will change state[i]
from EATING.
C) Would this code work if we changed if (state[i] !=
EATING) to while (state[i] !=
EATING)? Why or why not?
Worth 2 points:
Yes, this will still work. The waiting process will simply recheck
the condition when it wakes and see that state[i] == EATING, and
therefore fall through the loop.
D) Where are all of the places in the code where the monitor
lock is acquired? Where is the monitor lock released?
(You may show this on the code listing, as long as you do this clearly).
Worth 3 points:
A monitor lock is acquired whenever the process calls an
externally-visible procedure within the monitor class; the monitor
lock is released when it exits the procedure.
The monitor lock is also released when a process calls wait(); it is
reacquired when the process is woken.
E) How should the elements of mayEat be initialized?
Worth 2 points:
Condition variables have no state associated with them; therefore,
they cannot be initialized.
F) Does the code work correctly if the statement mayEat[i].signal()
is moved before state[i]=EATING in test()?
Briefly describe why or why not.
Worth 3 points:
Yes, the code will still work. These two statements occur when the
monitor lock is held. Therefore, their ordering appears atomic to all
other processes; another processes will see the effects of both
statements when it later acquires the monitor lock.
Assume the following C code is compiled to create the executable program, pointless, which is then run on a UNIX system.
#include |
A)How many processes will be part of the pointless program?
Worth 4 points:
4 processes (the original parent; one child, A, from the first fork; one
child, B, from the second fork; a "grandchild" from child A
performing the second fork).
B)Assume a preemptive scheduling policy in which a newly
created process is always given a higher, static priority than any of
the existing processes; also assume that processes never voluntarily
relinquish the CPU until they exit. What output will the user see on
the terminal when they run pointless? You may show this
output above, next to the code listing.
Worth 10 points: Approximately 5 points for getting the scheduling
correct and 5 points for getting the data correct.
If! (child A -- highest priority of child and parent)
a 1
b 2
If #2 (child of child A)
a 2
b 3
Else #2 (back to child A)
a 2
b 3
Else (parent -- only active process)
a 1
b 1
If #2 (child B)
a 2
b 2
Else #2 (parent)
a 2
b 2
Job | Arrival Time | CPU Time |
A | 0 | 6 |
B | 2 | 5 |
C | 3 | 2 |
For each of the following scheduling algorithms, draw the Gantt chart
and show the waiting time for each job (A, B, and C).
Grading: Most people did well on this question. In general, half
credit for the Gantt chart and half for the correct waiting times.
The most common error was forgetting to subtract off the arrival times
for jobs B and C.
A) FCFS:
Grading: 3 points
AAAAAABBBBBCC
A: 0
B: 4
C: 8
B) SJF:
Grading: 3 points
Note that this is non-preemptive!
AAAAAACCBBBBB
A: 0
B: 6
C: 3
C) STCF (or SCTF):
Grading: 4 points
Note that this one is preemptive.
AAACCAAABBBBB
A: 2
B: 6
C: 0
D) RR (assume time-slice of 1):
Grading: 4 points
AABCABCABABAB
A: 6
B: 6
C: 2
A)What are the goals of a multi-level feedback queue scheduler?
Grading: 3 points (many different answers accepted, though one must
address the goals of the scheduler, and not just how it
is implemented)
- To approximate the behavior of a STCF scheduler, by observing the
past behavior of a process and using that to predict its future
behavior
- To handle a wide mix of job types and classify their behavior
dynamically
- Improve response time of interactive jobs and throughput of
compute-bound jobs
- To increase the utilization of the CPU and other devices
(Note that real-time and system jobs are not usually
handled by a multi-level feedback queue scheduler)
B) Which types of jobs are given high priority in such a scheduler?
Why? Which jobs are given low priority? Why?
Grading: 4 points
- High: Interactive jobs; need to be scheduled promptly to get good
response time for interactive users. I/O bound jobs: need to be
scheduled promptly so that they can get their next request out to the
disk. Both job classes usually relinquish the CPU promptly; that is,
each CPU burst is relatively short and this helps approximate STCF.
- Low: Compute-bound jobs; just need to be scheduled eventually and it
doesn't matter exactly when.
C) Are high or low priority jobs generally given longer time-slices?
Why?
Grading: 2 points
Low priority jobs get a longer time-slice. Since these jobs are
compute-bound, they often have significant cache state; therefore,
scheduling them for longer intervals gives them better throughput and
minimizes the overhead of context switches.
D) What concrete rules can be used to raise or lower the priority of
a running process? What properties should the scheduler guarantee?
Grading: 5 points
- (1 pt) A process that consumes its entire time-slice can be identified as
compute-bound; therefore, its priority is decreased.
- (2 pts) A process that blocks frequently or does not consume its time-slice
is identified as interactive or I/O bound; therefore its priority is
increased. However, the scheduler must contain rules to ensure that a
process can not game it, but faking interactive behavior by blocking
for a very brief period. Therefore, the scheduler must ensure that a
process has been blocked for a "significant" amount of time.
- (2 pts) A process that has not been scheduled for a significant amount of
time is identified as starving (one of the key properties a scheduler
must guarantee); therefore, its priority is increased.
A number of simple allocation policies exist for managing memory in the heap. In this question, we will examine Best Fit, First Fit, and Next Fit (also called Rotating First Fit).
A) Describe the data structures that are needed to implement a
basic memory allocator. Specifically, what information does the
allocator need about the state of memory and how should this
information be organized?
Grading: Worth 4 points
The allocator must be able to find all of the free, or available,
space. This freelist is traditionally maintained as a linked list
(doubly-linked is good) where each element contains its size, starting
address, and a pointer to the next element. This list is sorted by
the address of each element.
B) What operations take place within the memory allocator when a
process frees memory; specifically, how are your data structures
modified?
Grading: Worth 4 points
The newly freed block must be inserted into its proper location in the
sorted free list. To see if this block should be coalesced, or
merged, with the block before or after, the starting address of this block is compared to the ending
address of the previous block and the ending address of this block is
compared to the starting address of the next block.
C) For different workloads, it has been shown that First Fit and Best
Fit tend to succeed in performing an allocation about equally well.
Given this similarity, what is the primary advantage of the First Fit
algorithm over Best Fit?
First fit only needs to search until it finds a block of sufficient
size, whereas Best fit must search through the entire list (or until
it finds a perfect match). Thus, first fit is usually faster.
D) Most systems end up using Next Fit over First Fit or Best Fit;
what is the primary advantage of the Next Fit algorithm over First
Fit?
First fit starts at the beginning of the free list each time; the
problem with this is that many small holes may be left here, which
won't be sufficient for the current allocation (especially if requests
are often for the same size memory).
Next fit looks where it left off the previous time; it will be
faster since it doesn't need to recheck those small holes.
In this question, you will consider a memory system that contains support for segmentation (without paging).
A) Systems that support segmentation usually cause less
internal fragmentation than systems that support dynamic relocation
with only a simple base-and-bounds implementation. Why?
Grading: Worth 3 points
With a simple base-and-bounds implementation, the entire address space
of the process must be contiguous, even if it is sparse. This implies that the heap and
stack must be in the same contiguous range; since there is likely to
be a lot of extra, reserved space between the current stack and heap,
this space is wasted, internal to the process.
With segmentation, the stack and heap can be placed in separate
segments and no reserved space between them needs to be allocated.
B) What are the locations in the system that the segment
table for a process may reside? Be precise.
Grading: Worth 3 points
In the PCB (process control block) when the process is not running
(the PCB is kept in the address space of the OS, which is likely in
main memory, but could hypothetically be paged out to disk)
In the MMU (memory management unit) when the process is running.
C) Consider a system where logical and physical addresses are 15 bits, with 3 bits for the segment identifier and 12 bits for the segment offset. Assume that a process has the following segment table:
Segment | Base | Bounds | R/W |
0 | 0x254 | 0x200 | 10 |
1 | 0x5e0 | 0x98 | 11 |
2 | 0x108 | 0xff | 11 |
3 | 0x000 | 0x0 | 00 |
4 | 0x000 | 0x0 | 00 |
5 | 0x4c4 | 0x80 | 11 |
6 | 0xa08 | 0x3a0 | 11 |
7 | 0x000 | 0x0 | 00 |
What physical address corresponds to each of the following logical
addresses?
0x7318: invalid
Grading: Worth 9 points
0x1044: 0x624 (The very common answers of 0x634 and 0x614 were
also accepted :)
0x0112: 0x366
0x30a0: invalid
0x1100: invalid
0x2000: 0x108
This concludes your midterm exam. I hope you have a great Spring Break! <