ImageVerifierCode 换一换
格式:DOCX , 页数:21 ,大小:33.83KB ,
资源ID:4439956      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/4439956.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Linux 时钟管理.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Linux 时钟管理.docx

1、Linux 时钟管理Linux 时钟管理Linux 中的定时器在 Linux 内核中主要有两种类型的定时器。一类称为 timeout 类型,另一类称为 timer 类型。timeout 类型的定时器通常用于检测各种错误条件,例如用于检测网卡收发数据包是否会超时的定时器,IO 设备的读写是否会超时的定时器等等。通常情况下这些错误很少发生,因此,使用 timeout 类型的定时器一般在超时之前就会被移除,从而很少产生真正的函数调用和系统开销。总的来说,使用 timeout 类型的定时器产生的系统开销很小,它是下文提及的 timer wheel 通常使用的环境。此外,在使用 timeout 类型定时

2、器的地方往往并不关心超时处理,因此超时精确与否,早 0.01 秒或者晚 0.01 秒并不十分重要,这在下文论述 deferrable timers 时会进一步介绍。timer 类型的定时器与 timeout 类型的定时器正相反,使用 timer 类型的定时器往往要求在精确的时钟条件下完成特定的事件,通常是周期性的并且依赖超时机制进行处理。例如设备驱动通常会定时读写设备来进行数据交互。如何高效的管理 timer 类型的定时器对提高系统的处理效率十分重要,下文在介绍 hrtimer 时会有更加详细的论述。内核需要进行时钟管理,离不开底层的硬件支持。在早期是通过 8253 芯片提供的 PIT(Pro

3、grammable Interval Timer)来提供时钟,但是 PIT 的频率很低,只能提供最高 1ms 的时钟精度,由于 PIT 触发的中断速度太慢,会导致很大的时延,对于像音视频这类对时间精度要求更高的应用并不足够,会极大的影响用户体验。随着硬件平台的不断发展变化,陆续出现了 TSC(Time Stamp Counter),HPET(High Precision Event Timer),ACPI PM Timer(ACPI Power Management Timer),CPU Local APIC Timer 等精度更高的时钟。这些时钟陆续被 Linux 的时钟子系统所采纳,从而不

4、断的提高 Linux 时钟子系统的性能和灵活性。这些不同的时钟会在下文不同的章节中分别进行介绍。回页首Timer wheel在 Linux 2.6.16 之前,内核一直使用一种称为 timer wheel 的机制来管理时钟。这就是熟知的 kernel 一直采用的基于 HZ 的 timer 机制。Timer wheel 的核心数据结构如清单 1 所示:清单 1. Timer wheel 的核心数据结构 #define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6) #define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8) #define

5、TVN_SIZE (1 TVN_BITS) #define TVR_SIZE (1 lock); while (time_after_eq(jiffies, base-timer_jiffies) struct list_head work_list; struct list_head *head = &work_list; int index = base-timer_jiffies & TVR_MASK; /* * Cascade timers: */ if (!index & (!cascade(base, &base-tv2, INDEX(0) & (!cascade(base, &b

6、ase-tv3, INDEX(1) & !cascade(base, &base-tv4, INDEX(2) cascade(base, &base-tv5, INDEX(3); +base-timer_jiffies; list_replace_init(base-tv1.vec + index, &work_list); while (!list_empty(head) void (*fn)(unsigned long); unsigned long data; timer = list_first_entry(head, struct timer_list,entry); fn = ti

7、mer-function; data = timer-data; . . . . fn(data); . . . . base-timer_jiffies 用来记录在 TV1 中最接近超时的 tick 的位置。index 是用来遍历 TV1 的索引。每一次循环 index 会定位一个当前待处理的 tick,并处理这个 tick 下所有超时的 timer。base-timer_jiffies 会在每次循环后增加一个 jiffy,index 也会随之向前移动。当 index 变为 0 时表示 TV1 完成了一次完整的遍历,此时所有在 TV1 中的 timer 都被处理了,因此需要通过 cascad

8、e 将后面 TV2,TV3 等 timer list 中的 timer 向前移动,类似于进位。这种层叠的 timer list 实现机制可以大大降低每次检查超时 timer 的时间,每次中断只需要针对 TV1 进行检查,只有必要时才进行 cascade。即便如此,timer wheel 的实现机制仍然存在很大弊端。一个弊端就是 cascade 开销过大。在极端的条件下,同时会有多个 TV 需要进行 cascade 处理,会产生很大的时延。这也是为什么说 timeout 类型的定时器是 timer wheel 的主要应用环境,或者说 timer wheel 是为 timeout 类型的定时器优化

9、的。因为 timeout 类型的定时器的应用场景多是错误条件的检测,这类错误发生的机率很小,通常不到超时就被删除了,因此不会产生 cascade 的开销。另一方面,由于 timer wheel 是建立在 HZ 的基础上的,因此其计时精度无法进一步提高。毕竟一味的通过提高 HZ 值来提高计时精度并无意义,结果只能是产生大量的定时中断,增加额外的系统开销。因此,有必要将高精度的 timer 与低精度的 timer 分开,这样既可以确保低精度的 timeout 类型的定时器应用,也便于高精度的 timer 类型定时器的应用。还有一个重要的因素是 timer wheel 的实现与 jiffies 的耦

10、合性太强,非常不便于扩展。因此,自从 2.6.16 开始,一个新的 timer 子系统 hrtimer 被加入到内核中。回页首hrtimer (High-resolution Timer)hrtimer 首先要实现的功能就是要克服 timer wheel 的缺点:低精度以及与内核其他模块的高耦合性。在正式介绍 hrtimer 之前,有必要先介绍几个常用的基本概念:时钟源设备(clock-source device)系统中可以提供一定精度的计时设备都可以作为时钟源设备。如 TSC,HPET,ACPI PM-Timer,PIT 等。但是不同的时钟源提供的时钟精度是不一样的。像 TSC,HPET 等

11、时钟源既支持高精度模式(high-resolution mode)也支持低精度模式(low-resolution mode),而 PIT 只能支持低精度模式。此外,时钟源的计时都是单调递增的(monotonically),如果时钟源的计时出现翻转(即返回到 0 值),很容易造成计时错误, 内核的一个 patch(commit id: ff69f2)就是处理这类问题的一个很好示例。时钟源作为系统时钟的提供者,在可靠并且可用的前提下精度越高越好。在 Linux 中不同的时钟源有不同的 rating,具有更高 rating 的时钟源会优先被系统使用。如图 2 所示:表 1. 时钟源中 rating

12、的定义1 99 100 199 200 299 300 399 400 499非常差的时钟源,只能作为最后的选择。如 jiffies 基本可以使用但并非理想的时钟源。如 PIT 正确可用的时钟源。如 ACPI PM Timer,HPET 快速并且精确的时钟源。如 TSC 理想时钟源。如 kvm_clock,xen_clock时钟事件设备(clock-event device)系统中可以触发 one-shot(单次)或者周期性中断的设备都可以作为时钟事件设备。如 HPET,CPU Local APIC Timer 等。HPET 比较特别,它既可以做时钟源设备也可以做时钟事件设备。时钟事件设备的类

13、型分为全局和 per-CPU 两种类型。全局的时钟事件设备虽然附属于某一个特定的 CPU 上,但是完成的是系统相关的工作,例如完成系统的 tick 更新;per-CPU 的时钟事件设备主要完成 Local CPU 上的一些功能,例如对在当前 CPU 上运行进程的时间统计,profile,设置 Local CPU 上的下一次事件中断等。和时钟源设备的实现类似,时钟事件设备也通过 rating 来区分优先级关系。tick deviceTick device 用来处理周期性的 tick event。Tick device 其实是时钟事件设备的一个 wrapper,因此 tick device 也有

14、one-shot 和周期性这两种中断触发模式。每注册一个时钟事件设备,这个设备会自动被注册为一个 tick device。全局的 tick device 用来更新诸如 jiffies 这样的全局信息,per-CPU 的 tick device 则用来更新每个 CPU 相关的特定信息。broadcastCPU 的 C-STATECPU 在空闲时会根据空闲时间的长短选择进入不同的睡眠级别,称为 C-STATE。C0 为正常运行状态,C1 到 C7 为睡眠状态,数值越大,睡眠程度越深,也就越省电。CPU 空闲越久,进入睡眠的级别越高,但是唤醒所需的时间也越长。唤醒也是需要消耗能源的,因此,只有选择合

15、适的睡眠级别才能确保节能的最大化。Broadcast 的出现是为了应对这样一种情况:假定 CPU 使用 Local APIC Timer 作为 per-CPU 的 tick device,但是某些特定的 CPU(如 Intel 的 Westmere 之前的 CPU)在进入 C3+ 的状态时 Local APIC Timer 也会同时停止工作,进入睡眠状态。在这种情形下 broadcast 可以替代 Local APIC Timer 继续完成统计进程的执行时间等有关操作。本质上 broadcast 是发送一个 IPI(Inter-processor interrupt)中断给其他所有的 CPU,

16、当目标 CPU 收到这个 IPI 中断后就会调用原先 Local APIC Timer 正常工作时的中断处理函数,从而实现了同样的功能。目前主要在 x86 以及 MIPS 下会用到 broadcast 功能。Timekeeping & GTOD (Generic Time-of-Day)Timekeeping(可以理解为时间测量或者计时)是内核时间管理的一个核心组成部分。没有 Timekeeping,就无法更新系统时间,维持系统“心跳”。GTOD 是一个通用的框架,用来实现诸如设置系统时间 gettimeofday 或者修改系统时间 settimeofday 等工作。为了实现以上功能,Linu

17、x 实现了多种与时间相关但用于不同目的的数据结构。 struct timespec _kernel_time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ ; timespec 精度是纳秒。它用来保存从 00:00:00 GMT, 1 January 1970 开始经过的时间。内核使用全局变量 xtime 来记录这一信息,这就是通常所说的“Wall Time”或者“Real Time”。与此对应的是“System Time”。System Time 是一个单调递增的时间,每次系统启动时从 0 开始计时。 struct timev

18、al _kernel_time_t tv_sec; /* seconds */ _kernel_suseconds_t tv_usec; /* microseconds */ ; timeval 精度是微秒。timeval 主要用来指定一段时间间隔。 union ktime s64 tv64; #if BITS_PER_LONG != 64 & !defined(CONFIG_KTIME_SCALAR) struct # ifdef _BIG_ENDIAN s32 sec, nsec; # else s32 nsec, sec; # endif tv; #endif ; ktime_t 是 h

19、rtimer 主要使用的时间结构。无论使用哪种体系结构,ktime_t 始终保持 64bit 的精度,并且考虑了大小端的影响。 typedef u64 cycle_t; cycle_t 是从时钟源设备中读取的时钟类型。为了管理这些不同的时间结构,Linux 实现了一系列辅助函数来完成相互间的转换。ktime_to_timespec,ktime_to_timeval,ktime_to_ns/ktime_to_us,反过来有诸如 ns_to_ktime 等类似的函数。timeval_to_ns,timespec_to_ns,反过来有诸如 ns_to_timeval 等类似的函数。timeval_t

20、o_jiffies,timespec_to_jiffies,msecs_to_jiffies, usecs_to_jiffies, clock_t_to_jiffies 反过来有诸如 ns_to_timeval 等类似的函数。clocksource_cyc2ns / cyclecounter_cyc2ns有了以上的介绍,通过图 3 可以更加清晰的看到这几者之间的关联。图 2. 内核时钟子系统的结构关系 时钟源设备和时钟事件设备的引入,将原本放在各个体系结构中重复实现的冗余代码封装到各自的抽象层中,这样做不但消除了原来 timer wheel 与内核其他模块的紧耦合性,更重要的是系统可以在运行状

21、态动态更换时钟源设备和时钟事件设备而不影响系统正常使用,譬如当 CPU 由于睡眠要关闭当前使用的时钟源设备或者时钟事件设备时系统可以平滑的切换到其他仍处于工作状态的设备上。Timekeeping/GTOD 在使用时钟源设备的基础上也采用类似的封装实现了体系结构的无关性和通用性。hrtimer 则可以通过 timekeeping 提供的接口完成定时器的更新,通过时钟事件设备提供的事件机制,完成对 timer 的管理。在图 3 中还有一个重要的模块就是 tick device 的抽象,尤其是 dynamic tick。Dynamic tick 的出现是为了能在系统空闲时通过停止 tick 的运行以

22、达到降低 CPU 功耗的目的。使用 dynamic tick 的系统,只有在有实际工作时才会产生 tick,否则 tick 是处于停止状态。下文会有专门的章节进行论述。hrtimer 的实现机制hrtimer 是建立在 per-CPU 时钟事件设备上的,对于一个 SMP 系统,如果只有全局的时钟事件设备,hrtimer 无法工作。因为如果没有 per-CPU 时钟事件设备,时钟中断发生时系统必须产生必要的 IPI 中断来通知其他 CPU 完成相应的工作,而过多的 IPI 中断会带来很大的系统开销,这样会令使用 hrtimer 的代价太大,不如不用。为了支持 hrtimer,内核需要配置 CON

23、FIG_HIGH_RES_TIMERS=y。hrtimer 有两种工作模式:低精度模式(low-resolution mode)与高精度模式(high-resolution mode)。虽然 hrtimer 子系统是为高精度的 timer 准备的,但是系统可能在运行过程中动态切换到不同精度的时钟源设备,因此,hrtimer 必须能够在低精度模式与高精度模式下自由切换。由于低精度模式是建立在高精度模式之上的,因此即便系统只支持低精度模式,部分支持高精度模式的代码仍然会编译到内核当中。在低精度模式下,hrtimer 的核心处理函数是 hrtimer_run_queues,每一次 tick 中断都要

24、执行一次。如清单 3 所示。这个函数的调用流程为: update_process_times run_local_timers hrtimer_run_queues raise_softirq(TIMER_SOFTIRQ) 清单 3. 低精度模式下 hrtimer 的核心处理函数 void hrtimer_run_queues(void) struct rb_node *node; struct hrtimer_cpu_base *cpu_base = &_get_cpu_var(hrtimer_bases); struct hrtimer_clock_base *base; int inde

25、x, gettime = 1; if (hrtimer_hres_active() return; for (index = 0; index clock_baseindex; if (!base-first) continue; if (gettime) hrtimer_get_softirq_time(cpu_base); gettime = 0; raw_spin_lock(&cpu_base-lock); while (node = base-first) struct hrtimer *timer; timer = rb_entry(node, struct hrtimer, nod

26、e); if (base-softirq_time.tv64 softirq_time); raw_spin_unlock(&cpu_base-lock); hrtimer_bases 是实现 hrtimer 的核心数据结构,通过 hrtimer_bases,hrtimer 可以管理挂在每一个 CPU 上的所有 timer。每个 CPU 上的 timer list 不再使用 timer wheel 中多级链表的实现方式,而是采用了红黑树(Red-Black Tree)来进行管理。hrtimer_bases 的定义如清单 4 所示:清单 4. hrtimer_bases 的定义 DEFINE_PER_CPU(struct hrti

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1