转自:http://www.cnblogs.com/openix/p/3324243.html

参考:1、http://bbs.eyeler.com/thread-69-1-1.html                                                                           
         2、《Linxu Kernel Development》3ed_CN p166~p185
         3、《Professional Linux Kernel Architecture》1ed_CN p714~p760
         4、http://blog.csdn.net/droidphone/article/details/8017604
         5、2.6.34

此记录的主要目的记录下如上参考中的一些知识点、基本概念,以及作为后续记录的参考。

  对于unicore的内核2.6.32.9,其时间子系统是基于低分辨率周期时钟实现的,但是在此记录及后续的记录中将会谈下“低分辨率动态时钟、高分辨率动态时钟、高分辨率周期时钟”。

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

内核中的两种定时器:

1、timeout:表示将在一定时间之后发生的事件,但可以且通常会在发生之前取消。
2、timer: 用于实现时序,此类定时器通常都会到期,而且与超时类定时器相比,需要更高的时间分辨率。

timer wheel的实现要点在于:

timer wheel的实现可以自行参考内核代码,网上讲的也很多。
1、void __run_timers(struct tvec_base *base)
在当前第一组tv1的元素全被遍历后(函数也会执行),将会调用cascade函数,主要的功能就在于取下特定tv数组下的某一个数组元素中的链表,然后重新加入前面的各个数组的各个数组元数的链表中。方法比较巧妙,这样每次执行定时函数时只需从tv1上取出过期的节点即可执行,但是我们也需要注意执行cascade的时间也可能会很长。 2、void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
注意其中是如何索引到各个数组的数组元素下标的:
unsigned long idx = expires - base->timer_jiffies;下标
vec = base->tv[1..5].vec + i; 插入哪个链表

低分辨率定时器的重要性在于:

1)处理全局jiffies计数器。该值周期性地增长(如果使用了低分辨率动态时钟,可能不会周期性增长),它是一种特别简单的时间基准。

2)进行各进程统计。

内核中的各种time,注意下struct timekeeper timekeeper:

1)wall time
    RTC time
在SOC系统中,RTC可以集成到SOC芯片中,并做为一个单独的电压域,系统掉电时,可由后备电池供电,RTC中的时间信息不会丢失。内核和用户空间通过驱动程序访问RTC硬件来获取或设置时间信息。
    xtime
A value representation of the human time of day;日常生活中所见的钟表时间,精度可达纳秒级。
xtime和RTC时间一样,都是人们日常生活所使用的墙上时间,只是RTC时间的精度比较低,大多数情况下只能达到毫秒级的精度,如果是使用外部的RTC芯片,访问速度也比较慢,为此,内核维护了另一个wall time时间:xtime。因为xtime实际上是一个内存变量,它的访问速度非常快,内核大部分时间都是使用xtime来获得当前时间信息。xtime记录的是1970年1月1日到当前时刻所经历的纳秒数。
    xtime在正常情况下是递增的,但是用户可以主动向前或向后调整墙上时间,从而修改xtime。
2) monotonic time
A monotonically increasing value that represents the amount of time that the system has been running.
    开机后单调递增,它不像xtime可能因用户进行时间调整而产生改变,该时间不计算系统休眠的时间,即系统休眠时,monotonic不会递增。
monotonic时间不可以往后退,系统启动后只能不断递增。内核并没有直接定义一个特定的变量来记录monotonic时间,而是定义了一个变量wall_to_monotonic,记录了墙上时间和monotonic时间之间的偏移量,当需要获得monotonic时间时,把xtime和wall_to_monotonic相加即可。计算monotonic时间要去除系统休眠期间花费的时间,内核用total_sleep_time记录休眠的时间,每次休眠醒来后重新累加该时间,并调整wall_to_monotonic的值,使其在系统休眠醒来后,monotonic时间不会发生跳变。
3)raw_time
    monotonic时间虽然不受settimeofday的影响,但会受到ntp调整的影响,但是raw_time不受ntp的影响,它真的就是开完机后就单调地增加。
    xtime、monotonic time和raw_time可以通过用户空间的clock_gettime函数获得,对应的ID参数分别是CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_MONOTONIC_RAW。
4)clock source
A representation of a free running counter running at a known frequency, usually in hardware.
    xtime、monotonic time、raw time都是基于该时钟源进行计时操作,当有新的精度更高的时钟源被注册时,通过timekeeping_notify函数,change_clocksource函数将会被调用,timekeeper.clock字段将会被更新,指向新的clocksource
5)tick
A periodic interrupt generated by a hardware-timer, typically with a fixed interval defined by HZ: jiffies

对于SMP,内核区分如下两种时钟类型:

1)全局时钟(global clock),负责提供周期时钟,主要用于jiffies更新。
2)每个CPU一个局部时钟(local clock),用来进行进程统计、性能剖析和实现高分辨率定时器。
  全局时钟的角色,由一个明确选择的局部时钟承担(tick_setup_device函数中,会首次作出选择)

在设备第一次调用tick_seup_device时(即该时钟设备没有相关的时钟事件设备),内核执行如下操作:

1)如果没有选定时钟设备来承担全局时钟设备的角色,那么将选择当前设备来承担此职责,而tick_do_timer_cpu将设置为当前设备所属的处理器编号(注意,如果放弃职责该如何处理,可以参考tick_do_timer_cpu定义处的注释)。tick_period是时钟周期,单位是纳秒,它根据HZ值设置。
2)该设中设备设置为按周期模式工作。 关于上文中提到的2),什么时候会切换成one shot模式?
关注下hrtimer_run_queues,里面调用了tick_check_oneshot_change来判断是否可以激活高分辨率定时器。此外该函数还检查是否可以在低分辨率系统上启用动态时钟。(如果有一个支持单触发模式的时钟,而且其精度可以达到高分辨率定时器所要求的分辨率,即设置了CLOCK_SOURCE_VALID_FOR_HRES标志,那么tick_check_oneshot_change将通知内核可以使用高分辨率定时器)
留意下,在设备被设置成高分辨率周期时钟、高分辨率动态时钟时的中断处理函数的选择。(tick_handle_periodic、hrtimer_interrupt、tick_nohz_handler)

/*
* tick_do_timer_cpu is a timer core internal variable which holds the CPU NR
* which is responsible for calling do_timer(), i.e. the timekeeping stuff. This
* variable has two functions:
*
* 1) Prevent a thundering herd issue of a gazillion of CPUs trying to grab the
* timekeeping lock all at once. Only the CPU which is assigned to do the
* update is handling it.
*
* 2) Hand off the duty in the NOHZ idle case by setting the value to
* TICK_DO_TIMER_NONE, i.e. a non existing CPU. So the next cpu which looks
* at it will take over and keep the time keeping alive. The handover
* procedure also covers cpu hotplug.
*/
int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;

动态时钟&WHY

在关注耗电量的系统上,周期性时钟要求系统在一定的频率下,周期性的处于活动状态。因此,长时间休眠是不可能的。引入动态时钟后,只有在有些任务需要实际执行时,才激活周期时钟。否则,会临时禁用周期时钟。对该技术的支持可以在编译时选择,启动此选项的系统也称为无时钟系统(tickless system)
在系统无事所做的idle阶段,我们可以通过停止周期时钟来达到降低系统功耗的目的,只要有进程处于活动状态,时钟事件依然会被周期性地发出。

在内核中,如定义了CONFIG_NO_HZ宏,则说明内核支持动态时钟;但是我们得明白,CONFIG_NO_HZ并不意味着没有HZ的概念(周期性更新系统统计量),主要区别在于当我们配置CONFIG_NO_HZ时说明:

1)系统支持动态时钟。启用动态时钟时,也定义且使用了HZ,因为它是许多计时任务的基本量。动态和周期时钟在表面上没有什么区别,主要的区别在于 进/退 IDLE时的不同处理方法。
2)系统可能需要停止时钟机制(此时将不会再周期性的产生时钟中断,例如系统进入IDLE)或重启时钟机制(系统退出IDLE)。
3)根据2),由于可以暂时停止时钟机制,因此单触发时钟是实现动态时钟的先决条件。(但是请注意,即使时钟设备处于单触发模式,也并不一定启用了动态时钟!例如,在高分辨率模式下,时钟总是基于单触发定时器实现的。)

高分辨率时钟如何实现周期时钟的仿真:

在内核切换到高分辨率模式时,将调用tick_setup_sched_timer来激活时钟仿真层。这将为每个CPU安装一个高分辨率定时器。所需的struct hrtime实例保存在CPU变量tick_cpu_sched中:该定时器的回调函数选择了tick_sched_timer,通过返回HRTIMER_RESTART,定时器将自动重新进入队列,并在下一个时钟到期时激活。

内核需要处理的情形:

1、没有动态时钟的低分辨率系统,总是使用周期时钟。该内核不包括任何对单触发操作的支持。
2、启用了动态时钟特性的低分辨率系统,以单触发模式使用时钟设备。
3、高分辨率系统总是使用单触发模式,无论是否启用了动态时钟特性。

关于宏:

这一项显得“理论、教条”,因为我很少接触SMP、至于龙芯系类也只接触了一小段时间。
1、启用动态时钟:CONFIG_NO_HZ
2、启用高分辨率定时器支持:CONFIG_HIGH_RES_TIMERS
3、支持动态时钟和高分辨率定时器的必要前提:GENERIC_TIME、GENERIC_CLOCKEVENTS
4、支持时钟事件的单触发模式:CONFIG_TICK_ONESHOT,如果启用了动态时钟或高分辨率定时器,则自动选中该项

时钟源(struct clocksource):时间管理的支柱。本质上每个时钟源都提供了一个单调增加的计数器,通用的内核代码只能进行只读访问。不同时钟源的精度取决于底层硬件的能力。clocksource不能被编程、没有事件产生能力。

关于clocksource中几个关键域:
read:时钟源本身不会产生中断,要获得时钟源的当前计数,只能通过主动调用它的read回调函数来获得当前技术值,注意这里只获
得计数值,也就是所谓的cycle,要获得相应的时间,必须要借助clocksource的mult和shift字段进行转换计算。
void __clocksource_updatefreq_scale(struct clocksource *cs, u32 scale, u32 freq) mult和shift域:因为从clocksource中读到的值是一个cycle计数值,要转换为时间,必须知道驱动clocksource的时钟频率F,但是clocksource并没有保存时钟的频率F,内核使用的方法是:根据时钟的频率和期望的精度,事先计算出两个辅助常数mult和shift,然后使用下公式进行cycle和t的转换:
t = (cycle * mult) >> shift;
但是要保证:F = (1 << shift ) / mult;(一般PLL都是先倍频,再分频,很容易计算)
内核内部使用64位进行该转换计算:
static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)
|---->return ((u64) cycle * mult) >> shift;
问题在于如果mult太大,计算会发生溢出,因此mult值不能太大。内核假设cycle计数值被转换后的最大时间值为:10分钟(600s),原因在于CPU进入IDLE状态后,时间信息不会被更新,只要在10分钟内退出IDLE,clocksource的cycle计数值就可以被正确的转换为相
应的时间,然后系统的时间信息可以被正确的更新。结果不一定是10分钟,该值由clocksource_max_deferment进行计算,并保存在max_idle_ns字段中,tickless的代码需要考虑这个值,以防止在使用动态时钟时,系统保持IDLE状态的时间过长。

时钟事件设备(struct clock_event_device):向时钟增加了事件功能,在未来的某个时刻发生。这种设备也称为时钟事件源(clock event source)。clock_event_device可以被编程,可以工作在周期模式或单次触发模式,系统可以对它进行编程,以确定下次事件触发的时间,clock_event_device主要用于实现普通定时器和高精度定时器,同时也用于产生tick事件,供给进程调度子系统使用。在软件架构上,clock_evetn_device被分为两层,与硬件相关的放在machine层,与硬件无关的通用代码则被集中到了通用时间框架。
时钟设备(struct tick_device):扩展了时钟事件源的功能,各个时钟事件定期触发。但可以使用动态时钟机制,在一定时间间隔内停止周期时钟。                                                                   
下图引述自:http://blog.csdn.net/droidphone/article/details/8017604

linux 时间管理——概念、注意点(一)【转】的更多相关文章

  1. Linux时间管理涉及数据结构和传统低分辨率时钟的实现

    上篇文章大致描述了Linux时间管理的基本情况,看了一些大牛们的博客感觉自己写的内容很匮乏,但是没办法,只能通过这种方式提升自己……闲话不说,本节介绍下时间管理下重要的数据结构 设备相关数据结构 // ...

  2. linux时间管理

    /etc/sysconfig/clock         该配置文件可用来设置用户选择何种方式显示时间.如果硬件时钟为本地时间,则UTC设为0,并且不用设置环境变量TZ.如果硬件时钟为UTC时间,则要 ...

  3. linux时间管理 之 jiffies

    1.jiffies 又称时钟滴答,是一个全局变量,它的值在系统引导的时候初始化为0,在时钟中断初始化完成后,每次时钟中断发生,在时钟中断处理例程中都会将jiffies的值 +1. jiffies_64 ...

  4. linux设备驱动归纳总结(七):1.时间管理与内核延时【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxx ...

  5. 《Linux内核设计与实现》读书笔记(十一)- 定时器和时间管理【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/10/3070373.html 系统中有很多与时间相关的程序(比如定期执行的任务,某一时间执行的任务 ...

  6. linux下的时间管理概述

    2017/6/21 时间这一概念在生活中至关重要,而在操作系统中也同样重要,其在系统中的功能绝不仅仅是给用户提供时间这么简单,内核的许多机制都依赖于时间子系统.但凡是要在某个精确的时间执行某个事件,必 ...

  7. Linux电源管理(2)-Generic PM基本概念和软件架构【转】

    本文转载自:http://www.wowotech.net/pm_subsystem/generic_pm_architecture.html 1. 前言 这里的Generic PM,是蜗蜗自己起的名 ...

  8. 【Linux开发】linux设备驱动归纳总结(七):1.时间管理与内核延时

    linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. 5 个在 Linux 中管理文件类型和系统时间的有用命令

    对于想学习 Linux 的初学者来说要适应使用命令行或者终端可能非常困难.由于终端比图形用户界面程序更能帮助用户控制 Linux 系统,我们必须习惯在终端中运行命令.因此为了有效记忆 Linux 不同 ...

随机推荐

  1. C#解析HTML

    第一种方法:用System.Net.WebClient下载Web Page存到本地文件或者String中,用正则表达式来分析.这个方法可以用在Web Crawler等需要分析很多Web Page的应用 ...

  2. 在SQL语句中加入时间比较作为查询条件

    select * from 表名 where 列名 = ? and DATEDIFF(hh,时间列,'2016-08-22 15:05:59.000')<9

  3. 提高Baidu Map聚合的效率

    百度的MAP的例子里提供了一个聚合效果,地址是http://developer.baidu.com/map/jsdemo.htm#c1_4 ,效果图如下图: 这个效果很赞,但效率很低,当数据量达到50 ...

  4. js null 和 undefined

    undefined是一个特殊类型,null本质上是一个对象 typeof undefined//"undefined"typeof null//"object" ...

  5. TortoiseGit 相关操作

    1.TortoiseGit 记住用户名和密码的方法当你安装且配置好git后,在C:\Documents and Settings\Administrator\ 目录下有一个  .gitconfig 的 ...

  6. 发布Live Writer代码着色插件CNBlogs.CodeHighlighter

    在解决了使用Windows Live Writer发博所遇到的"建分类.加标签.写摘要"与"设置EntryName"的四个问题之后,我们趁热打铁,解决了第五个问 ...

  7. Nginx+uwsgi安装配置

    一.安装基础开发包 yum groupinstall "Development tools" yum install zlib-devel bzip2-devel pcre-dev ...

  8. logstash redis kafka传输 haproxy日志

    logstash 客户端收集 haproxy  tcp日志 input { file { path => "/data/haproxy/logs/haproxy_http.log&qu ...

  9. VitualBox环境下,实现windows系统与虚拟机Linux文件互传

    本次环境是Win7系统和ubuntu14(虚拟机) 1.首先需要安装VitualBox的增强功能,如图所示 2.安装完成后重启linux系统,然后在WIN7系统下创建共享文件夹(本文在D盘下创建名为V ...

  10. nmon监控工具的使用

    该工具的使用主要参考自http://blog.itpub.net/23135684/viewspace-626439/ http://nmon.sourceforge.net/pmwiki.php 生 ...