COMS W4118 Operating Systems I Spring 2018 Exam #2
Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit
Exam #2
COMS W4118 Operating Systems I
Spring 2018
Apr 24, 2018
There are 3 problems totaling 100 points:
Problem 1: 32 points
Problem 2: 27 points
Problem 3: 41 points
Assume the following programming environment unless noted otherwise:
- Current release of Arch Linux with 4.9.81 kernel, 64-bit x86 version,
running under VirtualBox, with 2 CPUs and 2G RAM assigned to the VM .
- All user level programs are compiled with gcc with no optimization.
- The host computer is equipped with a recent model of Intel x86 processor
which contains 2 or more CPU cores .
- All library function calls and system calls are successful when they are
invoked correctly . For example, you can assume that fork() will
successfully create a child process and malloc() will not return NULL
when it is called with a reasonable argument .
- Some of the programs omit #include statements to save space . Assume that
all necessary #includes are there . If the exam refers to code from
homework assignments, assume that it is referring to the solution code .
What to hand in and what to keep:
- At the end of the exam, you will hand in only the answer sheet, which is
the last two pages (one sheet printed double-sided) of the exam booklet .
- Make sure you write your name & UNI on the answer sheet.
- All other pages (i .e ., the rest of this exam booklet and any scratch
papers you have used during the exam) are yours to keep .
- Before you hand in your answer sheet, please copy down your answers back
onto the exam booklet so that you can verify your grade when the
solution is published in the mailing list .
- Please be clear and succinct on your answer sheet . If a question asks
for a single number or a single word, do not write anything else . If a
question asks for a short explanation, keep it short and precise . If
you give two different answers, we will take the one that will result in
a LOWER grade . If you give a vague explanation that can be interpreted
in multiple ways, we will choose the interpretation that will result in
the LOWEST possible grade .
+---------------------------------------------------------------------+
| PLEASE DO NOT OPEN THIS EXAM BOOKLET UNTIL YOU ARE TOLD TO DO SO! |
+---------------------------------------------------------------------+
[blank page]
Problem [1]: 32 points
----------------------------------------------------------------------------
For each of the following statements, please write "TRUE" or "FALSE".
(1.1)
BFS uses a single runqueue across all CPUs, but MuQSS, which is an evolution
of BFS, uses per-cpu multiple runqueues .
(1.2)
Unlike mutex and semaphore, spin locks can be used in interrupt context.
(1.3)
The Least-Recently-Used (LRU) page replacement algorithm is an approximation
of the clock algorithm .
(1.4)
Physical Address Extension (PAE), available on some x86 processors, uses
36-bit virtual address, increasing the range of virtual address space of
each process to 64GB .
____________________________________________________________________________
For (1 .5) and (1 .6), assume that a process executing a system call has
called spin_lock() function and has successfully acquired the spin lock .
(1.5)
The process holding the spin lock can be preempted by another process.
(1.6)
The process holding the spin lock can be preempted by an interrupt handler.
Problem [1] continued
----------------------------------------------------------------------------
For (1 .7)- (1 .8), recall the Freezer scheduling class . Assume that the
behavior of Freezer’s select_task_rq has been changed as follows:
static int select_task_rq_freezer(struct task_struct *p,
int _cpu, int _sd_flag, int _flags)
{
int cpu, cpu_choice;
int num_rq_task = 0;
struct rq *rq;
struct cpumask allowed = (struct cpumask) p->cpus_allowed;
/* assume that the SET_DEFAULT_CPU() macro exists */
cpu_choice = SET_DEFAULT_CPU(&allowed);
for_each_cpu(cpu, &allowed) {
rq = cpu_rq(cpu);
if (num_rq_task < rq->freezer .nr_running) {
num_rq_task = rq->freezer .nr_running;
cpu_choice = cpu;
}
}
return cpu_choice;
}
Answer the following TRUE/FALSE questions:
(1.7)
All CPU runqueues will contain roughly the same number of processes over
time .
(1.8)
A multi-threaded program will likely perform better under this
implementation than under the original Freezer implementation .
Problem [2]: 27 points
----------------------------------------------------------------------------
Consider the following program that inspects various memory addresses using
the inspect_cabinet system call that we have developed in HW7 .
For (2 .1)- (2 .5), complete the outputs by writing 0 or 1 or UNPREDICTABLE in
the blanks on the answer sheet . Write UNPREDICTABLE if the output can vary
from one run of the program to another . For (2 .6)- (2 .9), answer the
questions by writing SAME or DIFFERENT in the blanks on the answer sheet .
Recall that the program is run under the following condition: 4.9.81 kernel,
x86-64, running under VirtualBox with 2 CPUs and 2G RAM assigned to the VM .
/***********************************************************
* The following is the struct cab_info definition from HW7 .
* Note that uint64_t is the same as unsigned long .
*/
struct cab_info {
uint64_t
uint64_t
uint64_t
uint64_t
uint64_t
uint64_t
int soft_dirty;
int refcount;
};
static inline int inspect(void *vaddr, struct cab_info *inventory) {
int nr = 405; // syscall number for inspect_cabinet
pid_t pid = -1; // -1 for the calling process
return syscall(nr, pid, (uint64_t)vaddr, inventory);
}
int main(void)
{
int x = 0;
struct cab_info a;
inspect(&x, &a); // inspect address of a local variable
printf("(2 .1) a .paddr >= a .pf_paddr: %d\n",
a .paddr >= a .pf_paddr);
printf("(2 .2) a .pf_paddr == a .pgd_paddr: %d\n",
a.pf_paddr == a .pgd_paddr);
printf("(2 .3) a .paddr > (uint64_t) &x: %d\n",
a .paddr > (uint64_t) &x);
struct cab_info b;
inspect(&main, &b); // inspect address of a function
printf("(2 .4) b .dirty: %d\n",
b .dirty);
assert((unsigned long) &x > (unsigned long) &main);
printf("(2 .5) a .paddr > b .paddr: %d\n",
a .paddr > b .paddr);
// code continued on the next page
Problem [2] continued
----------------------------------------------------------------------------
// (2.6)
char *buf1 = malloc(4096);
assert(buf1 != NULL);
*buf1 = ’A’;
pid_t pid = fork();
struct cab_info c;
if (pid == 0) { // child process
inspect(buf1, &c);
printf("[A]: c .paddr: %lx\n", c .paddr);
exit(0); // child process terminates
} else {
waitpid(pid, NULL , 0); // wait for child process to terminate
inspect(buf1, &c);
printf("[A]: c .paddr: %lx\n", c .paddr);
}
// The above if/else block prints two lines that start with "[A]" .
// Are they same or different? Write SAME or DIFFERENT on answer sheet .
printf("(2.6) Two [A] lines are ________________\n");
// (2.7)
pid = fork();
if (pid == 0) { // child process
*buf1 = ’B’;
inspect(buf1, &c);
printf("[B]: c .paddr: %lx\n", c .paddr);
exit(0); // child process terminates
} else {
waitpid(pid, NULL , 0); // wait for child process to terminate
inspect(buf1, &c);
printf("[B]: c .paddr: %lx\n", c .paddr);
}
// The above if/else block prints two lines that start with "[B]" .
// Are they same or different? Write SAME or DIFFERENT on answer sheet .
printf("(2.7) Two [B] lines are ________________\n");
// Code continued on the next page
Problem [2] continued
----------------------------------------------------------------------------
// (2.8)
// The following mmap() call correctly creates a shared memory
// segment of 4096 bytes using anonymous memory mapping .
char *buf2 = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
assert(buf2 != NULL);
*buf2 = ’C’;
pid = fork();
if (pid == 0) { // child process
assert(*buf2 == ’C’);
inspect(buf2, &c);
printf("[C]: c .paddr: %lx\n", c .paddr);
exit(0); // child process terminates
} else {
waitpid(pid, NULL , 0); // wait for child process to terminate
assert(*buf2 == ’C’);
inspect(buf2, &c);
printf("[C]: c .paddr: %lx\n", c .paddr);
}
// The above if/else block prints two lines that start with "[C]" .
// Are they same or different? Write SAME or DIFFERENT on answer sheet .
printf("(2.8) Two [C] lines are ________________\n");
// (2.9)
pid = fork();
if (pid == 0) { // child process
*buf2 = ’D’;
inspect(buf2, &c);
printf("[D]: c .paddr: %lx\n", c .paddr);
exit(0); // child process terminates
} else {
waitpid(pid, NULL , 0); // wait for child process to terminate
inspect(buf2, &c);
printf("[D]: c .paddr: %lx\n", c .paddr);
}
// The above if/else block prints two lines that start with "[D]" .
// Are they same or different? Write SAME or DIFFERENT on answer sheet .
printf("(2.9) Two [D] lines are ________________\n");
return 0;
}
Problem [3]: 41 points
----------------------------------------------------------------------------
The following kernel module implements two Linux system calls,
update_temperature() and set_oven_alarm() .
A process can call set_oven_alarm() with a desired temperature, and they
will block until the oven reaches the desired temperature . Another process
can call update_temperature() to set the oven temperature, and wake up all
the processes which have been waiting on a temperature equal to or lower
than the new temperature .
1 struct temp_event {
2 long temperature;
3 int count;
4 struct list_head entries;
5 wait_queue_head_t q;
6 };
7
8 struct oven {
9 long temperature;
10 struct list_head entries;
11 };
12
13 struct oven kitchen_oven; 14
15 long update_temperature(long temp) {
16 struct list_head *pos;
17 struct temp_event *te;
18
19 kitchen_oven .temperature = temp;
20 list_for_each(pos, &kitchen_oven .entries) {
21 te = list_entry(pos, struct temp_event, entries);
22 if (temp >= te->temperature) {
23 wake_up_all(&te->q);
24 }
25 }
26 return 0;
27 }
28
29 struct temp_event *find_alarm(long temperature) {
30 struct list_head *pos;
31 struct temp_event *te;
32
33 list_for_each(pos, &kitchen_oven .entries) {
34 te = list_entry(pos, struct temp_event, entries);
35 if (te->temperature == temperature) {
36 return te;
37 }
38 }
39 return NULL;
40 }
41 // code continued on the next page
// Problem [3] continued
42 long set_oven_alarm(long temperature) {
43 struct temp_event *te;
44 struct temp_event *new;
45 char delete_new = 0x0;
46 char delete_curr = 0x0; 47
48 DEFINE_WAIT(wait);
49 new = kmalloc(sizeof(*new), GFP_KERNEL);
50 if (!new) {
51 return -ENOMEM;
52 }
53 if (kitchen_oven .temperature >= temperature) {
54 return 0;
55 }
56 te = find_alarm(temperature);
57 if (te) {
58 te->count++;
59 delete_new = 0x1;
60 } else {
61 te = new;
62 te->count = 1;
63 te->temperature = temperature;
64 init_waitqueue_head(&te->q);
65 list_add_tail(&te->entries, &kitchen_oven .entries);
66 }
67 while(1) {
68 prepare_to_wait(&te->q, &wait,TASK_UNINTERRUPTIBLE);
69 if ( _________________________________ ) {
70 _________________________ ;
71 _________________________ ;
72 }
73 if (delete_new) {
74 kfree(new);
75 }
76 schedule();
77 }
78 finish_wait(&te->q, &wait);
79 if (te->count == 0) {
80 list_del(&te->entries);
81 delete_curr = 0x1;
82 }
83 if (delete_curr) {
84 kfree(te);
85 }
86 return 0;
87 }
88
89 extern long (*update_temperature_ptr)(long temperature);
90 extern long (*set_oven_alarm_ptr)(long temperature); 91
92 int oven_init(void) {
93 update_temperature_ptr = update_temperature;
94 set_oven_alarm_ptr = set_oven_alarm;
95 kitchen_oven .temperature = 0;
96 INIT_LIST_HEAD(&kitchen_oven .entries);
97 return 0;
98 }
// Problem [3] continued
99 void oven_exit(void) {
100 update_temperature_ptr = NULL;
101 set_oven_alarm_ptr = NULL; 102 }
103 module_init(oven_init);
104 module_exit(oven_exit);
105 MODULE_LICENSE("GPL");
106 MODULE_DESCRIPTION("oven");
107 MODULE_AUTHOR("cs4118");
----------------------------------------------------------------------------
(3 .1) Fill in the blanks in the if statement of line numbers from 69 to 72.
You may write up to two statements inside the curly braces .
The code as shown above is not guarded against race conditions of multiple
threads concurrently updating temperatures and setting alarms . In
(3 .2)- (3 .4), we add a spinlock to make the two system calls fully
thread-safe .
(3.2) Between which adjacent lines should the following declaration appear?
spinlock_t lock;
If you think it should go between lines 200 and 201, you should write
"200,201" . Add only one spinlock declaration . The spinlock object
should not be declared as a global variable .
(3.3) Now we will add the following function/macro calls:
spin_lock_init(spinlock_t *lock)
spin_lock(spinlock_t *lock)
spin_unlock(spinlock_t *lock)
On the answer sheet, indicate where these calls should be inserted by
writing the two adjacent line numbers and the call . Example:
247,248: spin_unlock(CORRECT_ARGUMENT);
250,251: spin_lock(CORRECT_ARGUMENT);
289,290: spin_lock_init(CORRECT_ARGUMENT);
294,295: spin_unlock(CORRECT_ARGUMENT);
Substitute CORRECT_ARGUMENT with the actual argument, of course . The
function calls must be placed to make the critical sections as small
as possible .
(3 .4) Even after successfully writing in all of the spinlock calls, you
realize that your module causes a kernel panic under a highly
concurrent workload . Add a single line of code that will fix this
issue .
Write the two adjacent line numbers and the code. (Hint: memory!)
COMS 4118 Spring 2018: Exam #2 Answer Sheet, page 1 of 2
UNI: Name:
----------------------------------------------------------------------------
[1] Write TRUE or FALSE:
(1.1) ____________________________
(1.2) ____________________________
(1.3) ____________________________
(1.4) ____________________________
(1.5) ____________________________
(1.6) ____________________________
(1.7) ____________________________
(1.8) ____________________________
[2]
For (2.1)- (2.5), write 0 or 1 or UNPREDICTABLE.
(2.1) a.paddr >= a.pf_paddr: ________________________________
(2.2) a.pf_paddr == a.pgd_paddr: ________________________________
(2.3) a.paddr > (uint64_t) &x: ________________________________
(2.4) b.dirty: ________________________________
(2.5) a.paddr > b.paddr: ________________________________
For (2.6)- (2.9), write SAME or DIFFERENT.
(2.6) Two [A] lines are ________________________________
(2.7) Two [B] lines are ________________________________
(2.8) Two [C] lines are ________________________________
(2.9) Two [D] lines are ________________________________
COMS 4118 Spring 2018
Exam #2
Answer Sheet, page 2 of 2
+---+---+---+---+---+---+---+---+
Your UNI: | | | | | | | | |
+---+---+---+---+---+---+---+---+
Your Name: _______________________________________________________________
front->UNI: ________________
left->UNI: ________________ [You] right->UNI: ________________
back->UNI: ________________
[3]
(3.1)
if ( ___________________________________________________________________ ) {
______________________________________________________________________ ;
______________________________________________________________________ ;
}
(3.2) _______________________________________
(3.3)
(3.4) ______________________________________________________________________
2023-04-18