Linux Admin

Linux Process Management: ps, top, kill, jobs, cron, nice

Part of pathway: Linux Mastery: 300 Commands

Every Process Has a PID and a Parent

Linux organizes every running program as a process with a unique numeric PID and a parent PID, forming a tree that starts at PID 1 (systemd on modern systems). Knowing how to inspect that tree, signal individual processes, schedule recurring jobs, and tune priority is fundamental sysadmin work.

This article is a working reference for Linux process management: viewing processes, sending signals, foreground/background control, scheduling with cron and at, and the priority knobs (nice, renice, ionice).

Viewing Processes — ps

ps                                       # your processes in this terminal
ps aux                                   # ALL processes (BSD-style)
ps -ef                                   # ALL processes (System V-style)
ps -eLf                                  # threads visible
ps aux | grep nginx                      # filter for nginx processes
ps -p 1234                               # specific PID
ps -ppid 1234                            # children of PID 1234
ps --sort=-%mem | head                   # top memory consumers
ps --sort=-%cpu | head                   # top CPU consumers

Two flavors of ps survive: BSD (ps aux) and System V (ps -ef). Either works on Linux. The output looks slightly different but conveys the same data.

top, htop, btop

top                                      # default interactive viewer
htop                                     # nicer UI, mouse support (install separately)
btop                                     # the prettiest, modern Rust-based

Inside top:

  • P — sort by CPU
  • M — sort by memory
  • k — kill a process (prompts for PID)
  • r — renice a process
  • 1 — show per-CPU breakdown
  • q — quit

Process Trees — pstree

pstree                                   # full tree
pstree -p                                # with PIDs
pstree alice                             # only alice's processes
pstree 1234                              # tree starting from PID 1234

Useful when debugging “who spawned this orphan?” questions.

Signals — kill Doesn’t Just Kill

The kill command sends signals to processes. The signal you send determines what happens.

Signal Number Default action
SIGHUP 1 Reload config (in well-behaved daemons)
SIGINT 2 Interrupt (what Ctrl-C sends)
SIGQUIT 3 Quit + core dump
SIGTERM 15 Polite termination — default for kill
SIGKILL 9 Forceful kill — cannot be caught or ignored
SIGSTOP 19 Pause (cannot be caught)
SIGCONT 18 Resume from stop
kill 1234                                # send SIGTERM (default)
kill -15 1234                            # explicit SIGTERM
kill -9 1234                             # SIGKILL (force)
kill -HUP 1234                           # send SIGHUP (config reload)
kill -l                                  # list all signal names
killall nginx                            # by name (every match)
pkill -f "python.*serve.py"              # by command-line regex

Try SIGTERM first, give it a few seconds, then SIGKILL only if needed. SIGKILL doesn’t let the process clean up its file handles, network sockets, or temp files. It’s the right tool for hung processes, the wrong tool for the first attempt.

Foreground, Background, and Job Control

command &                                # run in background
jobs                                     # list background jobs
fg                                       # bring most-recent to foreground
fg %2                                    # bring job 2 to foreground
bg %2                                    # resume job 2 in background
Ctrl-Z                                   # suspend the foreground job
disown %1                                # detach from shell so it survives logout
nohup command &                          # immune to hangup signal at logout
setsid command                           # run in a new session

For long-running commands you want to survive logout, the modern answer is tmux or screen — persistent terminal sessions you can detach from and reattach to. Use tmux new -s work, then Ctrl-b d to detach, tmux attach -t work to come back.

Scheduling — cron and at

cron — recurring jobs

crontab -e                               # edit your cron file
crontab -l                               # list it
crontab -r                               # delete it (be careful!)

# Cron syntax: minute hour day-of-month month day-of-week command
# 0 3 * * *      run at 3 AM every day
# */5 * * * *    every 5 minutes
# 0 9-17 * * 1-5 every hour 9-17, weekdays only

0 2 * * * /usr/local/bin/backup.sh
*/15 * * * * /usr/bin/check-health.sh

Always use full paths in cron. The cron environment is minimal — $PATH is short, no shell aliases, often no $HOME. Test your cron command exactly as cron will: env -i /bin/sh -c '/your/command'.

at — one-shot future

echo "backup-now.sh" | at 03:00 tomorrow
atq                                      # list scheduled at jobs
atrm 5                                   # delete at job 5

systemd timers — modern alternative

systemctl list-timers
systemctl status backup.timer

Timer units are configured as pairs (backup.service + backup.timer). They survive reboots cleanly, log to journald, and integrate with the rest of systemd. The right choice for new infrastructure on systemd-based systems.

Priority — nice, renice, ionice

nice -n 10 ./big-job.sh                  # lower CPU priority (range -20 to +19)
renice -n 5 -p 1234                      # change priority of running PID
ionice -c 3 ./big-io.sh                  # idle I/O class
nice -n -5 ./important.sh                # raise priority (root only for negative)

Niceness is counter-intuitive: higher number = nicer = lower priority. Default is 0; range is -20 (highest) to +19 (lowest). Only root can lower the niceness (raise priority).

Common Pitfalls

  • SIGKILL as the first reach. Try SIGTERM first; processes that catch SIGTERM clean up properly. SIGKILL is the kick-down-the-door option.
  • Cron silently failing. Cron emails errors by default but if mail isn’t configured, errors disappear. Always redirect: 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  • $PATH in cron. The cron environment doesn’t have your shell’s PATH. Use absolute paths or set PATH=... at the top of the crontab.
  • nohup doesn’t imply background. nohup handles the SIGHUP signal at logout, but you still need & to background the process.
  • Killing a parent doesn’t kill children. They get re-parented to PID 1 (init/systemd). To kill a process group: kill -- -PGID (note the leading negative).
  • killall on macOS vs Linux. Different semantics across platforms. On Linux, killall name kills all processes matching name; on Solaris, it killed all processes period. Triple-check before running on unfamiliar systems.

Conclusion

Five habits:

  1. htop open in a tmux pane during any non-trivial workload.
  2. Try SIGTERM, wait 5 seconds, then SIGKILL only if needed.
  3. Cron jobs always: full paths, output redirection, 2>&1.
  4. For long workloads, tmux or systemd-units, not nohup.
  5. Use nice and ionice on heavy batch jobs so they don’t starve interactive workloads.

Related Linux Admin troubleshooting

For common errors and fixes related to this topic, see:

Leave a Reply