Back to blog
Linux

Linux Process States, Zombie Processes, Orphan Processes, and Pipes

Introduction

Processes are one of the most important parts of Linux. Every command, program, or background service runs as a process, and Linux keeps track of all of them using different process states.

In this post, I walk through a few core process concepts in Linux. I used htop to view process states, wrote a small C program to create a zombie process, wrote another C program to create an orphan process, then tested process communication using a simple producer-consumer example and a named pipe.

Viewing Process States with htop

A good place to start is by looking at processes in real time. I used htop for that.

sudo apt update
sudo apt install htop

Then start it with:

htop

Inside htop, each process has a state shown in the S column. Two common ones are R for Running and S for Sleeping. A running process is currently using the CPU. A sleeping process is waiting for something, such as input, an event, or system resources.

When I checked htop, I could see examples of both states. The htop process itself appeared as running, while background services such as systemd-journald and dbus-daemon appeared as sleeping. That makes sense because background services often stay idle until something needs their attention.

Creating a Zombie Process

A zombie process is a child process that has finished running, but its parent has not collected its exit status yet. The child is already done, but it still appears in the process table.

To demonstrate this, I created a file called zombie.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t child_pid = fork();

    if (child_pid > 0) {
        sleep(30);
    } else {
        exit(0);
    }

    return 0;
}

fork() creates a child process. The child exits right away with exit(0), while the parent stays alive for 30 seconds using sleep(30). Because the parent does not call wait(), the child stays in the zombie state during that time.

gcc zombie.c -o zombie
./zombie

In another terminal, open htop and look for the process. You should see the child process in state Z, which means zombie. This shows that even though the child has finished, Linux still keeps a small record of it until the parent collects the result.

Creating an Orphan Process

An orphan process happens when the parent process exits before the child process finishes. When that happens, Linux does not leave the child unmanaged. Instead, the child is adopted by init, which usually has PID 1.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid > 0) {
        exit(0);
    } else {
        sleep(30);
        printf("Orphan process done\n");
    }

    return 0;
}

The parent exits immediately while the child keeps running for 30 seconds.

gcc orphan.c -o orphan
./orphan

After running it, check htop. The child process continues running, but now it is adopted by PID 1. This shows how Linux keeps process management organized even after the original parent is gone.

A Simple Producer-Consumer Example

Another important idea in operating systems is the producer-consumer pattern. A producer creates data and a consumer reads it. To simulate this, I used a normal text file called buffer.txt.

In one terminal, I ran a loop that keeps adding timestamped lines into the file:

while true; do
    echo "Message produced at $(date)" >> buffer.txt
    sleep 2
done

In a second terminal, I watched the file for new lines:

tail -f buffer.txt

tail -f keeps watching the file and prints new lines as they are added. This shows the main idea clearly, though it is not a full producer-consumer solution with synchronization. One limitation is that the file keeps growing if the producer writes too much, which in a real system could waste disk space or other resources.

Using a Named Pipe

A more direct way for processes to communicate is with a named pipe, also called a FIFO. Unlike a normal file, a named pipe does not store data for later use. Data is passed directly from the writer to the reader.

mkfifo mypipe

In one terminal, start the reader:

cat < mypipe

In another terminal, write a message:

echo "Hello from Producer" > mypipe

The message appeared immediately in the reading terminal. A normal file stores data on disk, but a named pipe passes data directly between processes. Once the data is read, it is gone. This makes named pipes useful when two processes need to exchange data directly without using files as storage.

Why These Concepts Matter

These examples are simple, but they connect to real operating system behavior. Process states help when checking system activity and troubleshooting performance issues. Zombie processes can indicate that a program is not cleaning up child processes properly. Orphan processes show how Linux safely reassigns children when needed. Producer-consumer patterns appear in many systems that generate and process data, and named pipes are a lightweight communication method that avoids the overhead of regular files.

Conclusion

Working through these examples helped me better understand how Linux handles processes and communication. Using htop made it easy to see process states in real time. The zombie and orphan programs showed what happens when parent and child processes do not end at the same time. The producer-consumer example and named pipe demonstrated two simple ways that processes can pass information.