linux驱动中的Timer和delay

本文详细介绍了Linux内核中的定时器机制及其操作函数,包括初始化、添加、删除及修改定时器的方法,并探讨了不同延时技术的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux驱动中的Timer和delay
定时器分为硬件和软件定时器,软件定时器最终还是要依靠硬件定时器来完成。内核在时钟中断发生后检测各定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行。实质上,时钟中断处理程序执行update_process_timers函数,该函数调用run_local_timers函数,这个函数处理TIMER_SOFTIRQ软中断,运行当前处理上到期的所有定时器。
Linux内核中定义提供了一些用于操作定时器的数据结构和函数如下:
1)timer_list:说定时器,当然要来个定时器的结构体
struct timer_list{ 
struct list_head entry; //定时器列表
unsignedlong expires; //定时器到期时间
void (*function)(unsignedlong) ;//定时器处理函数
unsignedlong data; //作为参数被传入定时器处理函数
struct timer_base_s *base; }
2)初始化定时器:void init_timer(struct timer_list *timer);经过这个初始化后,entry的next为NULL,并给base赋值
3)增加定时器:void add_timer(struct timer_list *timer); 该函数用于注册内核定时器,并将定时器加入到内核动态定时器链表中。
4)删除定时器:int del_timer(struct timer_list *timer);说明:del_timer_sync是del_timer的同步版,主要在多处理器系统中使用,如果编译内核时不支持SMP,del_timer_sync和del_timer等价.
5)修改定时器:int mod_timer(struct timer_list *timer, unsigned long expires);下边是一个使用定时器的模版: 
struct xxx_dev /*second设备结构体*/ { struct cdev cdev; /*cdev结构体*/ ... struct timer_list xxx_timer; /*设备要使用的定时器*/ }; int xxx_func1(...) //xxx驱动中某函数 { struct xxx_dev *dev = filp->private_data;/*初始化定时器*/ init_timer(&dev->xxx_timer); dev->xxx_timer.function = &xxx_do_handle; dev->xxx_timer.data = (unsignedlong)dev; dev->xxx_timer.expires = jiffies + delay; add_timer(&dev->xxx_timer); /*添加(注册)定时器*/ ... return 0;}  int xxx_func2(...) //驱动中某函数 { ... del_timer(&second_devp->s_timer); ...} staticvoid xxx_do_timer(unsignedlong arg) //定时器处理函数 { struct xxx_device *dev = (struct xxx_device *)(arg); ... //调度定时器再执行 dev->xxx_timer.expires = jiffies + delay; add_timer(&dev->xxx_timer);}
 
在内核定时器中,常常少不了要说下内核延迟的事,请接着往下看:
1)短延迟:在linux内核中提供了三个函数来分别实现纳秒,微秒,毫秒延迟,原理上是忙等待,它根据CPU频率进行一定次数的循环
void ndelay(unsigned long nsecs);                  
 void udelay(unsigned long usecs);                 
void mdelay(unsigned long msecs);
毫秒延迟已经相当大了,当然微秒延迟当然要小一些,在内核中,为了性能,最好不要用mdelay,这会耗费大量cpu资源,那么咋办呢,凉拌..
void msleep(unsigned int millisecs);   
unsigned long msleep_interruptible(unsigned int millisecs);   
void ssleep(unsigned int seconds);
这三个是内核专门提供该我们用来处理毫秒以上的延迟。上述函数将使得调用它的进程睡眠参数指定的秒数,其中第二个是可以被打断的,其余的两个是不可以的。
2)长延迟:内核中进行延迟最常用的方法就是比较当前的jiffies和目标jiffies(当前的加上时间间隔的jiffies),直到未来的jiffies达到目标jiffies。比如:
unsignedlong delay = jiffies + 100; //延迟100个jiffieswhile(time_before(jiffies, delay));
与time_before对应的还有一个time_after().其实就是#define time_before(a,b)  time_after(b,a);
另外两个是time_after_eq(a,b)和time_before_eq(a,b)
3)睡着延迟:这显然是比忙等待好的方法,因为在未到来之前,进程会处于睡眠状态,把CPU空出来,让CPU可以做别的事情,等时间到了,调用schedule_timeout()就可以唤醒它并重新调度执行。msleep和msleep_interruptible本质上都是依靠包含了schedule_timeout的schedule_timeout_uninterruptible()和schedule_
timeout_interruptible()实现。就像下边这样:
void msleep(unsignedint msecs) 

unsignedlong timeout = msecs_to_jiffies(msecs) + 1; 
while(timeout) timeout = schedule_timeout_uninterruptible(timeout);
}
 unsignedlong msleep_interruptible(unsignedint msecs)
 { 
unsignedlong timeout = msecs_to_jiffies(msecs) + 1; 
while(timeout && !signal_pending(current)) timeout = schedule_timeout_interruptible(timeout);
 return jiffies_to_msecs(timeout);

signedlong __sched schedule_timeout_interruptible()signedlong timeout) { __set_current_state(TASK_INTERRUPTIBLE); return schedule_timeout(timeout);} signedlong __sched schedule_timeout_uninterruptible()signedlong timeout) { __set_current_state(TASK_UNINTERRUPTIBLE); return schedule_timeout(timeout);}另外还有如下:time_on_timeout(wait_queue_head_t *q, unsigned long  timeout);interruptible_sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);这两个将当前进程添加到等待队列,从而在等待队列上睡眠,当超时发生时,进程将被唤醒。
<think>好的,我现在需要回答用户关于Linux驱动程序中如何实现延时的问题。首先,我得回忆一下Linux内核中处理延时的相关方法。记得在用户空间,我们通常用sleep()之类的函数,但内核空间不能直接使用这些,因为驱动运行在内核态,需要不同的机制。 接下来,我应该分清楚延时的类型:短延时、中等延时长延时。短延时可能用纳秒、微秒级别的,比如udelay()或ndelay()。中等延时可能是毫秒级的,比如mdelay()。这些函数都是忙等待,会占用CPU时间,所以不能用于较长的延时。 然后是长延时,这时候应该让出CPU,避免忙等。可能涉及到msleep()、ssleep()之类的函数,这些会让进程进入睡眠状态,调度其他进程运行。需要确认这些函数的区别,比如msleep()是否可中断,还有msleep_interruptible()的用法。 还要注意内核中的时间单位转换,比如jiffiesHZ的关系。jiffies是内核的时间单位,HZ是每秒的定时器中断次数。处理延时可能需要用到schedule_timeout()或者wait_event_timeout()这样的机制,允许任务在等待一段时间后被唤醒。 另外,需要考虑不可睡眠的上下文,比如中断处理例程中不能使用可能睡眠的函数。这时候可能需要使用忙等待的延时函数,或者重新设计代码逻辑,避免在中断上下文中进行长延时。 可能还要提到高精度定时器hrtimer,用于需要更精确时间的场景。hrtimer的使用方法,比如初始化、设置回调函数启动定时器。 最后,提醒用户根据具体场景选择合适的延时方法,考虑延时精度、是否可睡眠以及上下文环境。例如,在中断处理中只能用ndelay、udelay等,而在进程上下文中可以使用睡眠式的函数。</think>在Linux驱动程序中,实现延时需要根据不同的场景精度要求选择合适的方法。以下是逐步说明: --- ### 一、短延时(纳秒/微秒级) 适用于需要极高精度的短暂等待(如硬件寄存器操作)。 1. **ndelay(ns)** 纳秒级延时: ```c #include <linux/delay.h> ndelay(unsigned long nsecs); // 示例:ndelay(100); 等待100纳秒 ``` 2. **udelay(us)** 微秒级延时: ```c udelay(unsigned long usecs); // 示例:udelay(200); 等待200微秒 ``` 3. **mdelay(ms)** 毫秒级忙等待(慎用!会占用CPU): ```c mdelay(unsigned long msecs); // 示例:mdelay(5); 等待5毫秒 ``` --- ### 二、长延时(毫秒级以上) 需要让出CPU以节省资源,通常用于进程上下文(可睡眠场景)。 1. **msleep(ms)** 毫秒级休眠(不可中断): ```c #include <linux/delay.h> msleep(unsigned int milliseconds); // 示例:msleep(100); 休眠100毫秒 ``` 2. **msleep_interruptible(ms)** 可中断的休眠: ```c unsigned long msleep_interruptible(unsigned int msecs); // 返回值:剩余未休眠的时间(若被信号中断) ``` 3. **ssleep(sec)** 秒级休眠: ```c ssleep(unsigned int seconds); // 示例:ssleep(2); 休眠2秒 ``` --- ### 三、基于调度器的延时 在等待期间允许其他任务运行,适合需要精确控制延时的场景。 1. **schedule_timeout()** 结合`jiffies`实现可中断/不可中断的延时: ```c #include <linux/sched.h> set_current_state(TASK_UNINTERRUPTIBLE); // 设置不可中断状态 schedule_timeout(msecs_to_jiffies(ms)); // 示例:休眠50毫秒 ``` 2. **wait_event_timeout()** 在等待队列中休眠,直到超时或条件满足: ```c wait_event_timeout(wq, condition, timeout); ``` --- ### 四、高精度定时器(hrtimer) 适用于需要纳秒级精度的周期性或单次任务。 1. 定义回调函数: ```c enum hrtimer_restart timer_callback(struct hrtimer *timer) { // 定时器到期后执行的操作 return HRTIMER_NORESTART; } ``` 2. 初始化启动定时器: ```c struct hrtimer my_timer; hrtimer_init(&my_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); my_timer.function = timer_callback; hrtimer_start(&my_timer, ns_to_ktime(1000000), HRTIMER_MODE_REL); // 1毫秒后触发 ``` --- ### 五、注意事项 1. **上下文限制** - 中断上下文(如`中断处理函数`)中禁止使用休眠函数(如`msleep`),只能用`ndelay/udelay`。 - 进程上下文中优先选择可休眠的延时方法。 2. **精度问题** - 实际延时时间受系统负载内核配置(如`CONFIG_HZ`)影响。 3. **替代方案** - 若需周期性操作,优先使用内核工作队列(`workqueue`)或定时任务。 --- ### 总结 - **短延时(硬件操作)**:`udelay()`, `ndelay()` - **长延时(可睡眠)**:`msleep()`, `ssleep()` - **调度控制**:`schedule_timeout()` - **高精度需求**:`hrtimer` 根据具体场景选择合适方法,避免在错误上下文中使用延时函数!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值