Unit 4 - Notes
CSE325
Unit 4: Process Management & Signals
1. Process Creation using fork()
Concept
The fork() system call is the primary mechanism used in Unix-like operating systems to create a new process. When a process calls fork(), it creates a duplicate of itself.
- Parent Process: The process that invokes
fork(). - Child Process: The new process created.
Key Characteristics
- Duplication: The child gets an exact copy of the parent's memory (text, data, heap, and stack segments). However, modern OSs use Copy-on-Write (COW), meaning physical memory is only duplicated when one of the processes modifies the data.
- Concurrency: Both processes continue execution from the instruction immediately following the
fork()call. - Return Values:
fork()returns different values to distinguish between the two processes:- Returns 0 to the Child Process.
- Returns Positive Integer (Child's PID) to the Parent Process.
- Returns -1 if the creation failed.

Code Example: fork()
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Fork failed");
return 1;
} else if (pid == 0) {
// Child process block
printf("I am the Child Process. PID: %d\n", getpid());
} else {
// Parent process block
printf("I am the Parent Process. PID: %d, Child PID: %d\n", getpid(), pid);
}
return 0;
}
2. Program Execution using exec()
Concept
While fork() creates a copy of the calling process, the exec() family of system calls replaces the current process image with a new process image. It loads a new program into the current process's memory space.
Key Characteristics
- No Return: If
exec()is successful, it never returns to the calling function because the original code is overwritten. It only returns-1if an error occurs. - PID Retention: The Process ID (PID) remains the same; only the code, data, heap, and stack are replaced.
The exec Family
The exec function has several variations based on how arguments are passed and whether the PATH environment variable is searched:
execl(path, arg0, ..., NULL): List of arguments.execv(path, argv[]): Vector (array) of arguments.execvp(file, argv[]): Vector of arguments + searches PATH.execle(path, arg0, ..., NULL, envp[]): List of arguments + Environment variables.

Code Example: execvp()
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Before exec system call\n");
// Arguments: Program name, argument, NULL terminator
char *args[] = {"ls", "-l", NULL};
// execvp searches for "ls" in system PATH
execvp(args[0], args);
// This line is only reached if execvp fails
printf("This line will not be printed if execvp is successful\n");
return 0;
}
3. Orphan and Zombie Processes
These are special states of processes resulting from the parent-child relationship.
Zombie Process (Defunct)
- Definition: A process that has completed execution (via
exit()) but still has an entry in the process table. - Cause: The child has died, but the parent has not yet called
wait()to read the child's exit status. - System Impact: Zombies do not consume memory or CPU, but they consume a Process ID (PID). If too many accumulate, the system may run out of PIDs.
- Prevention: The parent must call
wait()orwaitpid().
Orphan Process
- Definition: A child process that is still running after its parent process has terminated.
- Mechanism: When a parent dies, the kernel detects orphan children.
- Adoption: Orphans are immediately adopted by the
initprocess (PID 1) orsystemd. Theinitprocess periodically callswait()to clean up these children when they eventually exit, preventing them from becoming zombies.

Code Example: Creating a Zombie
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
pid_t pid = fork();
if (pid > 0) {
// Parent sleeps, allowing child to exit first
printf("Parent sleeping (Check 'ps -l' for 'Z' state)...\n");
sleep(10);
} else {
// Child exits immediately
printf("Child exiting...\n");
exit(0);
}
return 0;
}
4. Process Synchronization using wait()
Concept
Process synchronization ensures that the parent process can coordinate its execution with the termination of its child. The wait() system call blocks the calling process until one of its child processes exits.
System Calls
- *`pid_t wait(int status)`**:
- Blocks the parent until any child terminates.
- Returns the PID of the terminated child.
- Stores exit status in the integer pointed to by
status.
- *`pid_t waitpid(pid_t pid, int status, int options)`**:
- Waits for a specific child (specified by
pid). - Can be non-blocking using
WNOHANGoption.
- Waits for a specific child (specified by
Status Macros
To interpret the integer status returned by wait, use macros:
WIFEXITED(status): True if child exited normally.WEXITSTATUS(status): Returns the exit code of the child.WIFSIGNALED(status): True if child was killed by a signal.
Code Example: wait()
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("Child running...\n");
sleep(2); // Simulate work
printf("Child finished.\n");
exit(5); // Exit with code 5
} else {
int status;
printf("Parent waiting for child...\n");
pid_t child_pid = wait(&status); // Blocks here
if (WIFEXITED(status)) {
printf("Child %d terminated normally with exit code: %d\n",
child_pid, WEXITSTATUS(status));
}
}
return 0;
}
5. Signals
Concept
Signals are software interrupts sent to a program to indicate that an important event has occurred. They are a form of inter-process communication (IPC) but are asynchronous.
Common Signals
| Signal | Description | Default Action |
|---|---|---|
| SIGINT | Interrupt from keyboard (Ctrl+C) | Terminate |
| SIGQUIT | Quit from keyboard (Ctrl+) | Core Dump |
| SIGKILL | Kill signal (cannot be caught/ignored) | Terminate |
| SIGSTOP | Stop process (cannot be caught) | Pause/Stop |
| SIGCHLD | Child stopped or terminated | Ignore |
| SIGALRM | Timer signal from alarm() |
Terminate |
Signal Handling
A process can react to a signal in three ways:
- Default: Let the kernel handle it (usually kills the process).
- Ignore: Discard the signal (except SIGKILL/SIGSTOP).
- Catch (Handle): Execute a user-defined function when the signal arrives.

Code Example: Catching SIGINT
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
// Signal Handler Function
void handle_sigint(int sig) {
printf("\nCaught signal %d (SIGINT). Not exiting!\n", sig);
}
int main() {
// Register the signal handler
signal(SIGINT, handle_sigint);
printf("Press Ctrl+C. I will trap it inside the loop.\n");
while(1) {
printf("Running... \n");
sleep(1);
}
return 0;
}