Today: Processes Recall OS' job: * scheduling processes (illusion of excluse use of CPU) * manage processes' virtual memory(illusion of excluse use of memory) * I/O (file system, networking) Process: an instance of a running program C file ----gcc---> binary executable ----./a.out----> process ---ctrl-c---> process killed Each process corresponds to some meta-data (i.e. state) in OS process_id state (runntable, blocked etc.) user id open files rip other saved registers VM structure called task_struct in Linux Only OS should be allowed to modify OS data structure Privileged vs. unprivileged mode of execution this is a h/w primitive OS runs in privileged mode (can mess with h/w, e.g. change pagetable) user processes run in unprivileged mode (cannot mess with OS data structure, h/w) How to get into privileged mode? 1. exception (e.g. divide by zero, access memory not allowed according to PTE, hw "call into" kernel mode) 2. traps (user programs asks for kernel assistance) 3. interrupt (timer, or device events such as packet arrival, keyboard press. needed for multi-processing and context switch) How to get out of privileged mode? special h/w instruction (iret) return to where the user program was not necessarily the same program, may context-switch to a different one Traps are used to implement syscalls (syscall is doing function call invoking some kernel functions) Each syscall has a number 0 read 1 write 2 open 3 close ... 57 fork 59 execve 60 exit 62 kill user code kernel code | | | syscall --------------------> | open the requested file <-------------------- next | | Exceptions are also when kernel takes control | | |-----h/w exception-------> check process vm data structure, fetch page from disk | <------------------------ | | Multiprocessing goal: run many processes simulataneously Why: - play music / browse the web while running labs Many processes running on the computer: stack stack heap heap data data code code saved saved memory: registers registers CPU state:registers+rip (next instruction of P1) Switch from P1 to P2: * save rip & other registers in P1's PCB * load saved registers of P2 P1 User code kernel code P2 user code | | ___timer interrupt_________> decide it's P2's turn save P1 state load P2 iret ----------------------------> | <---- timer interrupt-------| P1 & P2 executes at disjoint intervals of time But, as timer interrupt is frequent (per 10 ms), P1 & P2 appear to be executing concurrently. * Creating and killing processes * How to create a new process? One process creates another process via the fork() syscall - all processes are created by some other processes. the first process, called init, is created by OS. - the process of your lab program is created by the shell program. * The fork() syscall - creates a new (child) process (almost completely) identical to the parent - same code, data, heap, stack, register state except different return values of the fork() syscall - returns child process id in parent process - returns zero in child process - "called once, returned twice" - child process inherits parent's open file descriptors void main() { pid_t pid = fork(); if (pid == 0) { printf("In child\n"); }else{ //pid < 0 if fork() failed printf("In parent, child pid=%d\n", pid); } } Draw picture one process, about to execute fork() make another copy of the process, show they process in different code path afterwards Notes on fork() * execution of parent and child processes are concurrent / interleaving is non-determinstic both are possible: In child In parent,... or In parent,.. In child * Duplicate but separate address space add int x = 0; x++; in child x--; in parent Another example: void main() { 1: printf("hello\n"); 2: fork(); 3: printf("world\n"); 4: fork(); 5: printf("Bye\n"); } How many processes are created? L1 --> L2 --> L3 --->L4---> L5 | +-----> L5 | +----> L3 ---->L4---> L5 + ---->L5 What are the possible printouts? hello hello world world world bye bye bye bye world bye bye bye bye both above are possible, and others... how about hello bye world world bye bye bye? Another example: void main() { 1:printf("hello\n"); 2:if (fork() == 0) { 3: printf("big\n"); 4: if (fork() == 0) { 5: printf("world\n"); } } 6: printf("Bye\n"); } L1--->L2---> L3 --->L4 ---> L5 ---> L6 | +----> L6 | +----> L6 hello big world bye bye bye hello bye big bye world bye infeasible: hello bye big bye bye world * Recap fork, by drawing memory copying from parent to child * wait: synchronize with child processes int waitpid(pid_t pid, int *child_status, int options) wait() to wait for any child, not a specific one add line: else {waitpid(pid, &status, 0)} good practice for parent to wait (reap) child otherwise, the state maintained in OS about child cannot be freed --> child becomes zombie what about zombies: if parent dies, then the first process (init) reaps its (zombie) children if parent does not die, then zombies lie around * execv load program in the current process int execv(char *filename, char *argv[]) - *overwrites* code, data, stack (retains PID, opened files) - called once, never returns * Do exercise void main() { for (int i = 0; i < 3; i++) { if (fork() == 0) { printf("%d\n", i); } } } How many processes are in total? 8 What's the output? 0 1 1 2 2 2 2 Modify to the following... void main() { pid_t pids[3]; for (int i = 0; i < 3; i++) { pids[i] = fork(); if (pids[i] == 0) { printf("%d\n", i); return; } } for (int i = 0; i < 3; i++) { waitpid(pids[i], NULL, 0); } } How many processes total? 4 output: 0 1 2 void main() { char buffer[100]; char *argv[3] = { "/bin/echo", buffer, 0 }; for (int i = 0; i < 3; i++) { pids[i] = fork(); if (pids[i] == 0) { sprintf(buffer, "%d", i); execv(argv[0], argv); //delete return } } for (int i = 0; i < 3; i++) { waitpid(pids[i], NULL, 0); } } how many processes in total?