给大家上点儿干货

来源:互联网 发布:大数据技术论文 编辑:程序博客网 时间:2024/06/05 05:05

本文系本站原创,欢迎转载!

转载请注明出处:http://ericxiao.cublog.cn/

------------------------------------------

一:前言

时钟是整个操作系统的脉搏,它为进程的时间片调度,定时事件提供了依据.另外,用户空间的很多操作都依赖于时钟,例如select.poll,make.操作系统管理的时间为分两种,一种称为当前时间,也即我们日常生活所用的时间.这个时间一般保存在CMOS中.主板中有特定的芯片为其提供计时依据.另外一种时间称为相对时间.例如系统运行时间.显然对计算机而然,相对时间比当前时间更为重要.

二:与时钟有关的硬件处理.

1):实时时钟(RTC)

该时钟独立于CPU和其它芯片.即使PC断电,该时钟还是继续运行.该计时由一块单独的芯片处理,并把时钟值存放CMOS.该时间可参在IRQ8上周期性的产生时间信号.频率在2Hz ~ 8192Hz之间.但在linux中,只是用RTC来获取当前时间.

2):时间戳计时器(TSC)

CPU附带了一个64位的时间戳寄存器,当时钟信号到来的时候.该寄存器内容自动加1

3):可编程中断定时器(PIC)

该设备可以周期性的发送一个时间中断信号.发送中断信号的间隔可以对其进行编程控制.在linux系统中,该中断时间间隔由HZ表示.这个时间间隔也被称为一个节拍(tick).

4):CPU本地定时器

在处理器的本地APIC还提供了另外的一定定时设备.CPU本地定时器也可以单次或者周期性的产生中断信号.与上次描述的PIC相比.它有以下几点的区别:

APIC本地计时器是32位.而PIC是16位.由此APIC本地计时器可以提供更低频率的中断信号

本地APIC只把中断信号发送给本地CPU.而PIC发送的中断信号任何CPU都可以处理

APIC定时器是基于总线时钟信号的.而PIC有自己的内部时钟振荡器

5):高精度计时器(HPET)

在linux2.6中增加了对HPET的支持.HPET是一种由微软和intel联合开发的新型定时芯片.该设备有一组寄时器,每个寄时器对应有自己的时钟信号,时钟信号到来的时候就会自动加1.

实际上,在intel多理器系统与单处理器系统还有所不同:

在单处理系统中.所有计时活动过由PIC产生的时钟中断信号触发的

在多处理系统中,所有普通活动是由PIC产生的中断触发.所有具体的CPU活动,都由本地APIC触发的.

三:时钟中断相关代码分析

time_init()是时钟初始化函数,他由asmlinkage void __init start_kernel()调用.具体代码如下:

//时钟中断初始化

void __init time_init(void)

{

//如果定义了HPET

#ifdef CONFIG_HPET_TIMER

     if (is_hpet_capable()) {

         /*

          *HPET initialization needs to do memory-mapped io. So, let

          *us do a late initialization after mem_init().

          */

        late_time_init = hpet_time_init;

         return;

     }

#endif

 

     //从cmos 中取得实时时间

     xtime.tv_sec = get_cmos_time();

     //初始化wall_to_monotonic

     wall_to_monotonic.tv_sec =-xtime.tv_sec;

     xtime.tv_nsec = (INITIAL_JIFFIES% HZ) * (NSEC_PER_SEC / HZ);

     wall_to_monotonic.tv_nsec =-xtime.tv_nsec;

     //选择一个合适的定时器

     cur_timer = select_timer();

     printk(KERN_INFO "Using %sfor high-res timesource\n",cur_timer->name);

     //注册时间中断信号处理函数

     time_init_hook();

}

该函数从cmos取得了当前时间.并为调整时间精度选择了合适的定时器

转入time_init_hook():

 

void __init time_init_hook(void)

{

     //注册中断处理函数

     setup_irq(0, &irq0);

}

Irq0定义如下:

static struct irqaction irq0  = { timer_interrupt,SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};

对应的中断处理函数为:timer_interrupt():

irqreturn_t timer_interrupt(int irq, void *dev_id, structpt_regs *regs)

{

     //因为该函数会修改xtime的值,为避免多处理器竞争.先加锁

     write_seqlock(&xtime_lock);

     //记录上一次时间中断的精确时间.做调准时钟用

     cur_timer->mark_offset();

     do_timer_interrupt(irq, NULL,regs);

     //解锁

    write_sequnlock(&xtime_lock);

     return IRQ_HANDLED;

}

核心处理函数为 do_timer_interrupt():

static inline void do_timer_interrupt(int irq, void*dev_id,

                      struct pt_regs *regs)

{

#ifdef CONFIG_X86_IO_APIC

     if (timer_ack) {

        spin_lock(&i8259A_lock);

        outb(0x0c, PIC_MASTER_OCW3);

         /* Ackthe IRQ; AEOI will end it automatically. */

        inb(PIC_MASTER_POLL);

        spin_unlock(&i8259A_lock);

     }

#endif

 

     do_timer_interrupt_hook(regs);

 

     //如果要进行时间同步,那就隔一段时间把当前时间写回coms

     if ((time_status &STA_UNSYNC) == 0 &&

         xtime.tv_sec> last_rtc_update + 660 &&

        (xtime.tv_nsec / 1000)

             >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 &&

        (xtime.tv_nsec / 1000)

             <= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2) {

         /*horrible...FIXME */

         if(efi_enabled) {

             if(efi_set_rtc_mmss(xtime.tv_sec) == 0)

                  last_rtc_update = xtime.tv_sec;

             else

                  last_rtc_update = xtime.tv_sec - 600;

         } elseif (set_rtc_mmss(xtime.tv_sec) == 0)

             last_rtc_update = xtime.tv_sec;

         else

             last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */

     }

 

#ifdef CONFIG_MCA

     if( MCA_bus ) {

         /* ThePS/2 uses level-triggered interrupts.  You can't

         turnthem off, nor would you want to (any attempt to

         enableedge-triggered interrupts usually gets intercepted by a

         specialhardware circuit).  Hence we have to acknowledge

         thetimer interrupt.  Through some incredibly stupid

         designidea, the reset for IRQ 0 is done by setting the

         high bitof the PPI port B (0x61).  Note that some PS/2s,

         notablythe 55SX, work fine if this is removed.  */

 

         irq =inb_p( 0x61 );   /* read the current state */

         outb_p(irq|0x80, 0x61 );   /* reset the IRQ */

     }

#endif

}

我们忽略选择编译部份,转到do_timer_interrupt_hook()

static inline void do_timer_interrupt_hook(struct pt_regs*regs)

{

     do_timer(regs);

/*

 * In the SMP case we use the local APIC timerinterrupt to do the

 * profiling, except when we simulate SMP mode on auniprocessor

 * system, in that case we have to call the localinterrupt handler.

 */

#ifndef CONFIG_X86_LOCAL_APIC

     //更新内核代码监管器。在每次时钟中断的时候。取得每一次中断前的esp,进而可以得到运行的函//数地址。这样就可以统计运行时间最长的函内核函数区域。以便于内核管理者优化

     profile_tick(CPU_PROFILING,regs);

#else

     if (!using_apic_timer)

        smp_local_timer_interrupt(regs);

#endif

}

这里有几个重要的操作.先看do_timer():

void do_timer(struct pt_regs *regs)

{

     // 更新jiffies计数.jiffies_64与jiffies在链接的时候,实际是指向同一个区域

     jiffies_64++;

#ifndef CONFIG_SMP

     /* SMP process accounting usesthe local APIC timer */

     //更新当前运行进程的与时钟相关的信息

    update_process_times(user_mode(regs));

#endif

     //更新当前时间.xtime的更新

     update_times();

}

Update_process_times()代码如下:

void update_process_times(int user_tick)

{

     struct task_struct *p = current;

     int cpu = smp_processor_id(),system = user_tick ^ 1;

 

     update_one_process(p, user_tick,system, cpu);

     //激活时间软中断

     run_local_timers();

     //减少时间片。这个函数涉及到的东西过多,等到进程调度的时候再来分析。请关注本站更新*^_^*

     scheduler_tick(user_tick,system);

}

先看update_one_process():

static void update_one_process(struct task_struct *p,unsigned long user,

             unsigned long system, int cpu)

{

     do_process_times(p, user,system);

 

     //检查进程的定时器

     do_it_virt(p, user);

     do_it_prof(p);

}   

在这里简单介绍一下do_it_virt()与do_it_prof():

这两个函数主要检查用户空间的进程定时器是否到期.在进程的内存描述符有相关的字段.如下:

 

struct task_struct{

unsigned long it_real_value, it_prof_value,it_virt_value;

unsigned long it_real_incr, it_prof_incr, it_virt_incr;

struct timer_list real_timer;

}

(1)真实间隔定时器(ITIMER_REAL):这种间隔定时器在启动后,不管进程是否运行,每个时钟滴答都将其间隔计数器减1。当减到0值时,内核向进程发送SIGALRM信号。结构类型task_struct中的成员it_real_incr则表示真实间隔定时器的间隔计数器的初始值,而成员it_real_value则表示真实间隔定时器的间隔计数器的当前值。由于这种间隔定时器本质上与上一节的内核定时器时一样的,因此Linux实际上是通过real_timer这个内嵌在task_struct结构中的内核动态定时器来实现真实间隔定时器ITIMER_REAL的。

(2)虚拟间隔定时器ITIMER_VIRT:也称为进程的用户态间隔定时器。结构类型task_struct中成员it_virt_incr和it_virt_value分别表示虚拟间隔定时器的间隔计数器的初始值和当前值,二者均以时钟滴答次数位计数单位。当虚拟间隔定时器启动后,只有当进程在用户态下运行时,一次时钟滴答才能使间隔计数器当前值it_virt_value减1。当减到0值时,内核向进程发送SIGVTALRM信号(虚拟闹钟信号),并将it_virt_value重置为初值it_virt_incr。具体请见7.4.3节中的do_it_virt()函数的实现。

(3)PROF间隔定时器ITIMER_PROF:进程的task_struct结构中的it_prof_value和it_prof_incr成员分别表示PROF间隔定时器的间隔计数器的当前值和初始值(均以时钟滴答为单位)。当一个进程的PROF间隔定时器启动后,则只要该进程处于运行中,而不管是在用户态或核心态下执行,每个时钟滴答都使间隔计数器it_prof_value值减1。当减到0值时,内核向进程发送SIGPROF信号,并将it_prof_value重置为初值it_prof_incr.

 

Do_process_times():
static inline void do_process_times(struct task_struct *p,

     unsigned long user, unsignedlong system)

{

     unsigned long psecs;

 

     //p->utime:在用户空间所花的时间

     psecs = (p->utime += user);

     //p->stime:在系统空间所花的时间

     psecs += (p->stime +=system);

 

     //如果运行的时间片到达

     if (psecs / HZ >=p->rlim[RLIMIT_CPU].rlim_cur) {

         /* SendSIGXCPU every second.. */

         //每秒发送一个SIGXCPU

         if(!(psecs % HZ))

             send_sig(SIGXCPU, p, 1);

         /* andSIGKILL when we go over max.. */

         //发送SIGKILL

         if(psecs / HZ >= p->rlim[RLIMIT_CPU].rlim_max)

             send_sig(SIGKILL, p, 1);

     }

}

该函数检查当前进程的时间片是否到达,如果到达就给当前进程发送SIGKILL和SIGXCPU

do_it_virt()/do_it_prof()检查过程的定时器是否到期.如果到期就给进程发送相应的信号:

static inline void do_it_virt(struct task_struct * p,unsigned long ticks)

{

     unsigned long it_virt =p->it_virt_value;

 

     if (it_virt) {

         it_virt-= ticks;

         if(!it_virt) {

             it_virt = p->it_virt_incr;

             //发送SIGVTALRM

             send_sig(SIGVTALRM, p, 1);

         }

        p->it_virt_value = it_virt;

     }

}

返回到update_process_times()的其它函数:

run_local_timers()

void run_local_timers(void)

{

     raise_softirq(TIMER_SOFTIRQ);

}

激活时间软中断.这个函数我们在IRQ中断中已经分析过了,不再赘述

我们在do_timer()还漏掉了一个函数:

static inline void update_times(void)

{

         unsignedlong ticks;

 

        //wall_jiffies:上一次更新的值

         ticks =jiffies - wall_jiffies;

         if(ticks) {

                  wall_jiffies += ticks;

                  //更新xtime

                  update_wall_time(ticks);

         }

         //统计TASK_RUNNING TASK_UNINTERRUPTIBLE进程数量

        calc_load(ticks);

}

四:定时器

在模块的编写过程中,我们经常使用定时器来等待一段时间之后再来执行某一个操作。为方便分析,写了下列一段测试程序:

#include <linux/config.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/interrupt.h>

#include <linux/delay.h>

#include <linux/timer.h>

 

MODULE_LICENSE("GPL");

void test_timerfuc(unsigned long x)

{

        printk("Eric xiao test ......\n");

}

//声明一个定个器

struct timer_list test_timer =TIMER_INITIALIZER(test_timerfuc, 0, 0);

 

int kernel_test_init()

{

        printk("test_init\n");

         //修改定时器到期时间。为3个HZ。一个HZ产生一个时钟中断

        mod_timer(&test_timer,jiffies+3*HZ);

         //把定时器加入时钟软中断处理链表

        add_timer(&test_timer);

 

}

 

int kernel_test_exit()

{

        printk("test_exit\n");

 

         return0;

}

module_init(kernel_test_init);

module_exit(kernel_test_exit);

上面的例子程序比较简单,我们从这个例子开始研究linux下的定时器实现。

TIMER_INITIALIZER():

1):TIMER_INITIALIZER()用来声明一个定时器,它的定义如下:

#define TIMER_INITIALIZER(_function, _expires, _data){             \

                  .function =(_function),                           \

                  .expires =(_expires),                               \

                  .data =(_data),                               \

                  .base =NULL,                                        \

                  .magic =TIMER_MAGIC,                             \

                  .lock =SPIN_LOCK_UNLOCKED,                        \

         }

Struct timer_list定义如下:

struct timer_list {

         //用来形成链表

         structlist_head entry;

         //定始器到达时间

         unsignedlong expires;

 

        spinlock_t lock;

         unsignedlong magic;

         //定时器时间到达后,所要运行的函数

         void(*function)(unsigned long);

         //定时器函数对应的参数

         unsignedlong data;

 

         //挂载这个定时器的tvec_t_base_s.这个结构我们等会会看到

struct tvec_t_base_s *base; 

};

从上面的过程中我们可以看到TIMER_INITIALIZER()只是根据传入的参数初始化了struct timer_list结构.并把magic 成员初始化成TIMER_MAGIC

2): mod_timer():修改定时器的到时时间

int mod_timer(struct timer_list *timer, unsigned longexpires)

{

         //如果该定时器没有定义fuction

        BUG_ON(!timer->function);

         //判断timer的magic是否为TIMER_MAGIC.如果不是,则将其修正为TIMER_MAGIC

        check_timer(timer);

 

         //如果要调整的时间就是定时器的定时时间而且已经被激活,则直接返回

         if(timer->expires == expires && timer_pending(timer))

                  return 1;

         //调用_mod_timer().呆会再给出分析

         return__mod_timer(timer, expires);

}

3): add_timer()用来将定时器挂载到定时软中断队列,激活该定时器

static inline void add_timer(struct timer_list * timer)

{

        __mod_timer(timer, timer->expires);

}

可以看到mod_timer与add_timer 最后都会调用__mod_timer().为了分析这个函数,我们先来了解一下定时系统相关的数据结构.

tvec_bases: per cpu变量,它的定义如下:

static DEFINE_PER_CPU(tvec_base_t, tvec_bases) = {SPIN_LOCK_UNLOCKED };

由此可以看到tves_bases的数型数据为teves_base_t.数据结构的定义如下:

typedef struct tvec_t_base_s tvec_base_t;

struct tvec_t_base_s的定义:

struct tvec_t_base_s {

        spinlock_t lock;

         //上一次运行计时器的jiffies 值

         unsignedlong timer_jiffies;

         structtimer_list *running_timer;

         //tv1tv2 tv3 tv4 tv5是五个链表数组

        tvec_root_t tv1;

         tvec_ttv2;

         tvec_ttv3;

         tvec_ttv4;

         tvec_ttv5;

} ____cacheline_aligned_in_smp;

Tves_root_t与tvec_t的定义如下:

#define TVN_BITS 6

#define TVR_BITS 8

#define TVN_SIZE (1 << TVN_BITS)

#define TVR_SIZE (1 << TVR_BITS)

#define TVN_MASK (TVN_SIZE - 1)

#define TVR_MASK (TVR_SIZE - 1)

 

typedef struct tvec_s {

         structlist_head vec[TVN_SIZE];

} tvec_t;

 

typedef struct tvec_root_s {

         structlist_head vec[TVR_SIZE];

} tvec_root_t;

系统规定定时器最大超时时间间隔为0xFFFFFFFF.即为一个32位数.即使在64位系统上.如果超过此值也会将其强制设这oxFFFFFFFF(这在后面的代码分析中可以看到).内核最关心的就是间隔在0~255个HZ之间的定时器.次重要的是间隔在255~1<<(8+6)之间的定时器.第三重要的是间隔在1<<(8+6) ~ 1<<(8+6+6)之间的定器.依次往下推.也就是把32位的定时间隔为份了五个部份.1个8位.4个6位.所以内核定义了五个链表数组.第一个链表数组大小为8位大小,也即上面定义的 #define TVR_SIZE(1 << TVR_BITS).其它的四个数组大小为6位大小.即上面定义的#define TVN_SIZE (1<< TVN_BITS)

在加入定时器的时候,按照时间间隔把定时器加入到相应的数组即可.了解这点之后,就可以来看__mod_timer()的代码了:

//修改timer或者新增一个timer都会调用此接口

int __mod_timer(struct timer_list *timer, unsigned longexpires)

{

        tvec_base_t *old_base, *new_base;

         unsignedlong flags;

         int ret= 0;

 

         //入口参数检测

        BUG_ON(!timer->function);

 

        check_timer(timer);

 

        spin_lock_irqsave(&timer->lock, flags);

         //取得当前CPU对应的tvec_bases

         new_base= &__get_cpu_var(tvec_bases);

repeat:

         //该定时器所在的tvec_bases.对于新增的timer.它的base字段为NULL

         old_base= timer->base;

 

         /*

          *Prevent deadlocks via ordering by old_base < new_base.

          */

 

         //在把timer从当前tvec_bases摘下来之前,要充分考虑好竞争的情况

         if(old_base && (new_base != old_base)) {

                  //按次序获得锁

                  if (old_base < new_base) {

                           spin_lock(&new_base->lock);

                           spin_lock(&old_base->lock);

                  } else {

                           spin_lock(&old_base->lock);

                           spin_lock(&new_base->lock);

                  }

                  /*

                   * The timer base might have been cancelled while we were

                   * trying to take the lock(s):

                   */

                   //如果timer->base !=old_base.那就是说在Lock的时候.其它CPU更改它的值

                   //那就解锁.重新判断

                  if (timer->base != old_base) {

                           spin_unlock(&new_base->lock);

                           spin_unlock(&old_base->lock);

                           goto repeat;

                  }

         } else {

                  //old_base == NULl 或者是 new_base==old_base的情况

                  //获得锁

                  spin_lock(&new_base->lock);

                  //同理,在Lock的时候timer会生了改变

                  if (timer->base != old_base) {

                           spin_unlock(&new_base->lock);

                           goto repeat;

                  }

         }

 

         /*

          *Delete the previous timeout (if there was any), and install

          *the new one:

          */

          //将其从其它的tvec_bases上删除.注意运行到这里的话,说话已经被Lock了

         if(old_base) {

                  list_del(&timer->entry);

                  ret = 1;

         }

         //修改它的定时器到达时间

        timer->expires = expires;

         //将其添加到new_base中

        internal_add_timer(new_base, timer);

         //修改base字段

        timer->base = new_base;

 

         //操作完了,解锁

         if(old_base && (new_base != old_base))

                  spin_unlock(&old_base->lock);

        spin_unlock(&new_base->lock);

        spin_unlock_irqrestore(&timer->lock, flags);

 

         returnret;

}

internal_add_timer()的代码如下:

static void internal_add_timer(tvec_base_t *base, structtimer_list *timer)

{

         //定时器到达的时间

         unsignedlong expires = timer->expires;

         //计算时间间间隔

         unsignedlong idx = expires - base->timer_jiffies;

         structlist_head *vec;

 

         //根据时间间隔,将timer放入相应数组的相应位置

         if (idx< TVR_SIZE) {

                  int i = expires & TVR_MASK;

                  vec = base->tv1.vec + i;

         } elseif (idx < 1 << (TVR_BITS + TVN_BITS)) {

                  int i = (expires >> TVR_BITS) & TVN_MASK;

                  vec = base->tv2.vec + i;

         } elseif (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {

                  int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;

                  vec = base->tv3.vec + i;

         } elseif (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {

                  int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;

                  vec = base->tv4.vec + i;

         } elseif ((signed long) idx < 0) {

                  /*

                   * Can happen if you add a timer with expires == jiffies,

                   * or you set a timer to go off in the past

                   */

                   //如果间隔小于0

                  vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);

         } else {

                  int i;

                  /* If the timeout is larger than 0xffffffff on 64-bit

                   * architectures then we use the maximum timeout:

                   */

                   //时间间隔超长,将其设为oxFFFFFFFF

                  if (idx > 0xffffffffUL) {

                           idx = 0xffffffffUL;

                           expires = idx + base->timer_jiffies;

                  }

                  i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;

                  vec = base->tv5.vec + i;

         }

         /*

          *Timers are FIFO:

          */

          //加入到链表末尾

        list_add_tail(&timer->entry, vec);

}

计算时间间隔即可知道要加入到哪一个数组.哪又怎么计算加入到该数组的那一项呢

对于间隔时间在0~255的定时器: 它的计算方式是将定时器到达时间的低八位与低八位为1的数相与而成

对于第1个六位,它是先将到达时间右移8位.然后与低六位全为1的数相与而成

对于第2个六位, 它是先将到达时间右移8+6位.然后与低六位全为1的数相与而成

依次为下推…

在后面结合超时时间到达的情况再来分析相关部份

4):定时器更新

每过一个HZ,就会检查当前是否有定时器的定时器时间到达.如果有,运行它所注册的函数,再将其删除.为了分析这一过程,我们先从定时器系统的初始化看起.

asmlinkage void __init start_kernel(void)

{

         ……

        init_timers();

         ……

}

Init_timers()的定义如下:

void __init init_timers(void)

{

        timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,

                                    (void *)(long)smp_processor_id());

        register_cpu_notifier(&timers_nb);

         //注册TIMER_SOFTIRQ软中断

        open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);

}

timer_cpu_notify()àinit_timers_cpu():

代码如下:

static void /* __devinit */ init_timers_cpu(int cpu)

{

         int j;

        tvec_base_t *base;

     //初始化各个数组中的链表 

         base =&per_cpu(tvec_bases, cpu);

        spin_lock_init(&base->lock);

         for (j =0; j < TVN_SIZE; j++) {

                  INIT_LIST_HEAD(base->tv5.vec + j);

                  INIT_LIST_HEAD(base->tv4.vec + j);

                  INIT_LIST_HEAD(base->tv3.vec + j);

                  INIT_LIST_HEAD(base->tv2.vec + j);

         }

         for (j =0; j < TVR_SIZE; j++)

                  INIT_LIST_HEAD(base->tv1.vec + j);

         //将最近到达时间设为当前jiffies

        base->timer_jiffies = jiffies;

}

我们在前面分析过,每当时钟当断函数到来的时候,就会打开定时器的软中断.运行其软中断函数.run_timer_softirq()

代码如下:

static void run_timer_softirq(struct softirq_action *h)

{

         //取得当于CPU的tvec_base_t结构

         tvec_base_t*base = &__get_cpu_var(tvec_bases);

         //如果jiffies > base->timer_jiffies

         if(time_after_eq(jiffies, base->timer_jiffies))

                  __run_timers(base);

}

__run_timers()代码如下:

static inline void __run_timers(tvec_base_t *base)

{

         structtimer_list *timer;

         unsignedlong flags;

 

        spin_lock_irqsave(&base->lock, flags);

         //因为CPU可能关闭中断,引起时钟中断信号丢失.可能jiffies要大base->timer_jiffies 好几个

         //HZ

         while(time_after_eq(jiffies, base->timer_jiffies)) {

                  //定义并初始化一个链表

                  struct list_head work_list = LIST_HEAD_INIT(work_list);

                  struct list_head *head = &work_list;

                 int index = base->timer_jiffies & TVR_MASK;

 

                  /*

                   * Cascade timers:

                   */

                   //当index == 0时,说明已经循环了一个周期

                  //则将tv2填充tv1.如果tv2为空,则用tv3填充tv2.依次类推......

                  if (!index &&

                           (!cascade(base, &base->tv2, INDEX(0))) &&

                                    (!cascade(base, &base->tv3, INDEX(1))) &&

                                              !cascade(base, &base->tv4, INDEX(2)))

                           cascade(base, &base->tv5, INDEX(3));

                  //更新base->timer_jiffies

                  ++base->timer_jiffies;

                  //将base->tv1.vec项移至work_list.并将base->tv1.vec置空

                  list_splice_init(base->tv1.vec + index, &work_list);

repeat:

                  //work_List中的定时器是已经到时的定时器

                  if (!list_empty(head)) {

                           void (*fn)(unsigned long);

                           unsigned long data;

 

                           //遍历链表中的每一项.运行它所对应的函数,并将定时器从链表上脱落

                           timer = list_entry(head->next,struct timer_list,entry);

                          fn = timer->function;

                          data = timer->data;

 

                           list_del(&timer->entry);

                           set_running_timer(base, timer);

                           smp_wmb();

                           timer->base = NULL;

                           spin_unlock_irqrestore(&base->lock, flags);

                           fn(data);

                           spin_lock_irq(&base->lock);

                           goto repeat;

                  }

         }

        set_running_timer(base, NULL);

        spin_unlock_irqrestore(&base->lock, flags);

}

如果base->timer_jiffies低八位为零.说明它向第九位有进位.所以把第九位到十五位对应的定时器搬到前八位对应的数组.如果第九位到十五位为空的话.就到它的上个六位去搬数据.上面的代码也说明.要经过1<<8个HZ才会更新全部数组中的定时器.这样做的效率是很高的.

分析下里面的两个重要的子函数:

static int cascade(tvec_base_t *base, tvec_t *tv, intindex)

{

         /*cascade all the timers from tv up one level */

         structlist_head *head, *curr;

 

         //取数组中序号对应的链表

         head =tv->vec + index;

         curr =head->next;

         /*

          *We are removing _all_ timers from the list, so we don't  have to

          *detach them individually, just clear the list afterwards.

          */

          //遍历这个链表,将定时器重新插入到base中

         while(curr != head) {

                  struct timer_list *tmp;

 

                  tmp = list_entry(curr, struct timer_list, entry);

                  BUG_ON(tmp->base != base);

                  curr = curr->next;

                  internal_add_timer(base, tmp);

         }

         //将链表设为初始化状态

        INIT_LIST_HEAD(head);

 

         returnindex;

}

 

//将list中的数据放入head中,并将list置为空

static inline void list_splice_init(struct list_head*list,

                                        struct list_head *head)

{

         if(!list_empty(list)) {

                  __list_splice(list, head);

                  INIT_LIST_HEAD(list);

         }

}

//将list中的数据放入head

static inline void __list_splice(struct list_head *list,

                                     struct list_head *head)

{

         //list的第一个元素

         structlist_head *first = list->next;

         //list的最后一个元素

         structlist_head *last = list->prev;

         //head的第一个元素

         structlist_head *at = head->next;

 

         将first对应的链表链接至head

        first->prev = head;

        head->next = first;

 

         //将head 原有的数据加入到链表末尾

        last->next = at;

        at->prev = last;

}

5):del_timer()删除定时器

//删除一个timer

int del_timer(struct timer_list *timer)

{

         unsignedlong flags;

        tvec_base_t *base;

 

        check_timer(timer);

 

repeat:

        base =timer->base;

         //该定时器没有被激活

         if(!base)

                  return 0;

         //加锁

        spin_lock_irqsave(&base->lock, flags);

         //如果在加锁的过程中,有其它操作改变了timer

         if (base!= timer->base) {

                  spin_unlock_irqrestore(&base->lock, flags);

                  goto repeat;

         }

         //将timer从链表中删除

        list_del(&timer->entry);

        timer->base = NULL;

        spin_unlock_irqrestore(&base->lock, flags);

 

         return1;

}

6): del_timer_sync()有竞争情况下的定时器删除

在SMP系统中,可能要删除的定时器正在某一个CPU上运行.为了防止这种在情况.在删除定时器的时候,应该优先使用del_timer_synsc().它会一直等待所有CPU上的定时器执行完成.

int del_timer_sync(struct timer_list *timer)

{

        tvec_base_t *base;

         int i,ret = 0;

 

        check_timer(timer);

 

del_again:

         //删除些定时器

         ret +=del_timer(timer);

         //遍历CPU

        for_each_online_cpu(i) {

                  base = &per_cpu(tvec_bases, i);

                  //如果此CPU正在运行这个timer

                  if (base->running_timer == timer) {

                           //一直等待,直到这个CPU执行完

                           while (base->running_timer == timer) {

                                    cpu_relax();

                                    preempt_check_resched();

                           }

                           break;

                  }

         }

        smp_rmb();

         //如果这个timer又被调用.再删除

         if(timer_pending(timer))

                  goto del_again;

 

         returnret;

}

定时器部份到这里就介绍完了.为了管理定时器.内核用了一个很巧妙的数据结构.值得好好的体会.

五:小结

2.6内核在时钟管理子系统的修改比较大.因为在2.6完全摒弃掉了下半部机制.2.4中下半部处理的大部份都放在了中断处理程序里,只有定时器控制被移到了时钟软中断.另外时钟中断初始化涉及到了很多硬件的操作.需要查阅相关资料才能完全理解.