COMS 4118 Operating Systems I Spring 2022 Exam 1
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) |
2023-04-18