时间管理

       相对于事件驱动而言,内核中有大量的函数都是基于时间驱动的。期中有些是周期执行的,而另一些函数则需要等待一个相对时间后才运行。内核还必须管理系统的运行时间和当前日期及时间。如果某个事件在5s后被调度执行,那么系统需要的是相对时间。如果要求管理当前日期时间,则内核不但要计算流逝时间而且还要计算绝对时间。

内核中的时间概念
       内核必须在硬件的帮助下才能计算和管理时间,硬件为内核提供了一个系统定时器用以计算流逝的时间,系统定时器以某种频率自行触发,该频率可以通过编程预定,称为节拍率,当时钟中断发生时,内核就是通过一种特殊的中断处理程序对其进行处理。因为预编的节拍率对内核来说是可知的,所以内核知道连续两次中断的时间间隔,这个时间间隔称为节拍,他等于节拍率分之一秒。内核就是通过这种已知的时钟中断间隔来计算实际时间和系统运行时间的。

节拍率:HZ
       系统定时器的频率(节拍率)是通过静态预处理定义的,也就是HZ。在系统启动时按照HZ值对硬件进行设置。体系结构不同HZ值也不同。节拍率有一个HZ频率,一个周期为1/HZ秒。选择一个合适的频率,需要取得各个方面的折中。提高节拍率意味着时钟中断产生的更加频繁所以中断处理程序也会更频繁的执行,如此一来,更高的时钟中断解析度可以提高时间驱动事件的解析度,还提高了时间驱动事件的准确度。提高节拍率等同于提高中断解析度。而更高的时钟中断频度和准确度又会带来如下优点:
       内核定时器能够以更高的频度和更高的准确度运行。依赖定时值运行的系统调用能够以更高的精度运行。对资源消耗和系统运行时间等得测量会有更精细的解析度。提高进程抢占的准确度,同时也会加快调度的响应时间。
       高节拍率也会产生副作用。节拍率越高意味着时钟中断频率越高,也就意味着系统负载较重,因为中断处理程序占用处理器的时间越多。不过在现代计算机系统上面节拍率从100上升到1000并不会导致难以承受的负担。

       linux内核还支持‘无节拍操作’这样的选项,当编译内核时设置了config_hz配置选项,系统就会根据这个选项动态调度时钟中断。并不是每隔固定时间触发时钟中断,而是按照需要动态调度和重新设置。

jiffies
       全局变量jiffies用来记录自系统启动以来产生的总节拍数,启动时内核将此变量初始化为0,此后每次时钟中断处理程序都会增加该变量的值,系统运行时间以秒为单位计算,就等于jiffies/HZ。把时钟转化为秒经常会用在内核和用户空间进行交互的时候,而内核本身很少用到绝对时间。jiffies变量总是无符号长整形。和任何C整形一样,jiffies变量的值超过他的最大存放范围后就会发生溢出,如果节拍计数达到了最大值后还要继续增加的话,他会回绕到0。在2.6内核以前,如果改变内核中的HZ值,会给用户空间的某些程序造成异常,因为内核是以节拍数/秒的形式给用户空间导出这个值的。所以如果在内核中更改的HZ的值,就打破了用户空间的常量关系,用户空间并不知道新的HZ值。要避免上面的错误,内核必须更改所有导出的jiffies值,而内核定义USER_HZ来代表用户空间看到的HZ值。

硬时钟和定时器
       体系结构提供了两种设备进行计时,一种是前面的系统定时器,另一种是实时时钟。实时时钟(RTC)是用来持久存放系统时间的设备,即使系统关闭后,他也可以依靠主板上的微型电池保持系统计时。当系统启动时,内核通过读取RTC来初始化墙上时间,该时间存放在xtime变量中,虽然内核不会在系统启动后再读取xtime的变量,但是有些体系结构如X86会周期性的将当前时间值存回RTC中。

       系统定时器是内核定时机制中最重要的角色,虽然各个体系系统定时器的实现不同,但是基本思想没有区别:提供一种周期性的中断触发机制。在X86体系中,主要采用可编程中断时钟(PIT),内核在启动时对PIT进行初始化使其能够以HZ/秒的频率产生时钟中断。

时钟中断处理程序
       时钟中断处理程序可以分为两部分:体系结构相关部分和体系结构无关部分。与体系结构相关的程序作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时能够运行。主要执行如下工作:需要时应答或者重新设置系统时钟。周期性的使用墙上时间更新实时时钟。调用体系结构无关的时钟例程:tick_periodic()。获得xtime_lock锁,以便对访问jiffies_64和墙上时间xtime进行保护。

       中断服务程序主要通过调用与体系结构无关的例程,tick_periodic()执行下面的内容:给jiffies_64变量增加1。更新资源消耗的统计值。执行已经到期的动态定时器。更新墙上时间,该时间存放在xtime变量中。计算平均负载。

定时器
       定时器有时也称为动态定时器或内核定时器,是管理内核流逝时间的基础。因为内核经常需要推后执行某些代码。定时器工作很简单,执行一个初始化动作,设置一个超时时间,指定超时发生后执行的函数,然后激活定时器就可以了。指定的函数将在定时器到期时自动执行。
       定时器由结构体timer_list()表示:

struct timer_list {
         struct list_head entry;
         unsigned long expires; 
         spinlock_t lock;
         unsigned long magic; 
         void (*function)(unsigned long);
         unsigned long data; 
         struct tvec_t_base_s *base;
};
           1  创建定时器时需要先定义它:struct timer_list my_timer;
           2  然后通过一个辅助函数初始化定时器树结构的内部值,初始化必须在使用其他定时器管理函数对定时器操作之前完成:init_timer(&my_timer)
           3  然后就可以填充结构中需要的值了。my_timer.expires=jiffies+delay,my_timer.data=0,my_timer.function=func。expires表示超时时间,如果jiffies的计数大于expires,则执行处理函数func。data参数使你可以利用同一个处理函数注册多个定时器,值通过该参数就能区别对待他们,如果不需要该参数,可以简单传入0.
           4  最后,设置完成后还需要激活定时器:add_timer(&my_timer)。

      虽然内核可以保证不会在超时时间到期之前执行定时器处理函数,但是有可能延迟定时器执行。
             mod_timer(&my_timer,jiffies+new_delay)是用来修改已经激活的定时器超时时间的。
             del_timer(&my_timer)在定时器超时前停止他。

      因为定时器与当前执行代码是异步的,所以可能存在潜在的竞争条件。一般情况下使用del_timer_sync函数取代del_timer函数,因为无法确定在删除定时器时,它是否正在其他处理器上运行。

延迟执行
       内核代码除了使用定时器和下半部机制外,还需要其他方法来推迟执行任务,这种推迟通常发生在等待硬件完成某些工作时,而且等待时间非常短。内核提供了多种延迟方法处理各种延迟需求,最简单的延迟方法是忙等待,该方法想要延迟的时间是节拍的整数倍,或者精确率不高时才可以使用,对于系统其它部分,忙等待不算一个好方法,因为当代码等待时,处理器只能在原地等待,而不去处理其他任务,更好的方法是在等待时允许内核重新调度执行其他任务。cond_ceshed()函数在设置了need_reshed标志时会调度一个新程序投入运行,因为该方法需要调用调度程序,所以只能在进程上下文中使用。

       有时内核代码不但需要延迟,而且需要延迟精确的时间,这种情况多发生在和硬件同步时,内核提供了ms、ns、us级别的延迟函数。衙门是void udelay/ndelay/mdelay(unsigned long secs)。udelay依靠执行数次循环达到延迟效果,而mdelay则是通过udelay函数实现的。

       更理想的方法是使用schedule_timeout()函数执行延迟,该方法会让需要延迟的执行任务睡眠到指定的延迟时间耗尽后再重新运行。



转载于:https://my.oschina.net/guol/blog/94605

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值