CSCI 340 Exam 1
Duration: 75 Minutes
Closed books, notes, and cell phones.
Q1 [15 points]. Short Answer Questions (5 points each)
(a) List 3 major components of an operating system? Briefly explain their responsibilities.
Instructor Solution
(b) Briefly define the following terms: (1) multi-tasking (2) timesharing. What’s the goal of multi-tasking? What’s the goal of time-sharing?
Instructor Solution
Time-sharing is the ability of the OS to allocate time quotas (quantum) to each process so that a process does NOT hog the CPU. If the currently executing process has used up its time quantum, the OS takes the CPU away from it and schedules a new process. This way, every process in the ready queue gets a fair share of the CPU. The goal of timesharing is to improve process response time.
(c) Why is it necessary to have two modes of operation, e.g., user and kernel mode, in modern OSs? Briefly explain the difference between the user mode and kernel mode.
Instructor Solution
To protect one process from another and the OS from the user processes, modern CPUs provide two modes of operation: user mode & kernel mode. In the user mode, the CPU is executing user code. In this mode, some privileged instructions (instructions related to I/O and memory management) can NOT be executed. If the user code tries to execute any of these privileged instructions, a trap will occur, and the user process will be terminated. In the kernel mode, the CPU is executing OS system (kernel) code. In this mode, all instructions of the CPU can be executed.
Q2 [20 points]. Short Answer Questions (5 points each)
(a) Describe 2 methods by which arguments are passed from a user process to the kernel in system calls. How does Linux pass arguments to the kernel during a system call?
Instructor Solution
- Via registers. This is what Linux does.
- Via stack
- Via a dedicated piece of memory such a C structure
(b) Draw the process transition diagram. Mark each transition appropriately.
Instructor Solution

(c) What’s the name of the “first” process in Linux. What’s its process id? Who creates it?
Instructor Solution
“init” or “systemd” is the name of the first process in Linux. Its process id is 1. It is created during system initialization (boot sequence). It sits at the top of the process hierarchy.
(d) What’s a “zombie” process? What information does the kernel store for a zombie process? When does the kernel completely clean up any information related to a zombie process?
Instructor Solution
When a process in Linux terminates (either executing exit system call or due to a fault such as divide by zero etc.), it enters the Zombie state. In this state, the OS reclaims all the resources used by the process but its PCB is kept because the PCB stores the exit status of the process. When the parent process calls “wait” or “waitpid” system call to retrieve the exit status of the process, then this Zombie process is totally cleaned up from the system. But until then, its PCB stays in OS memory and the process is in the Zombie state.
Q3 [15 points]. System Call
Consider the hypothetical Linux system call int add(int a, int b, int *c), which computes the sum of a and b in the kernel and copies the result into the buffer pointed to by c. Assume that the system call number for “add” is 40. The system call returns 0 if there is no error, -1 in the case of an error. Further assume that Linux runs on a 32-bit x86 CPU.
(a) Describe the set of actions that occur from the time the user invokes this system call until the system call returns. Specifically, describe in detail how the user program traps to the kernel, how the parameters a, b and c are passed to the kernel, and the set of actions that the Linux kernel takes to execute the system call and return to the user code.
Instructor Solution

The figure shown above gives all the details of this system call. Before trapping to the kernel, the user program calls a user-library function named “add”, which puts the system call number 40 into eax register, the parameters b, c and d into ebx, ecs and edx registers respectively and traps to the kernel by executing “int 0x80” instruction. Because this is a soft-interrupt instruction, the CPU looks at the Interrupt Vector Table, specifically, entry 0x80, which contains the address of the trap handler (entry_int80_32). The CPU now starts executing the trap handler. The trap handler first saves the current application state to the process’s kernel stack, then validates the system call number, and if it is valid, calls the actual system call handler function by referencing “sys_call_table” array of function pointers. System call handler function starts by validating the parameters. Specifically, the user buffer “c” must be validated. If the buffer is invalid (NULL pointer or invalid memory address), then the system call fails returning - 1 to the user. If it is valid, “a+b” is stored in “*c” and the system call returns 0 indicating a successful operation.
(b) Under what circumstance(s) would the system call return an error, i.e., returns -1?
Instructor Solution
The system call may fail under two conditions:
- The system call number passed in “eax” is invalid
- user buffer pointer “c” is invalid, i.e., NULL or points to an invalid memory address.
Q4 [15 points]. Program Output
What is the output of the following code segments when run in Linux?
int g = 0;
void T(char *str) {
g++;
printf("[%s] g: %d\n", str, g);
}
main() {
int pid;
char *str = "Parent";
g++;
if ((pid = fork()) == 0) {
str = "Child1";
T(str);
exit(0);
}
waitpid(pid);
g++;
T(str);
if ((pid = fork()) == 0) {
str = "Child2";
T(str);
} else
waitpid(pid);
T(str);
}Instructor Solution
Output:
[Child1] g: 2
[Parent] g: 3
[Child2] g: 4
[Child2] g: 5
[Parent] g: 4
int var = 0;
main() {
int pid;
if ((pid = fork()) == 0) {
var = 5;
execl("/bin/ls", "ls", NULL);
printf("var: %d\n", var);
} else {
var = 10;
waitpid(pid);
}
printf("var: %d\n", var);
}Instructor Solution
Output:
<List of files & folders in the current folder>
var: 10
Q5 [15 points]. Process Management
How many processes are created when the following piece of code is executed in Linux? Draw the process tree for the processes thus created and show what’s printed by each process on the screen.
main(int argc, char *argv[]) {
int i = 3;
while (i <= 8) {
if (fork() == 0)
i += 2;
else
i += 3;
}
printf("i = %d\n", i);
return 0;
}Instructor Solution

| Process | Output |
|---|---|
| P1 | i = 9 |
| P2 | i = 11 |
| P3 | i = 11 |
| P4 | i = 10 |
| P5 | i = 10 |
| P6 | i = 10 |
| P7 | i = 9 |
Q6 [20 points]. IPC
Assume that there is a system utility program “/bin/rand_nums” that prints 5 random integers on the screen. Here is a sample run of this program:
bash% /bin/rand_nums
3 7 1 6 2
bash%
You are asked to implement a program called “sum.c” that will run “/bin/rand_nums”, get the 5 numbers generated, compute their sum and print it on the screen. To make this possible, you will need to create a child process to run “/bin/rand_nums”, create a pipe for the child process to send the generated numbers to your program over the pipe. Make sure that you connect descriptor 1 of “/bin/rand_nums” to the “write end” of the pipe so that the numbers are sent to your program over the pipe. Also connect your descriptor 0 to the “read end” of the pipe so that you can use “scanf” to read the numbers as if they are coming from the keyboard. Here is a sample run of your program:
bash% ./sum
The sum of the 5 random numbers is 19
bash%
Recall that you create a new child using fork(), start a new executable using execl(char *execFilename, char *arg1, …), create a pipe using pipe(int fd[2]) and copy the contents of fd1 to the contents of fd2 using dup2(int fd1, int fd2) system calls.
Instructor Solution
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd[2];
pipe(fd); // From child to parent
if (fork() == 0) {
// Child
dup2(fd[1], 1);
close(fd[0]);
execl("./rand_nums", "rand_nums", NULL);
}
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
int sum = 0;
for (int i = 0; i < 5; i++) {
int num;
scanf("%d", &num);
sum += num;
}
printf("The sum of the 5 random numbers is %d\n", sum);
return 0;
}