c4-fork Introductions to the fork() call Version 20011023 The only way to create a new process is with the fork() call. A "parent" makes a fork() call, which results in a new "child" process being created. All processes on the system form a tree, using the parent/child relationship. Just before a fork() by process 200. Kernel User (PID=200) +-----------------+ +----------------+ |PCBs | | | |+-------------+ | |----------------| || PID 100 | | text| | || PPID 52 | | PC-> | TRAP fork | || ... | | | | |+-------------+ | |----------------| ||... | | | | || | | |----------------| |+-------------+ | data| | || PID 200 | | | | || PPID 100 | | | | || ... | | |----------------| |+-------------+ | | | +-----------------+ +----------------+ Just after the fork() "Parent" "Child" Kernel User (PID=200) User (PID=201) +-----------------+ +----------------+ +----------------+ |PCBs | | | | | |+-------------+ | |----------------| |----------------| || PID 100 | | text| | text| | || PPID 52 | | | TRAP fork | | TRAP fork | || ... | | PC-> | | PC-> | | |+-------------+ | |----------------| |----------------| ||... | | | | | | || | | |----------------| |----------------| |+-------------+ | data| | data| | || PID 200 | | | | | | || PPID 100 | | | | | | || ... | | |----------------| |----------------| |+-------------+ | | | | | || PID 201 | | +----------------+ +----------------+ || PPID 200 | | || ... | | |+-------------+ | +-----------------+ Note the text and data segments on the parent and child are idential! Questions you should ask: 1) How is the new virtual memory space built? (fork() vs vfork()) 2) How is the new PCB built? UID, EUID, GID, EGID, umask, limits, nice ... times (user, sys, child_user, child_sys) fd table IPCs (semiphores, shared memory, messages) 3) Which process runs next? (race conditions) 4) What happens if both processes read/write? 5) How do open streams act? "Sane Structured" use of a fork() call. #include #include int main() { pid_t pid; ... code P_B if((pid = fork()) > 0) { /* Parent process */ ... code P_A } else if (pid == 0) { /* Child process */ ... code C_A } else { /* Parent process, fork() failed */ ... code P_AE } ... code PC } You should always use a fork() in the above structured manner to avoid as many problems as possible. Concurrent programs are very hard to get correct and to debug compared to non-parallel programs. In the above pseudo code: Code: P_B (Before fork) is only executed by the parent (child does not exist yet). P_A (After fork) is only executed by the parent (child now exists) C_A (After fork) is only executed by the child. P_AE (After fork, that had an Error) is only executed by the parent if the fork had an error (no child exists). PC could be executed by both parent and child. It is "bad" practice to have a child ever leave code C_A and execute code PC. The child should either make an exec() call in code C_A or an exit() call. "Sane Structured" use of a fork(), waitpid(), and exit() calls. This is the very common case where a parent should wait() for a child to exit() before going on. We expand the code P_A and C_A above. #include #include #include int main() { pid_t pid; int stat; int st; ... code P_B if((pid = fork()) > 0) { /* Parent process */ if((wpid=waitpid(pid, &stat, 0)) != pid) { /* signal was recieved by Parent while waiting, "error" */ ... } else { /* Child exited "normally" */ if( WIFEXITED(stat) ) { /* Child exited "normally", Parent gets child's "st" */ st = WEXITSTATUS(stat); ... } else { /* Child did not do a "normal" exit() */ ... } } ... } else if (pid == 0) { /* Child process */ ... exit(st); } else { /* Parent process, fork() failed */ ... code P_AE } /* Now only Parent can get here */ ... code PC } ---------------------------------------------------------------------------- Properties inherited by the child from the parent. - uid, euid, gid, egid - supplemtray gids - sid, pgid - environment and argc/argv - controlling terminal - set uid flag, set gid flag - cwd - root directory - umask - nice - signal mask and dispositions - open file descriptors and close on exec flags - attached shared memory segments - Other IPCs (semiphores, message queues ...) - resource limits - copy of the virtual memory, test/data segments Differences between a parent and child - the return value of fork() - pid, ppid - childs time accounting set to 0 (tms_utime, tms_stime, tms_cutime, tms_cstime) - file locks are not inherited - pending alarms are cleared for the child - pending signals are cleared for the childS See the fork(2) man page for additional information.