1.linuxHZ

Linux核心几个重要跟时间有关的名词或变数,以下将介绍HZ、tick与jiffies。

HZ

Linux核心每隔固定周期会发出timerinterrupt(IRQ0),HZ是拿来定义每1秒有几次timerinterrupts。举例来说,HZ为1000,代表每秒有1000次timerinterrupts。HZ可在编译核心时设定,如下所示(以核心版本2.6.20-15为例):

adrian@adrian-desktop:~$cd/usr/src/linux

adrian@adrian-desktop:/usr/src/linux$makemenuconfig

Processortypeandfeatures--->Timerfrequency(250HZ)--->

其中HZ可设定100、250、300或1000。

小实验

观察/proc/interrupt的timer中断次数,并于1秒后再度观察其值。理论上linux入门,二者应当相差250左右。

adrian@adrian-desktop:~$cat/proc/interrupts|greptimer&&sleep1&&cat/proc/interrupts|greptimer

0:9309306IO-APIC-edgetimer

0:9309562IO-APIC-edgetimer

前面四个栏位分别为中断号码、CPU中断次数、PIC与装置名称。

要检测系统上HZ的值是哪些,就执行命令

catkernel/.config|grep'^CONFIG_HZ='

2.Tick

linux 使用定时器_定时器使用教程_定时器使用视频

Tick是HZ的倒数,意即timerinterrupt每发生一次中断的时间。如HZ为250时,tick为4微秒(millisecond)。

3.Jiffies

Jiffies为Linux核心变数(unsignedlong),它被拿来记录系统自开机以来,早已过了多少tick。每发生一次timerinterrupt,Jiffies变数会被加一。值得注意的是,Jiffies于系统开机时,并非初始化成零,而是被设为-300*HZ(arch/i386/kernel/time.c),即代表系统于开机五分钟后,jiffies便会溢位。那溢位怎样办?事实上,Linux核心定义几个macro(timer_after、time_after_eq、time_before与time_before_eq),即使是溢位,也能借由这几个macro正确地取得jiffies的内容。

另外,80x86构架定义一个与jiffies相关的变数jiffies_64,此变数64位元linux 使用定时器,要等到此变数溢位可能要好几百万年。因而要等到溢位这刻发生应当很难吧。

3.1jiffies及其溢出

全局变量jiffies取值为自操作系统启动以来的时钟滴答的数量,在头文件中定义,数据类型为unsignedlongvolatile(32位无符号长整型)。

jiffies转换为秒可采用公式:(jiffies/HZ)估算,

将秒转换为jiffies可采用公式:(seconds*HZ)估算。

当时钟中断发生时,jiffies值就加1。为此连续累加一年又四个多月后才会溢出(假设HZ=100,1个jiffies等于1/100秒,jiffies可记录的最大秒数为(2^32-1)/100=42949672.95秒,约合497天或1.38年),即当取值抵达最大值时继续加1,就变为了0。

3.4Linux内核怎样来避免jiffies溢出

Linux内核中提供了以下四个宏,可有效解决因为jiffies溢出而导致程序逻辑出错的情况。下边是从LinuxKernel2.6.7版本中摘取下来的代码:

/*

*Theseinlinesdealwithtimerwrappingcorrectly.Youare

*stronglyencouragedtousethem

*1.Becausepeopleotherwiseforget

*2.Becauseifthetimerwrapchangesinfutureyouwon'thaveto

*alteryourdrivercode.

*time_after(a,b)returnstrueifthetimeaisaftertimeb.

*Dothiswith"=0"toonlytestthesignoftheresult.A

*goodcompilerwouldgeneratebettercode(andareallygoodcompiler

*wouldn'tcare).Gcciscurrentlyneither.

*/

#definetime_after(a,b)

(typecheck(unsignedlong,a)&&

typecheck(unsignedlong,b)&&

((long)(b)-(long)(a)<0))

#definetime_before(a,b)time_after(b,a)

#definetime_after_eq(a,b)

(typecheck(unsignedlong,a)&&

typecheck(unsignedlong,b)&&

((long)(a)-(long)(b)>=0))

#definetime_before_eq(a,b)time_after_eq(b,a)

在宏time_after中,首先确保两个输入参数a和b的数据类型为unsignedlong,之后才执行实际的比较。

8.推论

定时器使用视频_linux 使用定时器_定时器使用教程

系统中采用jiffies来估算时间,但因为jiffies溢出可能导致时间比较的错误linux 使用定时器,因此强烈建议在编码中使用time_after等宏来比较时间先后关系,这种宏可以放心使用。

内核时钟:

内核使用硬件提供的不同时钟来提供依赖于时间的服务,如busy-waiting(浪费CPU周期)和sleep-waiting(舍弃CPU)

5.HZandJiffies

jiffies记录了系统启动后的滴答数,常用的函数:time_before()、time_after()、time_after_eq()、time_before_eq()。由于jiffies随时钟滴答变化,不能用编译器优化它,应取volatile值。

32位jiffies变量会在50天后溢出,太小,因而内核提供变量jiffies_64来hold64位jiffies。该64位的低32位即为jiffies,在32位机上须要三天指令来形参64位数据,不是原子的,因而内核提供函数get_jiffies_64()。

6.LongDelays

busy-wait:timebefore(),使CPU忙等待;sleep-wait:shedule_timeout(截止时间);无论在内核空间还是用户空间,都没有比HZ更精确的控制了,由于时间片都是按照滴答更新的,并且虽然定义了您的进程在超过指定时间后运行,调度器也可能依照优先级选择其他进程执行。

sleep-wait():wait_event_timeout()用于在满足某个条件或超时后重新执行,msleep()睡眠指定的ms后重新步入就绪队列,这种长延后仅适用于进程上下文,在中断上下文中不能睡眠也不能长时间busy-waiting。

内核提供了timerAPI来在一定时间后执行某个函数:

#include

structtimer_listmy_timer;

init_timer(&my_timer);/*Alsoseesetup_timer()*/

my_timer.expire=jiffies+n*HZ;/*nisthetimeoutinnumberofseconds*/

my_timer.function=timer_func;/*Functiontoexecute

afternseconds*/

my_timer.data=func_parameter;/*Parametertobepassedtotimer_func*/

add_timer(&my_timer);/*Startthetimer*/

倘若您想周期性执行上述代码,这么把它们加入timer_func()函数。您使用mod_timer()来改变my_timer的超时值,del_timer()来删除my_timer,用timer_pending()查看是否my_timer处于挂起状态。

用户空间函数clock_settime()和clock_gettime()用于获取内核时钟服务。用户应用程序使用setitimer()和getitimer()来控制alarm讯号的传递当指定超时发生后。

8.RealTimeClock

RTC时钟track绝对时间。RTC电瓶常超过computer生存期。可以用RTC完成以下功能:(1)读或设置绝对时钟,并在clockupdates时形成中断;(2)以2HZ到8192HZ来形成周期性中断;(3)设置alarms。

jiffies仅是相对于系统启动的相对时间,假如想获取absolutetime或walltime,则须要使用RTC,内核用变量xtime来记录,当系统启动时,读取RTC并记录在xtime中,当系统halt时,则将walltime写回RTC,函数do_gettimeofday()来读取walltime。

#include

staticstructtimevalcurr_time;

do_gettimeofday(&curr_time);

my_timestamp=cpu_to_le32(_sec);/*Recordtimestamp*/

用户空间获取walltime的函数:time()返回calendartime或从00:00:00onJanuary1,1970的秒数;(2)localtime():返回calendartimeinbroken-downformat;(3)mktime():与localtime()相反;(4)gettimeofday()以microsecond精确度返回calendar时间。

另外一个获取RTC的方式是通过字符设备/dev/rtc,一个时刻仅容许一个处理器访问它。

9.时钟和定时器

时钟和定时器对Linux内核来说非常重要。首先,内核要管理系统的运行时间(uptime)和当前墙壁时间(walltime)linux解压rar,即当前实际时间。其次,内核中大量的活动由时间驱动。

9.1实时时钟

内核必须利用硬件来实现时间管理。实时时钟是拿来持久储存系统时间的设备,它通过显卡电瓶供电,所以即使在关掉计算机系统以后,实时时钟始终能继续工作。

系统启动时,内核读取实时时钟,将所读的时间储存在变量xtime中作为墙壁时间(walltime),xtime保存着从1970年1月1日0:00到当前时刻所经历的秒数。似乎在Intelx86机器上,内核会周期性地将当前时间存回实时时钟中,但应当明晰,实时时钟的主要作用就是在启动时初始化墙壁时间xtime。

9.2系统定时器与动态定时器

周期性发生的风波都是由系统定时器驱动。在X86体系结构上,系统定时器一般是一种可编程硬件芯片,其形成的中断就是时钟中断。时钟中断对应的处理程序负责更新系统时间和执行周期性运行的任务。系统定时器的频度称为节拍率(tickrate),在内核中表示为HZ。

以X86为例,在2.4之前的内核中其大小为100;从内核2.6开始,HZ=1000,也就是说每秒时钟中断发生1000次。这一变化促使系统定时器的精度(resolution)由10ms提升到1ms,这大大增强了系统对于时间驱动风波调度的精确性。过分频繁的时钟中断不可防止地降低了系统开支。

与系统定时器相对的是动态定时器,它是调度风波(执行调度程序)在未来某个时刻发生的时机。内核可以动态地创建或销毁动态定时器。

系统定时器及其中断处理程序是内核管理机制的中枢,下边是一些借助系统定时器周期执行的工作(中断处理程序所做的工作):

(1)更新系统运行时间(uptime)

(2)更新当前墙壁时间(walltime)

(3)在对称多处理器系统(SMP)上,均衡调度各处理器上的运行队列

(4)检测当前进程是否用完了时间片(timeslice),倘若耗尽,则进行重新调度

(5)运行超时的动态定时器

(6)更新资源用尽和处理器时间的统计值

内核动态定时器依赖于系统时钟中断,由于只有在系统时钟中断发生后内核就会去检测当前是否有超时的动态定时器。

---------------------------------------------------------

X86体系结构中,内核2.6.X的HZ=1000,即系统时钟中断执行细度为1ms,这意味着系统中周期事情最快为1ms执行一次,而不可能有更高的精度。动态定时器随时都可能超时,但因为只有在系统时钟中断到来时内核才能检测执行超时的动态定时器,所以动态定时器的平均偏差大概为半个系统时钟周期(即0.5ms).

另外。HZ是怎样定义的呢??可查看arch/arm/include/asm/param.h

本文原创地址:https://www.linuxprobe.com/srjxlhxsjgnh.html编辑:刘遄,审核员:暂无