目录
内核中的睡眠、延时函数
一、睡眠函数种类
1、原子上下文
使用delay系列函数
ndelay(unsigned long nsecs)//首选使用
udelay(unsigned long usecs)
mdelay(unsigned long msecs)
这些函数使用对时钟速度的jiffie估计,并将忙于等待足够的循环周期以实现所需的延迟。
mdelay是udelay的宏包装器,以免udelay传递大参数时可能发生溢出.
2、非原子上下文
使用sleep系列的函数
usleep_range(umin,umax); //推荐使用
msleep(ms);
msleep_interruptible(ms);
ssleep(s);
二、使用环境
1、使用环境的不同,选择不同的延时
-- Backed by busy-wait loop:
udelay(unsigned long usecs)
-- Backed by hrtimers:
usleep_range(unsigned long min, unsigned long max)
-- Backed by jiffies / legacy_timers
msleep(unsigned long msecs)
msleep_interruptible(unsigned long msecs)
2、驱动机制不同
1)睡眠时间 ( < ~10us? ):
使用 udelay
为什么不使用 usleep?因为在较慢的系统上(如嵌入式)为usleep设置hrtimer的开销可能不值得。
2)睡眠时间 (10us - 20ms):
使用usleep_range
为什么不使用msleep 定时 (1ms - 20ms),
睡眠时间经常比设定的长 http://lkml.org/lkml/2007/8/3/250
为什么没有 "usleep",什么是好的范围?
usleep建立在hrtimers计时器之上,唤醒将非常精确。因此,一个简单的usleep函数可能会引入大量不需要的中断。 随着范围的引入,调度器可以自由地将您的唤醒与可能由于其他原因发生的任何其他唤醒合并,或者在最坏的情况下,为您的上限触发一个中断。
3)睡眠时间>10ms
使用 msleep or msleep_interruptible
msleep 设置当前的进程状态为TASK_UNINTERRUPTIBLE
msleep_interruptible 设置当前的进程状态为TASK_INTERRUPTIBLE
4)睡眠时间>1s
使用ssleep
3、内核中的计算函数执行的函数
u64 ktime_get_real_ns(void);
实例计算foo() bar()函数的延时
#include <linux/ktime.h>
t1 = ktime_get_real_ns();
foo();
bar();
t2 = ktime_get_real_ns();
time_taken_ns = (t2 -> t1);
三、实测两类函数的延时以及原因
计算时间的函数
#define DILLY_DALLY(code_str, run_this) do { \
u64 t1, t2; \
t1 = ktime_get_real_ns(); \
run_this; \
t2 = ktime_get_real_ns(); \
pr_info(code_str "-> actual: %11llu ns = %7llu us = %4llu ms\n", \
(t2-t1), (t2-t1)/1000, (t2-t1)/1000000);\
} while(0)
1、测试系统中的睡眠函数
static int __init delays_and_sleeps_init(void)
{
//delay
DILLY_DALLY("ndelay() for 10 ns", ndelay(10));
DILLY_DALLY("udelay() for 10,000 ns", udelay(10));
DILLY_DALLY("mdelay() for 10,000,000 ns", mdelay(10));
//sleep
DILLY_DALLY("usleep_range(10,20) for 10,000 ns", usleep_range(10, 20));
DILLY_DALLY("msleep(10) for 10,000,000 ns", msleep(10));
DILLY_DALLY("msleep_interruptible(10) ", msleep_interruptible(10));
DILLY_DALLY("ssleep(1) ", ssleep(1));
}
2、输出结果
1. *delay() functions (atomic, in a delay loop):
[ 4176.946228] ndelay() for 10 ns-> actual: 219 ns = 0 us = 0 ms
[ 4176.946239] udelay() for 10,000 ns-> actual: 9956 ns = 9 us = 0 ms
[ 4176.956217] mdelay() for 10,000,000 ns-> actual: 9976582 ns = 9976 us = 9 ms
[ 4176.956219]
2. *sleep() functions (process ctx, sleeps/schedule()'s out):
[ 6372.868032] usleep_range(10,20) for 10,000 ns-> actual: 57183 ns = 57 us = 0 ms
[ 6372.886923] msleep(10) for 10,000,000 ns-> actual: 18880096 ns = 18880 us = 18 ms
[ 6372.906874] msleep_interruptible(10) -> actual: 19943338 ns = 19943 us = 19 ms
[ 6373.915691] ssleep(1) -> actual: 1008605369 ns = 1008605 us = 1008 ms
3、输出结果现象与解释
1、udelay() 与mdelay() 函数实际用的时间比参数要少?这是为什么呢?
* Please note that ndelay(), udelay() and mdelay() may return early for
* several reasons:
* 1. computed loops_per_jiffy too low (due to the time taken to
* execute the timer interrupt.)
* 2. cache behaviour affecting the time it takes to execute the
* loop function.
* 3. CPU clock rate changes.
*
2、sleep()系列的函数用了更多的时间,在标准的linux上
原因有下
- 标准的linux系统,高分辨率的定时器,它支持需要小于一个jiffy分辨率的计时器;
- 将Linux操作系统配置为RTOS,将减少这个问题
参考内核