4.5 Process Creation
In FreeBSD, new processes are created with the fork family of system calls. The fork system call creates a complete copy of the parent process. The rfork system call creates a new process entry that shares a selected set of resources from its parent rather than making copies of everything. The vfork system call differs from fork in how the virtual-memory resources are treated; vfork also ensures that the parent will not run until the child does either an exec or exit system call. The vfork system call is described in Section 5.6.
The process created by a fork is termed a child process of the original parent process. From a user's point of view, the child process is an exact duplicate of the parent process except for two values: the child PID and the parent PID. A call to fork returns the child PID to the parent and zero to the child process. Thus, a program can identify whether it is the parent or child process after a fork by checking this return value.
A fork involves three main steps:
-
Allocating and initializing a new process structure for the child process
-
Duplicating the context of the parent (including the thread structure and virtual-memory resources) for the child process
-
Scheduling the child process to run
The second step is intimately related to the operation of the memory-management facilities described in Chapter 5. Consequently, only those actions related to process management will be described here.
The kernel begins by allocating memory for the new process and thread entries (see Figure 4.1). These thread and process entries are initialized in three steps: One part is copied from the parent's corresponding structure, another part is zeroed, and the rest is explicitly initialized. The zeroed fields include recent CPU utilization, wait channel, swap and sleep time, timers, tracing, and pending-signal information. The copied portions include all the privileges and limitations inherited from the parent, including the following:
-
The process group and session
-
The signal state (ignored, caught, and blocked signal masks)
-
The kg_nice scheduling parameter
-
A reference to the parent's credential
-
A reference to the parent's set of open files
-
A reference to the parent's limits
The explicitly set information includes
-
Entry onto the list of all processes
-
Entry onto the child list of the parent and the back pointer to the parent
-
Entry onto the parent's process-group list
-
Entry onto the hash structure that allows the process to be looked up by its PID
-
A pointer to the process's statistics structure, allocated in its user structure
-
A pointer to the process's signal-actions structure, allocated in its user structure
-
A new PID for the process
The new PID must be unique among all processes. Early versions of BSD verified the uniqueness of a PID by performing a linear search of the process table. This search became infeasible on large systems with many processes. FreeBSD maintains a range of unallocated PIDs between lastpid and pidchecked. It allocates a new PID by incrementing and then using the value of lastpid. When the newly selected PID reaches pidchecked, the system calculates a new range of unused PIDs by making a single scan of all existing processes (not just the active ones are scannedzombie and swapped processes also are checked).
The final step is to copy the parent's address space. To duplicate a process's image, the kernel invokes the memory-management facilities through a call to vm_forkproc(). The vm_forkproc() routine is passed a pointer to the initialized process structure for the child process and is expected to allocate all the resources that the child will need to execute. The call to vm_forkproc() returns through a different execution path directly into user mode in the child process and via the normal execution path in the parent process.
Once the child process is fully built, its thread is made known to the scheduler by being placed on the run queue. The alternate return path will set the return value of fork system call in the child to 0. The normal execution return path in the parent sets the return value of the fork system call to be the new PID.