Program, process and thread

Three words that are easy to mix up:

  • A program is a file on disk: notepad.exe, /usr/bin/python3. It does nothing until someone starts it.
  • A process is a program that is running: the code loaded into memory plus everything the operating system keeps track of for it. Start Notepad three times and you have one program and three processes, each with its own memory and its own open file.
  • A thread is one sequence of instructions being executed inside a process. Every process has at least one thread; many have dozens. A browser may use one thread for the user interface, others for downloading and decoding images, so the window keeps responding while a page loads. All threads of a process share its memory and open files.

The operating system schedules threads on the CPU cores, but it assigns resources — memory, files, permissions — to processes. How programmers use threads and processes, and the problems of sharing memory between threads, is explained in Concurrency Basics. This page is the operating system's view.

What the OS keeps for every process

For each process the kernel keeps a data structure, traditionally called the process control block (PCB). Windows calls it an EPROCESS structure, Linux a task_struct. It holds:

  • Identity: the process ID, the parent's process ID, and the program file it was started from.
  • State: ready, running, waiting and so on (below), and scheduling information such as priority and CPU time used so far.
  • Memory: the process's private address space and the page tables that map it to physical memory (see Memory Management).
  • Open resources: files, network connections, registry keys and other objects the process is using. Windows calls these handles; Unix-like systems call open files file descriptors.
  • Security context: the user account the process runs as and what it may do — an access token on Windows, user and group IDs on Linux. A process started by an ordinary user cannot do more than that user may, which is why malware started by a standard user does less damage than malware started by an administrator.
  • Threads: for each thread, its own saved CPU registers, its own stack and a thread ID.

Process ID

A process ID (PID) is the number the kernel gives a process when it starts, unique among the processes running at that moment. Tools and commands use it to point at one exact process — "stop PID 8424" is unambiguous, while "stop notepad" might hit three windows. PIDs are reused after a process ends, so a PID written down yesterday may belong to something else today. A few are fixed: on Windows PID 0 is the System Idle Process and PID 4 is System (the kernel's own threads); on Linux PID 1 is the init system, usually systemd.

Process states

A process is not running all the time — on a busy system most processes are waiting almost always. The kernel tracks each one's process state, which says whether it can use the CPU right now. The classic model has five states (strictly, on a multithreaded system each thread has a state, but the idea is the same):

  • New: being created; memory and PCB are being set up.
  • Ready: could run, waiting only for a free CPU core.
  • Running: executing on a core right now.
  • Waiting (blocked): cannot continue until something happens — data from the disk or network, a key press, a timer, a lock.
  • Terminated: finished; the kernel is releasing its resources.
Process state diagram. New goes to Ready when admitted. Ready goes to Running when dispatched. Running goes back to Ready when its time slice is over, to Waiting when it waits for input or output, and to Terminated when it exits. Waiting goes to Ready when the input or output is done. New Ready Running Terminated Waiting admit dispatch time slice over exit wait for I/O I/O done
The five-state model. A waiting process never goes straight back to running: when its data arrives it joins the ready queue and waits for a core like everyone else.

Two details in the diagram explain a lot of everyday behavior. A process that is waiting uses no CPU at all, which is why hundreds of processes can exist while the CPU is 95% idle. And the only way from waiting back to running is through ready: when a slow disk finally delivers, the process still has to wait its turn.

This simulation runs a short scripted scenario for two processes on one core, and rejects a transition the model does not allow:

# Which event moves a process from which state to which state
TRANSITIONS = {
    ("new", "admit"): "ready",
    ("ready", "dispatch"): "running",
    ("running", "timeout"): "ready",       # time slice used up
    ("running", "wait_io"): "waiting",     # asked for disk or network data
    ("waiting", "io_done"): "ready",
    ("running", "exit"): "terminated",
}

state = {"editor": "new", "backup": "new"}
script = [
    ("editor", "admit"), ("backup", "admit"),
    ("editor", "dispatch"), ("editor", "wait_io"),
    ("backup", "dispatch"), ("editor", "dispatch"),
    ("editor", "io_done"), ("backup", "timeout"),
    ("editor", "dispatch"), ("editor", "exit"),
]

for step, (name, event) in enumerate(script, start=1):
    old = state[name]
    new = TRANSITIONS.get((old, event))
    if new is None:
        print(f"{step:2}. {name:6} {event:8} refused: not possible from {old}")
        continue
    state[name] = new
    print(f"{step:2}. {name:6} {event:8} {old} -> {new}")

print("Final:", state)
 1. editor admit    new -> ready
 2. backup admit    new -> ready
 3. editor dispatch ready -> running
 4. editor wait_io  running -> waiting
 5. backup dispatch ready -> running
 6. editor dispatch refused: not possible from waiting
 7. editor io_done  waiting -> ready
 8. backup timeout  running -> ready
 9. editor dispatch ready -> running
10. editor exit     running -> terminated
Final: {'editor': 'terminated', 'backup': 'ready'}

Step 4 is the important moment: while the editor waits for the disk, the core is free, and the backup gets it at step 5. Step 6 shows why the ready state exists: the editor cannot be dispatched while it is still waiting. Real systems add more states — Linux ps shows, among others, R (running or ready), S (sleeping, that is waiting), D (waiting for input/output — I/O — and not interruptible), T (stopped) and Z (zombie, below).

Context switches

A computer with 8 cores routinely runs a few thousand threads. It can, because the kernel keeps swapping which threads are on the cores. A context switch is that swap: the kernel saves the complete CPU state of the running thread — its registers, including the program counter (the address of the next instruction) and the stack pointer — into the thread's saved state, and loads the saved state of the next thread. The next thread continues exactly where it stopped, unaware it was ever paused.

A context switch happens for one of two reasons:

  • Voluntary: the thread has to wait (for a file, the network, a lock) and gives up the core.
  • Involuntary: the thread's time slice ran out, or a more important thread became ready, and the scheduler takes the core away. See CPU Scheduling.

Switches are fast — on the order of microseconds — but not free. Switching between two threads of the same process only swaps registers. Switching to a thread of a different process also switches the address space, which may throw away cached address translations, and the new thread starts with CPU caches full of the previous thread's data. That hidden cost is one reason why creating hundreds of busy threads does not make a program faster. Tens of thousands of switches per second are normal on a desktop; Windows Performance Monitor shows them as the counter System › Context Switches/sec, and Linux shows them in the cs column of vmstat.

Parent and child processes

Every process except the very first is started by another process. The parent process is the process that created another one, its child; the kernel records the parent's PID (PPID) in the child's PCB. Double-click a file in File Explorer and explorer.exe is the parent; type a command in PowerShell and the PowerShell process is the parent. Together these links form a process tree.

On Linux and macOS the tree is strict and visible: PID 1 (systemd on most Linux distributions, launchd on macOS) starts everything else, and a child is created by the parent calling fork and then exec. Two Unix terms come up in troubleshooting:

  • An orphan is a child whose parent ended first. It keeps running and is adopted by PID 1 (or a designated "subreaper" process), so nothing breaks.
  • A zombie is a child that has already ended but whose parent has not yet collected its exit status (with the wait system call). It uses no CPU or memory, only a slot in the process table, and shows as Z or <defunct> in ps. You cannot kill a zombie — it is already dead. A few are harmless; a growing number means the parent program has a bug, and restarting the parent clears them.

Windows also records each process's parent, but treats the link more loosely: when a parent ends, its children simply keep running. To stop a parent together with everything it started, use End process tree on the Details tab of Task Manager, or taskkill /T.

Services and daemons

Many processes have no window at all. They start at boot, run whether or not anyone is logged on, and provide something other programs rely on: printing, networking, updates, remote access, antivirus scanning.

Windows services

A service is a Windows program that runs in the background under the control of the Service Control Manager, usually starting at boot and running without a user logged on. Services run under their own accounts: the built-in accounts LocalSystem (full control of the computer), LocalService and NetworkService (limited rights), or a dedicated service account. Services are hosted in Service Host processes (svchost.exe). On PCs with more than about 3.5 GB of RAM, Windows 10 and 11 give most services their own svchost.exe, which is why Task Manager shows dozens of them.

You manage services in the Services console (services.msc), on the Services tab of Task Manager, or from the command line. Each service has a startup type:

Startup type Behavior
Automatic Starts during boot.
Automatic (Delayed Start) Starts shortly after boot has finished, so it does not slow down startup and sign-in.
Manual Starts only when a user, a program or a trigger (such as a device being plugged in) starts it.
Disabled Cannot be started at all, not even by hand.

The Recovery tab of a service's properties sets what happens when it crashes, for example restart it after one minute. A classic help desk fix is restarting the Print Spooler service when print jobs are stuck (PowerShell must run as administrator for the restart; illustrative output):

Get-Service -Name Spooler
Restart-Service -Name Spooler
Status   Name               DisplayName
------   ----               -----------
Running  Spooler            Print Spooler

In Command Prompt, sc query spooler shows the state, and net stop spooler and net start spooler stop and start it.

Daemons on Linux and macOS

A daemon is the Unix name for a background process that is not attached to any terminal or user session and provides a service, usually from boot until shutdown. Their names traditionally end in d: sshd (the Secure Shell server), cron/crond (scheduled tasks), systemd itself. On most Linux distributions systemd starts and supervises daemons as units; on macOS, launchd does the same job. (The name comes from the helpful spirits of Greek mythology, not from demons.)

systemctl status ssh
sudo systemctl restart ssh
sudo systemctl enable ssh      # start automatically at boot
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)
     Active: active (running) since Wed 2026-09-30 08:14:02 UTC; 1 day 2h ago
   Main PID: 912 (sshd)

The unit is called ssh on Debian and Ubuntu and sshd on Red Hat–based distributions. "enabled" is the equivalent of the Windows startup type Automatic.

Viewing and stopping processes

Task Manager and Resource Monitor

Task Manager (Ctrl+Shift+Esc) is the first stop on Windows:

  • Processes groups what runs into apps, background processes and Windows processes, with CPU, memory, disk and network use. "Not responding" means an app has stopped handling its window messages — often because it is waiting on something, not necessarily crashed. End task closes it; unsaved work is lost.
  • Details is the technician's view: one line per process with its PID, status, the user it runs as, and optional columns (right-click a column header) such as threads, handles and command line. Here you find End process tree and Set priority.
  • Services lists services and their state.

Resource Monitor (resmon, or from the Performance tab of Task Manager) goes deeper. Its CPU tab can search the handles of all processes: when Windows says "The action can't be completed because the file is open in another program", search for the file name to find which process holds it.

tasklist and taskkill

In Command Prompt, tasklist lists processes and taskkill ends them, by image name (/IM) or by PID (/PID). Without switches, taskkill asks the program to close, like clicking its close button; /F forces termination immediately; /T also ends every child process. Illustrative output:

tasklist /FI "IMAGENAME eq notepad.exe"
taskkill /IM notepad.exe
taskkill /PID 8424 /F
Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
Notepad.exe                   8424 Console                    1     61,248 K

SUCCESS: Sent termination signal to the process "Notepad.exe" with PID 8424.
SUCCESS: The process with PID 8424 has been terminated.

PowerShell

PowerShell's Get-Process returns process objects you can sort and filter; Stop-Process ends a process at once (it does not ask the program to close first). The five processes that have used the most CPU time (illustrative output):

Get-Process | Sort-Object CPU -Descending | Select-Object -First 5 -Property Id, ProcessName, CPU
Stop-Process -Id 8424
  Id ProcessName        CPU
  -- -----------        ---
6120 msedge       842.53125
5932 explorer     311.0625
7716 ms-teams     204.765625
3144 svchost       96.203125
9012 OneDrive      41.46875

CPU is the total processor time in seconds since the process started, not its current load. Current load is what Task Manager's CPU column shows.

ps, top and kill on Linux

ps takes a snapshot of the processes; top (or the friendlier htop, if installed) shows a live view sorted by CPU use. This ps command shows PID, parent PID, owner and state as a tree (shortened, illustrative):

ps -eo pid,ppid,user,stat,comm --forest
    PID    PPID USER     STAT COMMAND
      1       0 root     Ss   systemd
    655       1 root     Ss   cron
    912       1 root     Ss   sshd
   4410     912 root     Ss    \_ sshd
   4432    4410 alex     S         \_ sshd
   4433    4432 alex     Ss            \_ bash
   4501    4433 alex     R+                \_ ps

kill does not really kill: it sends a signal, and the signal decides what happens.

Signal Number Meaning Sent by
SIGTERM 15 Please terminate. The program can save its work, close files and exit cleanly — or ignore it. kill PID (the default)
SIGKILL 9 Terminate now. The kernel removes the process; the program cannot catch, block or react to it. kill -9 PID or kill -KILL PID
SIGINT 2 Interrupt from the keyboard. Ctrl+C in the terminal
SIGHUP 1 Terminal closed; many daemons treat it as "reload your configuration". kill -HUP PID
kill 4620          # SIGTERM: ask politely
kill -9 4620       # SIGKILL: only if it is still running after a few seconds
pkill firefox      # SIGTERM to every process whose name contains firefox (use -x for an exact match)

You can signal your own processes; signaling another user's processes needs sudo.