定时测量
一、Linux内核提供两种主要的定时测
- 获得当前的时间和日期:系统调用:time(), ftime()以及gettimeofday()
- 维持定时器:settimer(), alarm()
二、定时测量是由基于固定频率振荡器和计数器的几个硬件电路完成的:RTC等
三、实时时钟RTC
- 能在IRQ8上发出周期性的中断,频率在2HZ~8192之间
- 与CMOS RAM往往集成在一个芯片内
- 内核通过0x70和0x71两个端口访问RTC
- 对应的设备文件为/dev/rtc
四、时间戳计数器TSC
- 在80x86微处理器中,有一个CLK输入引线:接收外部振荡器的时钟信号
- rdtsc指令用于读该寄存器
- 与后面介绍的可编程间隔定时器相比,TSC可以获得更精确的时钟。为此,Linux在系统初始化的时候必须确定时钟信号CLK的频率(即CPU的实际频率)。通过tsc_calibrate获得CPU实际频率。
五、可编程间隔定时器PIT
- 经过适当编程后,可以周期性的给出时钟中断
- 通过IRQ0发出时钟中断
六、Tick的长短
- 短
- 优点:分辨率高
- 缺点:需要较多的CPU时间处理,会导致用户程序运行变慢
- 适用于非常强大的机器,这种机器能够承担较大的系统开销
七、与系统时钟相关的宏定义
- 宏定义HZ。
- 宏定义Hz记录了不同体系结构下,系统时钟所要求的可编程定时器产生中断的频率。
- 宏定义CLOCK_TICK_RATE。
- 宏定义CLOCK_TICK_RATE记录了不同体系结构下,驱动可编程定时器工作的输入时钟频率。
- 宏定义LATCH。
- 宏定义LATCH记录了上述两个宏定义的比值,用于在内核初始化过程中设置可编程定时器中计数器寄存器counter的初始值。
八、在init_pit_timer ()中初始化时钟中断频率。
九、计时体系结构中的关键数据结构和变量
- 计时时钟源
- Jiffies变量
- 记录系统自启动以来系统产生的tick数
- 每次时钟中断+1(在系统响应时钟中断,时钟中断处理程序timer_interrupt()将该变量的值加1 )
- 因为一秒钟内产生系统时钟中断次数等于宏定义HZ的值,所以变量jiffies的值在一秒内增加HZ。
- 为了防止jiffies回绕造成程序的逻辑错误,Linux内核中提供了以下4个宏定义用于jiffies间的比较。
- time_after(a,b)
- time_before(a,b)
- time_after_eq(a,b)
- time_before_eq(a,b)
- 采用这4个宏定义进行jiffies间的比较,可以有效地解决因jiffies溢出造成的程序逻辑出错。
- Xtime变量:该结构用来表示当前时刻距UNIX时间基准1970/01/01/00:00:00的相对时间。
- Tv_sec:存放自1970年1月1日(UTC)午夜以来经过的秒数。
- Tv_nsec:存放自上一秒开始经过的纳秒数。
- Xtime变量每个节拍更新一次,大约一秒更新1000次。
十、X86中的时钟中断源及其初始化
- start_kernel->tick_init滴答相关初始化->init_timers定时机制相关初始化->hrtimers_init高精度定时器相关初始化->timekeeping_init Xtime相关初始化->time_init->sched_clock_init调度相关
思考
一、明确主要功能
- 定时测量是由基于固定频率振荡器和计数器的几个硬件电路完成的。
- 更新自系统启动以来所经过的时间
- 更新时间和日期
- 确定当前进程的执行时间,考虑是否要抢占
- 更新资源使用统计计数
- 检查到期的软定时器
二、主要数据结构
- 计时时钟源
- 时钟源抽象
- 数据结构clocksource
- 缺省时钟源
- 注册时钟源:clocksource_register ,clocksource_list按rating排序
- pit作为时钟源:init_pit_clocksource将会注册此时钟源
- 以tsc作为时钟源:init_tsc_clocksource将会注册此时钟源
- 时钟源抽象
- Jiffies变量
- 记录系统自启动以来系统产生的tick数。每次时钟中断+1(在系统响应时钟中断,时钟中断处理程序timer_interrupt()将该变量的值加1 )
- 因为一秒钟内产生系统时钟中断次数等于宏定义HZ的值,所以变量jiffies的值在一秒内增加HZ。
- 为了防止jiffies回绕造成程序的逻辑错误,Linux内核中提供了以下4个宏定义用于jiffies间的比较。time_after(a,b) ,time_before(a,b),time_after_eq(a,b),time_before_eq(a,b)。采用这4个宏定义进行jiffies间的比较,可以有效地解决因jiffies溢出造成的程序逻辑出错。(timeout = jiffies+HZ,if(time_after(jiffies,timeout)){timeout();exit;})
Xtime变量
- 该结构用来表示当前时刻距UNIX时间基准1970/01/01/00:00:00的相对时间
- Tv_sec:存放自1970年1月1日(UTC)午夜以来经过的秒数。
- Tv_nsec:存放自上一秒开始经过的纳秒数。
- Xtime变量每个节拍更新一次,大约一秒更新1000次。
- 变量xtime的初值在系统初始化过程通过读取系统实时钟芯片RTC的值来为变量xtime赋初值;该变量的值在系统运行过程中由系统时钟中断处理程序负责在每次时钟中断时进行更新。
主要初始化过程以及时钟中断处理函数注册和调用过程;
- start_kernel->tick_init滴答相关初始化->init_timers定时机制相关初始化->hrtimers_init高精度定时器相关初始化->timekeeping_init Xtime相关初始化->time_init->sched_clock_init调度相关
- time_init->choose_time_init()->hpet_time_init()->setup_pit_timer(注册pit_clockevent为Clockevent设备,并设置global_clock_event) 和 time_init_hook()来设置系统时钟中断处 理程序。
- 时钟中断处理函数:timer_interrupt(irq,dev_id)。
注册
- tick_init调用clockevents_register_notifier注册tick_notifier到clockevents_chain上
调用过程(需要加顺序锁和解锁)
global_clock_event在setup_pit_timer中被初始化为pit_clockevent。其event_handler在注册过程中,被初始化为tick_handle_periodic。tick_handle_periodic的关键是tick_periodic,该函数调用do_timer(完成变量xtime的更新)和update_process_times。(Raise_softirq()激活本地CPU上的软件定时器中断任务队列。Scheduler_tick该函数使当前进程的时间片计数器减一。)
数据结构:
- expires用于和系统核心变量jiffies进行比较。
- 成员变量function:该函数指针变量保存了内核定时器超时后要执行的函数,即定时器超时处理函数。
- 成员变量data:该无符号长整型变量用作定时器超时处理函数的参数。
- 成员变量base:该指针变量表明了该内核定时器节点归属于系统中哪一个处理器,在使用函数init_timer()初始化内核定时器节点的过程中,将该指针指向了一个每处理器变量tvec_bases的成员变量t_base。
- 创建并激活一个动态定时器
- 创建一个新的timer_list对象
- 调用init_timer初始化,并设置定时器要处理的函数和参数设置定时时间
- 使用add_timer加入到合适的链表中
- 通常定时器只能执行一次,如果要周期性的执行,必须再次将其加入链表
- 明确内核对相关时钟源的组织,明确计时时钟源和中断时钟源的区别
- 计(定)时时钟源:(决定每次改变Xtime的多少)
- 中断时钟源:是由周期性时钟中断的中断函数产生的。(决定什么时候改变Xtime的值,不决定改多少)
- 当这两个时钟源是一个的时候,Xtime每次增加一个tick。
- 当其不相等的时候,Xtime增加就不是一个tick。由精度的高低决定。一般定时时钟源精度高(除了发生时钟中断是一个tick这种情况)。Xtime在定时处理函数中更改。
- 定时处理函数执行流:
第一件事、改变xtime的值。
第二件事、把当前的xtime的值回写到cmos中。在Linux中精度最低的时钟源是cmos。
第三件事、当前的cpu的时钟的节拍值加1。
第四件事、给当前的进程时间片减1。给系统当中的sleep等挂起的进程也减1.
第五件事、给CPU的资源统计计数。
参考资料:
深入理解Linux内核
备注:
转载请注明出处:http://blog.csdn.net/wsyw126/article/details/51803788
作者:WSYW126