定时测量

来源:互联网 发布:qq绑定软件管理 编辑:程序博客网 时间:2024/04/29 10:10

      1linux内核给用户很多选择选项,为了兼容不同的硬件提供了多种选项,硬件定时器(HPETTSC),软件定时器。 
        2)
注意处理竞争条件(在多核情况下,会引入在单核情况下不存在的竞争条件,例如,定时器函数在【被删除函数】终止时可能在其他CPU上运行【和删除函数不在一个CPU上】,del_timer_sync()删除指定的定时器后,需要检查定时函数是否还在其他CPU上运行,如果是,则需要等到定时器函数结束) 
      3
)在体系结构中分为 全局时钟 CPU本地时钟 
    
  4) 监管内核代码,依赖于eip寄存器的值,来揭示中断发生前内核在做什么。 
      5)看门狗的实现依赖于 非屏蔽中断(NMI),它们在每个CPU上产生周期性的NMI中断。 
  用tv1,tv2,tv3,tv4,tv5将list_head元素分为在将来2^8-1,2^14-1, 2^20-1, 2^26-1和2^32-1时钟节拍内将要到期的所有动态定时器。(分而治之的思想)


一、定时器 
     

       RTC振源内部提供,自己有电源,电脑关机后也能自己位置时间,Linux只用RTC来获取时间和日期,可以通过/dev/rtc对设备文件进行操作,可以对RTC编程。 

       TSC:振源外部提供,注意它的振源频率可能是变幻的(有些电脑可以节约功耗,变频工作),所以即使他的频率高,但是不一定选取。

TSC64位)每个时钟信号到来时加1其单位与时钟节拍频率有关,如果时钟节拍频率是1GHz,则单位是ns(即每1na增加1,是计数器(是在上电后工作,RTC可以在断电后工作,RTC有自己的电源)。calibrate_tsc()通过计算一个大约5ms(这个时间是有PIT提供的)的时间间隔内所产生的时钟信号的 个数 来算出 CPU实际频率。 

       PIT :振源内波提供,他是可以编写的。。你可以规定他的频率,所以你可以在电脑启动时候得知一个固定的时间,但是频率不高。大约能提供1ms一次中断。通常使用0X40 ~ 0x43 I/O端口的一个8254CMOS芯片。 

       HPETACPIPMT:振源外部提供,他们两个都是高精度,但是明显HPET精度更高(听名字都知道哈。。==!)

二、常用数据结构 
    1
)定时器对象:timer_opt 
       { name, 
          mark_offset,
上一个节拍的准确时间 
          get_offset,
上一个节拍开始经过的时间 (这个主要是拿来获取字节拍,假如你的时钟中断1ms一次,那你要知道100ns这个时间段的时候就会用到这个)
         monotonic_clock, 纳秒数 
         delay 
        } 
     2jiffies32位)变量  
     
系统启动后每次时钟节拍加1(大约50天会回绕wraparound到 0)。 
     初始化为0xfffb6c20,(-300 000)。大约5分钟内能益出,目的是检查有缺陷的(不能对jiffies作溢出检测)内核代码 
   
  unsigned long longget_jiffies_64(void) 
     { 
         unsigned long seq; 
         unsigned long long ret; 
         do{ 
               seq=read_seqbegin(&xtime_lock); 
               ret=jiffies_64; 
         }while(read_seqretry(&xtime_lock,seq)); (注意这里是检查是否读取的和上次一样,如果不一样就说明中途被改过了。。就要重新读取)
         return ret; 
     } 
      voidupdate_times(void) 
      { 
         unsigned long ticks; 
         ticks= jiffies –wall_jiffies; //wall_jiffies变量存储的xtime变量最后更新时间 
         if(ticks){ 
             wall_jiffies +=ticks; 
             update_wall_time 
         } 
          calc_load(ticks); 
      } 
     3)xtime 变量 
      保存当前时间和日期,timespec类型的数据结构{tv_sec,tv_nsec}。

三、记录系统负载 
     1
)监管内核代码(readprofiler 
     
为了确定内核的热点”hotspot。,内核从堆栈取回中断发生前的eip寄存器的值,用这个值揭示中断发生前内核正在做什么。 
     
内核启动时“profile=N”,2^N表示要监管代码的大小。 
      /proc/profile
中保存采集的数据。 
      2
)检查NMI监视器:这个是一个看门狗,他每次中断时不能屏蔽的,每次中断检查一个变量是否加1,正常情况下这个变量每次时钟中断(定时器的中断)都加1,如果出现中断没有发生,或者没有捕获,则说明该cup肯定有什么问题。


四、动态定时器和间隔定时器         
     
两种类型定时器:动态定时器(只能由内核使用),间隔定时器(可以由进程在用户态创建) 
      1
)动态定时器 
        struct timer_list{ 
             struct list_head entry; 
              unsignedlong expires;
定时器到期时间 
             spinlock_t lock; 
             unsigned long magic; 
             void (*function) (unsigned long);
定时器到期执行函数的地址 
            unsigned long data; 
传递给定时器函数的参数(可以存放设备ID,实现处理多个设备驱动程序的超时问题) 
            tvec_base_t * base; 
        } 
         del_singleshot_timer_sync
()函数不能重新激活 

 


        typedef struct tvec_t_base_s { 
     spinlock_t lock; 
          unsigned longtimer_jiffies; 
          struct timer_list*running_timer; 
tvec_root_t tv1; 
将来2^8-1节拍 
         tvec_ttv2;           2^14-1 
    tvec_ttv3;           2^20-1 
          tvec_ttv4;         2^26-1 
          tvec_ttv5;         2^32 -1 
       }tvec_base_t; 
  

定时器管理思想:这里我们主要是将某个时间点,后面的256个时间中断将到时的定时器(这里可以看出定时器的精度问题)放到tv1,在25664的的时钟中断放在tv2,以此类推。每次中断都来,我们都检查tv1上是否为空,为空我们才把tv2移动到tv1,在检查tv2上是否为空,为空我们在吧tv3移动过来,依次类推。这样我们可以节约很多处理时间。。
        if(!index &&  (!
cascade(base,&base->tv2,(base->timer_jiffies>>8)&63)) && 
           //
tv2中的所有动态定时器移到base->tv1适当的链表上。 
         (!cascade(base, &base->tv3,(base->timer_jiffies>>14)&63)) && 

      (!cascade(base,&base->tv4,(base->timer_jiffies>>20)&63)) ) 
          cascade(base,&base->tv5,(base->timer_jiffies>>26)&63);

    

2)延迟函数:延迟函数用到的就是定时器方法中的delay;根据精度不同提供udelay(单位us),ndelay(单位ns);


总体总结:我们电脑会选取一个定时器,一般有多个,我们选取其中一个,然后这个定时器为我们输送一个定时的中断(如1ms一次中断),然后每次中断来到,我们就要更新系统时间,然后是触发软中断,去检查哪些挂着的软定器是否到时。(当然还有其他的一些,如记录负载等等),但是我们主要是这个,所以软定器不一定准,第一是由于给cpu的中断精度不高。第二是中断来的时候,依次执行,假如tv1数组上tv1[0]上面挂有很多个软定时器,则大家可以想象肯定后面的要慢点。。。所以这个定时器是不绝对准的。

 


0 0