内核定时器和延时

时间:2022-04-22 23:31:45

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);
这个函数有启动定时器的功能

使用思路:


  1. open 函数中setup_timer,想要用定时器的地方mod_timer.
  2. 定义 timer_list 结构体,在想要调用的地方填充上 timer_list里边的成员,init_timer,add_timer

注意事项:


  1. 定时器的每次添加之后,执行完定时器后就会失效,要想循环使用,需要在定时函数中添加
  2. 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); –> 可被打断