Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1777227
  • 博文数量: 272
  • 博客积分: 1272
  • 博客等级: 少尉
  • 技术积分: 1866
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-09 15:51
文章分类

全部博文(272)

文章存档

2016年(16)

2015年(28)

2014年(97)

2013年(59)

2012年(25)

2011年(47)

分类: LINUX

2013-11-18 10:23:29

定时器分为硬件和软件定时器,软件定时器最终还是要依靠硬件定时器来完成。内核在时钟中断发生后检测各定时器是否到期,到期后的定时器处理函数将作为软中断在底半部执行。实质上,时钟中断处理程序执行update_process_timers函数,该函数调用run_local_timers函数,这个函数处理TIMER_SOFTIRQ软中断,运行当前处理上到期的所有定时器。

Linux内核中定义提供了一些用于操作定时器的数据结构和函数如下:

1)timer_list:说定时器,当然要来个定时器的结构体


struct timer_list{ 
struct list_head entry; //定时器列表
unsigned long expires; //定时器到期时间 
void (*function)(unsigned long) ;//定时器处理函数 
unsigned long 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 = (unsigned long)dev;
  dev->xxx_timer.expires = jiffies + delay;
  
  add_timer(&dev->xxx_timer); /*添加(注册)定时器*/ ... return 0;
} int xxx_func2(...) //驱动中某函数 {
  ...
  del_timer(&second_devp->s_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);
}

在定时器函数中往往会在做完具体工作后,延迟expires并将定时器再次添加到内核定时器链表中,以便定时器能被再次触发(这句话我也是从别处抄来的,别告诉小王哈)。

在内核定时器中,常常少不了要说下内核延迟的事,请接着往下看:

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。比如:


unsigned long delay = jiffies + 100; //延迟100个jiffies while(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(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);
}


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);
}

另外还有如下:
time_on_timeout(wait_queue_head_t *q, unsigned long  timeout);
interruptible_sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);
这两个将当前进程添加到等待队列,从而在等待队列上睡眠,当超时发生时,进程将被唤醒。
阅读(1164) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~