Concurrency and Memory Management in C Programming, Exams of Operating Systems

Various topics related to concurrent programming and memory management in c programming, including segment registers, dining philosophers problem, sigsegv, pair locks, and a cheat-sheet of useful equations. It includes code examples and explanations for each topic, as well as questions and exercises for understanding the concepts.

Typology: Exams

2012/2013

Uploaded on 03/28/2013

rohit-sharma
rohit-sharma 🇮🇳

4.3

(11)

200 documents

1 / 23

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Computer Science 15-410: Operating Systems
Mid-Term Exam (C), Spring 2010
1. Please read the entire exam before starting to write. This should help you
avoid getting bogged down on one problem.
2. Be sure to put your name and Andrew ID below and also put your Andrew ID at the top of
each following page.
3. This is a closed-book in-class exam. You may not use any reference materials during the
exam.
4. If you have a clarification question, please write it down on the card we have provided. Please
don’t ask us questions of the form “If I answered like this, would it be ok?” or “Are you
looking for ...?”
5. The weight of each question is indicated on the exam. Weights of question parts are estimates
which may be revised during the grading process and are for your guidance only.
6. Please be concise in your answers. You will receive partial credit for partially correct answers,
but truly extraneous remarks may count against your grade.
7. Write legibly even if you must slow down to do so! If you spend some time to
think clearly about a problem, you will probably have time to write your answer legibly.
Andrew
Username
Full
Name
Question Max Points Grader
1. 10
2. 15
3. 15
4. 15
5. 15
70
Please note that there are system-call and thread-library “cheat sheets”
at the end of the exam.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17

Partial preview of the text

Download Concurrency and Memory Management in C Programming and more Exams Operating Systems in PDF only on Docsity!

Computer Science 15-410: Operating Systems

Mid-Term Exam (C), Spring 2010

1. Please read the entire exam before starting to write. This should help you

avoid getting bogged down on one problem.

  1. Be sure to put your name and Andrew ID below and also put your Andrew ID at the top of each following page.
  2. This is a closed-book in-class exam. You may not use any reference materials during the exam.
  3. If you have a clarification question, please write it down on the card we have provided. Please don’t ask us questions of the form “If I answered like this, would it be ok?” or “Are you looking for ...?”
  4. The weight of each question is indicated on the exam. Weights of question parts are estimates which may be revised during the grading process and are for your guidance only.
  5. Please be concise in your answers. You will receive partial credit for partially correct answers, but truly extraneous remarks may count against your grade.

7. Write legibly even if you must slow down to do so! If you spend some time to

think clearly about a problem, you will probably have time to write your answer legibly.

Andrew

Username

Full

Name

Question Max Points Grader

Please note that there are system-call and thread-library “cheat sheets”

at the end of the exam.

  1. 10 points Short answer.

We are expecting each part of this question to be answered by three to six sentences. Your goal is to make it clear to your grader that you understand the concept as it applies to this course and can apply it when necessary.

(a) 5 points Explain how an “exception” and an “interrupt” are related. Provide an example of each one and discuss how the hardware treats them differently.

  1. 15 points Dining Philosophers

Consider the code below which implements a small “Dining Philosophers” system using standard concurrency primitives.

#include <stdlib.h> #include <thread.h> #include <mutex.h> #include <cond.h> #include <rand.h> #include <stdio.h> #include <syscall.h>

#define DINERS 3

/* "right hand rule" mapping diner numbers to left/right chopsticks

  • diner 0’s left chopstick is (0+2)%3==2, right chopstick is 0
  • diner 1’s left chopstick is (1+2)%3==0, right chopstick is 1
  • diner 2’s left chopstick is (2+2)%3==1, right chopstick is 2 */ #define LSTICK(d) (((d)+(DINERS-1)) % DINERS) #define RSTICK(d) (d)

mutex_t table; int stick[DINERS]; /* stick -> -1 or owner / cond_t avail[DINERS]; / that stick is newly free */

mutex_t prng_lock; unsigned long genrand_r(void);

void *diner(void *vsd);

int main() { int i;

sgenrand(get_ticks()); thr_init(161024); mutex_init(&table); mutex_init(&prng_lock); / "set the table": / for (i = 0; i < DINERS; ++i) { stick[i] = -1; cond_init(&avail[i]); } / "Gentlemen, Be Seated!" (Robert A. Heinlein) */ for (i = 0; i < DINERS; ++i) { thr_create(diner, (void )i); } thr_exit(0); return(99); / on the exam, we definitely won’t get here */ }

void start_eating(int d) { mutex_lock(&table);

while (stick[RSTICK(d)] != -1) { cond_wait(&avail[RSTICK(d)], &table); } stick[RSTICK(d)] = d;

while (stick[LSTICK(d)] != -1) { cond_wait(&avail[LSTICK(d)], &table); } stick[LSTICK(d)] = d;

mutex_unlock(&table); }

void done_eating(int d) { mutex_lock(&table); stick[LSTICK(d)] = stick[RSTICK(d)] = -1; cond_signal(&avail[RSTICK(d)]); cond_signal(&avail[LSTICK(d)]); mutex_unlock(&table); }

void *diner(void *vsd) { int d = (int) vsd;

while (1) { sleep(genrand_r() % 100); start_eating(d); sleep(genrand_r() % 10); done_eating(d); } }

unsigned long genrand_r(void) { unsigned long ul; mutex_lock(&prng_lock); ul = genrand(); mutex_unlock(&prng_lock); return (ul); }

You may use this page as extra space for your dining-philosophers solution if you wish.

  1. 15 points SIGSEGV

Consider the following code which might be observed early (hopefully very very early!) in the development of a Project 0.

#include <stdlib.h> #include <stdio.h> #include <signal.h>

/* This code is compiled with -O0, i.e., with the optimizer turned off.

  • Also, we assume sizeof (void*) >= sizeof (long), which is ok for IA
  • but not portable to other platforms. */

static volatile int broken = 0; /* communicate from handler to checker */

/* This function sets the global "broken" flag to 1 on a seg fault.

  • The function causing the fault can then check the "broken" flag. */ void segv(int ignored) { broken = 1; }

/* Check for validity of address. */ int tryit(void *addr) { volatile int buffer; broken = 0; buffer = *(int *)addr; return (!broken); }

int main(int argc, char *argv[]) { void *addr; struct sigaction sa;

sa.sa_handler = segv; sigfillset(&sa.sa_mask); /* don’t want other signals while in segv() */ sa.sa_flags = 0; sigaction(SIGSEGV, &sa, NULL);

if (argc > 1) { addr = (void ) strtoul(argv[1], NULL, 16); / convert from hex string */

if (tryit(addr)) { printf("Ok: %p\n", addr); return 0; } else { printf("Not ok: %p\n", addr); return 1; } } else { printf("%s: no address specified\n", argv[0]); return 2; } }

(a) 10 points In the case where the program doesn’t promptly print out a diagnosis for a given address, what is happening, and when? Is it really hung, or should the user wait longer? If so, how long? Your answer should demonstrate that you understand and can apply the concepts and abstractions relevant to this class; answers which are “correct” but too abstract will receive only partial credit. We are expecting some answers to be roughly two paragraphs long; other answers may show a well-commented execution trace.

You may use this page as extra space for your SIGSEGV solution if you wish.

  1. 15 points “Pair locks”

When you implemented your thread library for Project 2, you implemented several objects which provide locking: mutexes, semaphores, and reader/writer locks. Many other kinds of lock exist, and we will probably discuss some of them later in the semester.

In this question we will discuss a special kind of lock proposed for a special situation. Imagine a system in which many resources come in pairs (perhaps the system has a pair of tape drives and a pair of CD burners), and imagine further that processes, depending on their immediate needs, use either the first element of a pair, the second element, or both.

In an attempt to avoid “something bad” happening if processes used regular locks when trying to acquire some or both devices in a pair, a special lock called a “pair lock” has been devised.

When a pair lock is initialized, both resources managed by the pair lock pair are available. When pl lock() is called, the caller specifies whether to lock the first resource, the second resource, or both—pl lock() returns when all requested elements have been acquired. pl unlock() releases the specified elements of a resource pair, which must currently be held by the caller. When a thread calls pl lock() on a pair lock, it must use pl unlock() to release all elements it holds before it any later acquisition of elements from the pair using pl lock().

For the purposes of the exam you should assume an error-free environment (invocations of pl lock() and pl unlock() are always legal; memory allocation will always succeed; thread-library primitives will not detect internal inconsistencies or otherwise “fail,” etc.). If you wish, you may assume that the mutexes provided by the underlying thread library provide bounded waiting; you may also assume, if you wish, that the underlying condition variables are “as FIFO as possible.” You may wish to refer to the “cheat sheets” at the end of the exam.

#include <stdlib.h> #include <thread.h> #include <mutex.h> #include <cond.h> #include <stdio.h> #include <syscall.h>

typedef struct pl { mutex_t m; cond_t released; int avail[2]; } pl_t;

/* Initialize a pair-lock */ void pl_init(pl_t *pl) { mutex_init(&pl->m); cond_init(&pl->released); pl->avail[0] = pl->avail[1] = 1; }

/* Lock one or both resources controlled by the pair-lock. */ void pl_lock(pl_t *pl, int want[2]) { mutex_lock(&pl->m);

while ((want[0] && !pl->avail[0]) || (want[1] && !pl->avail[1])) { cond_wait(&pl->released, &pl->m); }

/* good to go! */ if (want[0]) { pl->avail[0] = 0; } if (want[1]) { pl->avail[1] = 0; } mutex_unlock(&pl->m); return; }

/* Unlock resources controlled by a pair-lock.

  • The results are undefined if the resources we are
  • directed to unlock are not currently locked by us. */ void pl_unlock(pl_t *pl, int unlock[2]) { mutex_lock(&pl->m); if (unlock[0]) { pl->avail[0] = 1; } if (unlock[1]) { pl->avail[1] = 1; } cond_signal(&pl->released); mutex_unlock(&pl->m); }

You may use this page as extra space for your pair-lock solution if you wish.

(b) 7 points Explain how to solve the problem you identified. If you completely understand the problem, your answer to this part can be very short: a sentence explaining a particular code change and a short paragraph explaining why it works. Alternatively, you may present a struct pl t and code for pl init(), pl lock() and pl unlock(); if you take that approach, be sure to briefly summarize how your solution works.

  1. 15 points print ints().

Imagine that in your “spare time” (whatever that might be), you begin working on a small embedded operating system for a very small, power-constrained device which has the ability to shut down individual parts of its RAM to save power (the contents are lost). Naturally, you find you need some way to emit debugging output. However, you are shocked to learn that a standard implementation of printf() compiles to more than four kilobytes of code space; your power budget doesn’t allow you to waste that much RAM just for pretty debugging messages. You decide to investigate a simpler, hopefully smaller debugging facility and come up with the following.

print ints(int count, ...) - print a list of integer parameters; the number of integers to print is specified as the first parameter to the function, and the other integers are passed as the remaining parameters. The printed integers are separated by spaces and after all the integers are printed a newline character is printed as well.

Here are some sample invocations.

  • print ints(1,42) - will print just 42 followed by a newline.
  • print ints(3,1,2,3) - will print the smallest three positive integers followed by a newline.
  • print ints(3,1,2,3,42) - will also print the smallest three positive integers followed by a newline (note the invocation error).
  • print ints(1048576,1) - the results are undefined (not enough ints!).

You decide to prototype this first on a system you are familiar with, namely IA-32 (32-bit Intel x86), using gcc as your compiler, gas as your assembler, and GNU ld as your linker.

Now please write the code for print ints(). You may make use of two simple underlying functions: print int(int i) prints a single integer and print char(char c) prints a single character. You should not need to make calls to any functions or macros other than those, plus ones you write. Most of your code should be in C, though you may use a small amount of assembly code if you wish. Please make sure your code is easy to read, clearly accomplishes its goals, and is at least lightly commented when necessary (don’t worry—you don’t need to provide any Doxygen comments; a few informal but helpful comments should suffice).

You may use this page as extra space for your print ints() solution if you wish.