HPlogo HP-UX Process Management: White Paper > Chapter 1 Process Management

Process creation

» 

Technical documentation

Complete book in PDF

 » Table of Contents

Process 0 is created and initialized at system boot time but all other processes are created by a fork() or vfork() system call.

  • The fork() system call causes the creation of a new process. The new (child) process is an exact copy of the calling (parent) process.

  • vfork() differs from fork() only in that the child process can share code and data with the calling process (parent process). This speeds cloning activity significantly at a risk to the integrity of the parent process if vfork() is misused.

NOTE: The use of vfork() for any purpose except as a prelude to an immediate exec() or exit() is not supported. Any program that relies upon the differences between fork() and vfork() is not portable across HP-UX systems.

Table 1-4 Comparison of fork() and vfork()

fork()vfork()
Sets context to point to parent.Child process is an exact copy of the parent process. (See fork(2) manpage for inherited attributes.) Can share parent's data and code. vfork() returns 0 in the child's context and (later) the pid of the child in the parent's context.
Copy on accessChild borrows the parent's memory and thread of control until a call to exec() or exit().Parent must sleep while the child is using its resources, since child shares stack and uarea
Must reserve swapNo reservation of swap

 

NOTE: At user (application) level, processes or threads can create new processes via fork() or vfork().

At kernel level, only threads can fork new processes.

Figure 1-10 Comparing fork() and vfork() at process creation

[Comparing fork() and vfork() at process creation]

When fork'd, the child process inherits the following attributes from the parent process:

  • Real, effective, and saved user IDs.

  • Real, effective, and saved group IDs.

  • List of supplementary group IDs (see getgroups(2)).

  • Process group ID.

  • File descriptors.

  • Close-on-exec flags (see exec(2)).

  • Signal handling settings (SIG_DFL, SIG_IGN, address).

  • Signal mask (see sigvector(2)).

  • Profiling on/off status (see profil(2)).

  • Command name in the accounting record (see acct(4)).

  • Nice value (see nice(2)).

  • All attached shared memory segments (see shmop(2)).

  • Current working directory

  • Root directory (see chroot(2)).

  • File mode creation mask (see umask(2)).

  • File size limit (see ulimit(2)).

  • Real-time priority (see rtprio(2)).

Each child file descriptor shares a common open file description with the corresponding parent file descriptor. Thus, changes to the file offset, file access mode, and file status flags of file descriptors in the parent also affect those in the child, and vice-versa.

The child process differs from the parent process in the following ways:

  • The child process has a unique process ID.

  • The child process has a different parent process ID (which is the process ID of the parent process).

  • The set of signals pending for the child process is initialized to the empty set.

  • The trace flag (see the ptrace(2) PT_SETTRC request is cleared in the child process.

  • The AFORK flag in the ac_flags component of the accounting record is set in the child process.

  • Process locks, text locks, and data locks are not inherited by the child (see plock(2)).

  • All semadj values are cleared (see semop(2)).

  • The child process's values for tms_utime, tms_stime, tms_cutime, and tms_cstime are set to zero..

  • The time left until an alarm clock signal is reset to 0 (clearing any pending alarm), and all interval timers are set to 0 (disabled).

The fork1() Routine

Both fork() and vfork() call the fork1() routine to create a new process, specifying as forktype:

  • FORK_PROCESS when the fork() system call is used

  • FORK_VFORK when the vfork() system call is used

The next table itemizes the subroutines performed by fork1().

Table 1-5 fork1(forktype)

SubroutinePurpose
getnewpid()

Set up unique process ID. The following PIDs are reserved for the system:

  • 0 -- PID_SWAPPER

  • 1 -- PID_PAGEOUT

  • 2 -- PID_INIT

  • 3 -- PID_STAT

  • 4 -- PID_UNHASH

  • 5 -- PID_NETISR

  • 6 -- PID_SOCKREGD

  • 7 -- PID_MAXSYS

getnewtid()

Set up unique thread ID for the main thread of the new process. The following TIDs are reserved for the system:

  • 0 -- TID_SWAPPER

  • 1 -- TID_INIT

  • 2 -- TID_PAGEOUT

  • 3 -- TID_STAT

  • 4 -- TID_UNHASH

  • 5 -- TID_NETISR

  • 6 -- TID_SOCKREGD

  • 7 -- TID_MAXSYS

proc_count()Verify that a user process does not exceed nproc (maximum number of proc table entries)
allocproc()Allocate space for the proc structure entry and clear it.
Allocate memory required for the process by a call to kmalloc.
Remove the allocated process table slot from the free list.
Mark the entry "process creation in progress", corresponding to a p_flag definition of SIDL (process creation state).
allocthread()Allocate space for the thread structure and add it to the active thread list.Initialize the entry to a kthread flag state of TSIDL (thread creation state)
link_thread_to_proc()Link the child thread structure to its proc structure
thread_hash(), proc_hash()Hash the child thread structure for its TID and the proc structure for its UID and PID.

link_thread_to_active()

link_proc_to_active()

Link the child thread structure to the active threads list and the child process to the active process list

 

If fork1() is called with a forktype of FORK_VFORK, memory is allocated and initialized for a vforkinfo() structure, which is referenced through the proc structures of the parent/child vfork() pair. The vforkinfo structure holds state information about the vfork() and a copy of the parent stack. The vforkinfo() structure is used throughout process creation while the child decides to exit() or exec(). struct vforkinfo is found in proc_private.h.

fork1() switches to newproc(), giving it forktype, proc table slot, and thread with which to create the new process.

The newproc() Routine

When fork1 calls newproc(), things proceed differently depending on whether forktype is FORK_VFORK or FORK_PROCESS.

newproc(FORK_VFORK)

newproc() gets a pointer to the parent process and ascertains that no other forks or exits are occurring at the same time.

  • newproc() verifies the kt_stat (thread state) is TSIDL; if not it panics.

  • newproc() gets a pointer to the parent process and thread.

  • When called by vfork, newproc() allocates the vforkinfo buffer, by calling vfork_buffer_init()

    • vfork_buffer_init() determines the kernel stack size, uarea size, and room for growth, allocates memory, fills in vforkbuf information, saves frame_size, pointer to parent's uarea, and pointer to last buffer.

  • newproc() computes the size of the fork, then acquires the sched_lock. While holding the lock, newproc() updates process statistics.

  • By calling fork_inherit, newproc()performs all direct assignments from the parent to the child proc structure.

    NOTE: POSIX mandates that the child process and thread inherit the scheduling policy and priority from the parent process that forked them.
  • newproc() calls dbfork() subroutine to set up the child's flags to adhere to locking rules (documented in proc_private.h). The child process p_flag is set to SLOAD and the child thread kt_flag is set to TSFIRSTTHREAD.

  • newproc() calls reload_ticksleft() to update the child thread (ct) and mark the parent proc pointer (pp) unswappable by calling make_unswappable(). This is to prevent to parent from being swapped while the environment is copied for the child.

  • newproc() releases the spinlock, determines whether the parent is a graphics process (if so, sets up the child save state), simulates the environment of the new process, prevents the parent from being swapped, then switches to procdup(), providing it forktype and addresses of parent and child process and thread.

newproc(FORK_PROCESS)

If newproc() is called by fork1(FORK_PROCESS), newproc() creates the new child process by calling makechild().

  • newproc() calls make_unswappable() to prevent the parent from being swapped.

  • newproc() switches to procdup(), passing it forktype and addresses of parent and child process and thread.

Figure 1-11 Kernel-level view of the newproc() routine

[Kernel-level view of the newproc() routine]

The procdup() Routine

newproc() calls procdup() when most of the child's structure has been created, passing to it the forktype, parent's proc pointer (pp), child's proc pointer (cp), parent's thread (pt), and child's thread (ct).

As it executes, procdup() does the following:

  • Builds a uarea and address space for the child.

  • Duplicates the parent's virtual address space.

  • Creates a new region for the child's uarea.

  • Attaches the region: PF_NOPAGE keeps vhand from paging the uarea.

  • Marks the pregion to be owned by new child process. The address space is owned by the process.

  • Places the child thread on the run queue by calling setrq(). The child thread is marked SET_RUNRUN and the thread is unlocked.

Figure 1-12 Kernel-level view of procdup() routine

[Kernel-level view of procdup() routine]

vfork State information in struct vforkinfo

To prepare a vfork'd process to run, the vfork_state is maintained in struct vforkinfo. Five states are defined:

  • VFORK_INIT

  • VFORK_PARENT

  • VFORK_CHILDRUN

  • VFORK_CHILDEXIT

  • VFORK_BAD

During the fork1() routine, vfork_buffer_init sets the vfork_state in vforkinfo to VFORK_INIT. When procdup() places the child thread on the run queue by calling setrq(), it also sets the vfork_state to VFORK_PARENT. The parent sleeps and is not awakened until the child exits or execs. At this point the parent and child share the same uarea and stack.

If the process was initiated with fork() rather than vfork(), the spinlock is unlocked and the process is made swappable.

The child process runs using the parent's stack until it does an exec() or exit().