Processes#
A process is the operator’s smallest unit of observation on a live
host. Every running program carries a PID, a parent, an owning user,
an environment, and a set of open file descriptors, and the kernel
exposes all of it under /proc/<pid>/ for anyone with the right to
read it. Reading processes is how the operator answers “what is this
box doing right now,” whether the goal is hunting an implant, tracing
a misbehaving service, or surveying the attack surface before moving.
The day-to-day toolkit is short: ps, top / htop, pgrep,
pstree, kill, strace, lsof. On-disk state files at the
bottom of the page are what to reach for when the live tools run out.
Every process is a kernel-tracked record. Each row below is a slice of
that record, and each one has a file under /proc/<pid>/ an
operator can read directly.
Field |
What it is |
|---|---|
PID |
Numeric process id; unique while the process is alive |
PPID |
Parent’s PID; PID 1 ( |
UID / GID |
Owning user / group; real, effective, saved triples |
Command |
|
Executable |
On-disk binary at |
CWD |
Current working directory at |
Environment |
Inherited env vars at |
File descs. |
Open files / sockets / pipes at |
Memory |
VSZ / RSS / mappings at |
State |
R / S / D / Z / T (run, sleep, uninterruptible, zombie, stopped) |
Threads |
Per-thread tasks at |
Namespaces |
Isolation handles at |
Cgroup |
Resource limits at |
Capabilities |
Fine-grained privileges at |
Inspecting#
ps is the snapshot tool, top / htop are the live views,
pgrep finds by name, and pstree shows the parent-child
relationships. ps aux is the most-typed incantation in Linux
admin; memorize it.
Command |
Effect |
|---|---|
|
BSD-style snapshot of every process |
|
SysV-style snapshot (more detail) |
|
Custom columns + sort |
|
PIDs matching a name + full command line |
|
PID(s) for an exact program name |
|
Live, full-screen view (built-in) |
|
Friendlier |
|
Process tree with PIDs |
ps aux lists every process on the system with USER, PID, %CPU,
%MEM, VSZ, RSS, TTY, STAT, START, TIME, COMMAND:
$ ps aux | head
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 168196 13312 ? Ss Apr28 0:14 /sbin/init
root 412 0.0 0.0 10000 4608 ? Ss Apr28 0:00 /lib/systemd/systemd-journald
root 1023 0.0 0.0 72420 6400 ? Ss Apr28 0:00 /usr/sbin/sshd -D
operator 3091 0.2 0.4 720180 71040 pts/0 Sl 09:14 0:03 /usr/bin/python3 worker.py
-o picks columns; --sort orders the output. Top memory hogs
in one line:
$ ps -o pid,user,%mem,rss,cmd --sort=-%mem | head -5
PID USER %MEM RSS CMD
3091 operator 0.4 71040 /usr/bin/python3 worker.py
1023 root 0.0 6400 /usr/sbin/sshd -D
pgrep -a prints PID + full command line for everything matching
a regex; -f matches against the full command, not just the name:
$ pgrep -af nginx
2811 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
2812 nginx: worker process
htop is a full-screen, scrollable, color view; F5 toggles
the tree, F9 sends a signal:
$ htop
pstree -p shows parent-child links with PIDs in parentheses,
useful when one binary forks many children:
$ pstree -p $(pgrep -o nginx)
nginx(2811)─┬─nginx(2812)
├─nginx(2813)
└─nginx(2814)
Starting and Stopping#
A foreground command holds the terminal until it finishes; &
backgrounds it; nohup and disown keep it alive past logout.
kill sends a signal (SIGTERM by default), pkill /
killall send by name.
Command |
Effect |
|---|---|
|
Run in the foreground |
|
Run in the background |
|
Background + survive logout (logs to |
|
Detach a backgrounded job from the shell |
|
List the shell’s background jobs |
|
Foreground / background job |
|
Send |
|
Send |
|
Kill every process matching the regex |
|
Kill by exact program name |
Ctrl-Z suspends a foreground job; bg resumes it in the
background; fg brings it back:
$ ./long-running-task
^Z
[1]+ Stopped ./long-running-task
$ bg %1
$ jobs
[1]+ Running ./long-running-task &
nohup redirects output to nohup.out so the process survives
SIGHUP when the terminal closes; disown then removes the
shell’s record so it isn’t killed at logout:
$ nohup ./worker.py > worker.log 2>&1 &
$ disown %1
kill PID defaults to SIGTERM (graceful and trappable).
kill -9 is the nuclear option:
$ kill 3091
$ kill -9 3091
pkill matches by command line with -f; very useful when the
binary is wrapped:
$ pkill -f 'python3 worker.py'
Signals#
Signals are the Unix IPC mechanism for “hey, do something.” The kernel
delivers a number to a process, and the process either runs a handler,
takes the default action (often exit or core dump), or ignores it. A
few signals matter day-to-day; the rest are application-defined.
Signal numbers and default actions are POSIX-defined (POSIX.1
<signal.h>), so the same names and behaviors hold across Linux,
BSD, and macOS, with Linux extending the real-time range
SIGRTMIN..``SIGRTMAX``.
Signal |
Meaning |
|---|---|
|
Graceful termination; default for |
|
Forced termination; cannot be caught or ignored |
|
Interrupt ( |
|
Terminal hangup; many daemons reload config |
|
Pause / resume ( |
|
Terminal stop from |
|
A child changed state; supervisors must |
|
Wrote to a pipe with no reader (common in shell pipelines) |
|
|
|
Application-defined (e.g. |
|
Segmentation fault; invalid memory access |
|
|
|
Terminal window-size change (tmux, less, readline) |
|
|
|
Bus error (misaligned access, |
|
Arithmetic exception (divide by zero, etc.) |
|
Illegal instruction |
|
Bad system call (where seccomp violations land) |
|
Background job tried to read / write the controlling tty |
|
Real-time signals (34 to 64); queued, app-defined |
kill -HUP is the conventional “reload your config” signal for
daemons (nginx, sshd, rsyslog, …):
$ kill -HUP $(pgrep -o nginx)
kill -l lists all signals the kernel knows:
$ kill -l | head -3
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
Resource Limits#
Every process inherits rlimits, soft caps on file
descriptors, address space, CPU time, core dumps, etc. Read with
ulimit, set in /etc/security/limits.conf for login
sessions or in a systemd unit’s LimitNOFILE= for a service.
Command / file |
Effect |
|---|---|
|
Show this shell’s effective limits |
|
Raise open-file limit (this shell) |
|
Allow core dumps |
|
Persistent per-user / per-group limits |
|
Defaults for systemd-managed services |
|
Per-unit override in a |
ulimit -a shows everything the current shell will pass to
its children:
$ ulimit -a
open files (-n) 1024
max user processes (-u) 31621
virtual memory (kbytes, -v) unlimited
core file size (blocks, -c) 0
A common production change: bump the per-process FD limit so a
high-traffic daemon doesn’t EMFILE:
$ ulimit -n 65535
$ ulimit -n
65535
Tracing and Profiling#
Trace what a process is doing without source code or a debugger
attached. strace shows syscalls, ltrace shows library calls,
perf profiles CPU usage and hot paths, and lsof lists every
file, socket, and pipe the process has open.
Command |
Effect |
|---|---|
|
Attach and trace syscalls |
|
Filter to a single syscall |
|
Trace child processes; log to file |
|
Trace library calls |
|
Live CPU profiler |
|
Sample then analyze |
|
Files opened by |
|
Process listening on a TCP / UDP port |
strace attached to a running process; Ctrl-C to detach:
$ sudo strace -p 2811
strace: Process 2811 attached
epoll_wait(8, [{events=EPOLLIN, data={u32=12, u64=12}}], 512, -1) = 1
accept4(12, {sa_family=AF_INET, sin_port=htons(54221), sin_addr=inet_addr("10.0.0.5")}, [16], SOCK_NONBLOCK) = 14
strace -e filters to one syscall family, great for finding
which file a program tried to open:
$ strace -e openat ls / 2>&1 | tail -5
lsof -i :PORT is the “who’s holding port X” query:
$ sudo lsof -i :443
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 2811 root 7u IPv4 18723 0t0 TCP *:https (LISTEN)
nginx 2812 nginx 7u IPv4 18723 0t0 TCP *:https (LISTEN)
On-disk state#
Process state lives in the kernel’s virtual filesystems (/proc
and /sys) and a small set of configuration files. The operator
reads these directly when the live tools aren’t enough, usually to
dig into limits, mappings, or scheduling details that ps and
top flatten out.
Per-process state (/proc/<pid>/)#
/proc/<pid>/cmdline, launch command (NUL-separated)./proc/<pid>/comm, short process name./proc/<pid>/status, human-readable summary (state, threads, uids, capabilities, memory)./proc/<pid>/stat/statm, machine-readable variants./proc/<pid>/environ, environment variables (NUL-separated)./proc/<pid>/exe, symlink to the executable./proc/<pid>/cwd, symlink to current working directory./proc/<pid>/root, symlink to its root directory (chroot-aware)./proc/<pid>/fd/, one symlink per open file descriptor./proc/<pid>/fdinfo/, per-fd metadata (offset, flags)./proc/<pid>/maps, memory map (binaries, shared libs, heap, stack)./proc/<pid>/smaps, per-mapping memory stats (PSS, RSS)./proc/<pid>/limits, effective rlimits./proc/<pid>/ns/, namespaces (mnt, pid, net, user, …)./proc/<pid>/cgroup, cgroup memberships./proc/<pid>/oom_score/oom_adj, OOM-killer scoring./proc/<pid>/io, read / write byte counts./proc/<pid>/mountinfo, the process’s view of mounts./proc/<pid>/sched, scheduler statistics.
Self-references#
/proc/self/, “this process”; the kernel resolves it per-reader./proc/thread-self/, “this thread”.
Global process / kernel state#
/proc/cpuinfo, CPU topology and flags./proc/meminfo, memory statistics./proc/loadavg, 1 / 5 / 15-minute load average + last PID./proc/uptime, seconds since boot, idle time./proc/stat, CPU time, context switches, fork count./proc/swaps, active swap areas./proc/sysrq-trigger, magic SysRq.
Tunables#
/proc/sys/kernel/, kernel parameters (e.g.pid_max,threads-max,core_pattern)./proc/sys/fs/, filesystem-related (file-max,inotify/max_user_watches)./proc/sys/vm/, memory tuning (swappiness,overcommit_memory)./etc/sysctl.conf//etc/sysctl.d/*.conf, persisted tunings.
Cgroups (resource control)#
/sys/fs/cgroup/, cgroup v2 unified hierarchy (modern systems)./sys/fs/cgroup/<slice>/<unit>/, per-service controllers./proc/cgroups, enabled controllers.
PID files and runtime#
/run/, runtime state, cleared at boot./run/<service>.pid, conventional PID file location./run/user/<uid>/, per-user runtime (XDG_RUNTIME_DIR).
Useful peeks#
$$ is the current shell’s PID; the next two commands show this
shell’s status and the executable behind it:
$ cat /proc/$$/status | head -5
$ readlink /proc/$$/exe
Name: bash
Umask: 0022
State: S (sleeping)
Tgid: 3091
Ngid: 0
/usr/bin/bash
cmdline and environ use NUL separators, so pipe through
tr to make them readable:
$ cat /proc/<pid>/cmdline | tr '\0' ' '
$ cat /proc/<pid>/environ | tr '\0' '\n'
pmap summarises /proc/<pid>/maps in one line per mapping:
$ pmap $(pgrep -o nginx) | tail
Common Tasks#
Inventory. Snapshot every running process and the user it runs as.
$ ps -eo pid,user,ppid,cmd --sort=user
$ ps auxf
$ pstree -p
Hunt anomalies. Find processes that should not be there.
$ ls -l /proc/*/exe 2>/dev/null | grep deleted
$ ls -l /proc/*/exe 2>/dev/null | grep -E '/tmp|/dev/shm'
$ ps -eo pid,user,cmd | awk '$2=="root"'
Trace a suspect. Watch what a single process is doing now.
$ strace -f -p <pid>
$ lsof -p <pid>
$ cat /proc/<pid>/environ | tr '\0' '\n'
$ cat /proc/<pid>/maps | head
Pivot via parent / child. A surprise parent is a finding.
$ cat /proc/<pid>/status | grep -E 'PPid|Uid|Gid'
$ pgrep -af <name> | xargs -I{} echo {}
$ ps -o pid,ppid,user,cmd --ppid <ppid>
Check resource limits. Raised limits are unusual.
$ cat /proc/<pid>/limits
$ prlimit --pid <pid>
$ ulimit -a
Control without killing. Pause for inspection, resume cleanly.
$ kill -STOP <pid>
$ kill -CONT <pid>
$ renice +10 -p <pid>
$ ionice -c 3 -p <pid>
Find the resource hog (CPU, RAM, I/O).
$ ps -eo pid,user,%cpu,%mem,cmd --sort=-%cpu | head
$ ps -eo pid,user,%mem,rss,cmd --sort=-%mem | head
$ top -b -n1 | head -20
$ sudo iotop -boqq -n 3 2>/dev/null
Find the process bound to a port or file. Who has it open.
$ sudo ss -tulnp | grep :443
$ sudo lsof -iTCP:443 -sTCP:LISTEN -P -n
$ sudo lsof /var/log/syslog
$ sudo fuser -v /var/log/syslog
Kill cleanly, then forcefully. Escalate signals.
$ kill <pid> # SIGTERM, give it a chance
$ kill -INT <pid> # like Ctrl-C
$ kill -HUP <pid> # reload config
$ kill -KILL <pid> # last resort
$ pkill -f 'pattern'; killall -9 nginx
Run a long job that survives logout. Background, detach, schedule.
$ nohup ./job.sh >run.log 2>&1 &
$ disown -h %1
$ setsid -f ./job.sh
$ at now + 10 minutes <<<'/usr/local/bin/job.sh'
Inspect open files and sockets. A process’s footprint.
$ ls -l /proc/<pid>/fd/ | head
$ lsof -p <pid>
$ ss -tnp | grep <pid>
References#
man 7 signal(complete signal list).man 5 proc(standard reference for/proc).Services for systemd as the supervisor of long-running processes.
Permissions for users, capabilities, sudo for what a process can do.