1. 内核定时器:
内核在时钟中断发生后检测各定时器是否到期,在linux内核中提供了一组函数和数据结构来完成定时触发工作/周期的事务。
timer_list 结构体: – 表示一个定时器
struct timer_list {
struct list_head entry; /* 定时器列表 */
unsigned long expires; /*定时器到期时间*/
void (*function)(unsigned long); /* 定时器处理函数指针 */
unsigned long data; /* 作为参数被传入定时器处理函数 */
struct timer_base_s *base;
...
};
expires,定时器的到期时间,单位是jiffies
function,定时器到期,要执行的函数
data,传入要执行的函数的参数
初始化定时器:
1.void init_timer(struct timer_list * timer);
功能是初始化 timer_list 结构体的 entry 的next 为 NULL,并给 base 指针赋值
2.TIMER_INITIALIZER(_function, _expires, _data)
功能是赋值 timer_list 结构体的 function、expires、data和 base 成员
函数原型是:
#define TIMER_INITIALIZER(_function, _expires, _data) { \
.entry = { .prev = TIMER_ENTRY_STATIC }, \
.function = (_function), \
.expires = (_expires), \
.data = (_data), \
.base = &boot_tvec_bases, \
}
3.DEFINE_TIMER(_name, _function, _expires, _data)
一个给结构体赋值的快捷方式,
函数原型是:
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
4.static inline void
setup_timer(struct timer_list * timer, void (*function)(unsigned long), unsigned long data)
这个函数也可以给 定时器 结构体成员赋值,
函数原型是:
static inline void setup_timer(struct timer_list * timer,
void (*function)(unsigned long),
unsigned long data)
{
timer->function = function;
timer->data = data;
init_timer(timer);
}
增加定时器:
void add_timer(struct timer_list * timer);
注册内核定时器,将定时器加入到内核动态定时器链表,即启动定时器
删除定时器:
int del_timer(struct timer_list * timer);
–> 直接删除 int del_timer_sync(struct timer_list * timer);
–> 等待定时器处理完之后删除,此函数不能出现在中断上下文。一般用在多 CPU 场合,定时器被另一个 CPU 使用的情况。
修改定时器:
int mod_timer(struct timer_list *timer, unsigned long expires);
这个函数有启动定时器的功能
使用思路:
- open 函数中setup_timer,想要用定时器的地方mod_timer.
- 定义 timer_list 结构体,在想要调用的地方填充上 timer_list里边的成员,init_timer,add_timer
注意事项:
- 定时器的每次添加之后,执行完定时器后就会失效,要想循环使用,需要在定时函数中添加
- del_timer 是删除没有发生的定时器,如果已经发生了,删除不删除应该无所谓
使用模板:
/*xxx 设备结构体*/
struct xxx_dev {
struct cdev cdev;
...
timer_list xxx_timer;/*设备要使用的定时器*/
};
/*xxx 驱动中的某函数*/
xxx_func1(…)
{
struct xxx_dev *dev = filp->private_data;
...
/*初始化定时器*/
init_timer(&dev->xxx_timer);
dev->xxx_timer.function = &xxx_do_timer;
dev->xxx_timer.data = (unsigned long)dev;
/*设备结构体指针作为定时器处理函数参数*/
dev->xxx_timer.expires = jiffies + delay; /* 定时器的到期时间往往是目前 jiffies 的基础上添加一个时延,若为 Hz,则表示延迟 1s。 */
/*添加(注册)定时器*/
add_timer(&dev->xxx_timer);
...
}
/*xxx 驱动中的某函数*/
xxx_func2(…)
{
...
/*删除定时器*/
del_timer (&dev->xxx_timer);
...
}
/*定时器处理函数*/
static void xxx_do_timer(unsigned long arg)
{
struct xxx_device *dev = (struct xxx_device *)(arg);
...
/*调度定时器再执行*/
dev->xxx_timer.expires = jiffies + delay;
add_timer(&dev->xxx_timer);
...
}
2. delayed_work
delayed_work是对于周期性的任务,linux提供的一个封装好的快捷方式
本质是利用定时器和工作队列实现的,功能就是延时执行
delayed_work 结构体:
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
struct work_struct {
atomic_long_t data;
#define WORK_STRUCT_PENDING 0
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
struct list_head entry;
work_func_t func;
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
调度:
int schedule_delayed_work(struct delayed_work *work, unsigned long delay);
当delay(单位是jiffies)延时后,work成员中的work_func_t 类型成员 func() 会被执行
如果要周期性的执行任务,需要在 delayed_work 的工作函数中再次调用 schedule_delayed_work()
ms 转化成 jiffies
msecs_to_jiffies(const unsigned int m);
取消 delayed_work:
int cancel_delayed_work(struct delayed_work *work);
int cancel_delayed_work_sync(struct delayed_work *work);
3. 内核延时
3.1 短延时:粗略的延时
忙等待的形式
void ndelay(unsigned long nsecs);
–> ns void udelay(unsigned long usecs);
–> us void mdelay(unsigned long msecs);
–> ms
睡眠等待的形式,这样线程放弃对系统资源的占用,解放cpu
void msleep(unsigned int millisecs);
–> 不可被打断 unsigned long msleep_interruptible(unsigned int millisecs);
–> 可被打断 void ssleep(unsigned int seconds);
–> 不可被打断
3.2 长延时:
一个直观的方式是比较当前 jiffies 和目标 jiffies。 time_after()
函数原型:
#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
time_before()
函数原型:
#define time_before(a,b) time_after(b,a)
一个忙等待先延时 100 个jiffies 再延迟 2s 的实例:
/*延迟 100 个 jiffies*/
unsigned long delay = jiffies + 100;
while (time_before(jiffies, delay));
/*再延迟 2s*/
unsigned long delay = jiffies + 2*Hz;
while (time_before(jiffies, delay));
3.3 睡着延时:
3.1 schedule_timeout() :
schedule_timeout_uninterruptible()
–> 调用 schedule_timeout()之前置进程状态为 TASK_
INTERRUPTIBLE schedule_timeout_interruptible()
–> 置进程状态为TASK_UNINTERRUPTIBLE
源码:
signed long _ _sched schedule_timeout_interruptible(signed long timeout)
{
_ _set_current_state(TASK_INTERRUPTIBLE);
return schedule_timeout(timeout);
}
signed long _ _sched schedule_timeout_uninterruptible(signed long timeout)
{
_ _set_current_state(TASK_UNINTERRUPTIBLE);
return schedule_timeout(timeout);
}
使用实例:
void msleep(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;
while (timeout)
timeout = schedule_timeout_uninterruptible(timeout);
}
unsigned long msleep_interruptible(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;
while (timeout && !signal_pending(current))
timeout = schedule_timeout_interruptible(timeout);
return jiffies_to_msecs(timeout);
}
3.2 sleep_on_timeout
功能:
将当前进程添加到等待队列中,在等待队列中睡眠,当超时发生时,进程被唤醒 sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);
–> 不可被打断 interruptible_sleep_on_timeout(wait_queue_head_t*q, unsigned long timeout);
–> 可被打断