Linux Process Management: ps, kill, jobs, nohup and systemctl

System · 8 min read · Updated 25 Oct 2026

On this page
  1. Listing processes with ps
  2. pgrep and pkill
  3. Watching live: top and htop
  4. Stopping processes: kill and signals
  5. Background jobs: &, jobs, fg, bg
  6. Keep jobs running after logout: nohup and disown
  7. Priorities: nice and renice
  8. Services with systemd
  9. Quick reference

Every running program on Linux is a process with a numeric ID (the PID). Knowing how to find processes, stop them cleanly, and keep long jobs running after you log out is everyday work, whether you are dealing with a frozen script on your laptop or a stuck service on a server.

Listing processes with ps

ps aux shows every process on the system:

$ ps aux | head -3
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1 168992 13424 ?        Ss   Oct20   0:12 /sbin/init
mysql        914  1.8 12.2 2592212 498012 ?      Ssl  Oct20  85:18 /usr/sbin/mariadbd

The columns you will look at most: PID, %CPU and %MEM, RSS (memory actually in use, in KiB), STAT (state: R running, S sleeping, D waiting on disk, Z zombie, T stopped) and COMMAND. ps -ef is an alternative format that also shows each process's parent PID.

A few useful variations (the --sort option is from the procps version of ps found on Linux):

$ ps aux --sort=-%mem | head -6            # top 5 memory users
$ ps aux --sort=-%cpu | head -6            # top 5 CPU users
$ ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head
$ ps -o pid,ppid,etime,cmd -p 1234         # details for one PID, with elapsed time
$ ps -u alice                              # processes of one user

When you search with ps aux | grep nginx, the grep itself shows up in the results. The bracket trick hides it, because the pattern [n]ginx does not match its own command line:

$ ps aux | grep '[n]ginx'

pgrep and pkill

pgrep is the cleaner way to find processes by name. It prints matching PIDs:

$ pgrep nginx              # PIDs of processes named nginx
$ pgrep -a nginx           # PIDs with full command lines
$ pgrep -u www-data php    # only processes owned by www-data
$ pgrep -f "python3 app.py"   # match against the full command line

pkill takes the same options and sends a signal to every match:

$ pkill -f "python3 app.py"     # send TERM to matching processes
$ pkill -HUP nginx              # send HUP instead
Run pgrep with the same pattern before pkill, especially with -f. A short pattern like pkill -f py matches every command line containing "py", which can include things you did not mean to stop.

Watching live: top and htop

top shows processes updating in real time, sorted by CPU. Useful keys while it runs: P sorts by CPU, M by memory, 1 shows each CPU core, k kills a process by PID, and q quits. The header shows load average, memory and swap use.

htop is a friendlier alternative with colors, scrolling and mouse support. It is not always installed; on Debian and Ubuntu run sudo apt install htop. In htop, F6 chooses the sort column, F9 sends a signal, and F5 shows a process tree.

Stopping processes: kill and signals

kill sends a signal to a process. Despite the name, many signals do not kill anything; they are messages, and well-written programs react to them. The ones you need:

SignalNumberMeaning
TERM15Please exit. The default for kill. The program can clean up first.
INT2Interrupt, the same as pressing Ctrl+C.
HUP1Hang up. Many daemons reload their configuration on HUP.
KILL9Stop immediately. Cannot be caught or ignored; no cleanup happens.
STOP / CONT19 / 18Pause and resume (numbers are for Linux on x86 and ARM).
$ kill 1234          # polite: send TERM
$ kill -HUP 1234     # ask a daemon to reload
$ kill -9 1234       # force, as a last resort
$ kill -l            # list all signal names

Always try plain kill first and give the program a few seconds. kill -9 gives it no chance to flush data, remove lock files or finish writing, which can leave corrupted files behind. Use it only for processes that ignore TERM.

A process in state D (waiting on disk or a network filesystem) may not respond even to KILL until the I/O finishes. A Z (zombie) process is already dead; it disappears once its parent collects its exit status.

On Linux, killall nginx kills processes by exact name, much like pkill. On some other Unix systems killall does something far more drastic, so pkill is the safer habit.

Background jobs: &, jobs, fg, bg

Bash can run several commands from one terminal using job control. End a command with & to start it in the background, so you get your prompt back right away:

$ tar -czf backup.tar.gz ~/projects &
[1] 48213
$ jobs
[1]+  Running                 tar -czf backup.tar.gz ~/projects &

[1] is the job number and 48213 the PID. If you started something in the foreground and want your prompt back, press Ctrl+Z to suspend it, then bg to let it continue in the background:

$ ./long-import.sh
^Z
[1]+  Stopped                 ./long-import.sh
$ bg
[1]+ ./long-import.sh &
$ fg %1        # bring it back to the foreground

Refer to jobs as %1, %2 and so on: kill %1 sends TERM to job 1. wait pauses until all background jobs finish, which is handy in scripts. Background jobs still print to your terminal, so redirect their output if it gets in the way (see the redirection guide).

Keep jobs running after logout: nohup and disown

When you close a terminal or your SSH connection drops, Bash sends HUP to its jobs and they usually exit. To keep a long job running, start it with nohup:

$ nohup ./long-import.sh > import.log 2>&1 &

nohup makes the command ignore HUP. Without a redirect it writes output to nohup.out. If a job is already running and you forgot nohup, detach it from the shell with disown:

$ ./long-import.sh > import.log 2>&1 &
$ disown %1      # or press Ctrl+Z, run bg, then disown

After disown, the job no longer appears in jobs and Bash will not send it HUP on exit. For anything interactive, or that you want to check on later, a terminal multiplexer such as tmux or screen is more convenient: you can detach, log out, and reattach later to the same session.

Priorities: nice and renice

The "niceness" of a process, from -20 to 19, affects how much CPU time it gets when the system is busy. Higher means nicer to others, so lower priority. The default is 0.

$ nice -n 10 ./heavy-report.sh       # start with lower priority
$ renice -n 15 -p 48213              # lower the priority of a running process
$ sudo renice -n -5 -p 48213         # raising priority needs root

Regular users can only make their processes nicer. nice is a good habit for big compressions and batch jobs on a server that also serves users. For disk-heavy jobs, ionice -c3 does the same for disk access on Linux.

Services with systemd

On most current distributions, long-running services such as web servers and databases are managed by systemd, not started by hand. Use systemctl rather than killing service processes yourself, or systemd may simply restart them:

$ systemctl status nginx              # is it running? recent log lines
$ sudo systemctl restart nginx        # stop and start
$ sudo systemctl reload nginx         # reload config without dropping connections
$ sudo systemctl enable --now nginx   # start now and at every boot
$ systemctl list-units --type=service --state=failed   # anything broken?

Service logs go to the journal. journalctl reads them:

$ journalctl -u nginx -n 50           # last 50 lines for one service
$ journalctl -u nginx -f              # follow new lines live
$ journalctl -u nginx --since "1 hour ago"
$ journalctl -p err -b                # errors since the last boot

You may need sudo or membership in the adm or systemd-journal group to read other services' logs. See systemctl(1) and journalctl(1) for more.

Quick reference

TaskCommand
All processesps aux
Top memory usersps aux --sort=-%mem | head
Find by namepgrep -a name
Live viewtop or htop
Ask to stopkill PID
Force stopkill -9 PID
Reload a daemonkill -HUP PID or systemctl reload name
Run in backgroundcommand &
Suspend / resumeCtrl+Z, then bg or fg
Survive logoutnohup command > log 2>&1 &
Lower prioritynice -n 10 command
Service status / logssystemctl status name, journalctl -u name

For the keyboard side of job control, see the keyboard shortcuts guide.