内核定时器和延时

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); –> 可被打断

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值