Skip to content

Process Management

A process is an instance of one or more related tasks (threads) executing on your computer. It is not the same as a program or a command - a single command may actually start several processes simultaneously.

Processes

Key properties of every process:

  • Has a PID (Process ID), PPID (Parent PID), and PGID (Process Group ID)
  • Has its own program code, data, variables, file descriptors, and environment
  • Is isolated in its own user space to protect it from other processes
  • Cannot access hardware directly - must use system calls (the fundamental interface between an application and the kernel)
  • Is preemptively scheduled - only the kernel can preempt a process; processes cannot preempt each other

The maximum PID is 32768 (a 16-bit number). You can raise this limit via /proc/sys/kernel/pid_max for high-load servers. When the system reaches pid_max, PIDs wrap around and start again from 300.


Every process, at any moment, can take a snapshot of itself: its CPU registers, where it is in the program, what is in its memory, open files. This snapshot is the context of the process.

Because processes are constantly scheduled in and out - either sharing CPU time with others, or sleeping while waiting for I/O or user input - the kernel must be able to:

  1. Save the entire context when swapping a process out
  2. Restore the exact context when resuming execution

This mechanism is called context switching and is the core of how multitasking works.


IDDescription
RUID (Real User ID)Identifies the user who started the process
EUID (Effective User ID)Determines actual access rights; may differ from RUID
RGID (Real Group ID)Identifies the group of the process owner
EGID (Effective Group ID)Determines actual group-level access rights

User and Group IDs

Programs marked with the s execute bit run with the user ID of the file owner, not the user who launched them. These are setuid programs.

Example: passwd is a setuid program owned by root. Any user can run it, and the process runs as root to write to /etc/passwd and /etc/shadow. This is intentional - but setuid programs owned by root are a potential security risk and should be audited carefully.


TypeDescriptionExamples
InteractiveStarted by a user (CLI or GUI); needs a terminalbash, firefox, top
BatchAutomated; disconnected from terminal; run in FIFO queueupdatedb, ldconfig
DaemonRuns continuously in background; no controlling terminalhttpd, sshd, libvirtd
ThreadLightweight task under a main process; shares memory with it; can end independentlyfirefox, gnome-terminal-server
Kernel ThreadInternal kernel tasks; users cannot control themkthreadd, migration, ksoftirqd

Daemons are background processes whose sole purpose is to provide services to users or the system:

  • Only operate when needed - efficient by design
  • Many start at boot; others start on demand (xinetd, socket-activated services)
  • Names often end in d: httpd, sshd, crond, systemd-udevd
  • May respond to external events (udevd) or elapsed time (crond)
  • Have no controlling terminal and no standard input/output
  • Kernel-created processes appear with names in square brackets [kworker/0:0] in ps output

StateWhat it means
Running (R)Currently executing on a CPU, or in the run queue ready to execute
Sleeping (S/D)Waiting for an event (I/O, signal, timer); S = interruptible, D = uninterruptible
Stopped (T)Execution suspended - e.g. by CTRL-Z or a debugger (SIGSTOP)
Zombie (Z)Finished execution but parent hasn’t called wait() yet; holds a process table slot but no resources

Process states

The kernel scheduler constantly shifts processes on and off CPUs, sharing time according to priority, how much time a process needs, and how much it has already received.

  • Runnable processes live on the run queue (one per CPU core)
  • Sleeping processes live on the wait queue
  • Zombie processes are cleaned up when the parent calls wait() or when the parent exits

ID TypeDescription
PIDUnique process ID; assigned sequentially at creation
PPIDParent Process ID; if parent dies, orphans are adopted by init (PID 1) or kthreadd (PID 2)
TIDThread ID; same as PID for single-threaded processes; each thread in a multi-threaded process gets a unique TID

PID 1 is always init (or systemd on modern systems). Kernel threads appear early in the PID sequence, wrapped in [brackets] in ps output.


Processes run in one of two CPU modes - enforced by hardware, not software:

Execution modes

ModeAccessIntel Ring
User modeLimited; cannot directly access hardware or other processes’ memoryRing 3
Kernel modeFull access to hardware, memory, all resourcesRing 0
  • Each core has its own independent execution mode
  • A process starts in user mode; when it needs a privileged operation, it makes a system call, which triggers a context switch to kernel mode
  • After the system call completes, execution returns to user mode
  • Application code never runs in kernel mode - only the kernel code of the system call does
  • Kernel mode is also used for hardware interrupts, scheduling routines, and other management tasks

All processes in Linux are created via fork and exec:

  • fork: Creates an exact copy (child) of the current process. The original parent continues running.
  • exec: Replaces the child’s address space with a new program. The child’s PID is preserved.

The combination fork + exec is the standard mechanism for launching programs.

What Happens When You Run a Command in Bash

Section titled “What Happens When You Run a Command in Bash”
  1. A new process is forked from your shell
  2. A wait system call puts the parent shell to sleep
  3. The program is loaded into the child’s space via exec (replacing bash)
  4. The command runs to completion and the child exits
  5. The shell is woken by the child’s death and prints a new prompt

For background commands (append &): the shell skips the wait call and immediately returns a new prompt while the child runs in parallel.

For shell built-ins (echo, cd, kill): neither fork nor exec is issued - they run directly within the shell process.


ulimit is a bash built-in that displays or sets per-process resource limits. Admins use it to:

  • Restrict: prevent a runaway process from consuming all memory, open files, or CPU time
  • Expand: allow a server to handle more than the default 1024 open file descriptors
Terminal window
ulimit -a # show all limits for current shell
ulimit -n 1600 # set max open files to 1600 (current session only)
ulimit -H -n # show hard limit for open files
ulimit -S -n # show soft limit for open files
LimitWho controls itDescription
HardRoot onlyMaximum ceiling a user can raise to
SoftAny userCurrent active limit; can be raised up to hard limit

Changes made with ulimit only affect the current shell session. To persist limits across reboots for all users, edit /etc/security/limits.conf.


When multiple processes compete for CPU time, the scheduler chooses based on priority. Linux uses a nice value (niceness) to express priority:

  • Range: -20 (highest priority) to +19 (lowest priority)
  • Default nice value for new processes: 0
  • Lower nice = process gets more CPU time; it is less “nice” to other processes
  • Higher nice = process yields to others; it is more “polite”

Nice values

Terminal window
nice -n 10 myprog # run myprog at nice value 10 (low priority)
nice -n 19 myprog # run at lowest priority
nice -n -20 myprog # run at highest priority (requires root)
renice +5 -p 20003 # raise nice value of running PID 20003 to 5
renice -5 -p 20003 # lower nice value (requires root)

The load average measures how many processes are competing for CPU over time. It counts:

  • Processes actively executing on a CPU
  • Processes runnable but waiting for CPU time
  • Processes in uninterruptible sleep (typically waiting for I/O)
Terminal window
uptime
# 19:26:28 up 2 days, 12:08, 2 users, load average: 0.45, 0.17, 0.12

The three numbers represent the 1-minute, 5-minute, and 15-minute averages:

ValueMeaning (single CPU)
< 1.0System has spare capacity
= 1.0Fully utilized but not overloaded
> 1.0Overloaded - processes waiting for CPU

For multi-core systems, divide by the number of cores. A load of 4.00 on a 4-core system = 100% utilized.

A spike in the 1-minute average is usually harmless (startup burst). A sustained high value in the 5- and 15-minute averages warrants investigation.

Terminal window
# Check load average
uptime
w
top # shown on the first line

By default, commands run in the foreground - your shell waits for them to finish.

Terminal window
long_command & # run in background, shell returns immediately
jobs # list background jobs in this shell session
jobs -l # also shows PIDs
CTRL-Z # suspend the current foreground job
bg %1 # resume job 1 in the background
fg %1 # bring job 1 back to foreground
fg # bring most recent background job to foreground
CTRL-C # terminate the current foreground job

/proc is a virtual filesystem that exposes kernel data structures as files. Every active process gets a directory named after its PID:

/proc/1234/ - directory for process PID 1234
/proc/1234/status - name, state, memory, UIDs
/proc/1234/cmdline - command + arguments
/proc/1234/fd/ - open file descriptors
/proc/1234/maps - memory map
/proc/self/ - always points to the current process
ToolPurpose
psPoint-in-time snapshot of processes
topLive, auto-refreshing process view
htopEnhanced top with colors, mouse, per-core bars
pstreeProcess hierarchy as a tree
uptimeSystem uptime + load averages
mpstatPer-CPU utilization
iostatCPU + block device I/O statistics
sarCollect and report system activity over time
straceTrace system calls made by a process
numastatNUMA memory allocation statistics

ps takes a snapshot of currently running processes. Two option styles exist from different UNIX lineages:

Terminal window
# System V style (with dashes)
ps -ef # all processes, full detail
ps -u username # processes for one user
ps -eLf # one line per thread
# BSD style (no dashes)
ps aux # all processes, all users
ps axo pid,ppid,stat,comm # custom columns
ps --sort=-%cpu | head # top CPU consumers
ps --sort=-%mem | head # top memory consumers
# Target specific command
ps -C nginx # processes named 'nginx'
Terminal window
pstree # tree of all processes
pstree -p # include PIDs
pstree username # only show processes from a user

Repeated entries are collapsed; threads appear in {curly braces}.

top refreshes every 2 seconds. Press q to quit.

Understanding the top header:

LineContent
Line 1Uptime, logged-in users, load averages (1m/5m/15m)
Line 2Process counts: total, running, sleeping, stopped, zombie
Line 3CPU time split: us (user), sy (kernel), ni (nice), id (idle), wa (I/O wait), hi (hardware IRQ), si (soft IRQ), st (steal)
Line 4RAM: total, free, used, buff/cache
Line 5Swap: total, free, used, available

High wa (I/O wait) with high load = disk bottleneck. High us = CPU-bound application. High sy = kernel overhead.

Process columns:

ColumnMeaning
PIDProcess ID
USEROwner
PRPriority (kernel value)
NINice value
VIRTTotal virtual memory claimed
RESResident (physical) memory used
SHRShared memory
SState (R/S/D/Z/T)
%CPUCPU usage since last refresh
%MEMPhysical memory percentage
TIME+Total CPU time consumed
COMMANDProcess name/command

Interactive keys in top:

KeyAction
kKill a process (prompt for PID and signal)
rRenice a process
fField/column configuration
oChange sort order
ASort by top resource consumer
mToggle memory display
tToggle CPU summary display
1Toggle per-core CPU breakdown
qQuit

Signals are software interrupts sent to processes. The kernel, users, and other processes can send them.

Terminal window
kill -l # list all signals
# Terminate a process
kill PID # sends SIGTERM (15) - polite request to exit
kill -9 PID # sends SIGKILL - cannot be caught or ignored
kill -SIGTERM PID # same as kill PID
# Other useful signals
kill -1 PID # SIGHUP - reload config (many daemons)
kill -2 PID # SIGINT - same as CTRL-C
kill -15 PID # SIGTERM - graceful termination
kill -19 PID # SIGSTOP - pause (same as CTRL-Z, cannot be ignored)
kill -18 PID # SIGCONT - resume stopped process
# By name
killall nginx # send SIGTERM to all processes named 'nginx'
pkill -9 -u username # kill all processes by user