Understanding Linux Processes, Zombie Processes, and Multithreading
This article explains what a Linux process is, how the kernel schedules multiple processes, the nature and dangers of zombie processes, techniques to prevent them, and the fundamentals of multithreading, including thread creation, scheduling, priorities, and synchronization issues.
When a program starts executing, the portion of memory it occupies is called a process. Linux, as a multitasking operating system, can have many processes ready to run, but a single‑CPU machine executes only one instruction at a time within each time slice.
Linux achieves the illusion of simultaneous execution through process scheduling : each process receives a short time quantum (typically milliseconds). The scheduler selects a process to run; when its quantum expires, the process finishes, blocks, or is pre‑empted, and the scheduler picks another process, making it appear that all processes run concurrently.
Every newly created process is assigned a process control block (PCB) that stores essential information such as the process ID (PID). On the common i386 architecture, PIDs range from 0 to 32767, uniquely identifying each process.
Zombie Process Generation
A zombie process is one that has terminated but still occupies an entry in the process table. Although zombies do not consume CPU or memory resources, an excess can fill the process table and cause system instability.
When a child calls exit(), the kernel retains its exit status and resource usage in the process table until the parent reads this information via wait() or waitpid(). If the parent does not handle the SIGCHLD signal or explicitly ignore it, the zombie remains. If the parent exits, the init process adopts the orphaned child and reaps it.
Causes of Zombie Processes
During fork(), the kernel creates a new entry (the “entry point”) in the process table and records the parent’s PID. After the child finishes and calls exit(), its PCB is replaced with exit information that persists until the parent retrieves it, creating the zombie state.
How to Avoid Zombie Processes
Have the parent call wait() or waitpid() to collect the child's status.
Install a SIGCHLD handler with signal() so the parent can reap the child when notified.
Ignore SIGCHLD (e.g., signal(SIGCHLD, SIG_IGN)) to let the kernel automatically clean up the child.
Use the double‑fork technique: the parent forks a child, the child immediately forks a grandchild and exits; the grandchild is adopted by init, which reaps it.
Process vs. Thread
A process represents an instance of a program and owns its own address space. To perform work, a process must contain at least one thread, which executes code within that address space. Multiple threads within the same process share the address space but have separate CPU registers and stacks.
Multithreading Implementation
The first thread created with a process is the primary (or main) thread, generated automatically by the OS. Additional threads can be spawned from this primary thread, and each receives its own time slice (quantum). The OS cycles through threads, giving the impression of parallel execution.
Issues with Multithreading
While multithreading adds flexibility, it can introduce new problems such as data races. For example, printing a document in a separate thread may allow the user to edit the document simultaneously, leading to inconsistent output. Solutions include locking the document during printing, or printing a temporary copy while the original remains editable.
Thread Classification (MFC)
In MFC, threads are classified as worker threads (background computation without UI) and user‑interface threads (which include a message loop for handling UI events).
Thread Priority
Threads have a base priority (0–31). The scheduler prefers higher‑priority threads, allocating CPU time to priority 31 first, then 30, and so on. The OS may also adjust priorities dynamically, e.g., temporarily boosting the thread handling a key‑press.
Thread Synchronization
To avoid data corruption, threads must synchronize access to shared resources. In Win32/MFC, common synchronization objects include critical sections, mutexes, semaphores, and events. Critical sections are simple but work only within a single process. An alternative is the “linearization” method: perform all writes to a particular data structure in a single thread, guaranteeing ordered execution.
Summary
Threads are lighter‑weight execution units that share a process’s address space, allowing fast communication and context switches, while processes provide stronger isolation and resource management. Choosing between processes and threads depends on the need for isolation versus performance, as well as the complexity of synchronization required.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
