⚙️ Lesson 7: Processes: What Is Running and How to Stop It
See what your computer is doing right now, run things in the background, and stop a program politely or firmly
What is a process?
Every time a program runs, Linux starts a process: one running copy of that program. Run the same program three times and you have three processes. Each process has:
- a PID, its process ID, a number the system uses to refer to it;
- an owner, the user it runs as, which decides what it is allowed to do (lesson 5);
- a parent, shown as the PPID: the process that started it. When you type a command, your shell is the parent of the process that runs it.
Every process on a Linux machine has a parent, all the way back to process number 1, the very first one. On a normal Linux computer that is the system manager (lesson 8). In the practice container from lesson 1 it is simply the bash shell you are typing in, because a container starts from your shell and nothing else. That is why a container shows only a handful of processes, where a real machine shows hundreds.
Open your practice machine and follow along. Everything below was run on a real Ubuntu 24.04. Your PID numbers will be different on your machine, and that is fine.
See your processes with ps
ubuntu@practice:~$ ps
PID TTY TIME CMD
1 pts/0 00:00:00 bash
9 pts/0 00:00:00 ps
ubuntu@practice:~$ ps -f
UID PID PPID C STIME TTY TIME CMD
ubuntu 1 0 0 14:22 pts/0 00:00:00 /bin/bash
ubuntu 10 1 0 14:22 pts/0 00:00:00 ps -f
ubuntu@practice:~$ sleep 300 &
[1] 11
ubuntu@practice:~$ ps -f
UID PID PPID C STIME TTY TIME CMD
ubuntu 1 0 0 14:22 pts/0 00:00:00 /bin/bash
ubuntu 11 1 0 14:22 pts/0 00:00:00 sleep 300
ubuntu 12 1 0 14:22 pts/0 00:00:00 ps -f
ubuntu@practice:~$ jobs
[1]+ Running sleep 300 &
- ps on its own shows the processes running in this terminal: bash, which is the shell, as PID 1, and ps itself. (ps always shows itself, because it was running when it looked.)
- ps -f is the full format. The columns that matter: UID is the owner, PID the process number, PPID the parent's number (ps has PPID 1, so its parent is bash), STIME when it started, TIME how much processor time it has used, and CMD the command itself.
- sleep 300 & started a command that just waits for 300 seconds. It is the perfect stand-in for a long-running program. The & at the end means "run it in the background": the prompt came back immediately instead of waiting. The shell printed [1] 11: this is job number 1, and its process ID is 11.
- ps -f now lists sleep 300 too, as process 11, a child of bash (PPID 1).
- jobs shows the jobs your shell is running for you: [1]+ Running sleep 300 &.
Foreground, background and suspended
A program you start normally runs in the foreground: you wait until it finishes, and the prompt only comes back afterwards. You can move programs between foreground and background:
ubuntu@practice:~$ fg sleep 300 ^Z [1]+ Stopped sleep 300 ubuntu@practice:~$ jobs [1]+ Stopped sleep 300 ubuntu@practice:~$ bg [1]+ sleep 300 & ubuntu@practice:~$ jobs [1]+ Running sleep 300 & ubuntu@practice:~$ kill %1 ubuntu@practice:~$ jobs [1]+ Terminated sleep 300
- fg brought job 1 back to the foreground, so the shell waits for it and you get no prompt.
- Ctrl+Z then suspended it. The shell printed [1]+ Stopped. A stopped process is not dead: it is frozen, exactly as it was, and can carry on. (Do not confuse it with Ctrl+C from lesson 2, which asks the program to stop for good.)
- bg told the stopped job to carry on in the background, and jobs shows Running again.
- kill %1 ended job 1. The %1 means "job number 1 of this shell". The message Terminated appears the next time the shell has something to say.
Everything running: ps aux
ubuntu@practice:~$ sleep 500 & [1] 13 ubuntu@practice:~$ sleep 600 & [2] 14 ubuntu@practice:~$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 1 0.3 0.0 4600 4116 pts/0 Ss 14:22 0:00 /bin/bash ubuntu 13 0.0 0.0 2708 1528 pts/0 S 14:22 0:00 sleep 500 ubuntu 14 0.0 0.0 2708 1556 pts/0 S 14:22 0:00 sleep 600 ubuntu 15 0.0 0.0 7904 3980 pts/0 R+ 14:23 0:00 ps aux ubuntu@practice:~$ pgrep -a sleep 13 sleep 500 14 sleep 600
ps aux lists all the processes on the machine, not only the ones in your terminal. The letters are three options together: a for processes of all users, u for a user-friendly layout with CPU and memory, and x for processes that have no terminal. The columns:
- %CPU and %MEM show how much processor and memory each one is using.
- VSZ and RSS are memory sizes in kilobytes. RSS is the amount really held in memory right now.
- STAT is the state. S means sleeping, which is what most processes are doing, most of the time: they are waiting for something to happen. R means running. A + means it is in the foreground, and s marks the leader of a session.
On a real machine ps aux prints hundreds of lines, and you find the one you want with grep (lesson 4) and a pipe (lesson 11). A quicker way to find a process by name is pgrep -a: pgrep -a sleep printed the PID and command of each sleep: 13 and 14.
A live view: top
ps takes a photograph. top is a live video, updating every few seconds, sorted with the busiest processes first. It takes over the whole terminal, so we ran it once in "batch" mode (-b, one round with -n 1) to capture a snapshot:
ubuntu@practice:~$ top -b -n 1
top - 14:22:05 up 3 days, 4:29, 0 user, load average: 0.33, 0.51, 0.62
Tasks: 4 total, 1 running, 3 sleeping, 0 stopped, 0 zombie
%Cpu(s): 4.8 us, 2.4 sy, 0.0 ni, 92.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 31778.5 total, 10952.0 free, 5850.2 used, 14886.0 buff/cache
MiB Swap: 8192.0 total, 8192.0 free, 0.0 used. 25928.3 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 ubuntu 20 0 4600 4080 3476 S 0.0 0.0 0:00.05 bash
13 ubuntu 20 0 2708 1656 1556 S 0.0 0.0 0:00.00 sleep
14 ubuntu 20 0 2708 1652 1556 S 0.0 0.0 0:00.00 sleep
17 ubuntu 20 0 8732 5064 3004 R 0.0 0.0 0:00.00 top
The top part is a summary of the whole machine:
- up 3 days is how long the computer has been running. load average shows how busy the processor has been over the last 1, 5 and 15 minutes. As a rough guide, a number lower than your count of processor cores means the machine is not overloaded.
- Tasks counts the processes by state.
- %Cpu(s) shows how the processor spends its time: us running programs, sy running the system itself, and id idle. Ours was 92.8 percent idle.
- MiB Mem is the memory: total, free, used and cache. The line that matters most is avail Mem, the memory that programs can really use. Linux uses spare memory as a cache to make things faster, so a low "free" number is normal and not a problem. (Inside a container, top shows the figures of the whole computer, which is why the numbers look big.)
The table lists processes. PID and USER you know. %CPU and %MEM show what each one uses, and TIME+ the total processor time. S is the state again, and COMMAND the name.
In real, interactive top, press q to quit, k to kill a process (it asks for the PID), h for help, and the Shift+M and Shift+P keys to sort by memory or by processor use (from the documentation, not run here). A friendlier look-alike called htop can be installed with apt (lesson 6).
Stopping a process: kill and signals
Despite its name, kill does not necessarily kill. It sends a signal: a short message to a process. The signal decides what happens. Two matter most:
ubuntu@practice:~$ kill -l 15 TERM ubuntu@practice:~$ kill -l 9 KILL ubuntu@practice:~$ kill 13 ubuntu@practice:~$ jobs [1]- Terminated sleep 500 [2]+ Running sleep 600 & ubuntu@practice:~$ kill -9 14 ubuntu@practice:~$ jobs [2]+ Killed sleep 600 ubuntu@practice:~$ pgrep -a sleep ubuntu@practice:~$ echo $? 1
- kill -l 15 and kill -l 9 translate the numbers to their names: 15 is TERM and 9 is KILL.
- TERM (15) is the default, the polite request: "please finish what you are doing and exit". A program can catch this, save its work, clean up and leave. kill 13 (without a number option) sent TERM to process 13, and jobs then showed Terminated.
- KILL (9) is the end: the system removes the process immediately, and the program gets no chance to clean up or save anything. kill -9 14 took out process 14, and jobs said Killed. Afterwards pgrep -a sleep printed nothing, and echo $? said 1, which is how pgrep says "I found nothing".
So why have two? Because a program can ignore TERM, as this example shows. We start a loop that is told to ignore it:
ubuntu@practice:~$ sh -c 'trap "" TERM; while true; do sleep 1; done' & [1] 18 ubuntu@practice:~$ ps -f UID PID PPID C STIME TTY TIME CMD ubuntu 1 0 0 14:22 pts/0 00:00:00 /bin/bash ubuntu 18 1 0 14:23 pts/0 00:00:00 sh -c trap "" TERM; while tr ubuntu 19 18 0 14:23 pts/0 00:00:00 sleep 1 ubuntu 20 1 0 14:23 pts/0 00:00:00 ps -f ubuntu@practice:~$ kill 18 ubuntu@practice:~$ ps -f UID PID PPID C STIME TTY TIME CMD ubuntu 1 0 0 14:22 pts/0 00:00:00 /bin/bash ubuntu 18 1 0 14:23 pts/0 00:00:00 sh -c trap "" TERM; while tr ubuntu 21 18 0 14:23 pts/0 00:00:00 sleep 1 ubuntu 22 1 0 14:23 pts/0 00:00:00 ps -f ubuntu@practice:~$ kill -9 18 ubuntu@practice:~$ ps -f UID PID PPID C STIME TTY TIME CMD ubuntu 1 0 0 14:22 pts/0 00:00:00 /bin/bash ubuntu 24 1 0 14:23 pts/0 00:00:00 ps -f [1]+ Killed sh -c 'trap "" TERM; while true; do sleep 1; done'
kill 18 sent TERM, and nothing happened: process 18 is still listed afterwards (the sleep 1 under it is just the loop's helper, and it gets a new number every second). Only kill -9 18 worked: the next ps -f shows it is gone, and the shell reported Killed.
The rule that follows is: **always try plain kill first**, and give the program a moment. Use kill -9 only when a process truly will not stop. Killing a program with -9 skips its cleanup, which can leave half-written files, so never do it casually to something important like a database.
Two more things to know. You can only kill your own processes; only the administrator can kill other people's (lesson 5, and the same ownership idea). And pkill NAME kills by name instead of by number, so pkill sleep ends every sleep (from the documentation, not run here). Check what matches with pgrep -a NAME first.
Because some processes only understand <span class="command">kill -9</span>.
🧪 Try it yourself
1. Start two background sleep jobs, one for 400 seconds and one for 500. List them with jobs and with pgrep -a sleep. 2. End the first with kill %1. Check with jobs. 3. Start another sleep 600 in the foreground (without &), suspend it with Ctrl+Z, then let it carry on in the background with bg. 4. Run ps -f and find the PPID of your sleep. Which process is its parent? 5. Run top -b -n 1 and read the %Cpu(s) line. How much of the processor is idle? 6. Run sh -c 'trap "" TERM; while true; do sleep 1; done' &. Try kill on it, check it is still there, then finish it with kill -9.
The cheat sheet
- ps your terminal's processes. ps -f with the parent. ps aux everything. pgrep -a NAME find by name.
- COMMAND & run in the background. jobs list your jobs. fg bring one to the front. Ctrl+Z suspend. bg carry on in the background.
- top live view (q to quit). top -b -n 1 one snapshot.
- kill PID ask to stop (TERM). kill -9 PID force (KILL, last resort). kill %1 for job number 1.
- kill -l 9 shows the name of signal number 9.
- Only the owner (or root) can kill a process. Try plain kill first.
← Lesson 6 · ↑ All lessons · Lesson 8: Services and Logs: Starting, Stopping and Reading What Your System Says →