33.1 前言
对于Linux驱动开发来说,我们不可避免会使用到延时函数,其中最为常见的延时函数有msleep、usleep(sleep这些是睡眠不占用cpu的),还有mdelay、udelay(delay是CPU忙等待,定时精准但占用cpu),上述延时函数各有优缺点。对于普通情况的延时,也就是多一秒也没大关系的用sleep类延时函数完全没问题,对于有精准延时的用delay类函数一般也没啥问题。如果涉及到硬件时序操作的,我们必定要使用精准的延时函数,如果在对应的地方使用不精准的sleep类延时函数,则会出现下面时序巨大的时序偏差。
然而,如果使用delay函数对于系统性能来说是一个硬伤害,CPU忙等待表示CPU干不了其它事情,只能瞎等。因此,为了解决这种资源浪费问题,必须用另外一个办法解决,那就是使用高精度定时器+条件等待,实现延时函数的功能。
33.2 hrtimer高精度定时器
内核从2.6.16开始加入了高精度定时器架构,高精度定时器架构具有稳定快速的查找、插入、删除定时能力及排序功能,红黑树(rbtree)来组织hrtimer,红黑树已经以库的形式存在于内核中,并被成功地使用在内存管理子系统和文件系统中,随着系统的运行,hrtimer不停地被创建和销毁,新的hrtimer按顺序被插入到红黑树中,树的最左边的节点就是最快到期的定时器,内核用一个hrtimer结构来表示一个高精度定时器。
hrtimer相关函数
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id, enum hrtimer_mode mode) |
高精度定时器初始化;timer高精度定时器数据结构;clockid定时的时间类型(如实时时间、开机时间);mode定时器模式 |
hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) |
高精度定时器启动;timer定时器结构;tim定时时间;mode定时器模式 |
ktime_t ktime_set(const long secs, const unsigned long nsecs) |
定时器时间设定函数;secs秒;nsecs纳秒 |
hrtimer_forward_now(struct hrtimer *timer, ktime_t interval) |
设置超时时间;interval下次超时时间 |
int hrtimer_cancel(struct hrtimer *timer) |
关闭定时器 |
enum hrtimer_restart (*function)(struct hrtimer *) |
定时器超时回调函数 |
33.3 Linux等待队列
在驱动开发中,有时不可避免需要进行阻塞,如当硬件数据未准备好,此时程序必须阻塞等待数据准备好才能读,否则读取到的数据内容将是无效数据。为了支持应用层这种阻塞与非阻塞的功能,驱动程序就需要提供阻塞(如等待队列,中断)和非阻塞方式(如轮询,异步通知)机制。
Linux内核中,等待队列是常用的阻塞条件出发机制,在数据或任务不能被读取或执行时,将要执行的任务放入等待队列中,当条件到达及唤醒等待队列时再执行即可。
等待队列阻塞分为ASK_UNINTERRUPTIBLE(不可打断)模式睡眠、TASK_INTERRUPTIBLE(可打断)模式睡眠,在不可打断睡眠中,就算发送kill信号程序也不会响应。
等待队列函数
wait_queue_head_t queue; |
等待队列结构 |
init_waitqueue_head(wait_queue_head_t* queue) |
初始化等待队列; |
wait_event(*queue, int condition) |
不可打断等待;condition等待条件 |
wait_event_interruptible(*queue, int condition) |
可打断等待模式; |
wait_event_timeout(queue, condition, timeout) |
超时条件等待不可中断函数; |
wait_event_interruptible_timeout(queue, condition, timeout) |
超时条件等待可中断函数 |
wake_up(wait_queue_head_t *queue); |
等待唤醒queue所有进程 |
wake_up_interruptible(wait_queue_head_t *queue) |
等待唤醒queue所有进程 |
33.4 高精度定时器延时驱动实例
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/jiffies.h>
struct _stIdelayMngr{
int nQueWaiting; /*等待队列条件*/
wait_queue_head_t iwaitQue; /*等待队列*/
struct hrtimer ihrtimer; /*高精度定时器*/
};
struct _stIdelayMngr stIdelayMngr;
//示例程序
int hrtimer_main()
{
while(1)
{
/*启动高精度定时器,结合等待队列延时1ms*/
hrtimer_start(&stIdelayMngr.ihrtimer,ktime_set(0,1000*1000),HRTIMER_MODE_REL);
wait_event(stIdelayMngr.iwaitQue,stIdelayMngr.nQueWaiting);
stIdelayMngr.nQueWaiting = 0;
/*do something you want*/
//.....
}
return 0;
}
/* 定时器回调函数 */
static enum hrtimer_restart hrtimer_hander(struct hrtimer *timer)
{
stIdelayMngr.nQueWaiting = 1;
wake_up(&stIdelayMngr.iwaitQue);
return HRTIMER_NORESTART;
}
static int __init hrtimer_demo_init(void)
{
memset(&stIdelayMngr,0,sizoef(stIdelayMngr));
/*初始化等待队列*/
stIdelayMngr.nQueWaiting = 0;
init_waitqueue_head(&stIdelayMngr.iwaitQue);
/*初始化高精度定时器*/
hrtimer_init(&stIdelayMngr.ihrtimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
stIdelayMngr.ihrtimer.function = hrtimer_hander; /* 设置回调函数 */
hrtimer_start(&stIdelayMngr.ihrtimer,ktime_set(1,0),HRTIMER_MODE_REL); /* hrtimer启动 */
return 0;
}
static void __exit hrtimer_demo_exit(void)
{
/* hrtimer注销 */
hrtimer_cancel(&stIdelayMngr.ihrtimer);
}
module_init(hrtimer_demo_init);
module_exit(hrtimer_demo_exit);
MODULE_LICENSE("GPL");