2026
Linux Processes: How Programs Run on the System
Every program running on Linux exists as a numbered process with a state, a parent and a place in the process tree. Here is how to observe them, what their states mean and how signals let you communicate with them from the outside.
Every program running on a Linux system exists as a process. Not an abstract concept, not a file sitting on disk waiting to be read. A real, numbered entry in the kernel's process table with memory allocated to it, file descriptors open and a current state that changes moment to moment as the system does its work.
Processes are the unit of execution on Linux. When you type a command into a shell, the shell forks a child process to run it. When systemd starts nginx, it becomes a process with its own PID. When that process spawns worker threads, those workers are processes too. Understanding what a process is and how to observe it makes almost every other Linux topic easier to reason about.
This is not a deep kernel internals article. It covers the parts you need on a daily basis: how processes are identified, what states they can be in, how signals work and the tools you reach for when something misbehaves.
PIDs and the process tree
Every process has a PID (process ID) which is a unique integer the kernel assigns at creation. The first process to run after the kernel boots is always PID 1. On modern systems that is systemd. Everything else on the machine is a descendant of PID 1.
Each process also records its PPID (parent process ID). Following PPIDs up the tree always leads back to PID 1. This hierarchy matters because signals can propagate through it and because when a parent exits before its children, those children get re-parented to init so they are never left without a parent to collect their exit status.
systemd(1)
├─sshd(612)
│ └─sshd(1891)
│ └─bash(1892)
│ └─pstree(2041)
├─nginx(891)
│ ├─nginx(892)
│ └─nginx(893)
pstree renders this hierarchy visually. The -p flag adds PIDs. When you are trying to figure out which process spawned a runaway child, this view is far more useful than a flat process list.
Process states
A process does not just exist. It is always in one of a handful of defined states. The STAT column in ps output shows a single letter for each process. Knowing what those letters mean saves time when something looks wrong.
R — Running
The process is either actively executing on a CPU or sitting in the run queue waiting for its turn. On a busy system several processes can be in this state at once.
S — Sleeping
The process is waiting for something: a timer, a network response, input from a file. It is not using any CPU. This is the normal state for most processes most of the time.
D — Uninterruptible sleep
Waiting on I/O in a way that cannot be interrupted by a signal. Usually means the process is blocked on a disk read or a kernel operation. A large number of processes in this state often points to a storage problem.
Z — Zombie
The process has finished but its parent has not yet called wait() to collect its exit status. It takes up an entry in the process table but no memory. The parent is responsible for cleaning it up.
T — Stopped
Execution is paused. This happens when you press Ctrl+Z in a terminal or when a process receives SIGSTOP. It can be resumed with SIGCONT.
Zombie processes
A few zombies on a system are normal. A growing number of them points to a bug in the parent process. It is spawning children but never calling wait()to collect their exit status. The fix is in the parent. Killing the zombie itself does nothing because it has already exited.
Viewing processes
Two commands cover most situations. ps gives you a snapshot. top gives you a live view that updates every few seconds.
ps auxAll processes on the system from all users, in BSD-style output with CPU and memory columns.
ps -efAll processes in full-format output. Shows PPID which ps aux omits by default.
ps -u www-dataOnly processes owned by the www-data user. Useful for narrowing down what a service account is running.
ps aux --sort=-%cpuAll processes sorted by CPU usage descending. Find what is consuming the most CPU quickly.
The columns in ps output each tell you something specific. These are the ones worth knowing:
PIDProcess ID. Unique number assigned to the process at creation.
PPIDParent process ID. The PID of the process that spawned this one.
TTYControlling terminal. A question mark means no terminal is attached.
STATCurrent state. The letter codes described in the states section above.
NINice value. Lower numbers mean higher scheduling priority. Ranges from -20 to 19.
%CPUPercentage of CPU time used since the last sample.
%MEMPercentage of physical memory the process is currently using.
top shows the same information but refreshes it live. Press P to sort by CPU, M to sort by memory and k to kill a process by PID without leaving the screen. htop is a more comfortable alternative if it is installed. It adds colour, mouse support and easier filtering.
Signals
Signals are how you communicate with a running process from outside it. They are notifications sent by the kernel, by other processes or by the terminal. Each signal has a number, a name and a default action that applies if the process has not registered a custom handler for it.
The kill command sends signals. Despite the name, it is not just for terminating processes. It sends any signal you ask for to any PID you specify.
SIGHUP (1)Originally meant the terminal connection was lost. Today most daemons treat it as an instruction to reload their configuration without restarting.
SIGINT (2)Interrupt from keyboard. What happens when you press Ctrl+C. Politely asks the process to stop what it is doing.
SIGTERM (15)The default signal sent by kill. Asks the process to terminate gracefully. The process can catch this and clean up before exiting.
SIGKILL (9)Cannot be caught or ignored. The kernel terminates the process immediately with no chance to clean up. Use only when SIGTERM has failed.
SIGSTOP (19)Pause execution. Like Ctrl+Z but cannot be caught or ignored by the process.
SIGCONT (18)Resume a stopped process. Used together with SIGSTOP and job control.
kill 1234Send SIGTERM to PID 1234. The process gets a chance to shut down gracefully.
kill -9 1234Send SIGKILL. No cleanup possible. Reserve this for when SIGTERM has not worked.
kill -HUP 1234Send SIGHUP. Most daemons reload their config file in response to this.
pkill nginxSend SIGTERM to every process whose name matches nginx. Saves you looking up the PID first.
killall -9 nginxSend SIGKILL to all processes named nginx. Similar to pkill with an explicit signal.
Priority and nice values
When several processes compete for CPU time, the kernel scheduler has to decide who goes next. Nice values give you a way to hint at relative priority. The range runs from -20 to 19. Lower values mean higher scheduling priority. The default for a new process is 0.
The name comes from the idea of being nice to other processes by voluntarily yielding CPU time. A process running at nice 19 gets scheduled less aggressively than one at nice 0. A process at nice -20 gets the most aggressive scheduling available. Only root can set negative nice values.
nice -n 10 ./long-job.shStart a new process with a nice value of 10. It will yield to lower-valued processes.
renice -n 5 -p 1234Change the nice value of the already-running process with PID 1234 to 5.
renice -n -5 -p 1234Raise priority. Requires root to set a negative nice value.
A practical use: if you need to run a CPU-intensive job on a production machine without affecting response times for other services, start it with nice -n 19. It will run when spare CPU is available and back off whenever something more important needs it.
The /proc filesystem
Everything ps and top know about a process they read from /proc. This is a virtual filesystem the kernel exposes at runtime. It looks like regular files on disk but nothing is actually stored there. The kernel generates the content on demand when you read it.
For every running process, there is a directory at /proc/[pid]/. Inside it is a collection of files containing everything the kernel knows about that process. You can read them with normal tools.
/proc/[pid]/statusHuman-readable summary of process state, memory usage, UID and signal masks.
/proc/[pid]/cmdlineThe full command that started the process, with arguments, separated by null bytes.
/proc/[pid]/fd/Directory of symbolic links, one per open file descriptor. Count them to find file descriptor leaks.
/proc/[pid]/environEnvironment variables the process was started with, separated by null bytes.
/proc/[pid]/mapsMemory map showing every region of virtual address space the process is using.
cat /proc/1234/status
ls -la /proc/1234/fd | wc -l
# count open file descriptors
tr '\0' ' ' < /proc/1234/cmdline
# read the command line with readable spacing
Foreground and background jobs
When you run a command in a terminal, it runs in the foreground by default. The shell waits for it to finish before giving you another prompt. You can move it to the background so the shell is free while the job keeps running.
long-job &The ampersand starts the command in the background immediately.
Ctrl+ZSuspend the foreground process. It stops running and the shell gets its prompt back.
bgResume the most recently suspended job in the background.
fgBring the most recently backgrounded job back to the foreground.
jobsList all background and suspended jobs in the current shell session.
fg %2Bring job number 2 to the foreground. Job numbers come from the jobs command output.
Background jobs in a terminal are tied to that shell session. If you close the terminal they get a SIGHUP and will usually exit. To run something that survives after you log out, use nohup or run it as a proper systemd service.
Where it fits
Processes are the foundation that every other Linux concept sits on top of. Daemons are processes. Services managed by systemd are processes. Containers are processes with namespace and cgroup boundaries around them. When something on a server misbehaves like consuming too much CPU, leaking memory, refusing to stop, it is a process problem and the tools here are the ones you reach for first.
The workflow that covers most incidents is the same each time: find the process with ps or top, inspect it through /proc if you need more detail, send it a signal to change its behaviour. That loop handles everything from a stuck cron job to a runaway database query eating all available memory.
Process knowledge also underpins what systemd does. When you enable a service and look at the unit file, the ExecStart line is just the command that launches a process. The restart policies, the cgroup accounting, the journal integration, all of it is built around managing that one process. Understanding processes directly means you can work at either level depending on what the situation calls for.