















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
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
1 / 23
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.
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.
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
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.
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.
static volatile int broken = 0; /* communicate from handler to checker */
/* This function sets the global "broken" flag to 1 on a seg fault.
/* 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.
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.
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.
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.
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.