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 socket 初步
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝