CS 350 Operating Systems Spring 2021

4. II Discussion 1: Question? • Why do OSes use the combination of “fork() + ()” to create a new process (from a program)? • Can we directly call exec() to create a new process? Or, do you have better ideas/designs for creating a new process? • Hints: To answer this question: think about the possible advantages of this solution.

Fork

Exec

New program image in execution 2 Discussion 1: Background • Motived by the UNIX shell • $ echo “Hello world!” • $ Hello world! • shell figures out where in the file system the executable “echo” resides, calls fork() to create a new child process, calls exec() to run the command, and then waits for the command to complete by calling (). • You can see that you can use fork(), exec() and wait() to implement a shell program – our project! • But what is the benefit of separating fork() and exec() in creating a new process?

3 Discussion 1: Case study • The following program implements the command: • $ wc p3. > p4.output # wc counts the word number in p3.c • # the result is redirected to p4.output

Close default standard output (to terminal)

open file p4.output

4 Discussion 1: Case study • $ wc p3.c # wc outputs the result to the default standard output, specified by STDOUT_FILENO • By default, STDOUT_FILENO points to the terminal output. • However, we close this default standard output → close(STDOUT_FILENO ); • Then, when we open the file “p4.output” • A will be assigned to this file • UNIX systems start looking for free file descriptors from the beginning of the file descriptor table (i.e., at zero). In this case, STDOUT_FILENO will be the first available one and thus get assigned with the following open(). • STDOUT_FILENO -> file “p4.output” • Thus, subsequent writes by the child process, which is wc, to the standard output file descriptor will be routed transparently to the newly-opened file “p4.output” 0 0 STDINPUT STDINPUT 1 STDOUT 1 STDOUT wc 2 wc 2 STDERR STDERR n n FDn… FDn… 5 Discussion 1: Case study • What is happening and what do you get from the above example? • What is the benefit by using the combination of fork() and exec() to the above example? • What is the fundamental reason bringing in the benefit? • If you have a different way for creating a new process, can your solution achieve the same goal?

6 Discussion 2: Memory Layout • A general memory layout (address space) • It is the process’s view of memory in the system • and how the process references data and instruction while being executed MAX 0xffffffff Function call arguments, return address, Stack return values, and temporal variables

Gap

Heap Dynamically allocated memory (e.g. malloc())

Data Global variables, constants etc

Text Program Code 0x00000000

7 Discussion 2: Memory Layout • After fork(), the child process has its own, seprate address space, with the initial content being the same as the parent process.

0xC0000000 0xC0000000 Stack Stack

Parent Child process process address address space Heap space Heap

int g = 2; Data int g = 2; Data

Text (R/O) Text (R/O) 0x08048000 0x08048000 0 0

8 Discussion 2: Connect the dots • Address space is a virtual thing (memory abstraction for a process). • Components of an address space are further mapped to the real physical locations, where data/instructions are stored. Address space (virtual view) Physical ELF Program memory Stack Mapping Stack ELF Header Exec() (loader) .text .rodata Heap Heap .data .bss .bss .data Data .rodata Code .text 9 Discussion 2: Memory Layout

• When a parent process uses fork() to create a child process, the two processes will have • separate address spaces – separate copies of the data, stack, and heap segments • but the contents in these segments are same. • The child’s stack, data, and heap segments are initially exact duplicates of the corresponding parts the parent’s memory. • After the fork(), each process can modify the variables in its data, stack, and heap segments without affecting the other process. • It is because, they have separate address spaces.

10 Discussion 2: An example

static int idata = 111; /* Allocated in data segment */

int main(int argc, char *argv[]) { int istack = 222; /* Allocated in stack segment */ pid_t childPid;

childPid = fork();

if (childPid == -1) { (-1); } else if (childPid == 0) {idata *= 3; istack *= 3;} /* Child Process: modify data */ else {sleep(3)} /* Parent process: give child a chance to execute */

/* Both parent and child come here */ printf("PID=%ld %s idata=%d istack=%d\n", (long) getpid(), (childPid == 0) ? "(child) " : "(parent)", idata, istack);

exit(0); } What are the results? 11 Discussion 2: Question for fork() Implementation

• To make the parent and the child process have separate copies of the data, stack, and heap segments after forking: • A straightforward fork () solution: • Directly copy everything from the parent address space to the child address space. • This was actually the implementation used by some early Unix systems. But not anymore… • What are the problems of the “direct copy” approach? Do you have some ideas to improve it?

12 0xC0000000 Stack Physical memory Parent process Heap Text (R/O) Data

Text (R/O) 0x08048000 Data 0 copy 0xC0000000 Data Stack

As an address space maps to the Child physical memory, shared parts from process virtual Hints different address spaces can map to memory Heap the same physical location (e.g., instructions) Data

Text (R/O) Thus, direct copy should only apply 0x08048000 for stack, heap and data segments. 0 13 Discussion 2: Question for fork() Implementation • But problems of direct copying parent’s memory when forking. • Slow process creation - the child process won’t be ready until all the memory copying is done. • Waste of resources and time • Many resources could be shared between parent and child. • fork() is usually immediately followed by an exec() function to run a different program, which causes a replacement of the child process text segment, and re-initialization of the data, stack and heap segments, making the direct copying when forking a waste. • Do you have a better solution? • Refer to online materials about “copy-on-write” -- a general programming technique. • Justify your solution, e.g., whether your fork() solution addresses the problems caused by the “direct copy” approach and to which degree? 14 Discussion 3: Who runs first?

• After fork(), either the parent or the child can be scheduled to run (assume we have one CPU core). • A test of executing fork() multiple times in 2.2.19: 99.97% of a million executions have the parent first to run. • In Linux 2.4, change was made to have the child to run first most of the time. But this change was later dropped from the 2.4 kernel series. • In Linux 2.6, the change to have the child run first was adopted. • Does this really matter – to run child or parent first?

15 Discussion 3: Who runs first?

• If we focus on locality • Instructions and data are usually cached in CPU caches (L1, L2, LLC) • If there is cache hit – instructions or data are in the CPU, the execution is very fast • For better locality, which policy is better – to schedule the parent process first or child process? • If we focus on data copy – aiming to reduce data duplication • If exec() immediately follows fork() for the child process, which policy is better?

16 Group written assignments • Three (open) questions to answer: 1. What is the advantage of using the combination of “fork() + exec()” to create a new process to execute a program? Do you have another/other solution(s) for process creation? Compare your solution(s) with the existing one and show the pros and cons. 2. To implement a fork() , instead of directly copying data, heap and stack of the parent process to the child process’s address space, do you have a more efficient approach? Why is your approach superior to the “direct copy” approach? 3. If you were the OS scheduler, who do you schedule first after fork() system call, child or parent? Please justify

your choices. 17 Submission Policy • Work together with your group members (via zoom meetings) • Write down the answers/solutions together – each of you should contribute to each of the questions. • One submission per group – the group leader create a google shared document for all answers and submit the sharable link to myCourse (due by 23:59:59, March 7th)

• More tips: • As these questions are more open, rather than producing a “correct” solution, you are more expected to reason about your solutions – to provide needed justifications to show why your solutions work and are better than others. • Working as a group, please tolerate, respect, and inspire each other. Try to come to the same for the understanding of the questions and finally reach certain levels of consensus for your answers.

• Enjoy! 18