您的位置:首页 > 运维架构 > Linux

Linux定时器与延时

2015-11-17 16:36 686 查看

内核定时器

依赖硬件定时器中断,内核在硬件中断发生后唤起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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 内核