




















Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
These are the Exam of Operating System which includes Caller-Save, Trap Frame, Interrupt Handling, Latest, Processor-Architecture News, Building, Management, Analysis, Management etc.Key important points are: Logic, System-Call, Thread-Library, Cheat Sheets, Information, Advance, Took Part, Main Exam, Typically ConGured, Designing
Typology: Cheat Sheet
1 / 28
This page cannot be seen from the preview
Don't miss anything!





















think clearly about a problem, you will probably have time to write your answer legibly.
I have not received advance information on the content of this 15-410 mid-term exam by dis- cussing it with anybody who took part in the main exam session or via any other avenue.
Signature: Date
(b) 5 points When designing a body of code, at times one finds oneself thinking, “I wonder if I can assume X?” According to the 15-410 design orthodoxy, immediately upon having such a thought one is required to ask oneself two questions. Please state those questions.
In class we briefly discussed the question of how reasonable or unreasonable the behavior of a multi-processor memory controller is likely to be when multiple processors simultaneously attempt an atomic memory operation (such as XCHG) on the same memory location. Below is pseudo-code for a possible implementation of arbitration logic inside a memory controller. This “code” will be run once per memory cycle and will receive a bit-mask of the processors which are trying to run an atomic memory operation in that memory cycle. In this simplified model, processors which, during this memory cycle, are either not accessing memory at all or are issuing non-atomic memory accesses are handled by some other mechanism we are not concerned with. Also, this model ignores the specific memory addresses being operated on: if, in a given memory cycle, two processors are trying to make atomic accesses, the logic chooses a (single) winner regardless of whether they were operating on the same address or different addresses.
If a given processor makes an atomic request in one cycle and is not selected as the winner by the arbitration logic, it will generally try again in the next cycle (e.g., LOCK XCHG will generally retry until it succeeds in doing its swap), though this is not strictly guaranteed (some other atomic instruction, which includes “fail” or “give-up” semantics, might be used, or a processor trying to do an XCHG might “give up” for a while if an interrupt causes it to switch to a different instruction stream). Also, if a processor is chosen as the winner during one cycle, it could conceivably try again in the next cycle (imagine a sequence of back-to-back LOCK XCHG instructions).
/**
/**
for (p = 0; p < NCPU; ++p) { if (reqmask & (1 << p)) { return (1 << p); } } return (0); }
(b) 7 points Write a small piece of code which does a better job, in terms of addressing the issue you identified in the previous part. Because “variables” in your code will end up as machine registers, your solution should not use a genuinely excessive amount of space (using four 32-bit registers for each processor seems like probably a lot of space). Because transistors are cheap these days, and the number of processors isn’t too large, you shouldn’t worry “too much” about execution time: O(n^2 ) is ok if you need it.
Your friend Luigi is thinking of running a barber shop downtown. He figures it will be mostly easy living: he can sit around the shop all day, sleeping most of the time, and cut some hair when people arrive in the waiting room. Depending on which vacant storefront he rents, he will end up with waiting rooms of various sizes. If his waiting room ever fills up, he will need to turn some customers away unsatisfied. In order to estimate how much space he should rent, he wants to run some simulations. The code for his model appears below. Note: For the purposes of this question, you may assume that condition variables are “as FIFO as possible.”
#define MAX_WAITERS 10 #define CUSTOMERS_PER_DAY 30 static int num_waiters = 0; static int barber_busy = 0; static int cut_done = 0, chair_full = 0; static mutex_t waiting_room_lock, chair_lock; static cond_t waiting_room_block, chair_block, barber_block;
static void cut_hair() // Luigi does this (reluctantly) { mutex_lock(&chair_lock); while(!chair_full) { cond_wait(&chair_block, &chair_lock); } mutex_unlock(&chair_lock); sleep(17); // Haircuts take some time mutex_lock(&chair_lock); cut_done = 1; cond_broadcast(&chair_block); // No more than two mutex_unlock(&chair_lock); } static void get_hair_cut() // Customers do this (they hope) { mutex_lock(&chair_lock); while (chair_full) { cond_wait(&chair_block, &chair_lock); } chair_full = 1; cond_broadcast(&chair_block); // No more than two while (!cut_done) { cond_wait(&chair_block, &chair_lock); } cut_done = 0; chair_full = 0; cond_broadcast(&chair_block); // No more than two mutex_unlock(&chair_lock); }
void *customer(void ignored) { mutex_lock(&waiting_room_lock); if (barber_busy || (num_waiters > 0)) { if (num_waiters == MAX_WAITERS) { / There’s no room to wait, so I will have to go home without
int main() { int ctids[CUSTOMERS_PER_DAY], btid; mutex_init(&waiting_room_lock); cond_init(&waiting_room_block); mutex_init(&chair_lock); cond_init(&chair_block); cond_init(&barber_block);
thr_init(3*PAGE_SIZE);
btid = thr_create(barber, NULL); assert(btid > -1);
int c; for(c = 0; c < CUSTOMERS_PER_DAY; c++) { ctids[c] = thr_create(customer, NULL); assert(ctids[c] > -1); sleep(25); } for(c = 0; c < CUSTOMERS_PER_DAY; c++) { thr_join(ctids[c], NULL); } task_vanish(0); // don’t wait around for Luigi: he’s probably asleep }
This code doesn’t work to Luigi’s satisfaction, but (since he’s a barber, not a computer scientist) he can’t clearly explain what’s going wrong or figure out why. Describe how this code can get Luigi’s virtual shop into a situation which is impossible in the real world or which would threaten Luigi’s business with bankruptcy, then back up your claim with an execution trace in the tabular format used in class. If you can’t describe and show a particular problem, a small amount of partial credit may be awarded for correct descriptions of how the code is incorrectly structured.
Suggestions for working on this problem:
You may use this page as extra space for the first part of the barbershop question if you wish.
(b) 5 points Briefly describe how to fix or restructure the code to solve the problem.
Your mission
In this problem you will implement “super semaphores” using mutexes and condition variables. You may not use other atomic or thread-synchronization synchronization operations, such as, but not limited to: semaphores (obviously), reader/writer locks, deschedule()/make runnable(), or any atomic instructions (XCHG, LL/SC). You may assume that there is a mutex implementation available for use, with the same interface as provided with P2; you may assume that this mutex implementation has bounded waiting (e.g., is FIFO) and does not block threads. You may likewise assume a P2-compliant condition-variable implementation, which you may assume to be strictly- FIFO if you wish. For the purposes of the exam, you may assume that library routines and system calls don’t “fail” (unless you indicate in your comments that you have arranged, and are expecting, a particular failure). However, you may not rely on any data-structure libraries such as splay trees, red-black trees, queues, stacks, or skip lists, lock-free or otherwise, that you do not implement as part of your solution. You may use non-synchronization-related thread-library routines in the “thr xxx() family,” e.g., thr getid().
It is strongly recommended that you rough out an implementation on the scrap paper provided at the end of the exam, or on the back of some other page, before you write anything on the next page. If we cannot understand the solution you provide on the next page, your grade will suffer!
There are multiple “legal” solutions (ones that operate defensibly or “reasonably well” for every valid execution sequence). Some solutions that are not only legal but also “nice” are too long and complicated for us to expect them as exam solutions. Observe that, compared to regular semaphores, these “super semaphores” have “interesting” states, in which multiple resources are available and multiple threads, with potentially different needs, are waiting. If you can spend a little more design time (say, ten minutes) to get a reasonable-length solution which is not only legal but also handles some of the “interesting” states in a “nice” fashion as opposed to a “crude” fashion, you will receive a slightly higher score (we expect valid answers to fall into three different classes, which may be scored differently). But don’t spend too much time thinking about “nice”... getting something that works is more important, plus there are other questions on the exam!
The remainder of this page is intentionally blank.
Please declare a struct sem and implement:
You do not need to implement sem destroy().
If you wish, you may also declare an auxiliary structure, struct aux, but this is strictly op- tional.
typedef struct sem {
} sem_t;
typedef struct aux {
} aux_t;
You may use this page as extra space for your super-semaphore solution if you wish.
Although the availability of computing power has been increasing at a remarkable pace in recent history, there is still a drive to squeeze as much computation as possible out of the hardware on hand. To that end, compiler writers have come up with all sorts of tricks to speed up code.
One technique which can give a small speedup to compiled code is omitting the frame pointer.
Recall from 15-213 and Project 0 that a compiled function generally has a “prologue” (start-up code) and an “epilogue” (clean-up code) looking like this:
push %ebp # prologue mov %esp, %ebp # prologue ... mov %ebp, %esp # epilogue pop %ebp # epilogue ret
Now, consider the following C function:
// sum first n values of f(): f(n-1)+f(n-2)...+f(0) int partial_series(int n) { int sum = 0; while (n > 0) { sum += f(n); n--; } return sum; }
(Continued on next page)