关闭

Linux定时器与延时

标签: linux内核
599人阅读 评论(0) 收藏 举报
分类:

内核定时器

依赖硬件定时器中断,内核在硬件中断发生后唤起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
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:12005次
    • 积分:283
    • 等级:
    • 排名:千里之外
    • 原创:15篇
    • 转载:1篇
    • 译文:2篇
    • 评论:6条
    天宫里的那些大神
    文章分类
    最新评论