Fundamentals 11 min read

Understanding Linux Process Sleep, Wakeup, and How to Avoid Invalid Wakeups

This article explains Linux process states, the difference between runnable, interruptible and uninterruptible sleep, how the scheduler and schedule() function manage sleep and wakeup, demonstrates code examples, and discusses the invalid wakeup problem with strategies to prevent it in kernel development.

Open Source Linux
Open Source Linux
Open Source Linux
Understanding Linux Process Sleep, Wakeup, and How to Avoid Invalid Wakeups

Linux Process Sleep and Wakeup

In Linux, a process that is only waiting for CPU time is called a runnable process and is placed on a run queue with the state flag TASK_RUNNING. When its time slice expires, the scheduler removes it from the run queue and selects another process to run.

A process can also voluntarily give up the CPU by calling the scheduling function schedule(). After being rescheduled, the process resumes execution at the line following the schedule() call.

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

Linux defines two sleep states:

Interruptible sleep with flag TASK_INTERRUPTIBLE.

Uninterruptible sleep with flag TASK_UNINTERRUPTIBLE.

Interruptible sleep ends when a condition becomes true (hardware interrupt, resource release, signal). Uninterruptible sleep is similar but does not respond to signals; it is used when a process must not be interrupted.

Modern kernels typically put a process to sleep by calling schedule(). The following code shows a simple sleep:

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

changes the process state from TASK_RUNNING to TASK_INTERRUPTIBLE. When schedule() is invoked by a TASK_RUNNING process, another process is scheduled.

If schedule() is called while the current process is in TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE, the current process is removed from the run queue and goes to sleep until another process wakes it.

The wake‑up is performed with wake_up_process(), which sets the sleeping process state back to TASK_RUNNING and re‑adds it to the run queue.

Invalid Wakeup

Invalid wakeup occurs when a process checks a condition, finds it true, but goes to sleep before the condition is re‑checked, causing it to remain asleep indefinitely.

This typically results from a race condition: a producer may wake a consumer before the consumer has actually entered the sleep state.

Example code shows process A sleeping on an empty list while process B adds an element and calls wake_up_process(). If B wakes A before A has set its state to TASK_INTERRUPTIBLE, the wake‑up is lost and A sleeps forever.

Avoiding Invalid Wakeup

To prevent this, the check for an empty list and the transition to sleep must be atomic. One solution is to set the process state to TASK_INTERRUPTIBLE before testing the condition, then call schedule() only if the condition is still false.

Rewritten A‑process code:

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);

This ensures that if B calls wake_up_process() while A is in the interruptible state, A will be awakened correctly.

Kernel Example

A typical kernel pattern for safe sleeping:

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

This pattern sets the state before checking the condition, loops while the condition is false, and restores the state to TASK_RUNNING before leaving the wait queue.

Another kernel snippet from kernel/sched.c shows a thread waiting for kthread_should_stop() using the same technique.

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

By placing the state change before the condition check, the kernel guarantees that wake‑up calls made while the condition is being evaluated will not be lost, thus avoiding invalid wakeups.

Summary

In Linux, avoiding invalid wakeups requires setting the process state to TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE before testing the wait condition, and resetting it to TASK_RUNNING once the condition is satisfied.

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.

Linuxprocess schedulinginvalid wakeupsleep state
Open Source Linux
Written by

Open Source Linux

Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.

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.