AlgoInsight

Free lesson · Operating Systems

This process is currently in the Running state

Easy12 min readoperating-systemsosconcurrency

A Process Is Not Always Running

When you think about a running program, you probably imagine the CPU executing its instructions continuously from start to finish. But that is not how it works. A process spends most of its life not running on the CPU. It might be waiting for data from disk. It might be waiting for its turn on the CPU because other processes are using it. It might be paused because the OS decided another process is more important right now.

The operating system tracks what every process is doing at every moment using a concept called process states. Each process is always in exactly one of five states, and the OS transitions processes between these states based on what happens - a disk read completes, a time slice expires, the process calls a blocking system call, and so on.

Understanding process states matters for interviews because it is the foundation of scheduling (Module 03), it explains why context switches happen, and it helps you reason about what is happening when a system is slow or unresponsive. When an interviewer asks "what is happening when my application appears to hang?", they want you to reason about which state the process is stuck in and why.

The Five Process States

Every process on the system is in one of these five states at any given moment:

stateDiagram-v2
    [*] --> New: Process created<br/>(fork called)
    New --> Ready: Admitted by OS<br/>(memory allocated,<br/>PCB created)
    Ready --> Running: Scheduler picks<br/>this process<br/>(dispatch)
    Running --> Ready: Time slice expired<br/>or higher-priority<br/>process arrived<br/>(preemption)
    Running --> Waiting: Process needs I/O<br/>or waits on a lock<br/>(blocked)
    Waiting --> Ready: I/O completed<br/>or lock acquired<br/>(event occurs)
    Running --> Terminated: Process calls exit()<br/>or is killed<br/>(termination)
    Terminated --> [*]: PCB cleaned up<br/>(parent called wait)
This state diagram shows five process states and the transitions between them. A process starts in the New state when fork is called. It moves to Ready when the OS finishes setting it up. The scheduler moves it from Ready to Running. From Running, it can go back to Ready if its time slice expires (preemption), go to Waiting if it needs I/O or a lock, or go to Terminated if it exits. From Waiting, it returns to Ready when the I/O completes or the lock is acquired. From Terminated, the process is cleaned up when the parent calls wait.

Let us walk through each state in detail.

New

A process enters the New state the moment it is being created. When fork() is called, the OS starts the process of setting up the new process - allocating a PID, creating the Process Control Block, setting up the memory mappings (using copy-on-write from the parent). During this brief period, the process is in the New state.

The New state is typically very short-lived. On modern systems, process creation takes microseconds. But it is a distinct state because the OS needs to complete several setup steps before the process is ready to run. If the system is under extreme memory pressure, the OS might delay admitting the new process from New to Ready until resources are available.

Ready

A process in the Ready state is fully set up and able to run - it has everything it needs - but it is waiting for the CPU. The CPU is currently running some other process, and this process is sitting in the ready queue waiting for its turn.

This is the most important state to understand for scheduling. The ready queue is where the CPU scheduler looks when it needs to pick the next process to run. How it picks - first-come-first-served, shortest job first, round robin, priority-based - is the subject of Module 03. For now, the key insight is that being "ready" means "I could run right now if the CPU were available."

On a system with 200 processes and 4 CPU cores, at most 4 processes can be in the Running state at any time. The rest are either Ready (waiting for CPU), Waiting (blocked on I/O or a lock), or New/Terminated.

Running

A process in the Running state is actively executing instructions on a CPU core. Its program counter is advancing, its instructions are being fetched and executed, and it is doing actual work.

A process leaves the Running state in one of three ways. First, its time slice expires - the OS gives each process a fixed amount of CPU time (typically 1–10 milliseconds). When the time is up, the OS preempts the process, moves it back to Ready, and picks the next process from the ready queue to run. Second, the process blocks on I/O - if the process calls read() to read data from disk, and the data is not in memory, the process cannot continue until the data arrives. The OS moves it to the Waiting state and runs something else. Third, the process exits - it calls exit() or is killed by a signal, and it moves to the Terminated state.

Waiting (Blocked)

A process in the Waiting state is unable to run because it is waiting for some external event to complete. The most common reasons a process enters the Waiting state are waiting for disk I/O to complete (the process called read() and the data is not in memory, so it has to wait for the disk), waiting for network I/O (the process is waiting for data to arrive over a network socket), waiting for a lock (the process tried to acquire a mutex that another process holds), waiting for user input (the process called read() on standard input and is waiting for the user to type something), and waiting for a timer (the process called sleep(5) and is waiting for 5 seconds to pass).

The critical distinction between Ready and Waiting is this: a Ready process could run if the CPU were available. A Waiting process cannot run regardless of CPU availability - it is blocked on something that has not happened yet.

When the event the process is waiting for completes - the disk read finishes, the network data arrives, the lock becomes available, the timer expires - the OS moves the process from Waiting back to Ready. It does not go directly from Waiting to Running because there might be other processes in the ready queue ahead of it. It has to wait its turn.

Terminated

A process enters the Terminated state when it finishes execution. This happens either because the process called exit() normally, it returned from its main() function, it received a fatal signal like SIGKILL, or it crashed due to an error like a segmentation fault.

In the Terminated state, the process has stopped executing but it has not fully disappeared yet. The OS keeps a small record of it in the process table - specifically, its exit status - until the parent process calls wait() to collect that status. This is important because the parent might want to know whether the child succeeded or failed, and what its exit code was.

If the parent never calls wait(), the terminated child stays in the process table indefinitely. This is a zombie process - a process that has finished executing but whose entry in the process table has not been cleaned up. We cover zombies in Topic 5.

Why Does a Running Process Go to Waiting Instead of Ready?

This is a subtlety that interviewers sometimes test. When a running process needs to do I/O (say, read from disk), it does not go back to the Ready state. It goes to the Waiting state. Why?

Because the process literally cannot continue. Its next instruction depends on the data it is waiting for. There is no point putting it in the ready queue - even if the scheduler gave it the CPU immediately, it would have nothing to do. It is waiting for an external event (the disk read completing), and until that event happens, the process is stuck.

By contrast, when a process's time slice expires, the process goes back to Ready - not Waiting. The process could continue running. It has instructions to execute. The OS just decided that another process deserves a turn. So the process goes to the ready queue, where it will eventually get the CPU back.

The distinction matters because Ready and Waiting processes are managed differently by the scheduler. Ready processes are in the CPU scheduling queue. Waiting processes are in I/O or event queues, indexed by what they are waiting for. When a disk read completes, the OS checks the I/O queue for disk events, finds the process waiting for that specific read, and moves it to the Ready queue.

graph TD
    subgraph CPU["CPU (Running)"]
        R1["Process A<br/>(Running)"]
    end
    
    subgraph RQ["Ready Queue"]
        Q1["Process B"] --> Q2["Process C"] --> Q3["Process D"]
    end
    
    subgraph WQ["Waiting Queues"]
        subgraph DQ["Disk I/O Queue"]
            W1["Process E<br/>(waiting for disk read)"]
            W2["Process F<br/>(waiting for disk write)"]
        end
        subgraph NQ["Network I/O Queue"]
            W3["Process G<br/>(waiting for socket data)"]
        end
        subgraph LQ["Lock Queue"]
            W4["Process H<br/>(waiting for mutex)"]
        end
    end
    
    R1 -.->|"time slice<br/>expires"| RQ
    R1 -.->|"needs I/O<br/>or lock"| WQ
    RQ -.->|"scheduled<br/>(dispatch)"| CPU
    WQ -.->|"event<br/>completes"| RQ
This diagram shows the relationship between the CPU, the ready queue, and the various waiting queues. Process A is currently running on the CPU. Processes B, C, and D are in the ready queue, waiting for CPU time. Processes E and F are in the disk I/O waiting queue. Process G is in the network I/O waiting queue. Process H is in the lock waiting queue. When Process A's time slice expires, it moves to the ready queue. If Process A needs I/O, it moves to the appropriate waiting queue. When a waiting process's event completes, it moves to the ready queue.

Interview: The interviewer asks: "Explain the process state diagram."

Structure your answer around the transitions, not just the states:

"A process starts in the New state when created. Once the OS has set it up, it moves to Ready - it can run but is waiting for the CPU. The scheduler dispatches a Ready process to the Running state. From Running, three things can happen: the time slice expires and it goes back to Ready (preemption), it needs I/O and goes to Waiting (blocking), or it exits and goes to Terminated. A Waiting process moves back to Ready when its I/O completes - not directly to Running, because it has to wait for the scheduler to pick it. A Terminated process is cleaned up when its parent calls wait()."

Then offer the key insight: "The important distinction is between Ready and Waiting. A Ready process is able to run and just needs CPU time. A Waiting process cannot run until an external event completes. Understanding this distinction is crucial for diagnosing performance issues - if many processes are Waiting on I/O, the bottleneck is the disk, not the CPU."

Seeing Process States in Real Life

On a Linux system, you can see the state of every process by looking at the /proc/[PID]/status file or by using the ps command. The STAT column in ps output shows the state:

ps aux

The STAT column uses single-letter codes:

Code State Meaning
R Running (or Ready) Process is running on a CPU or is in the ready queue
S Sleeping (Interruptible) Process is waiting for an event (I/O, timer, etc.) and can be woken by a signal
D Sleeping (Uninterruptible) Process is waiting for I/O and cannot be interrupted - usually disk I/O
T Stopped Process has been stopped by a signal (e.g., Ctrl+Z)
Z Zombie Process has terminated but parent has not called wait()

Notice that Linux does not distinguish "Running" from "Ready" in the ps output - both show as R. This makes sense because from the perspective of observing the system, the distinction changes thousands of times per second. A process that is "Ready" will be "Running" within milliseconds.

The D state (uninterruptible sleep) is worth knowing about for infrastructure interviews. A process in the D state is waiting for disk I/O and cannot be killed - not even by SIGKILL. If you see many processes in the D state, it usually means the disk subsystem is overwhelmed. This is a common diagnostic scenario in production systems, and mentioning it in an interview shows real-world awareness.

import os

# This process is currently in the Running state
# (it's executing these instructions right now)
pid = os.getpid()
print(f"My PID: {pid}")
print(f"I am currently in the Running state")
print(f"Check my state with: ps -p {pid} -o pid,stat,comm")

How State Transitions Connect to Context Switches

Every transition between Running and any other state involves a context switch - the OS saves the state of the current process and loads the state of the next process. This is the mechanism that makes multitasking work.

When the scheduler transitions a process from Running to Ready (because its time slice expired), the OS saves the process's registers, program counter, and stack pointer into its Process Control Block (PCB), selects the next process from the ready queue, loads that process's registers, program counter, and stack pointer from its PCB, and resumes the new process.

When a process transitions from Running to Waiting (because it called a blocking system call), the same save happens, but the process goes to a waiting queue instead of the ready queue.

Context switches are not free. Each one takes microseconds and invalidates CPU caches (because the new process will likely need different data in the cache). On a system with too many processes frequently entering and leaving the Running state, context switch overhead can become a real performance problem. This is one reason thread pools are preferred over thread-per-request models - by limiting the number of active threads, you bound the number of context switches.

Tip: The connection to system design: When an interviewer asks you to choose between a thread-per-request model and a thread pool model for a web server, one of the key tradeoffs is context switches. A thread-per-request model with 10,000 concurrent requests means 10,000 threads competing for CPU time, causing thousands of context switches per second. A thread pool with 200 threads bounds the context switches and keeps the CPU focused on actual work.

State Transitions in Different Scenarios

Understanding state transitions helps you diagnose and explain system behavior. Here are some common scenarios and the state transitions involved:

A CPU-bound process (e.g., computing the Fibonacci sequence): The process alternates between Running and Ready. It runs until its time slice expires, goes to Ready, waits for the scheduler, and then runs again. It rarely or never enters the Waiting state because it does not do I/O.

An I/O-bound process (e.g., reading a large file from disk): The process frequently transitions between Running, Waiting, and Ready. It runs briefly (to issue a read request), moves to Waiting (while the disk fetches the data), moves to Ready (when the data arrives), and runs again (to process the data and issue the next read request).

A server process (e.g., a web server waiting for requests): The process spends most of its time in the Waiting state, waiting for incoming network connections. When a request arrives, it transitions to Ready, then Running to process the request, then back to Waiting for the next request. A well-designed server minimizes the time it spends in Running and maximizes throughput by handling each request quickly.

A process waiting for a lock: The process transitions from Running to Waiting when it tries to acquire a mutex that another process holds. It stays in Waiting until the mutex is released. If many processes are competing for the same lock, they pile up in the Waiting state - this is lock contention, and it is one of the most common performance problems in concurrent systems.

Exercise: Starter: Open a terminal and run ps aux | awk '{print $8}' | sort | uniq -c | sort -rn. This counts how many processes are in each state on your system. You will likely see that the vast majority are in the S (sleeping/waiting) state, a few are in R (running/ready), and possibly one or two in other states. Why do you think most processes are sleeping?

Intermediate: Write a Python program that does the following: prints "Starting...", then calls time.sleep(10), then prints "Done." While it is sleeping, open another terminal and find its PID using ps aux | grep python. What state is it in during the sleep? (It should be S - sleeping.) Now modify the program to do a tight CPU loop instead of sleeping (while True: pass). What state is it in now? (R - running.)

Advanced: On a Linux system, monitor a process's state transitions in real time using watch -n 0.1 "cat /proc/[PID]/status | grep State". Start a program that alternates between CPU work and disk I/O (for example, repeatedly reading a large file and computing a hash of its contents). Watch the state flip between R (running) and S or D (sleeping). How often does it change? What does this tell you about the program's workload balance between CPU and I/O?

Back to Operating Systems