Processes II and Process Management
Process Creation and the Tree Hierarchy
A parent process creates child processes, which can in turn create other processes, resulting in a hierarchical tree of processes.
Processes throughout the system are identified and managed via a unique Process Identifier known as a .
When a parent process creates a child, there are two primary options for execution:
The parent and children execute concurrently.
The parent waits until some or all of its children have terminated before continuing.
The fork() System Call Mechanics
The primary mechanism for creating a new process in Unix-like systems is the
fork()system call.Execution Process:
fork()creates a child process by making an exact duplicate of the parent process. Post-fork, both processes begin executing concurrently from the next instruction.Unique Nature: The
fork()call is unique because it is called once but returns twice:Return to Parent: Returns the unique of the newly created child process.
Return to Child: Returns a value of .
Return on Failure: Returns if the process creation was unsuccessful.
Parent-Child Relationship: The process that initiates the call becomes the parent, while the new process becomes the child.
The Child process inherits both the User-level context and the Process context block from the parent process.
Memory Isolation: The child process inherits a copy of the parent's memory space, but they do not share the same memory. Each executes in its own independent environment.
Strategic Uses of fork()
Dual Code Execution: A process may duplicate itself so that the child and parent can execute different sections of code simultaneously.
Example: Network servers. The parent process waits for a service request from a client. When a request arrives, the parent calls
fork(), allowing the child to handle that specific request while the parent returns to waiting for the next client.
Program Execution: A process may want to execute a completely different program.
Example: Shells. In this scenario, the child process typically calls
exec()(or a variant) immediately after thefork()returns to replace its process image with a new program.exec() creates another piece of code for the child to execute basically used to execute a different command.
System Limits and fork() Failures
There are two primary reasons why a
fork()call might fail:The system has already reached the maximum allowable number of processes (indicating systemic resource exhaustion).
The total number of processes for the specific real User ID () has exceeded the system-defined limit.
System Configuration: The command
cat /proc/sys/kernel/pid_maxcan be used to view the maximum number of unique s the system supports.
Practice Task I: Process Creation and Command Execution
Objective: Demonstrate
fork(),exec(), andwait()for synchronization.Task Requirements:
Create a parent that spawns two child processes.
Child 1: Use
getpid()/getppid()to print IDs, then useexeclp()to run thedatecommand.Child 2: Use
getpid()/getppid()to print IDs, then useexeclp()to run thewhoamicommand.Parent: Use
wait()orwaitpid()to wait for both children, then print a confirmation message after each terminates.Use
_exit()in children afterexec()to prevent flushing parent buffers.
Implementation Details:
In the solution,
pid1 = fork()andpid2 = fork()are used to generate the IDs.Error handling is managed via
perror()andexit(1).The parent utilizes a loop:
for (int i = 0; i < 2; i++) { terminated_pid = wait(&status); }to synchronize.
Process Synchronization and the wait() Family
Importance of Synchronization:
Prevents data inconsistency between concurrent processes.
Prevents system deadlocks.
Prevents race conditions (errors occurring when multiple operations execute simultaneously. For example if two processes are accessing the same data while the first process changes the shared data but the second process is accessing the older version of the data.)
Program Execution Flow:
A program is loaded from the hard disk into an address space in main memory.
It is scheduled onto a CPU.
The CPU executes instructions sequentially, tracked by a Program Counter ().
wait() and waitpid() Functionality:
These calls force the parent process to suspend execution until a child process completes.
wait(): Suspends the parent until the first of its children terminates.waitpid(): Suspends the parent until a specific child process (identified by ) terminates.Result: On success, they return the of the terminated child. On error (e.g., if no child exists), they return .
statloc Argument:
pid_t wait(int *statloc)If
statlocisNULL(i.e.,(int *)0), the status is ignored.If a pointer to an integer is provided, it is bound to the child's status information.
Process States and Suspension (The 7-State Model)
Basic States: Ready, Running, and Blocked.
Process Suspension: If processes in main memory enter a blocked state, the OS may move one process to a "Suspended" state on the disk to free memory for other processes.
While the CPU is in this suspended state the OS preforms a context switch
Extended States:
Blocked_Suspend: A process that is blocked and resides on the disk.
Ready_Suspend: A process that is ready to run but is currently on the disk.
Transition: When a Suspended process becomes ready to run, it moves into the Ready_Suspend queue.
Process Termination Dynamics
Methods of Termination:
Executing the last statement and requesting deletion.
Calling
exit()from the<stdlib.h>library.A parent using
abort()to kill a child (common if the child exceeds resources, the task is no longer needed, or the parent is exiting).
Cascading Termination: Some OS architectures do not allow a child to continue if the parent has terminated.
Signaling: When a child process terminates, a
SIGCHLDsignal is sent to the parent process.
Zombies and Orphan Processes
Orphan Process: A child process whose parent has terminated while the child is still executing. Orphans are adopted by the system's
initprocess ().Zombie Process:
Occurs when a child terminates but its parent has not yet executed a
wait()call to query its exit status.A zombie is not an active process but maintains an entry in the system process table.
Resolution: When a parent terminates, both orphans and zombies are adopted by the
initprocess.
Practice Task II: Exit Status and sleep()
Objective: Handle a child process with a specific exit status and parent waiting.
Task Requirements:
Child: Print a message,
sleep(2)for two seconds, andexit(42).Parent: Use
wait(&status)and extract the status using the macroWEXITSTATUS(status)after checkingWIFEXITED(status).
Result: The output should confirm "Parent: Child finished with status ".
Process Identification and Groups
Process ID Identification Functions:
getpid(): Returns the process's own .getppid(): Returns the of the parent.getuid(): Returns the real User ID of the process.
Process Groups:
A logical cluster of processes belonging to the same application or function.
Creation: A child inherits its group from its parent.
Management: Use
setpgid()to change groups,setpgrp()to set a process's group to itself, andgetpgrp()orgetpgid()to retrieve IDs.Signal Distribution: Signals directed to a process group are delivered to every member. This is the basis for shell job control (e.g., Ctrl-C terminal signals).
Sessions: A collection of one or more process groups.
Process Data and File Descriptor Inheritance
Variable Copying: Since the child is a copy, it has its own instance of the parent's data. Changes to a variable in the child (e.g.,
glbvar++) do not affect the parent's variables.File Descriptors:
Both inherit the same open file descriptors.
Variables are passed by value (a copy is made).
The Read-Write () pointer for a file is maintained by the system and passed by reference.
shared Pointer Behavior: Because the pointer is shared, a
read()orwrite()in the child directly changes the current position in the file for the parent.
lseek() Function:
Repositions the file offset.
lseek(fd, 0L, SEEK_CUR)sets the offset to its current location plus bytes (essentially querying the current position).Returns the resulting offset location in bytes from the beginning of the file.
Summary of Termination and Cleanup
When a process terminates:
All open file descriptors are closed (though buffered streams like
stdoutare not automatically flushed).The process exit status is saved (low-order bits as the program status) to be reported via
wait.Children are assigned to the
initprocess ().A
SIGCHLDis sent to the parent.If the termination causes a process group to become orphaned and a member is stopped, a
SIGHUPandSIGCONTare sent to each process in that group.