Lecture 03: Process Concepts and Process Control Block

  1. What is a Process? (Defining the Core Unit)
  2. Process States & Transitions (The Lifecycle of Execution)
  3. The Process Control Block (PCB) (The OS’s Ledger)
  4. Context Switching & Queues (Enabling Multitasking)
  5. Real-world Observations & Conclusion

Slide Deck

Key Takeaways


Following up on the topics covered in Lecture 03, this addendum addresses specific questions raised during our class discussion regarding process management, process hierarchies, communication, and kernel-level structures.

1. Process Maintenance and the Process Tree

The OS maintains process information using a data structure called the Process Control Block (PCB). In Linux, this is specifically implemented as a C structure called task_struct.

While we often visualize processes running independently, the OS organizes them strictly as a tree. When the system boots, it creates a root process (traditionally init, or systemd in modern systems, with PID 1). Every subsequent process is created via a fork() system call from an existing process. The task_struct contains specific pointers to maintain this hierarchy: it has a pointer to its parent, a list of its children, and pointers to its siblings.

2. The Necessity of the Parent-Child Relationship

You might wonder why we enforce this tree structure instead of just throwing all processes into a flat list. The parent-child relationship is required for several critical reasons:

3. Orphans, init, and the “Direct Report” Dilemma

Why does an orphan need to be adopted by init? Why can’t it just tell the OS directly that it is done, and let the OS clean it up?

The fundamental design of Unix-like operating systems dictates that a process’s entry in the process table must remain until its exit status is read. The OS cannot simply erase the process because doing so would discard the exit code before anyone checks it. The OS kernel itself is passive; it doesn’t “read” exit codes, it simply stores them in the PCB.

If a parent dies before reading the child’s exit code, the child becomes an orphan. To ensure that the child’s exit code is eventually read (and its PCB memory freed), the OS re-parents the orphan to init. The init process is designed with an infinite loop that constantly calls wait(), acting as a global vacuum cleaner to read exit codes and allow the OS to finally wipe the orphaned PCBs.

4. Eliminating Zombie Processes

A zombie process occurs when a child has terminated, but its parent is still alive and has not yet called wait() to read the exit status. The child is dead, but its PCB still haunts the process table.

Here are the primary ways to eliminate or prevent zombies:

  1. Direct wait() / waitpid(): The parent explicitly calls these functions to reap the child.
  2. Signal Handling: When a child terminates, it sends a SIGCHLD signal to its parent. The parent can implement a signal handler to catch SIGCHLD and call waitpid() asynchronously.
  3. Ignoring SIGCHLD: The parent can explicitly tell the OS it doesn’t care about the exit status by setting the SIGCHLD action to SIG_IGN. The OS will then reap the child immediately upon termination.
  4. Terminating the Parent: If the parent is stuck and won’t reap the zombie, killing the parent turns the zombie into an orphan. It is then adopted by init and instantly reaped.

5. Exit Codes, Sleeping Parents, and SIG_IGN

When a child process terminates, it must communicate its final status back to its parent. This is done via Exit Codes (or Error Codes).

The Purpose and Usage of Exit Codes

An exit code is an 8-bit integer (0-255) returned by a process to the OS upon termination. By universal convention, an exit code of 0 means success, while any non-zero value indicates a specific failure or error condition.

Does a SIGCHLD get lost if the parent is sleeping?

If a parent process is executing a blocking system call like sleep() and a child terminates, the SIGCHLD signal does not get lost.

Instead, the OS marks the signal as “pending” for the parent process. A pending signal will immediately interrupt the parent’s sleep, waking it up to execute the signal handler. However, there is a crucial catch: standard POSIX signals do not queue. If three children terminate at the exact same microsecond while the parent is blocked, the OS might only register a single pending SIGCHLD. This is why robust SIGCHLD handlers use waitpid(-1, &status, WNOHANG) in a while loop to reap all available zombies, rather than assuming one signal equals one dead child.

How wait() relieves the system

When the parent finally calls wait(), it actively requests the OS to perform the final cleanup. The OS reads the exit code, hands it to the parent, destroys the PCB, and releases the process ID (PID) back to the system pool. The zombie is officially eliminated.

The SIG_IGN Strategy

If a parent process does not care whether its children succeed or fail, it can tell the OS to automatically discard their exit codes by setting the signal handler to “Ignore”:

signal(SIGCHLD, SIG_IGN);

6. Standard POSIX Signals and Their Usage

Signals are limited forms of inter-process communication (IPC) used in Unix, Unix-like, and other POSIX-compliant operating systems. They are essentially software interrupts sent to a program to indicate that an important event has occurred.

Here are the most common signals you will encounter and use:

7. Exploring the /proc Filesystem

To actually “see” the OS data structures in action without writing kernel code, Linux provides the /proc filesystem.

/proc is a pseudo-filesystem. It does not exist on your hard drive. Instead, it is generated on-the-fly by the kernel in RAM. It acts as a window into the OS’s process table and internal data structures.

You can read it using standard terminal commands just like normal files:

8. Linux Kernel Source Code References

For those interested in exploring the actual system code, here are direct links to the Torvalds Linux kernel repository on GitHub. These specific files contain the core logic for what we discussed today:

9. Niche and Advanced Learning Resources

If you want to dive deeper into these topics beyond the standard textbook:


Review Questions

  1. Write a simple C program where a parent process deliberately creates a zombie process by sleeping before calling wait(). Use the ps command in a separate terminal to verify the zombie state (look for Z+ or <defunct>).
  2. Modify the program above to use a SIGCHLD signal handler to asynchronously reap the child process without blocking the parent’s execution.
  3. Write a small script or C program that reads /proc/self/status and prints out its own Parent PID.

📎 Attached Resources

SDB Watermark