Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

Exam 1

COMS 4118 Operating Systems I

Spring 2022

March 9, 2022

There are 3 problems totaling 99 points + 1 points for taking the exam:

Attendance:  1 points

Problem 1:  20 points

Problem 2:  48 points

Problem 3:  31 points

Assume the following programming environment unless noted otherwise:

- Current stable release of Debian GNU/Linux, 64-bit version, running

under VMware, with two or more CPUs assigned to the VM.

- All user level programs are compiled with gcc with no optimization.

- 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.

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 both sides of 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!  |

+---------------------------------------------------------------------+

References

sem_t *sem_open(

const char *name, int oflag, mode_t mode, unsigned int value);

A common way to use sem_open() is to pass O_CREAT as oflag. In that

case, sem_open() creates a new named semaphore with the given mode and

initial value if it does not exist already, or opens an existing

semaphore, ignoring the mode and value parameters.

For example, assuming the variable str points to a string "foo", the

following block of code opens the semaphore named "/dev/shm/sem.foo". If

the semaphore is newly created, its initial value will be set to 1 and

its mode will be set to 0666 permissions.

char sem_name[strlen(str) + 2];

// sem_open() requires that ’/’ be prepended to the semaphore name.

sprintf(sem_name, "/%s", str);

sem_t *sem = sem_open(sem_name, O_CREAT, 0666, 1);

int

sem_init(sem_t *sem, int pshared, unsigned int value);

sem_init() initializes the unnamed semaphore object at the address

pointed to by sem with the given initial value.

For example, the following sem_init() call initializes a semaphore with

initial value 1, and specifies pshared=1, such that the semaphore is

shared between processes.

sem_init(&sem, 1, 1);

int

sem_wait(sem_t *sem);

sem_wait() decrements the semaphore pointed to by sem.

int

sem_post(sem_t *sem);

sem_post() increments the semaphore pointed to by sem.

int

rename(const char *oldname, const char *newname);

Atomically renames a file from oldname to newname, replacing newname if

it already exists. If oldname does not exist, no operation is performed

and the function returns -1.

size_t strlen(const char *s);

The strlen() function calculates the length of the string pointed to by

s, excluding the terminating null byte.

int strcmp(const char *s1, const char *s2);

strcmp() returns an integer indicating the result of a lexicographical

comparison of the two strings pointed to by s1 and s2 as follows:

- 0 if s1 and s2 are equal;

- a negative value if s1 is less than s2;

- a positive value if s1 is greater than s2.


Problem [1] (20 points): Write TRUE or FALSE on the answer sheet.

----------------------------------------------------------------------------

(1.1)

The select() function allows a program to monitor

multiple file descriptors,

waiting until one or more of the file descriptors

or writing.

become ready for reading

(1.2)

list_empty(), which tests whether a Linux list is

empty, is an O(1)

operation because it can simply check if next and

of the list are NULL.

prev pointers of the head

(1.3)

When there is an error in the arguments passed to

a system call, the system

call should set the global variable errno defined

space to EINVAL.

in the kernel address

(1.4)

The malloc() library function is not safe to call

handler. The same is true for free().

from within a signal

(1.5)

Each thread created by pthread_create() will have

its own task_struct.

Problem [2] (48 points)

----------------------------------------------------------------------------

The following programs use the macros

local_irq_disable()/local_irq_enable(), which execute privileged

architecture-specific instructions to disable/enable interrupts on the

current processor. Normally when a user program executes these instructions,

it would trigger an exception and the process would be terminated by the

kernel. To run these programs, we’ve patched the Linux kernel to configure

the CPU so that these instructions can be executed from user-mode (highly

insecure!).

The macros work by manipulating per-task CPU interrupt state, which will be

saved when we context-switch away from the task, and restored when we

context-switch back. This means it’s okay if we exit with interrupts

disabled; the interrupt state will simply go away.

We also have a syscall die_in(), which will ask the kernel to kill the

calling process after the given number of seconds has passed (e.g. die_in(1)

would kill the process after 1 second has passed), just as if the process

called exit() at that time. It is possible that the process dies at later

than the specified time if, for whatever reason, the current CPU doesn’t

enter kernel-mode, as the kernel will be unable to take action until then.

We ran each program on two lightly loaded machines: one is UP

("Uni-Processor", meaning there is a single CPU), and the other is SMP

("Symmetric Multi-Processor", meaning there are multiple CPUs); the two

machines are otherwise identical.  In each case, indicate whether you expect

the program to terminate in less than 1 second, about 1 second, more than 1

second (including if it runs forever), or if it’s unpredictable (e.g. due to

some kind of race condition). In a multi-process scenario (i.e. if fork() is

called), this means once all processes have terminated.

We use a macro MORE_THAN_ONE_SEC to spin for well beyond 1 second; for

example, on Kent’s Macbook with M1 Max running VMware Fusion, it is defined

as follows:

#define MORE_THAN_ONE_SEC size_t i = 0; i < 2000000000; i++

All programs are compiled without compiler optimizations.

Write the expected runtime of each program by writing "< 1", "= 1", "> 1",

or "UN" (unpredictable) on the answer sheet, for both UP and SMP cases.

For example, you would write "> 1" for the following program:

int main() {

for (MORE_THAN_ONE_SEC)

;

}

And "= 1" for this one:

int main() {

die_in(1);

for (MORE_THAN_ONE_SEC)

;

}


Problem [2] (continued)


(2.1)