Fundamentals 12 min read

Why Do Linux Processes Sleep and Wake? Understanding TASK States and Invalid Wakeups

This article explains how Linux processes transition between running and sleeping states, the difference between interruptible and uninterruptible sleep, common pitfalls such as invalid wakeups, and best‑practice kernel patterns to avoid race conditions when putting a process to sleep.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Why Do Linux Processes Sleep and Wake? Understanding TASK States and Invalid Wakeups

Linux Process Sleep and Wakeup

In Linux a process that is only waiting for CPU time is in the TASK_RUNNING state and resides on the run queue. When its time slice expires the scheduler removes it from the run queue and selects another TASK_RUNNING task.

A process can voluntarily relinquish the CPU by calling the scheduler function schedule(). After being rescheduled execution resumes at the line following the schedule() call.

If a process must wait for an event (device initialization, I/O completion, timer, etc.) it is removed from the run queue, placed on a wait queue and enters a sleep state.

Two Linux sleep states :

Interruptible sleep – flag TASK_INTERRUPTIBLE Uninterruptible sleep – flag TASK_UNINTERRUPTIBLE Interruptible sleep ends when a condition becomes true (hardware interrupt, resource release, signal). Uninterruptible sleep is similar but ignores signals; it is used when a process must not be awakened until a specific event occurs.

Typical code to put a process to sleep:

sleeping_task = current;
set_current_state(TASK_INTERRUPTIBLE);
schedule();
func1();
/* Rest of the code ... */
current

is a macro that points to the currently executing task. set_current_state() changes the task’s state from TASK_RUNNING to TASK_INTERRUPTIBLE. When schedule() is invoked the scheduler may pick another TASK_RUNNING task, causing the original task to sleep.

To wake a sleeping task the kernel calls wake_up_process(sleeping_task), which sets the task’s state back to TASK_RUNNING and re‑adds it to the run queue.

Invalid Wakeup Problem

A race can occur when a process checks a condition, finds it true, and then goes to sleep anyway. If the wake‑up happens before the process has actually entered the sleep state, the wake‑up is missed and the process blocks indefinitely.

Example with two processes A and B

// Process A
spin_lock(&list_lock);
if (list_empty(&list_head)) {
    spin_unlock(&list_lock);
    set_current_state(TASK_INTERRUPTIBLE);
    schedule();
    spin_lock(&list_lock);
}
/* Rest of the code ... */
spin_unlock(&list_lock);
// Process B
spin_lock(&list_lock);
list_add_tail(&list_head, new_node);
spin_unlock(&list_lock);
wake_up_process(processa_task);

If B wakes A after A has passed the condition check but before A executes set_current_state(), the wake‑up is ineffective and A will later sleep forever.

Avoiding Invalid Wakeups

The check‑and‑sleep sequence must be made atomic. One common solution is to set the task’s state to TASK_INTERRUPTIBLE *before* testing the condition and reset it to TASK_RUNNING if the condition is already satisfied.

Revised Process A

set_current_state(TASK_INTERRUPTIBLE);
spin_lock(&list_lock);
if (list_empty(&list_head)) {
    spin_unlock(&list_lock);
    schedule();
    spin_lock(&list_lock);
}
set_current_state(TASK_RUNNING);
/* Rest of the code ... */
spin_unlock(&list_lock);

Now, if B calls wake_up_process() after the condition check, the task is already in an interruptible state and will be correctly transitioned to TASK_RUNNING, preventing an invalid wakeup.

Kernel‑Level Wait‑Queue Example

A typical wait‑queue sleep pattern used in the kernel:

/* q is the wait queue we want to sleep on */
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
/* condition is the predicate we are waiting for */
while (!condition) {
    schedule();
    set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(q, &wait);

This ensures the task’s state is set to a sleeping state before the condition is examined and restored to TASK_RUNNING only after the condition becomes true, thereby avoiding missed wake‑ups.

Another real‑world snippet (from kernel/sched.c in Linux 2.6) shows a thread that sleeps until a stop request:

set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
    schedule();
    set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;

The key to preventing invalid wakeups is to set the task’s state to a sleeping state before checking the condition and to restore it to TASK_RUNNING only after the condition is satisfied.

Summary

To avoid invalid wakeups in Linux:

Set the task’s state to TASK_INTERRUPTIBLE (or TASK_UNINTERRUPTIBLE) *before* testing the wait condition.

If the condition is already true, reset the state to TASK_RUNNING immediately.

Use the standard wait‑queue pattern: DECLARE_WAITQUEUE, add_wait_queue, set_current_state, schedule in a loop, then set_current_state(TASK_RUNNING) and remove_wait_queue.

Following this pattern guarantees that a wake‑up cannot be missed, eliminating the race that leads to indefinite blocking.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Kernelprocess schedulinginvalid wakeupTASK_INTERRUPTIBLE
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.