炼狱,是为追逐光芒
分类: LINUX
2013-05-15 18:48:31
1.内核中时间的概念
(1)内核中有大量事件是基于时间驱动的,相对时间和绝对时间这两个概念对内核时间管理来说都至关重要。
(2)系统定时器和时钟中断处理程序是Linux系统内核管理机制中的中枢。内核必须在硬件的帮助下才能计算和管理时间,定时器以某种频率自行触发时钟中断。当中断发生时,内核就通过一种特殊的中断处理程序对其进行处理。
(3)内核可以动态创建和撤销动态定时器--- 一种用来推迟执行程序的工具。
墙上时间:就是实际时间,对用户空间的应用程序很重要,可以用来维护实际日期和时间。
系统运行时间:自系统启动以来经历的时间,对内核和用户空间都有用。
2.HZ
系统定时器频率(节拍率),是通过静态预处理定义的。在
更高的节拍率意味着时钟产生更加频繁,中断处理程序也会执行更频繁。更高的时钟中断解析度可提高时间事件驱动时间的解析度和准确度(时钟中断执行准确度是+/-1/(2HZ))。
高HZ优势:
①内核定时器能够以更高的频率和更高的准确度运行;
②依赖定时值执行的系统调用,比如poll()和select(),能够以更高的精度运行;
③对诸如资源消耗和系统运行时间等的测量会有更精细的解析度;
④提高进程抢占的准确度;
高HZ劣势
节拍率越高,时钟中断频率越高,中断程序占用处理器时间就越多。这样不但减少了处理器工作时间,还更频繁的打乱了处理器高速缓存,增加功耗。
无节拍OS
当内核配置CONFIG_HZ时,系统根据这个选项动态调度时钟中断,这样实质性受益是省电。
3.jiffies
(1)用来记录自系统启动以来产生的节拍总数,转化为时间(jiffies/HZ)
Jiffies总是全局的unsigned long变量,在32位体系结构上,它就是32位,在HZ=100时,497天溢出。在64位体系上是64位,我们看不到溢出。通过连接,jiffies被jiffies_64覆盖(在64位机两者相同,在32位机jiffies取jiffies_64低32位)。通过函数get_jiffies_64()可以读取完整64位jiffies值。
(2)为防止溢出问题,内核提供几个宏来比较节拍计数
点击(此处)折叠或打开
(3)用户空间和HZ
内核定义USER_HZ给应用层,当USER_HZ小于等于HZ,且两者互为整数倍时返回给应用的时间是 return x/(HZ/USER_HZ)
内核使用函数jiffies_64_to_clock_t()将64位jiffies从HZ转换到USER_HZ
点击(此处)折叠或打开
4.硬时钟和定时器
(1)实时时钟(RTC)用来持久存放系统时间,系统启动时,内核读取RTC来初始化墙上时间,该时间存放在xtime变量中。
(2)系统定时器,在X86中,采用可编程中断时钟(PIT)作为时钟中断源。
5.时钟中断处理函数
分两部分:体系结构相关和体系结构无关。
(1)体系结构相关例程,作为系统定时器的中断处理函数注册到内核,主要完成以下工作:
获取xtime_lock锁,以便对访问jiffies_64和墙上时间xtime进行保护;
需要时应答或重新设置系统时钟;
周期性地使用墙上时间更新实时时钟;
调用体系无关时钟例程:tick_periodic().
(2)中断主要通过体系无关例程tick_periodic()完成更多工作
给jiffies_64变量加1;
更新资源消耗的统计值,比如当前进程所消耗的系统时间和用户时间;
执行已经到期的动态定时器;
执行sheduler_tick()函数;
更新墙上时间,该事件存放在xtime中
点击(此处)折叠或打开
内核对进程进行时间计数,是根据中断发生时处理器所处模式统计的,它把上个节拍全部算给了进程。这种粒度的进程统计方式是传统unix具有的,现在还没有更加精密的统计算法。
run_lock_timer(),标记一个软中断去处理所有到期定时器。
以上全部工作每1/HZ秒发生一次。
6.实际时间
当前实际时间(墙上时间)定义在文件kernel/time/timekeeping.c中
点击(此处)折叠或打开
在用户空间取得墙上时间用gettimeofday(),其在内核中调用sys_systimeofday()实现,可以用settimtofday()来设置当前时间,它需要有CAP_SYS_TIME权能。
7.定时器
指定函数将在定时器到期时自动异步执行,执行后自动撤销。
定义在文件
点击(此处)折叠或打开
用户一般不操作这些结构体,而是通过内核定义好的一系列宏来处理
点击(此处)折叠或打开
在中断处理函数中,要保护好共享数据。
定时器作为软中断在下半部上下文中执行,在run_local_timers()中调用所有到时定时器。
内核采用分组定时器方法来管理定时器,可以减少搜索超时定时器所带来的负担。
8.延迟执行
(1)忙等待
该方法用于延迟时间是节拍的整数倍,或对精确度不高时使用
unsigned long timeout = jiffies+10;//延迟10个节拍
while(time_befor(jiffes,timeout)) ;
这是最简单的延迟方法,但是延迟独占CPU,很少用。改进版是:
unsigned long delay = jiffies + 5*HZ;
while (time_befor(jiffies,delay))
cond_resched();//调度一个新程序运行,该方法有效条件是系统中存在更重要的任务要运行
因为调用了调度程序,所以不能在中断上下文使用,只能在进程上下文使用。所有延迟方法在进程上下文都使用的很好,中断处理都应该尽快的执行,最好不用延迟。
延迟不管在哪种情况下,都不应该在持有锁时或禁止中断时使用。
(2)短延迟,比节拍短,短暂又精确的延迟
点击(此处)折叠或打开
udelay是由忙循环实现的,mdelay调用udelay实现,但是会睡眠,1ms以上的延迟就不要用udelay(),防止溢出。
BogoMIPS值记录处理器在给定时间内忙循环执行的次数,存放在loops_per_jiffy中
(3)schedule_timeout()
更理想的方案是schedule_timeout()函数,让需要延迟睡眠执行的任务睡眠到指定延迟时间耗尽再重新执行,同样,只保证延迟时间尽量接近睡眠时间,时间到期后,内核唤醒被延迟任务并将其重新放回运行队列。
set_current_state(TASK_INTERRUPTIBLE);//将任务设置为可中断睡眠状态
schedule_timeout(s*HZ); //睡眠s秒
①schedule_timeout()的实现
点击(此处)折叠或打开
若任务提前唤醒(比如收到信号),那么定时器被撤销,返回剩余时间。
②设置超时时间,在等待队列上睡眠
有时,等待队列上的某个任务可能既在等待一个特定事件到来,又在等待一个特定时间到期(看谁先到),这样可以使用schedule_timeout()代替schedule().当然,需要检查被唤醒的原因(可能是信号,也可能是时间到),然后执行相应操作。
leon_yu2013-09-02 09:35:40
Bean_lee:=(/proc/interrupts) / (/proc/uptime)?何解
输出都不是一个数字,尤其是interrupts
/proc/uptime是开机以来实际运行时间,单位秒;/proc/interrupts包括系统所有中断的信息,第一列表示中断号,第二列表示中断在该CPU上发生的次数,最后一列表示注册这个中断的设备名称,其中system tick就是系统定时器中断;也可用cat /proc/interrupts && sleep 1 && cat /proc/interrupts 查看一秒内系统定时器发生的中断次数;
回复 | 举报