定时机制连同一些更可见的内核活动(如检查超时)来驱使进程切换。
两种主要的定时测量:
定时测量是由基于固定频率振荡器和计数器的几个硬件电路完成的。
时钟电路用于跟踪当前时间和产生精确的时间度量。
定时器电路由内核编程,所以它们以udingde,预先定义的频率发出中断。
时钟电路的分类
当PC被切断电源,RTC还继续工作。
内核通过0x70和0x71I/O端口访问RTC。
能在IRQ8上发出周期性的中断,频率在2HZ~8192HZ之间,可编程
在80x86微处理器中,有一个CLK输入引线接收外部振荡器的时钟信号。TSC在每个时钟信号到来时加1.
TSC是一个64位的时间戳计数器寄存器,汇编指令rdtsc读这个寄存器。Linux在初始化时系统时必须确定时钟信号的频率。
获得tsc的时钟频率:calibrate_tsc()函数通过计算一个大约在5ms的时间间隔内所产生的时钟信号的个数来算出CPU实际频率。
Linux通过rdtscll()或rdtscl()用来读取TSC的事。
与可编程间隔定时器相比,TSC可以获得更精确的时钟。
使用I/O端口0x40~0x43
LInux给PC的第一个PIT进行编程,使它以大于1000Hz的频率向IRQ0发出时钟中断,即每1ms产生一次时钟中断,这个时间间隔叫做一个节拍(tick),它的长度以纳秒为单位存放在tick_nsec变量中。
由setup_pit_timer()进行初始化。在init_pit_timer()中初始化时钟中断频率。
与系统时钟信号有关的宏定义:
(1)宏定义Hz
在不同的体系机构下,系统时钟所要求的可编程定时器中断的频率,即每秒tick的个数
(2)宏定义CLOCK_TICK_RATE
记录了不同体系结构下,驱动可编程定时器工作的输入时钟频率
(3)宏定义LATCH
记录了上述两个宏定义的比值,用于在内核初始化过程中设置可编程定时器中计数器寄存器counter的初始值。
LInux的计时体系结构是一组与时间流相关的内核数据结构和函数。
功能:
内核有两个基本的计时函数:
在单处理器系统中,所有定时活动都由IRQ0上的时钟中断触发,包括:
为了使用一种统一的方法来处理可能存在的定时器资源,内核使用能够了“定时器对象”,它是timer_opts类型的一个描述符。其中最重要的两个方法:
mark_offset:由时钟中断处理程序调用,并以适当的数据结构记录每个节拍到来时的准确时间。
get_offset:使用已记录的值来计算上一次时钟中断(节拍)以来经过的时间。
这两种方法,使得Linuxd计时体系结构能够打到子节拍的分辨率,也就是说,内核能够以比节拍周期更高的精度来测定当前的时间,这种操作被称为“ 定时插补”。
在内核初始化期间,select_timer()函数设置cur_timer指向适当定时器对象(时钟源)的地址。变量timer_cur存放了某个定时器对应的那个的地址,该定时器是系统可利用的定时器资源中最好的。
一个计数器,用来记录自系统启动以来产生的节拍总数。
因为一秒钟内产生系统时钟中断次数等于宏定义HZ的值,所以变量jiffies的值在一秒内增加HZ。
xtime变量存放当前时间和日期,它是一个timespec类型的数据结构。以便内核对某些对象和事件作时间标记,如记录文件的创建时间、修改时间、上次访问时间,或者供用户进程通过系统调用来使用。
基本每个tick更新一次。
考点:tick_handle_periodic函数的功能(Linux的计时体系结构的功能)
tick_init调用clockevents_tegister_notifier注册tick_notifier到clockevents_chain上。
Update_wall_time()完成变量xtime的更新。
time_init_hook()来设置系统时钟中断处理程序。
在时钟中断处理函数中:
会调用tick_init函数,书上很多流程中的函数最终都是被这个函数所调用,流程如下:
软定时器:
动态定时器:被动态的创建和撤销,当前活动的动态定时器个数没有限制
定时器是一种软件功能,即允许在将来的某个时刻,函数在给定的时间间隔用完时被调用。每个定时器都包含一个字段,表示定时器将需要多长时间才到期。这个字段的初值就是jiffies的当前值加上合适的节拍数。
注意,对于必须严格遵守定时时间的那些实时应用而言,定时器并不适合,因为定时器的检查总是由可延迟函数进行。
run_timer的主要功能
动态定时器应用之delayed work
动态定时器应用之schedule_timeout: setup_time_on_stack(&timer, process_timeout, (unsigned long)current); timer时间到了之后,process_timeout函数将当前进程变为等待态。
当内核需要等待一个较短的时间间隔,如几毫秒,通常设备驱动器会等待预先定义的整个微秒直到硬件完成某些操作。这些情况下,内核使用udelay()和ndelay()函数:前者接收一个微秒级的时间间隔作为它的参数,并在指定的延迟结束后返回,后者与前者类似,但是指定延迟的参数是纳秒级的。