Lecture 5: Process Creation
Review: Linux process memory layout Stack: Automatically allocated and deallocated Local variables, parameters Heap: Manually allocated (malloc) and deallocated (free) Dynamic sized data structures Static data Global variables Text Program
In this lecture Process context Process creation
Process context: registers Stack Heap Static data Text (program) Stack pointer (SP) As discussed three pages ago, most programs perform file I/O using library code layered on top of kernel code. In this section we discuss just the kernel aspects of file I/O, looking at the abstraction and the high-level aspects of how this abstraction is implemented. The Unix file abstraction is very simple: files are simply arrays of bytes. Many systems have special system calls to make a file larger. In Unix, you simply write where you’ve never written before, and the file “magically” grows to the new size (within limits). The names of files are equally straightforward—just the names labeling the path that leads to the file within the directory tree. Finally, from the programmer’s point of view, all operations on files appear to be synchronous—when an I/O system call returns, as far as the process is concerned, the I/O has completed. (Things are different from the kernel’s point of view, as discussed later.) Program counter (PC)
Process context: file management Root directory Working directory Opened files
Process context: others Process id Parent process …
Process creation: fork() system call #include <unistd.h> pid_t fork(void); //prototype fork() returns a process id (a small integer) fork() returns twice! In the parent – fork returns the id of the child process In the child – fork returns a 0
Parent and child processes The child process is a copy of the parent process Same core image Same context (except process id) Registers Files Others
Creating a process: before fork() Stack Heap Static data Text (program) Fork() The only way to create a new process is to use the fork system call. PC parent process
Creating a process: after fork() Stack Heap Static data Text (program) fork() //return p Stack Heap Static data Text (program) fork() //return 0 The only way to create a new process is to use the fork system call. PC PC parent process child process process id = p
Example #include <unistd.h> #include <stdio.h> void main(void) { pid_t pid = fork(); if (pid > 0) printf(“I am the parent\n”); else if (pid == 0) printf(“I am the child\n”); else printf(“ERROR!\n”); }
Process hierarchies UNIX: Parent creates a child process, child can create more processes Forms a hierarchy UNIX calls this a "process group” All processes within a group are logically related Windows has no concept of process hierarchy all processes are created equal
Death and destruction All processes usually end at some time during runtime (with the exception of init) Processes may end either by: executing a return from the main function calling the exit(int) function calling the abort(void) function When a process exits, the OS delivers a termination status to the parent process.
Waiting Parent processes often wait for their child processes to end Parent processes do that via a wait() call pid_t wait(int * status); pid_t waitpid( pid_t pid, int * status,…);
Switching programs fork() creates a new process This would be almost useless if there was not a way to switch which program is associated with the new process The exec() system call is used to load a new program into an existing process
exec(): Loading a new image Stack Heap Static data Text (program) exec(prog, args) prog’s stack prog’s heap prog’s static data prog’s Text before after
exec() example: #include <unistd.h> main() { printf(“executing ls\n”); execl(“/bin/ls”, “ls”, “l”, (char*)0); exit(1); }
A stripped-down shell while (TRUE) { /* repeat forever */ type_prompt( ); /* display prompt */ read_command (command, parameters) /* input from terminal */ if (fork() != 0) { /* fork off child process */ /* Parent code */ waitpid( -1, &status, 0); /* wait for child to exit */ } else { /* Child code */ execve (command, parameters, 0); /* execute command */ }
More examples How many processes does this piece of code create? int main() { fork(); }
Bad example (don’t try this!) #include <unistd.h> #include <stdio.h> void main(void) { while (!fork()) printf("I am the child %d\n“, getpid()); printf("I am the parent %d\n“, getpid()); } fork bomb!
What is the output of this? int main() { int i; for (i=0; i<2; i++) { fork(); printf(“%d\n”,i); } return (0);