Understanding Linux Processes
When you type a command in a terminal, Linux creates a process. When a web server starts handling requests, each worker is a process. When your desktop draws a window, a process is doing that work. Understanding processes — what they are, how they are identified, how they relate to each other, and how to inspect and control them — is the foundation of Linux system administration.
What a process is
A process is an instance of a running program. The program itself is just a file on disk. When you run it, the kernel creates a process: it allocates memory, loads the program into that memory, sets up file descriptors, and schedules execution on the CPU.
Every process has:
- A PID (Process ID): a unique integer assigned by the kernel
- A PPID (Parent Process ID): the PID of the process that created it
- A UID and GID: the user and group the process runs as, determining its permissions
- A working directory: the directory the process sees as current
- File descriptors: open files, sockets, and pipes the process can read or write
- Environment variables: key-value pairs inherited from the parent
- Memory mappings: code, stack, heap, and shared libraries
# See your own process
echo $$ # current shell's PID
# See information about a specific process
cat /proc/$$/status
# See all running processes
ps aux
The process hierarchy
Every process on Linux (except PID 1) has a parent process. When you open a terminal, the terminal emulator is a process. When you type a command, the shell forks a child process to run it. That child may fork its own children. The result is a tree.
# See the full process tree
pstree
# See the tree with PIDs
pstree -p
# See the ancestry of a specific process
pstree -s -p $$
# The same information in ps format
ps --forest -aux
# Show parent-child relationships
ps -eo pid,ppid,comm | head -20
At the root of the tree is PID 1, which on all modern distributions is systemd. All other processes are descendants of PID 1, either directly (services started by systemd) or indirectly (processes spawned by those services or by user sessions).
Process states
The kernel tracks the state of every process at all times. The state determines what the process is currently doing and what can be done to it.
| State | Code | Meaning |
|---|---|---|
| Running | R | Executing on CPU, or ready and waiting for a CPU slot |
| Sleeping | S | Waiting for an event; can be interrupted by signals |
| Disk sleep | D | Waiting for I/O; cannot be interrupted (even by SIGKILL) |
| Zombie | Z | Finished, waiting for parent to collect exit status |
| Stopped | T | Paused by SIGSTOP or a debugger |
| Tracing stop | t | Paused by a debugger (distinguished from T) |
# See process states
ps aux | head -5
# The STAT column shows the state
# A + suffix means it is in the foreground
# An s suffix means it is a session leader
# An l suffix means it is multi-threaded
# Find processes in uninterruptible sleep (D state)
ps aux | awk '$8 == "D"'
# Find zombie processes
ps aux | awk '$8 == "Z"'
D-state processes are the ones that cannot be killed. They are stuck waiting for I/O — usually a disk or network filesystem that is not responding. The only way to recover a D-state process is to fix the underlying I/O problem or reboot.
Foreground, background, and daemons
Processes have three basic modes of relationship to a terminal session:
Foreground processes are attached to a terminal and receive keyboard input. When you run vim or top, it runs in the foreground. The shell waits for it to finish before giving you another prompt.
Background processes are running but not receiving keyboard input. You send a process to the background with & or by pressing Ctrl+Z (which stops it) then typing bg.
Daemons have no controlling terminal at all. They are started at boot and run indefinitely, providing services. sshd, nginx, cron are all daemons.
# Run a command in the background
long-running-command &
# List background jobs in the current shell
jobs
# Bring a background job to the foreground
fg %1
# Send a running foreground process to the background
# (press Ctrl+Z to stop it, then:)
bg %1
# List all background processes, not just in current shell
ps aux | grep -v grep | grep your-command
How the kernel schedules processes
Linux uses a preemptive scheduler called the Completely Fair Scheduler (CFS). Every process gets a share of CPU time proportional to its nice value: a number from -20 (highest priority) to 19 (lowest priority). The default nice value is 0.
Higher-priority processes (lower nice values) get more CPU time. Root can set any nice value; regular users can only increase their nice value (lower priority), not decrease it.
# Start a command with a specific nice value
nice -n 10 ./cpu-intensive-task
# Change the nice value of a running process
renice -n 15 -p 12345
# Start a very low priority background job
nice -n 19 ./backup-script &
# See nice values of running processes
ps -eo pid,ni,comm | sort -k2 -n | head -20
# Real-time priority (root only, bypasses CFS)
chrt -f 99 ./realtime-task
Process limits
Each process has resource limits enforced by the kernel:
# See limits for the current shell
ulimit -a
# Common limits
ulimit -n # max open file descriptors
ulimit -u # max processes per user
ulimit -v # max virtual memory
ulimit -s # max stack size
# Increase file descriptor limit for the current session
ulimit -n 65536
# Set limits permanently in /etc/security/limits.conf
# username hard nofile 65536
# username soft nofile 65536
# See limits for a running process
cat /proc/12345/limits
Signals
Processes communicate with each other and with the kernel through signals. A signal is an asynchronous notification sent to a process. The most common ones:
| Signal | Number | Default action | Use |
|---|---|---|---|
| SIGHUP | 1 | Terminate | Reload config (many daemons) |
| SIGINT | 2 | Terminate | Ctrl+C |
| SIGQUIT | 3 | Core dump | Ctrl+\ |
| SIGKILL | 9 | Terminate (cannot be caught) | Force kill |
| SIGTERM | 15 | Terminate | Graceful shutdown |
| SIGSTOP | 19 | Stop (cannot be caught) | Pause process |
| SIGCONT | 18 | Continue | Resume stopped process |
# Send SIGTERM to a process
kill 12345 # default is SIGTERM
kill -15 12345 # explicit SIGTERM
kill -TERM 12345 # by name
# Force kill
kill -9 12345
kill -KILL 12345
# Reload config (for daemons that support it)
kill -HUP 12345
kill -1 12345
# Pause and resume
kill -STOP 12345
kill -CONT 12345
The /proc filesystem
Every process has a directory under /proc named after its PID. This virtual filesystem exposes everything the kernel knows about the process:
# Explore a process's /proc directory
ls /proc/$$
# Key files
cat /proc/$$/cmdline # command that started this process
cat /proc/$$/status # state, memory, UIDs, GIDs
cat /proc/$$/environ # environment variables (null-separated)
cat /proc/$$/fd/ # open file descriptors (symlinks)
cat /proc/$$/maps # memory mappings
cat /proc/$$/net/tcp # open TCP connections
# Read environment variables in a readable format
tr '\0' '\n' < /proc/$$/environ
# See open files for a process
ls -la /proc/12345/fd
# Using lsof for the same purpose
lsof -p 12345
Practical process inspection
# Find a process by name
pgrep nginx
pgrep -a nginx # with full command line
pgrep -u alice # processes owned by alice
# Find and kill by name in one step
pkill nginx # SIGTERM to all processes named nginx
pkill -9 nginx # SIGKILL
# Find which process is using a port
ss -tlnp | grep :80
lsof -i :80
# Find which process is using a file
lsof /var/log/nginx/access.log
fuser /var/log/nginx/access.log
# See how long a process has been running
ps -p 12345 -o pid,etime,comm
# See all processes sorted by CPU
ps aux --sort=-%cpu | head -10
# See all processes sorted by memory
ps aux --sort=-%mem | head -10
Processes are the runtime unit of Linux. Every piece of work the system does happens inside one. Once the process model is clear — PIDs, parent-child trees, states, signals, and the /proc interface — reading system behaviour from tools like ps, top, and htop becomes straightforward rather than opaque.
Frequently Asked Questions
What is a process in Linux?
A process is a running instance of a program. Every command you execute, every service running in the background, and every system daemon is a process. Each process has a unique Process ID (PID), its own memory space, a set of open file descriptors, and an associated user and group that determine its permissions. The kernel tracks and schedules all processes.
What is the difference between a process and a thread?
A process is an independent program with its own memory space, file descriptors, and PID. A thread is a unit of execution within a process. Multiple threads share the same memory space and file descriptors but have their own stack and program counter. On Linux, threads are sometimes called lightweight processes (LWPs) because the kernel implements them similarly to processes. A multi-threaded program uses multiple threads within a single process to parallelize work without the overhead of inter-process communication.
What does the process state mean in Linux?
Linux processes can be in several states: R (Running or Runnable) means the process is executing on a CPU or waiting for a CPU slot. S (Sleeping/Interruptible Sleep) means it is waiting for an event such as I/O to complete and can be woken by a signal. D (Uninterruptible Sleep) means it is waiting for I/O and cannot be interrupted, even by signals — this is what causes unkillable processes. Z (Zombie) means the process has exited but its parent has not collected its exit status. T (Stopped) means it has been paused by a signal like SIGSTOP.
What is PID 1 on Linux?
PID 1 is the first process started by the kernel after boot, traditionally called init. On modern Linux distributions, PID 1 is systemd. It is the parent of all other processes on the system. PID 1 is special: the kernel does not apply the default signal handlers to it, so signals like SIGTERM do not kill it unless it explicitly handles them. If PID 1 exits, the kernel panics.
What is a zombie process?
A zombie process is a process that has finished executing but whose entry remains in the process table because its parent process has not yet called wait() to collect its exit status. Zombie processes consume no CPU or memory — they are just a process table entry. They disappear when the parent calls wait(). If the parent process exits without collecting zombie children, the zombies are reparented to PID 1 (systemd), which collects them. A large number of zombies usually indicates a bug in the parent process.