您好,欢迎访问三七文档
Linux中的时钟和定时测量定时测量Linux内核提供两种主要的定时测量获得当前的时间和日期系统调用:time(),ftime()以及gettimeofday()维持定时器settimer(),alarm()定时测量是由基于固定频率振荡器和计数器的几个硬件电路完成的RTC等等2020/1/11嵌入式OS2硬时钟80x86体系结构上,内核必须显式的与四种时钟打交道实时时钟Realtimeclock,RTC时间戳计数器Timestampcounter,TSC可编程间隔定时器Programmableintervaltimer,PITCPU本地定时器高精度事件定时器ACPI电源管理定时器用于跟踪当前时间产生周期性的时钟中断,用于计时实时时钟RTC基本上所有的PC都包含实时时钟独立于CPU与所有其他芯片依靠一个独立的小电池供电给RTC中的振荡器即使关闭PC电源,还会继续运转与CMOSRAM往往集成在一个芯片内例如:Motorala146818能在IRQ8上发出周期性的中断,频率在2HZ~8192之间可以对其编程实现一个闹钟Linux本身只使用RTC获得时间和日期对应的设备文件为/dev/rtc可以通过设备文件对其编程内核通过0x70和0x71两个端口访问RTC系统管理员可以通过执行时钟程序设置时钟时间戳计数器TSC在80x86微处理器中,有一个CLK输入引线接收外部振荡器的时钟信号从pentium开始,很多80x86微处理器都引入了一个TSC一个64位的、用作时间戳计数器的寄存器它在每个时钟信号(CLK)到来时+1例如时钟频率400MHz的微处理器,TSC每2.5ns就+1rdtsc指令用于读该寄存器注意使用这个寄存器时,内核必须考虑时钟信号的频率在Linux2.6.26中,rdtscll()和rdtscl()用来读取TSC的值。rdtscl:32位rdtscll:64位native_read_tsc与后面介绍的可编程间隔定时器相比,TSC可以获得更精确的时钟为此,Linux在系统初始化的时候必须确定时钟信号CLK的频率(即CPU的实际频率)如何获得CPU实际频率tsc_calibratetsc_calibrate根据在一个相对较长的时间间隔内(约5ms)所发生的TSC计数的个数进行计算那个间隔由可编程间隔定时器给出由于只在系统初始化的时候运行一次,因此本程序可以执行较长时间,而不会引起问题可编程间隔定时器PIT经过适当编程后,可以周期性的给出时钟中断通常是8254CMOS芯片使用I/O端口0x40~0x43Linux将PIT编程为:100Hz、1000Hz通过IRQ0发出时钟中断每若干毫秒(100Hz为10ms)产生一次时钟中断,即一个tickTick的长短短优点:分辨率高缺点:需要较多的CPU时间处理,会导致用户程序运行变慢适用于非常强大的机器,这种机器能够承担较大的系统开销Tick的长短设置取决于硬件体系结构的快慢一个折中,例如在大多数惠普的Alpha和Intel的IA-64上约1ms产生一个tickRawhideAlpha工作站采用更高(1200tick/秒)与系统时钟相关的宏定义(1)宏定义HZ。宏定义Hz记录了不同体系结构下,系统时钟所要求的可编程定时器产生中断的频率。(2)宏定义CLOCK_TICK_RATE。宏定义CLOCK_TICK_RATE记录了不同体系结构下,驱动可编程定时器工作的输入时钟频率。(3)宏定义LATCH。宏定义LATCH记录了上述两个宏定义的比值,用于在内核初始化过程中设置可编程定时器中计数器寄存器counter的初始值。下列宏决定时钟中断频率在Linux中,下列宏决定时钟中断频率对8254分频,获得HZ所需的时钟HZ:每秒钟时钟中断的个数,即每秒tick的个数8254芯片的内部振荡器频率,每秒多少次在init_pit_timer()中初始化时钟中断频率此后,只要允许处理时钟中断,约每10ms就会产生一个时钟中断1tick约为10ms(当HZ=100)Linux的计时体系结构更新自系统启动以来所经过的时间更新时间和日期确定当前进程的执行时间,考虑是否要抢占更新资源使用统计计数检查到期的软定时器定时器是一种软件功能,即允许在将来的某个时刻,函数在给定的时间间隔用完时被调用2020/1/11嵌入式OS15在单处理器系统中,所有定时活动都由IRQ0上的时钟中断触发,包括在中断中立即执行的部分,和作为下半部分延迟执行的部分计时体系结构中的关键数据结构和变量计时时钟源Jiffies变量Xtime变量2020/1/11嵌入式OS17时钟源时钟源抽象参见数据结构clocksource缺省时钟源注册时钟源:clocksource_registerclocksource_list按rating排序Jiffies时钟源(缺省时钟源)以tsc作为时钟源pit作为时钟源init_pit_clocksource将会注册此时钟源注册pit作为时钟源注册好的时钟源链表clocksource_list按rating排序,靠前会被选中相对时间和墙上时间相对时间:记录从系统启动直到当前时刻的系统时钟产生的滴答数,存储于系统核心变量jiffies(jiffies_64);相对时间由系统时钟中断进行维护,该变量记录了系统启动到当前时刻为止系统时钟产生中断的次数,用于提示内核或用户进程一段指定的时间已经过去了。墙上时间:系统的当前时间xtimeJiffies变量记录系统自启动以来系统产生的tick数每次时钟中断+1(在系统响应时钟中断,时钟中断处理程序timer_interrupt()将该变量的值加1)因为一秒钟内产生系统时钟中断次数等于宏定义HZ的值,所以变量jiffies的值在一秒内增加HZ。关于jiffies_642020/1/11嵌入式OS26jiffies溢出处理示例程序jiffies溢出导致逻辑错误用于jiffies比较的宏定义为了防止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溢出造成的程序逻辑出错。通过宏定义修复jiffies溢出导致逻辑错误墙上时间墙上时间,在系统启动过程中根据实时钟(RTC)芯片保存数据进行初始化,在系统运行期间由系统时钟维护并在合适的时刻和RTC芯片进行同步。墙上时间存储于系统核心变量xtime中,该变量记录了现实世界中的年月日格式的时间,以便内核对某些对象和事件作时间标记,如记录文件的创建时间、修改时间、上次访问时间,或者供用户进程通过系统调用来使用。Xtime变量存放当前时间和日期kernel/time/timekeeping.c2020/1/11嵌入式OS31该结构用来表示当前时刻距UNIX时间基准1970/01/01/00:00:00的相对时间。时间纪元1970年1月1日(UTC)午夜include/linux/time.h该结构用来表示当前时刻距UNIX时间基准1970/01/01/00:00:00的相对时间。2020/1/11嵌入式OS32变量xtime的初值在系统初始化过程通过读取系统实时钟芯片RTC的值来为变量xtime赋初值;该变量的值在系统运行过程中由系统时钟中断处理程序负责在每次时钟中断时进行更新。该变量的初始化语句如下。xtime.tv_sec=get_cmos_time();xtime.tv_nsec=(INITIAL_JIFFIES%HZ)*(NSEC_PER_SEC/HZ);Xtime的更新基本上每个tick更新一次参见:update_wall_time根据时钟源来更新xtime的秒数和纳秒数时钟源timekeeping_initXtime相关初始化,并确定Xtime的相关时钟源kernel/time/timekeeping.ckernel/time/timekeeping.c2020/1/11嵌入式OS34X86中的时钟中断源及其初始化start_kernelinit_timers定时机制相关初始化hrtimers_init高精度定时器相关初始化timekeeping_initXtime相关初始化time_inithpet_time_initsetup_pit_timer注册pit_clockevent为Clockevent设备并设置global_clock_eventtick_init滴答相关初始化sched_clock_init调度相关时钟初始化setup_pit_timer注册pit_clockevent为Clockevent设备并设置global_clock_eventPit功能函数确定time_init_hook()来设置系统时钟中断处理程序。时钟中断处理函数=&pit_clockeventglobal_clock_event在setup_pit_timer中被初始化为pit_clockevent其event_handler在注册过程中,被初始化为tick_handle_periodictick_handle_periodic的关键是tick_periodic,该函数调用do_timer和update_process_timesupdate_process_timestick_periodictick_set_periodic_handlertick_setup_periodictick_setup_devicetick_check_new_devicetick_init调用clockevents_register_notifier注册tick_notifier到clockevents_chain上notifier_call_chain将会遍历clockevents_chain__raw_notifier_call_chainraw_notifier_call_chainclockevents_do_notifyclockevents_register_devicedo_timer设置event_handler=tick_handle_periodickernel/timer.c2020/1/11嵌入式OS46Update_wall_time()完成变量xtime的更新kernel/timer.ckernel/time/timekeeping.c2020/1/11嵌入式OS47Raise_softirq()激活本地CPU上的软件定时器中断任务队列。Scheduler_tick该函数使当前进程的时间片计数器减一。kernel/timer.c2020/1/11嵌入式OS48软定时器和延迟函数软定时器动态定时器(内核)间隔定时器(可以用户)延迟函数2020/1/11嵌入式OS49动态定时器动态定时器被动态的创建和撤销,当前活动的动态定时器个数没有限制include/linux/timer.h2020/1/11嵌入式OS50动态定时器的数据结构expires用于和系统核心变量jiffies进行比较。成员变量function:该函数指针变量保存了内核定时器超时后要执行的函数,即定时器超时处理函数。成员变量data:该无符号长整型变量用作定时器超时处理函数的参数。成员变量base:该指针变量表明了该内核定时器节点归属于系统中哪一个处理器,在使用函数init_timer()初始化内核定时器节点的过程中,将该指针指向了一个每处理器变量tvec_bases的成员变量t_base。创建并激活一个动态定时器创建一个新的timer_list对象调用init_ti
本文标题:LInux定时器.
链接地址:https://www.777doc.com/doc-2884814 .html