CS 481: Solutions for Final Test |
This can be analyzed formally to a certain point: set up a state diagram to determine the number of jobs in the system, with states 0, 1, 2, etc. Then transitions from i to i+1 are governed by the arrival rate lambda and transitions in the reverse direction by the completion rate 3mu, except in the 3-CPU system, where the completion rate out of state 2 is only 2mu and out of state 1 only mu. Solving yields the predictable result that the single CPU system has a smaller average queue length---but not by enough to matter, as plugging in some numbers will show. Considering arrivals in groups of 3 shows that waiting time is likely to be longer on the single CPU system, as, with idle systems, all 3 jobs can start immediately on the 3-CPU system, but not on the other. In general, the 3-CPU system is less prone to bottlenecks and thus may deliver better performance on a short- to middle-term basis; on the other hand, the 3-CPU system is just a tad slower on a long-term basis (because of the smaller completion rate when only 1 or 2 jobs are in the system). Overall, an interactive system is probably better off with the 3-CPU system and a throughput-oriented system with the single CPU system.
On the other hand, if each of the 3 processors has its own queue, we lose many of the advantages of the 3-CPU system, without gaining anything. Each queue has a departure rate of mu and its arrival rate cannot be held easily to just lambda. Throughput and turnaround will both suffer seriously. In that case, the single-CPU system with 3mu completion rate is clearly preferable.
The first question to ask in any problem involving concurrent processes is ``what are the resources?'' In this case, the resources are the four intersections. (One could argue that every piece of pavement is a resource; such is indeed the case, but there is no contention about most of these pieces of pavement: only the pieces at the four intersections are contested by two or more streams of traffic.) We should also ask ``what are the processes?''---a question that is closely related to the previous one. Basically, each car is a process, although we can group the cars into four separate streams of traffic.
Each car needs to acquire two resources (intersections) in succession in order to pass through the square. The first is acquired, then released; the second is then acquired and released. So, on a car per car basis, there is no pattern of ``hold and wait'' and thus no possibility for deadlock. This is where we must either consider streams of traffic or introduce the second resource type (every piece of pavement around the square). A detailed model would need the second resource type; a deadlock pattern then involves enough cars to hold every piece of pavement around the square. A higher-level model involves only the streams and looks only at the deadlock situation: each stream holds the first intersection in its direction and requests the second, thereby establishing a ``hold and wait'' pattern. That the other three conditions are met is easy to see: the pattern is clearly circular; the intersections are obviously unshareable (!); and no preemption can occur since each stream is unidirectional.
A very simple rule to prevent deadlock is to forbid a car from entering an intersection unless there is sufficient room on the other side of the intersection for the car to cross the intersection and clear it. This rule prevents deadlock by preventing any ``hold and wait'' pattern at the intersections. Another rule would be to allow only parallel traffic at any given time (e.g. E-W with W-E or N-S with S-N), clearing the square completely before switching to the perpendicular direction. This rule would prevent deadlock by preventing any circular waiting pattern, but it is less flexible than the previous and not a ``simple rule,'' in the sense that individual drivers cannot obey it on the basis of information available to them.
see above
2**12 bytes or 4Kbytes
since we have 10 bits for a page number, a segment may have up to 2**10 pages, each of 4Kbytes, so 2**22 bytes, or 4Mbytes
since a segment may have up to 2**10 pages, a page table will have up to 2**10 entries, each taking 4 bytes, so a page table may use up to 2**12 bytes or 4Kbytes -- thus a page table always fits within one page
since the displacement is 12 bits, this means we need 36-12=24 bits for a frame number; thus the 4-bytes page table entry will contain 24 bits for the frame number, a presence bit, a dirty bit, and paging information (presumably) in the remaining 6 bits
since a page table fits within one page, we can assume that page tables always start at the top of a page; with that in mind, an entry in the segment table can point to the appropriate page table just by giving a frame number---24 bits; since page tables can probably be paged in and out of memory, we will also need a presence bit and a dirty bit; the remaining 6 bits should be associated with access control
Assume that every item is equally likely to occur and that all items are guaranteed to be in the array -- no unsuccessful searches.
We know that binary search, for a file of size 2^n, takes on the average n-1 searches; this is also its worst case (well, the worst case is n, but the difference does not matter). Now, the items checked in binary search are separated by increasingly smaller intervals; at the i-th search, the value of the interval to the i+1st point is 2^{n-i-1}. As soon as that value falls below what is brought in at once in memory, binary search will proceed without further page faults. Before that, it will cause one page fault at every search. Hence, since we search 2^{20} items and the page size is 2^{10}, 3 of them being brought in at once (i.e., one on either side), the number of page faults is i, where i satisfies 2^{20-i} = 2^{10}. Hence, 10 page faults will be required---plus the two faults needed to get the code and local variables, for a total of 12 faults. Average and worst-case behaviors are identical: by the time the two show a difference, the items searched are on the same page. In connection with the third part, note that, if only one page were brought in, just one more page fault would be caused.
Here, 2 useful pages are brought in at every fault (the k-1st page is useless, since we always search forward). Since 2^10 pages are needed to store the whole file, the worst-case number of faults is just 2^10/2 = 512. On the average, however, only half of the file is examined, for an average number of faults of 256. Thus, your boss' advice was bad. Note that, by bringing only one page at a time, we would fault in the worst case on all pages, for 1024 faults; and on average on half of that, for 512 faults. The 2 additional fault for code and local variables hardly matter...
Since we saw that binary search gained just 1 page fault, while sequential search (because of 2 useful pages) gained a factor of 2, the 3-pages-at-a-time strategy obviously helped sequential search more than binary search. With this strategy, sequential search makes about 26 times more page faults than binary search; without it, the ratio would be around 50.
One must consider the logical structure of file systems (directories, attributes, etc.), their physical structure (location of blocks on disks, linkage of the various blocks of a file, management of free disk blocks, etc.), and the addressing mechanism for disks (cylinder and sector) in order to develop a complete analogy. At the highest logical level in a file system, one locates a file through a directory; that's the equivalent of locating a segment through the job's segment table. Next a file is addressed by a pointer within the file, which effectively keeps a displacement within the entire file; this is equivalent to a displacement within the segment. Finding the proper element (record) in the file involves finding the correct disk block (possibly through several levels of indirection) and getting one record out of that block (although the entire block must be read in); finding the proper word within a segment involves finding the correct page and getting one word out of that page (although the entire page must be present and, if not, transferred). Allocating a new segment only involves setting up a new page table and finding space for it; creating a new file only involves setting up a new directory entry and finding space for it. Enlarging or shrinking a segment involves creating or deleting pages (which is mostly done by finding space on the secondary device); enlarging or shrinking a file involves allocating or releasing blocks.
The similarity is extremely close because, in effect, the virtual address space of a process is implemented as a file system on disk, with segments being methods of partitioning that space into logical entities (just as files partition a file system into logical entities) and with paging just being a form of caching to allow faster access to said virtual space. Thus we have virtual space and files; then their ``virtual implementation,'' in terms of pages and blocks; then this entire collection of pages/blocks, with associated tables and indices, is mapped onto physical devices -- a mix of disks and main store. Hence the same concepts hold throughout; only, they are simplified for ``normal'' files, as file access normally does not operate under the same stringent time constraints as virtual space access. Thus caching for files typically reduces to keeping some index blocks in main memory (the equivalent of the page table entries), but rarely extends to keeping much of the data there (the equivalent of keeping program and data pages in main memory). The problem of dirty pages has then no equivalent in file systems: since data blocks are kept only on disk, they are always up-to-date; only index blocks must be taken care of. Replacement algorithms are similarly simplified: they only take care of index blocks -- and also need not run as fast and thus can implement a full-fledge LRU policy without trouble. Etc., etc., etc.
Access control is expensive: it has to be done for every operation on the resource. If the control system is totally centralized, as would be the case with a protection matrix, the OS must intervene every single time -- an intolerable burden, espcially with time-critical resources such as memory. Thus we want to adopt a more efficient mechanism. Capabilities are much like entries in a page table---indeed, for memory resources, the two are essentially identical; lists of capabilities are thus much like lists of page table entries, i.e., like inverted page tables. Using them for access control causes a minimal burden. However, setting up capability lists is expensive; whenever a new resource is added to the system, huge numbers of capability lists may have to be updated. It is better to record the information with the resource in an access list, so as to minimize the bookkeeping, and let the capability lists grow as they need.
| Back to CS 481 home page |