CMPS 2300 - Exam 2 Review
"Register file"/ Registers
16 names locations on the processor that each hold one value
8.15 How many "Example" lines does this program print? void try() { if (Fork() == 0) { Fork(); Fork(); printf("Example\n"); return; } return; } int main() { try(); printf("Example\n"); exit(0); }
2 main{ try() { Fork(): (1) C prints "Example" (2) P prints "Example" exit(0); }
What does fork() do?
Creates a new running child process
What does waitpid() do?
Suspends current process unil specific process terminates
waitpid()
Suspends current process unil specific process terminates
What does execve() return?
Nothing, unless there's an error (when error, returns -1)
Logical control flow provides the illusion of exclusive...
Use of CPU
Virtual memory (Abstracted address space) provides the illusion of exclusive...
Use of memory
What does if(Fork() == 0) run? (Parent or child?)
child
fork
create process
Program Counter (CP)/ Instruction pointer
indicates the address in memory of teh next instruction to be executed
8.13 What is one possible output of the following program? int main() { int a = 5; if (Fork() != 0) printf("a=%d\n", --a); printf("a=%d\n", ++a); exit(0); }
int a=5: Fork() != 0 --> a--, prints "a=4" a++, prints "a=5" Output: a=4 a=5
int execve(_________, _________, _________)
int execve(char *filename, char *argv[ ], char *envp[ ])
kill
send signal to a process
3.24 C code: short loop_while (short a, short b) { short result = __________________; while (__________________) { result = __________________; a = __________________; } return result; } Assembly code: (a in %rdi, b in %rsi) loop_while: movl $0, %eax jmp .L2 .L3: leaq (,%rsi,%rdi), %rdx addq %rdx, %rax subq $1, %rdi .L2: cmpq %rsi, %rdi jg .L3 rep; ret
short loop_while (short a, short b) { short result = 0; while (a > b) { result = result + (a*b); a = a - 1; } return result; }
What does if(Fork() > 0) run? (Parent or child?)
usually parent (fork should return a pid value which is higher than 0)
void fork() { int child_status; if (fork() == 0) { printf("HC: hello from child\n"); exit(0); } else { printf("HP: hello from parent\n"); __________________ (Call for the parent to wait for the child); printf("CT: child has terminated\n"); } printf("Bye\n"); }
wait(&child_status)
Parent reaps a child by calling __________
wait()
System control for: Reap process
wait(), OR waitpid()
void fork11() { pid_t pid[N]; int i; int child_status; for (i = 0; i < N; i++) if ((pid[i] = fork()) == 0) exit(100+i); /* Child */ for (i = N-1; i >= 0; i--) { pid_t wpid = waitpid(pid[i], &child_status, 0); if (WIFEXITED(child_status)) printf("Child %d terminated with exit status %d\n", __________________, __________________; else printf("Child %d terminate abnormally\n", wpid); } }
wpid WEXITSTATUS(child_status)
execve
execute a program
System control for: Terminate process
exit()
System control for: Create process
fork()
int wait(int *child_status)
- Suspends current process until one of its children terminates - Return value is the PID of the child process that terminated - If child_status != NULL, then the integer it points to will be set to a value that indicates reason the child terminated and the exit status
What does fork() return?
0 to the child process the child's PID to the parent process -1 if error
How many output lines does this program produce? Hint: Draw a process graph to help. void doit() { fork(); printf("hello\n"); return; } int main() { doit(); printf("hello\n"); exit(0) } A. What if you put the 'printf' statement in main before doit()? B. What if you add another call to 'fork' at the beginning of doit()? C. What if you replace the return in doit() with exit(0)?
4 A. 3 B. 8 C. 2
8.11 How many "Example" output lines does this program print? int main() { int i; for (i = 3; i > 0; i--) Fork(); printf("Example\n"); exit(0); }
8 i = 3: Fork(): (1) child prints "Example" (2) parent prints "Example" i = 2: Fork(): (3) child prints "Example" (4) parent prints "Example" i = 1: Fork(): (5) child prints "Example" (6) parent prints "Example" i = 0: Fork(): (7) child prints "Example" (8) parent prints "Example"
8.12 How many "Example" output lines does this program print? void try() { Fork(); printf("Example\n"); Fork(); return; } int main() { try(); Fork(); printf("Example\n"); exit(0); }
8 main{ try(); Fork: try() { Fork(): creates C1, P1 (1) C1 prints "Example" (2) C2 prints "Example" Fork(): C1 --> C1.1 (3), P1.1 (4) P1 --> C1.2 (5), P1.2 (6) (all above print "Example") } Fork(): creates C2, P2 (7) C2 prints "Example" (8) P2 prints "Example" }
8.3 How many output lines does this program generate? int main() { int status; pid_t pid; printf("Start\n"); pid = Fork(); printf("%d\n", !pid); if (pid == 0) { printf("Child\n"); } else if ((waitpid(-1, &status, 0) > 0) && (WIFEXITED(status) != 0)) { printf("%d\n", WEXITSTATUS(status)); } printf("Stop\n"); exit(2); }
936036, 903636, and 093636
if ((pid = Fork()) == 0) { if (execve(myargv[0], myargv, environ) ___ 0) { printf("%s: Command not found.\n", myargv[0]); exit(1); } }
<
if ((pid = fork()) ___ 0) { fprintf(stderr, "fork error: %s\n", strerror(errno)); !exit(0);! }
<
8.10 In this chapter, we have introduced some functions with unusual call and return behaviors: getenv, setenv, unsetenv, and execve. Match each function with one of the following behaviors: A. Called once, returns only if there is an error B. Called once, returns nothing C. Called once, returns either a pointer or NULL
A - setenv; unsetenv B - execve C - getenv
8.4 What is one possible ordering of these output lines? (*Bonus: How many possible orderings are there?) int main() { int status; pid_t pid; printf("Start\n"); pid = Fork(); printf("%d\n", !pid); if (pid == 0) { printf("Child\n"); } else if ((waitpid(-1, &status, 0) > 0) && (WIFEXITED(status) != 0)) { printf("%d\n", WEXITSTATUS(status)); } printf("Stop\n"); exit(2); }
A. 7 (here are seven such vertices, thus the program will print seven lines of output) B. Any output sequence corresponding to a topological sort of the graph is possible. For example: Start, 0, 1, Child, Stop, 2, Stop is possible.
Annotate: if (fork() ______) { printf("Error!"); } else if (fork() ______) { printf("This is the child process"); } else { \* fork() == _______ *\ printf("This is the parent process"); }
Error: < 0 Child: == 0 Parnet: PID number
What does execve() do?
Loads and runs in the current process (overwrites code, data, and stack, retains PID, open files, and signal content)
2 key abstractions in a process
Logical control flow Virtual memory (a.k.a. Abstracted address space)
8.2 List all of the possible output sequences for the following program: int main() { if (Fork() == 0) { printf("9"); fflush(stdout); } else { printf("0"); fflush(stdout); waitpid(-1, NULL, 0); } printf("3"); fflush(stdout); printf("6"); exit(0); }
The parent and child execute nondisjoint sets of instructions, (which is only possible because the parent and child have identical code segments) A. The key idea here is that the child executes both printf statements. After the fork returns, it executes the printf in line 6. Then it falls out of the if statement and executes the printf in line 7. Here is the output produced by the child: p1: a=8 p2: a=9 B. The parent executes only the printf in line 7: p1: a=10
For int wait(int *child_status), what are the options for child_status?
WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG, WIFSTOPPED, WSTOPSIG, WIFCONTINUED
Context switch
When one process is scheduled to run and another is temporarily stopped
Add comments: int main(int argc, char **argv) { int pid; sigset_t mask_all, mask_one, prev_one; Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Signal(SIGCHLD, handler); /* ____________________: */ initjobs(); while (1) { /*____________________: */ Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); if ((pid = Fork()) == 0) { /* ____________________ */ /* ____________________: */ Sigprocmask(SIG_SETMASK, &prev_one, NULL); Execve("/bin/date", argv, NULL); } /* ____________________ */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); /* ____________________: */ "addjob(pid); /* ____________________: */ Sigprocmask(SIG_SETMASK, &prev_one, NULL); } exit(0); }
int main(int argc, char **argv) { int pid; sigset_t mask_all, mask_one, prev_one; Sigfillset(&mask_all); Sigemptyset(&mask_one); Sigaddset(&mask_one, SIGCHLD); Signal(SIGCHLD, handler); /* Initialize the job list: */ initjobs(); while (1) { /*Block SIGCHLD: */ Sigprocmask(SIG_BLOCK, &mask_one, &prev_one); if ((pid = Fork()) == 0) { /*Child process*/ /* Unblock SIGCHLD: */ Sigprocmask(SIG_SETMASK, &prev_one, NULL); Execve("/bin/date", argv, NULL); } /* Parent process */ Sigprocmask(SIG_BLOCK, &mask_all, NULL); /* Add the child to the job list: */ "addjob(pid); /* Unblock SIGCHLD: */ Sigprocmask(SIG_SETMASK, &prev_one, NULL); } exit(0); }
C code: long arith2 (long x, long y , long z) { long t1 = __________________: long t2 = __________________: long t3 = __________________: long t4 = __________________: return t4; } Assembly code: artith2: or rdi, rsi sar rdi, 3 not rdi mov rax, rdx sub rax, rdi ret
long arith2 (long x, long y , long z) { long t1 = x | y: long t2 = t1 >. 3: long t3 = ~ t2: long t4 = t3: return t4; 0 }
C code: long fun1 (long a, long b) { if (__________________) return __________________; else return __________________; } Assembly code: fun1: cmp rdi, rsi jge .L3 mov rax, rsi ret .L3: mov rax, rsi ret
long fun1 (long a, long b) { if (a < b) return a; else return b; }
3.28 C code: short test_two (unsigned short x) { short val = 0; short i; for ( . . . ; . . . ; . . . ) { . . . } return val ; } Assembly code: (x in %rdi) test_two: movl $1, %edx movl $65, %eax .L10: movq %rdi, %rcx andl $1, %ecx addq %rax, %rax orq %rcx, %rax shrq %rdi Shift right by 1 addq $1, %rdx jne .L10 rep; ret Reverse engineer the operation of this code and then do the following: A. Use the assembly-code version to fill in the missing parts of the C code. B. Explain why there is neither an initial test before the loop nor an initial jump to the test portion of the loop. C. Describe in English what this function computes
long fun_b (unsigned long x) { long val = 0; long i; for (i = 64; i != 0; i--) { val = (val << 1) | (x & 0x1); x >>= 1; } return val;
C code: long scale2 (long x, long y, long z) { long t = __________________: return t; } Assembly code: scale2: lea rax, [rdi+rdi*4] lea rax, [rax+rsi*2] lea rax, [rax+rdx*8] ret
long scale2 (long x, long y, long z) { long t = 5x + 2y + 8z: return t; }
8.18 Consider the following program: void end(void) { printf("2"); fflush(stdout); } int main() { if (Fork() == 0) atexit(end); if (Fork() == 0) { printf("0"); fflush(stdout); } else { printf("1"); fflush(stdout); } exit(0); } Determine which of the following outputs are possible. Note: The atexit function takes a pointer to a function and adds it to a list of functions (initially empty) that will be called when the exit function is called. A. 112002 B. 211020 C. 102120 D. 122001 E. 100212
main { if (Fork() (Creates C, P) == 0 (Child)): }
8.16 What is the output of the following program? int counter = 1; int main() { if (fork() == 0) { counter++; exit(0); } else { Wait(NULL); counter++; printf("counter = %d\n", counter); } exit(0); }
main{ if (fork() (creates C, P) == 0 (child case)) { : child counter starts at 1; counter++; (counter: 1+1 = 2) exit; } else (fork != 0 now, so parent case): (waits for the child to complete) parent counter starts at 1; counter++, prints "Counter = 2" } exit; } Output: Counter = 2
pid_t waitpid(______________, ______________, ______________)
pid_t pid int &status int options
An operating system allows...
processes to run concurrently
3.18 C code: short test(short x, short y, short z) { short val = __________________; if (__________________) { if (__________________) val = __________________; else val = __________________; } else if (__________________ ) val = __________________; return val; } Assembly code: (x in %rdi, y in %rsi, z in %rdx) test: leaq (%rdx,%rsi), %rax subq %rdi, %rax cmpq $5, %rdx jle .L2 cmpq $2, %rsi jle .L3 movq %rdi, %rax idivq %rdx, %rax ret .L3: movq %rdi, %rax idivq %rsi, %rax ret .L2: cmpq $3, %rdx jge .L4 movq %rdx, %rax idivq %rsi, %rax .L4: rep; ret
short test(short x, short y, short z) { short val = x + y - z; if (z > 5) { if (y > 2) val = x/y; else val = x/y; } else if (z < 3 ) val = z/y; return val; }
3.21 C code: short test(short x, short y, short z) { short val = __________________; if (__________________) { if (__________________) val = __________________; else val = __________________; } else if (__________________ ) val = __________________; return val; } Assembly code: (x in %rdi, y in %rsi) test: leaq 12(%rsi), %rbx testq %rdi, %rdi jge .L2 movq %rdi, %rbx imulq %rsi, %rbx movq %rdi, %rdx orq %rsi, %rdx cmpq %rsi, %rdi cmovge %rdx, %rbx ret .L2: idivq %rsi, %rdi cmpq $10, %rsi cmovge %rdi, %rbx ret
short test(short x, short y, short z) { short val = y + 12; if (x < 0) { if (x < y) val = x * y; else val = x | y; } else if (y > 10 ) val = x/y; return val; }
"Condition codes" (a.k.a. flags)
status information about recently executed instruction (especially important after cmp and test)
