您的位置:首页 > 其它

定时器和时间管理

2018-02-04 21:02 162 查看

                          定时器和时间管理

1.概述

    时间管理在内核中占有非常重要的地位,相对于事件驱动而言,内核中有大量的函数都是基于时间驱动的。比如:更新系统运行时间、实际时间;在smp系统上,均衡调度程序中各处理器上的运行队列;检查当前进程是否用尽了自己的时间片;动态定时器的实现;更新资源消耗和处理器时间的统计值等等... 以上的种种行为都是基于系统定时器产生的时钟中断来实现的。

2.硬时钟
    体系结构提供了两种设备进行计时:一种是系统定时器;另外一种是实时时钟(RTC)。
     实时时钟:RTC是用来持久存放实时时间的设备,即便系统关闭后,它也可以靠主板上的微型电池提供的电力保持时间的一直记录。当系统启动时,内核通过读取RTC来初始化墙上时间(当前的时间),该时间存放在内核的xtime变量中。在有些体系结构(比如x86)会周期性的将当前时间存回RTC中。总之,实时时钟最主要的作用仍是在启动时初始化xtime变量。
     系统定时器:是一种可编程硬件芯片,它能以固定频率产生中断,该中断就是所谓的定时器中断,它所对应的中断处理程序负责更新系统时间,也负责执行需要周期性运行的任务。系统定时器和时钟中断处理程序是Linux系统内核管理机制中的中枢,内核必须在硬件的帮助下才能计算和管理时间。系统定时器以某种频率自行触发时钟中断,该频率可以通过编程预定,称作节拍率,也就是HZ(赫兹)。比如,在X86系统体系结构中,系统定时器频率默认值为100,所以时钟中断的频率为100HZ(100/s)。系统启动时按照HZ值对硬件进行设置,体系结构不同,HZ的值也不同,该值可以调节。
     由于内核中众多子系统都必须依赖时钟中断工作,所以改变中断频率必然对整个系统造成很大的冲击。但节拍率的高低也是有两面性的。
    高HZ的优势:提高时钟频率,意味着时钟中断产生的更加频繁,所以中断处理程序也会更频繁的执行。主要带来以下好处:
    ---提高了时间驱动事件的准确度,比如定时器的误差;对资源消耗和系统运行时间等的测量;进程调度的及时性,误差会更小;poll()、select()超时精度提高带来的性能提升。比如HZ=100(1s执行100次)的时钟执行的粒度为10ms,即系统中的周期事件最快为10ms运行一次,不可能有更高的精度。
    高HZ的劣势:节拍率越高,意味着时钟中断频率越高,也就意味着系统负担越重。因为处理器必须花时间来执行时钟中断处理程序,导致处理器被中断处理程序占用过多,这样就减少了处理器处理其他工作的时间,而且还会更频繁地打乱处理器高速缓存并增加耗电。
3.jiffies
      全局变量jiffies用来记录自系统启动以来产生的节拍的总数,启动时内核将该变量初始化为0,每次时钟中断处理程序就会增加该变量的值,因为1s内时钟中断的次数等于HZ,所以jiffies一秒内增加的值也就是HZ。同理,jiffies/HZ就是系统从启动开始到现在所运行的总秒数。
jiffies定义于文件<Linux/jiffies.h>中:extern unsigned long volatile jiffies;
秒转化为jiffies:(seconds * HZ)
jiffies转化为秒:(jiffies/HZ)
unsigned long time_stamp= jiffies;  --->现在

unsigned long next_tick = jiffies + 1;--->从现在开始1个节拍

unsigned long later = jiffies + 5*HZ---->从现在开始5秒

unsigned long fraction = jiffies + HZ / 10;--->从现在起1/10秒

4.时钟中断处理程序
    时钟中断处理程序可以分为两个部分:体系结构相关部分和体系结构无关部分,与体系结构相关的例程作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时,它能够相应的运行。
    与体系结构相关:
    --->获得xtime_lock锁,以便对访问jiffies_64和墙上时间xtime进行保护。
    --->需要时应答或者重新设置系统时钟。
    --->周期性地使用墙上时间更新RTC。
    --->调用体系结构无关的时钟例程:tick_periodic()。

    与体系结构无关,主要通过tick_periodic()执行下面更多的工作:
    --->给jiffies_64变量增加1。
    --->更新资源消耗的统计值,比如当前进程所消耗的系统时间和用户时间。
    --->执行已经到期的动态定时器。
    --->更新墙上时间,该时间存放在xtime变量中。
    --->计算平均负载值。
tick_periodic
    do_timer
        jiffies_64 += ticks;
        update_wall_time---->更新实际的时间。
              account_process_tick--->对进程的时间进行更新,根据中断发生时处理器所处的模式进行分类统计,节拍全部算给某个模式,但进程在上一个节拍期间可能多次进入和退出内核模式,统计有误差。
                     if(user_tick)
                     {
                          account_user_time
                      }
                      else if(系统)
                      {
    account_system_time

                       }
                       else
                       {
                               account_idle_time
                        }
        calc_global_load

墙上时间:
struct timespec
{
 _kernel_time_t tv_sec; /* 秒 */

long tv_nsec; /* ns */
}
struct timespec xtime;
从用户空间获取墙上时间的主要接口是gettimeofday()。
5.定时器
     定时器(有时也称为动态定时器或者内核定时器)是管理内核流逝的时间的基础。当需要在后面某个时间点执行某个动作,就需要用到定时器。这是较长的时间事件处理工具,是基于时钟中断工作的。
定时器由结构timer_list表示,定义在<linux/timer.h>中:
struct timeer_list
{
 struct list_head entry;----->定时器链表的入口

unsigned long expires;---->以jiffies为单位的定时值
void (*function) (unsigned long);--->定时器处理函数
 unsigned long date; --->传给处理函数的长整形参数
}
创建定时器步骤:
struct timer timer_list my_timer;
init_timer(&my_timer);

my_timer.expires = jiffies + delay;
my_timer.data = 0;

my_timer.function = my_function

add_timer(&my_timer);
mod_timer(&my_timer, jiffies + delay);  新的定时值
del_timer(&my_timer);

6.短延迟
    有时内核代码不但需要很短暂的延迟(比时钟节拍还短,HZ=1000时,1个节拍为1ms),而且还要求延迟的时间很精确。这种情况多发生在和硬件同步时,需要短暂等待某个动作的完成,等待时间往往小于1ms,所以就不能使用jiffies类似的方法。内核提供三个可以处理ms、ns、ms级别的延迟函数:
void udelay(unsigned long usecs)--->依靠执行数次的循环达到延迟的效果
void ndelay (unsigned long nsecs)
void mdelay(unsigned long msecs)---->依靠包装udelay来完成延时
因为内核知道处理器在一秒内能执行多少次循环,所以udelay函数仅仅需要根据指定的延迟时间在一秒钟占的比例,就能决定需要进行多少次循环可以达到要求的延时。内核在启动时利用calibrate_delay()计算loops_per_jiffy(处理器空闲时有多快),可以从文件/proc/cpuinfo中读到它。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  系统时钟定时器