1.概览

  在设备驱动中,有时候在驱动中延迟执行一些指令以等待硬件完成。延时一般分为两大类,第一类为忙等待即CPU在此期间执行的指令目的地仅仅是为了消耗时间。第二类为休眠等待唤醒,即在延时点进入休眠,然后被唤醒,这也就实现了驱动的等待功能。

2.CPU忙等待延时

2.1 mdelay

  代码过于简单,下面则是示例代码

time_test_drv_init
    DELAY_TEST_INFO("start:%lld ns", ktime_get_boottime_ns());
    mdelay(1000);
    DELAY_TEST_INFO("after mdelay:%lld ns", ktime_get_boottime_ns());

  执行结果如下

[  121.908834] delayTest:delay_test_drv_init,27:start:121905834566 ns
[  122.931042] delayTest:delay_test_drv_init,33:after mdelay:122928044142 ns

2.2 使用 time_before 接口实现忙等待

  使用之前提到过的接口 time_before 再配合循环也是可以实现忙等待的,示例代码如下

unsigned long delayTime = jiffies + HZ;//delay 1s
while(time_before(jiffies, delayTime));

  实现的逻辑很简单,在每一个循环中都判断下当前时间是否超期预设值(delayTime),如果超期了那么就不成立退出循环了。
  使用这类的等待实际上非常浪费CPU的,所以尽量能不使用则不使用吧。

3.休眠等待延时

  这一类的等待在精度上是不如忙等待的,因为这需要一个休眠唤醒的过程,但对CPU的消耗则会小的多。不过对于这类接口是不能在原子和中断上下文中调用的,在使用这些接口时一定要确认当前所在的上下文。

3.1 usleep_range

  使用该接口来设置延时,内核会根据所设置的范围来决定能否复用已有的唤醒中断,因此相较于usleep这样更加节省系统资源。下面是使用示例

usleep_range(1000000,1500000);
DELAY_TEST_INFO("after usleep_range:%lld ns", ktime_get_boottime_ns());

  usleep_range的入参单位都是微妙,所以在示例代码中设置了本次延时可以接收的范围是1s~1.5s。
  下面是执行结果

[  122.931042] delayTest:delay_test_drv_init,33:after mdelay:122928044142 ns
[  124.256229] delayTest:delay_test_drv_init,38:after usleep_range:124253207756 ns

  124253207756 - 122928044142 = 1,325,163,614ns,可见还是满足期望值的。

3.2 msleep

  调用该接口后会在设置的毫秒之后被唤醒,但是接口msleep_interruptible则是可以被消耗所打断的,例如在调用msleep_interruptible进入休眠后,有信号发送到当前进入休眠等待的进程,那么该进程也会被唤醒,那么此时的休眠时间就不够所设了,下面时示例代码

msleep(1000);//http://lkml.org/lkml/2007/8/3/250
DELAY_TEST_INFO("after msleep:%lld ns", ktime_get_boottime_ns());
msleep_interruptible(1000);
DELAY_TEST_INFO("after msleep_interruptible:%lld ns", ktime_get_boottime_ns());

  如下则为执行结果

[  124.256229] delayTest:delay_test_drv_init,38:after usleep_range:124253207756 ns
[  125.280232] delayTest:delay_test_drv_init,40:after msleep:125277210986 ns
[  126.303898] delayTest:delay_test_drv_init,42:after msleep_interruptible:126300866101 ns

3.5 wait_event_interruptible_timeout

  这种延迟的实现是有点取巧的,但是好处就是延迟时间单位可以和jiffies的精度一致。本接口只要满足下面的任意一种条件则会返回,
   a)条件为真时
   c)当timeout时间用完时
  所以这里将条件设置为0,那么也就实现了延时的功能,下面是示例代码

struct wait_queue_head wait_head;
init_waitqueue_head(&wait_head);
wait_event_timeout(wait_head, 0 , HZ);//1s delay
DELAY_TEST_INFO("after wait_event_timeout:%lld ns", ktime_get_boottime_ns());

  执行结果如下

[  341.376510] delayTest:delay_test_drv_init,52:after msleep_interruptible:341373267986 ns new
[  342.400526] delayTest:delay_test_drv_init,59:after wait_event_timeout:342397288024 ns

4.源码如下

delayTest

https://gitee.com/solo-king/linux-kernel-base-usage/blob/master/flagstaff/delayTest.c
Logo

更多推荐