Name (Last, First): This exam consists of 5 questions on 9 pages; be sure you have the entire exam before starting. The point value of each question is indicated at its beginning; the entire exam has 100 points. Individual parts of a multi-part question are generally assigned approximately the same point value: exceptions are noted. This exam is open text and notes. However, you may NOT share material with another student during the exam. Be concise and clearly indicate your answer. Presentation and simplicity of your answers may affect your grade. Answer each question in the space following the question. If you find it necessary to continue an answer elsewhere, clearly indicate the location of its continuation and label its continuation with the question number and subpart if appropriate. You should read through all the questions first, then pace yourself. The questions begin on the next page. Problem Possible Score 1 20 2 20 3 20 4 20 5 20 Total 100 1
1. ( /20 points ) (a) Why are the clock and hardware interrupts important to modern operation systems? What important operating system feature could not be implemented without them? Explain your answer. (b) Recall that a context switch occurs when the operating system switches from one process to another. Consider the following user-level code fragment: x = atoi(argv[1]); p = getpid(); In MINIX, what is the minimum number of context switches required to execute this code? Explain your answer. Think carefully!. 2
2. ( /20 points ) We want to measure how long it takes to send 1 KB (1024 bytes) of data from one process to another process using a pipe. Your job is to add code to the following skeleton program to measure as accurately as possible the amount of time it takes to send 1 KB of data from one process to another. That is, minimize the measurement of any processing activity not involved in sending and receiving data over a pipe (e.g., you should not include the time involved in creating a new process). However, you should measure the time for the write(), the read() and the context switch from the writing process to the reading process. Recall the times() system call: clock_t times(struct tms *buf); struct tms { clock_t tms_utime; /* user time */ clock_t tms_stime; /* system time */ clock_t tms_cutime; /* user time of children */ clock_t tms_cstime; /* system time of children */ ; Explain how your solution works. #include<stdio.h> void main(void) { int pfd[2]; char buf[1024]; int totaltime = 0; pipe(pfd); if (fork() == 0) { write(pfd[1], buf, 1024); read(pfd[0], buf, 1024); wait(null); print("time to send 1KB is %d\n", totaltime); 3
3. ( /20 points ) Consider the following semaphore solution to readers-writers. semaphore s = 1; semaphore w = 1; int rcnt = 0; cobegin (i = 1 to R) /* Reader thread i */ while(1) { /* obtain access */ P(s); rcnt++; if (rcnt == 1) P(w); V(s); /* read database */... /* release access */ P(s); rcnt--; if (rcnt == 0) V(w); V(s); // (i = 1 to W) /* Writer thread i */ while(1) { /* obtain access */ P(w); /* write database */... /* release database */ V(w); coend (a) Suppose W1 is writing to the database and R1, R2, and R3 attempt to obtain access for reading in that order. (i) Explain what happens to R1, R2, and R3. Be specific. (ii) When W1 finishes writing, do R1, R2, and R3 get to access the database concurrently? Explain. (b) Is it possible for a reader and a writer to be blocked on w at the same time? Explain. (c) Is starvation possible in this solution? Explain. 4
4. ( /20 points ) In the MINIX kernel, the pick_proc() function is used to choose the next process to run. Currently, MINIX uses three queues for scheduling: a USER_Q, a SERVER_Q, and a TASK_Q. Consider modifying MINIX so that it uses a single queue for all processes rather than three queues. Assume the new, global ready queue is declared as follows: struct proc *rdy_head; struct proc *rdy_tail; (15 points) Rewrite pick_proc() assuming that all processes are on the single ready queue. However, your new pick_proc() should behave just as the original pick_proc(). /*===========================================================================* * pick_proc * *===========================================================================*/ PRIVATE void pick_proc() Problem continued on the next page 5
(5 points) Now, rewrite ready() also assuming that all processes are on the single ready queue. /*===========================================================================* * ready * *===========================================================================*/ PRIVATE void ready(rp) 6
5. ( /20 points ) Recall the monitor solution to the producer and consumer problem. In Lab 3 you were required to implement a Pthreads version of the producer consumer problem. We want to add a new kind of thread, a high priority consumer (Hicon for short). A Hicon thread is like a regular consumer, but its requests for items are to be serviced before requests form regular consumers. That is, if both a Hicon and a regular consumer are waiting for an item, then when a producer deposits a new item, the Hicon, not the regular consumer, should get that item. Modify/complete the following Pthreads program to solve this problem. Do so by crossing out pieces of code, inserting new code, declarations, etc. Be neat. The code continues on the next page. #include<stdio.h> #include<pthread.h> #define P 4 #define C 2 #define H 2 #define Psteps 2 #define Csteps 2 #define Hsteps 2 #define BUFSIZE 100 int buf[bufsize]; int front = 0; int rear = 0; int count = 0; pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty; void *producer(void *arg) { int i; int item; for (i = 0; i < Psteps; i++) { /* produce item */ item = i; pthread_mutex_lock(&mutex); while (count == BUFSIZE) { pthread_cond_wait(¬_full, &mutex); buf[rear] = item; rear = (rear + 1) % BUFSIZE; count++; pthread_cond_signal(¬_empty); pthread_mutex_unlock(&mutex); void *consumer(void *arg) { int i; int item; for (i = 0; i < Csteps; i++) { pthread_mutex_lock(&mutex); while (count == 0) { pthread_cond_wait(¬_empty, &mutex); item = buf[front]; front = (front + 1) % BUFSIZE; count--; pthread_cond_signal(¬_full); pthread_mutex_unlock(&mutex); /* consume item */ 7
void *hicon(void *arg) { int i; int item; for (i = 0; i < Hsteps; i++) { item = buf[front]; front = (front + 1) % BUFSIZE; count--; /* consume item */ int main(int argc, char *argv[]) { int i; pthread_t producers[p]; pthread_t consumers[c]; pthread_t hicons[h]; pthread_mutex_init(&mutex, NULL); pthread_cond_init(¬_full, NULL); pthread_cond_init(¬_empty, NULL); for (i = 0; i < C; i++) { pthread_create(&consumers[i], NULL, consumer, (void *) i); for (i = 0; i < H; i++) { pthread_create(&hicons[i], NULL, hicon, (void *) i); for (i = 0; i < P; i++) { pthread_create(&producers[i], NULL, producer, (void *) i); for (i = 0; i < P; i++) { pthread_join(producers[i], NULL); for (i = 0; i < C; i++) { pthread_join(consumers[i], NULL); for (i = 0; i < H; i++) { pthread_join(hicons[i], NULL); exit(0); 8
Continue your answers here if necessary. 9