Linux定时器与延时

内核定时器

依赖硬件定时器中断,内核在硬件中断发生后唤起TIMER_SOFTIRQ软中断,检测各软件定时器是否到期,到期就将定时器处理函数在底半部执行。
Kernel中可以利用一组函数对与定时器调用。

timer_list

用一个timer_list对应一个定时器xxx_timer。
(*function)成员对应timer的handler,long data对应传入参数。(指针可以强转为long在传入,比如传入dev结构体)
unsigned long expires是定时器到期时间,jiffies时间。
需要进行对结构体的初始化,定时器的添加等操作。
注意expires的初始化:

dev->xxx_timer.expires = jiffies + delay;

在handler函数内,如果需要不断触发,需要在最后再添加:

dev->xxx_timer.expires = jiffies + delay;
add_timer(&dev->xxx_timer);

jiffies
jiffies类型为无符号长整型(unsigned long),定义于文件linux\Jiffies.h,每有一次timer IRQ就记录一次。
HZ是内核的变量,驱动用的是内核下的HZ注意不要随便修改。2.6之后为了用户空间方便有USER_HZ。HZ代表实际1s需要的jiffies数量。jiffies+HZ就代表延迟1s,所以可以用HZ/100代表1/100s。
- 查看自己的HZ设定cat /boot/config-3.13.0-24-generic | grep “CONFIG_HZ”。HZ的不同值会影响timer(节拍)中断的频率.
Tick 是HZ的倒数,也就是每发生一次中断需要的实际时间。
而jiffies就是从开机到现在所发生的Tick的数量。

delayed_work

Linux利用wrok queue和timer提供了一种封装。
通过周期性调用schedule_delayed_work实现定时器操作。对于unsigned long delay参数,可以用msecs_to_jiffies(interval)来填写。将会是interval毫秒后触发的意思。

注:关于Linux时间的帖子[link 1]

内核延时

短延时

void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);

其实就是相当于忙等操作,原理:在kernel启动时,会有delay loop calibration, 延迟测试程序。算出lpj(loops per jiffy),也就是一个jiffy需要多少次loop。

回顾:HZ在编译内核时设定,HZ的不同会改变节拍器timer发出IRQ的情况,这样就有了一个irq节拍与实际时间的对应,做延迟测试,可以测每个节拍经过多少loop指令,这样就建立了程序执行语句数量lpj和节拍的关系,如此就可以得到指令与实际时间的对应关系。
运行mdelay(1)就可以设置一个loop,数量是lpj*HZ/1000这么多,所忙等的时间就是1ms。

由于msec已经是比较大的延时,内核中最好别用mdelay(),将消耗很多CPU资源。毫秒级以上的可以用:msleep()

void msleep(unsigned int msecs)
{
    unsigned long timeout = msecs_to_jiffies(msecs) + 1;
    //获取对应msecs后的jiffies时间。
    while (timeout)
        timeout = schedule_timeout_uninterruptible(timeout);
}
signed long __sched schedule_timeout_uninterruptible(signed long timeout)
{
    __set_current_state(TASK_UNINTERRUPTIBLE);//进程不可中断设定
    return schedule_timeout(timeout);
}

signed long __sched schedule_timeout_interruptible(signed long timeout)
{
     __set_current_state(TASK_INTERRUPTIBLE);//可中断
    return schedule_timeout(timeout);//运行schedule让出cpu
}

原理:向系统添加一个定时器,在定时器处理函数中唤醒参数对应的进程。

相似的还有ssleep()这两个都不可以被打断,而msleep_interruptible()可以被打断。

注意:由于手HZ设定和进程调度的影响,msleep的精度有限。但是delay是精确的,需要合理选用。

长延时

通过直接比较两个jifffies来看是否到时间,精度要求低。
可以自己比较,也可以用time_before(b, a)或者time_after(a, b);
将比较a是否到b(after b),为了防止编译器优化,jiffies被定义为volatile。

睡着延迟

用到schedule_timeout()事实上也就是msleep()所用到的。设定一个timer来唤醒自己。在时间达到之前不会被唤醒。
sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);可以实现让进程在等待队列上睡眠,在超时期限到来前任何时候都可能被唤醒。只不过有最长等待时间。


notification

source: 《Linux设备驱动开发详解》(第二版),内容为读书笔记和网络资料,有些资料原始来源不详,分享为了方便自己和他人查阅。如有侵权请及时告知,对于带来的不便非常抱歉。转载请注明来源。Terrence Zhou.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值