Master Linux Kernel Delay Mechanisms to Write Reliable Drivers
This article explains the different Linux kernel delay mechanisms—busy delays like udelay, sleep delays like mdelay, and high‑resolution timers—detailing their inner workings, appropriate use cases, performance trade‑offs, and common pitfalls so developers can write efficient, stable driver code for industrial‑grade systems.
In Linux driver development, many engineers focus on hardware register configuration and device registration while often ignoring the kernel's delay mechanisms, which are essential for precise timing and system stability.
1. Introduction to Linux Kernel Delays
Delay in the kernel pauses the execution flow for a specified period, similar to pressing a "pause" button. It is used to wait for slow hardware operations (e.g., disk writes) and to coordinate access to shared resources in multi‑threaded environments.
1.1 Definition and Role
Delays ensure that hardware has enough time to complete actions and that software threads access resources in the correct order, preventing race conditions and data inconsistency.
2. Classification of Kernel Delays
Linux provides two main categories: busy delays (Busy Delay) and sleep delays (Sleep Delay).
2.1 Busy Delay
A busy delay keeps the CPU occupied by executing an empty loop. It offers high precision for microsecond‑ or nanosecond‑scale waits. The classic example is udelay(), which is often used in I²C communication to wait a few microseconds after sending a byte.
Drawbacks: the CPU cannot perform other work during the wait, leading to high CPU utilization and potential performance degradation in real‑time systems.
2.2 Sleep Delay
Sleep delays put the current process to sleep, releasing the CPU for other tasks. Functions such as msleep() are suitable for millisecond‑ or second‑scale pauses, e.g., a network server sleeping while waiting for new connections.
Advantages: lower CPU usage. Limitations: higher latency, cannot be used in interrupt context, and the actual sleep time may be longer than requested due to scheduler overhead.
3. Detailed Look at udelay
3.1 What is udelay ?
udelayprovides precise microsecond delays by performing a busy‑wait loop. It is defined in <linux/delay.h> and calculates the required loop count using the loops_per_jiffy value derived from the CPU's BogoMIPS.
3.2 Usage
#include <linux/module.h>
#include <linux/delay.h>
static int __init my_module_init(void)
{
printk(KERN_INFO "Module starting...
");
// Delay 500 microseconds
udelay(500);
printk(KERN_INFO "500 microseconds delay finished.
");
return 0;
}
static void __exit my_module_exit(void)
{
printk(KERN_INFO "Module exiting...
");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");Notes: udelay can only be used in kernel space and should be limited to delays under 1 ms; longer waits should use mdelay or scheduler‑friendly functions.
3.3 Use Cases and Precautions
Typical scenarios include hardware initialization timing and protocol implementations (I²C, SPI) that require exact microsecond delays. Precautions: avoid long busy waits to prevent CPU starvation, and be aware that pre‑emptible kernels may interrupt the delay, affecting accuracy.
4. Detailed Look at mdelay
4.1 What is mdelay ?
mdelayimplements millisecond‑scale delays by repeatedly calling udelay. It is also defined in <linux/delay.h>.
4.2 Usage
#include <linux/module.h>
#include <linux/delay.h>
static int __init mdelay_example_init(void)
{
// Delay 500 milliseconds
mdelay(500);
printk(KERN_INFO "mdelay example: 500 milliseconds delay completed
");
return 0;
}
static void __exit mdelay_example_exit(void)
{
printk(KERN_INFO "mdelay example: module exiting
");
}
module_init(mdelay_example_init);
module_exit(mdelay_example_exit);
MODULE_LICENSE("GPL");Like udelay, mdelay occupies the CPU during the wait, so it should be used only for short millisecond delays.
4.3 Comparison with msleep
mdelayuses busy waiting, consuming CPU cycles, while msleep puts the task to sleep, freeing the CPU. mdelay offers higher timing accuracy but higher resource usage; msleep is preferred for longer, less time‑critical pauses.
5. High‑Resolution Timers ( hrtimer )
5.1 What is hrtimer ?
For scenarios demanding nanosecond precision (audio/video processing, real‑time control), hrtimer provides high‑resolution timing based on hardware clocks such as TSC or HPET. It stores active timers in a red‑black tree, allowing O(log N) operations even with thousands of timers.
5.2 Usage
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
static struct hrtimer my_hrtimer;
static enum hrtimer_restart my_callback(struct hrtimer *timer)
{
pr_info("hrtimer callback: timer expired
");
// Reschedule 1 second later
hrtimer_forward_now(timer, ktime_set(1, 0));
return HRTIMER_RESTART;
}
static int __init hrtimer_example_init(void)
{
hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
my_hrtimer.function = my_callback;
hrtimer_start(&my_hrtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
return 0;
}
static void __exit hrtimer_example_exit(void)
{
hrtimer_cancel(&my_hrtimer);
}
module_init(hrtimer_example_init);
module_exit(hrtimer_example_exit);
MODULE_LICENSE("GPL");The callback should be concise to avoid degrading timer precision. hrtimer supports one‑shot and periodic modes; periodic timers are implemented by returning HRTIMER_RESTART and advancing the expiry time.
5.3 Callback Contexts
Two execution contexts exist: soft‑interrupt (default) and hard‑interrupt. Soft‑interrupt callbacks cannot sleep, while hard‑interrupt callbacks must be extremely short to avoid impacting overall system latency.
6. Practical Guidelines for Using Kernel Delays
Correct usage of udelay, mdelay, and hrtimer is crucial for high‑quality driver code.
Avoid long busy‑wait delays on critical paths; prefer msleep or hrtimer when precision is not essential.
Keep hrtimer callbacks minimal; heavy computation should be deferred to a workqueue or thread.
Use printk or kernel tracing (e.g., ftrace, kgdb) to verify actual delay durations during debugging.
By understanding the underlying principles, performance trade‑offs, and appropriate scenarios for each delay mechanism, developers can move beyond basic coding and produce driver implementations that are efficient, stable, and suitable for industrial‑grade environments.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Deepin Linux
Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
