Linux时间系统的设计与实现.doc_第1页
Linux时间系统的设计与实现.doc_第2页
Linux时间系统的设计与实现.doc_第3页
Linux时间系统的设计与实现.doc_第4页
Linux时间系统的设计与实现.doc_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

尹能 linux时间系统的设计与实现 第18页 共18页 Linux时间系统的设计与实现学生姓名:xxx 指导老师:XXX摘 要时间系统是操作系统的重要组成部分 ,无论是在分时操作系统还是在实时操作系统中 ,时间系统都起着极为重要的作用。时间系统的主要功能是负责维护系统时间、计算 CPU的使用情况、为分时调度提供计时、提供定时器。概述了时间系统的主要功能 ,对 Linux时间系统的硬件基础进行介绍 ,介绍了 Linux操作系统中用来表示时间的数据结构。结合对源代码的分析 ,具体描述了 Linux独特的时钟中断处理过程和时间系统各功能的实现。最后 ,介绍了实时操作系统的特征 ,并针对如何改造时间系统 ,提高 Linux核心的实时处理能力提出了解决方案。关键词linux时间系统;linux-2.4.3.0源码;Red hat Enterprise linux 51引 言1.1 课题背景及意义首先, 是经济问题。中国是个人口大国,将来更要成为网络大国,如果每个人都用正版windows 那将是一笔不小的开销.linux就不存在这个问题。 当然, 更不可能每个人都用苹果的macos。其次,linux 是一个具有优良血统的系统, 它具有unix的特点,又具有极好的易用性,安全性,强大的网络服务功能。综合来说,linux具有良好的发展前景.中国对于推广linux 具有非常的价值。 1.2 课程设计目的(1)学会使用vi编辑器编辑C语言程序(2)学会Linux环境下gcc的使用(3)学会调试工具GDB的使用(4)学习重新编译Linux内核,理解、掌握Linux内核版本和Linux发行版本的区别(5) 学会在linux中设置,修改,显示时间 1.3 课题技术支持了解Linux的时钟 由于Linux时钟和Windows时钟从概念的分类、使用到设置都有很大的不同,所以,搞清楚Linux时钟的工作方式与设置操作,不仅对于Linux初学者有着重大意义,而且对于使用Linux服务器的用户来说尤为重要。1.4 可行性分析报告 Windows时钟大家可能十分熟悉了,Linux时钟在概念上类似Windows时钟显示当前系统时间,但在时钟分类和设置上却和Windows大相径庭。和Windows不同的是,Linux将时钟分为系统时钟(System Clock)和硬件(Real Time Clock,简称RTC)时钟两种。系统时间是指当前Linux Kernel中的时钟,而硬件时钟则是主板上由电池供电的那个主板硬件时钟,这个时钟可以在BIOS的“Standard BIOS Feture”项中进行设置。既然Linux有两个时钟系统,那么大家所使用的Linux默认使用哪种时钟系统呢?会不回出现两种系统时钟冲突的情况呢?这些疑问和担心不无道理。首先,Linux并没有默认哪个时钟系统。当Linux启动时,硬件时钟会去读取系统时钟的设置,然后系统时钟就会独立于硬件运作。从Linux启动过程来看,系统时钟和硬件时钟不会发生冲突,但Linux中的所有命令(包括函数)都是采用的系统时钟设置。不仅如此,系统时钟和硬件时钟还可以采用异步方式,见图1所示,即系统时间和硬件时间可以不同。这样做的好处对于普通用户意义不大,但对于Linux网络管理员却有很大的用处。例如,要将一个很大的网络中(跨越若干时区)的服务器同步,假如位于美国纽约的Linux服务器和北京的Linux服务器,其中一台服务器无须改变硬件时钟而只需临时设置一个系统时间,如要将北京服务器上的时间设置为纽约时间,两台服务器完成文件的同步后,再与原来的时钟同步一下即可。这样系统和硬件时钟就提供了更为灵活的操作。 2 实现原理通常,操作系统可以使用三种方法来表示系统的当前时间与日期:最简单的一种方法就是直接用一个64位的计数器来对时钟滴答进行计数。第二种方法就是用一个32位计数器来对秒进行计数,同时还用一个32位的辅助计数器对时钟滴答计数,直至累积到一秒为止。因为32位的秒计数超过136年,因此这种方法直至22世纪都可以让系统工作得很好。第三种方法是按系统启动以来的滴答次数进行计数,而不是相对于某个确定的外部时刻;当读外部后备时钟(如RTC)或用户输入实际时间时,根据当前的滴答次数计算系统当前时间。 21 基本概念 首先,有必要明确一些Linux内核时钟驱动中的基本概念。 (1)时钟周期(clock cycle)的频率:82538254 PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。Linux用宏CLOCK_TICK_RATE来表示8254 PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中: #define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ (2)时钟滴答(clock tick):我们知道,当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。 (3)时钟滴答的频率(HZ):也即1秒时间内PIT所产生的时钟滴答次数。类似地,这个值也是由PIT通道0的计数器初值决定的(反过来说,确定了时钟滴答的频率值后也就可以确定8254 PIT通道0的计数器初值)。Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和IA62平台HZ的值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下(include/asm-i386/param.h): #ifndef HZ #define HZ 100 #endif 根据HZ的值,我们也可以知道一次时钟滴答的具体时间间隔应该是(1000msHZ)10ms。 (4)时钟滴答的时间间隔:Linux用全局变量tick来表示时钟滴答的时间间隔长度,该变量定义在kernel/timer.c文件中,如下: long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */ tick变量的单位是微妙(s),由于在不同平台上宏HZ的值会有所不同,因此方程式tick1000000HZ的结果可能会是个小数,因此将其进行四舍五入成一个整数,所以Linux将tick定义成(1000000HZ2)HZ,其中被除数表达式中的HZ2的作用就是用来将tick值向上圆整成一个整型数。 另外,Linux还用宏TICK_SIZE来作为tick变量的引用别名(alias),其定义如下(archi386/kernel/time.c): #define TICK_SIZE tick (5)宏LATCH:Linux用宏LATCH来定义要写到PIT通道0的计数器中的值,它表示PIT将每隔多少个时钟周期产生一次时钟中断。显然LATCH应该由下列公式计算: LATCH(1秒之内的时钟周期个数)(1秒之内的时钟中断次数)(CLOCK_TICK_RATE)(HZ) 类似地,上述公式的结果可能会是个小数,应该对其进行四舍五入。所以,Linux将LATCH定义为(include/linux/timex.h): /* LATCH is used in the interval timer and ftape setup. */ #define LATCH (CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ 类似地,被除数表达式中的HZ2也是用来将LATCH向上圆整成一个整数。 22 表示系统当前时间的内核数据结构 作为一种UNIX类操作系统,Linux内核显然采用本节一开始所述的第三种方法来表示系统的当前时间。Linux内核在表示系统当前时间时用到了三个重要的数据结构: 全局变量jiffies:这是一个32位的无符号整数,用来表示自内核上一次启动以来的时钟滴答次数。每发生一次时钟滴答,内核的时钟中断处理函数timer_interrupt()都要将该全局变量jiffies加1。该变量定义在kernel/timer.c源文件中,如下所示: unsigned long volatile jiffies; C语言限定符volatile表示jiffies是一个易改变的变量,因此编译器将使对该变量的访问从不通过CPU内部cache来进行。 全局变量xtime:它是一个timeval结构类型的变量,用来表示当前时间距UNIX时间基准19700101 00:00:00的相对秒数值。结构timeval是Linux内核表示时间的一种格式(Linux内核对时间的表示有多种格式,每种格式都有不同的时间精度),其时间精度是微秒。该结构是内核表示时间时最常用的一种格式,它定义在头文件include/linux/time.h中,如下所示: struct timeval time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ ; 其中,成员tv_sec表示当前时间距UNIX时间基准的秒数值,而成员tv_usec则表示一秒之内的微秒值,且1000000tv_usec0。 Linux内核通过timeval结构类型的全局变量xtime来维持当前时间,该变量定义在kernel/timer.c文件中,如下所示: /* The current time */ volatile struct timeval xtime _attribute_ (aligned (16); 但是,全局变量xtime所维持的当前时间通常是供用户来检索和设置的,而其他内核模块通常很少使用它(其他内核模块用得最多的是jiffies),因此对xtime的更新并不是一项紧迫的任务,所以这一工作通常被延迟到时钟中断的底半部分(bottom half)中来进行。由于bottom half的执行时间带有不确定性,因此为了记住内核上一次更新xtime是什么时候,Linux内核定义了一个类似于jiffies的全局变量wall_jiffies,来保存内核上一次更新xtime时的jiffies值。时钟中断的底半部分每一次更新xtime的时侯都会将wall_jiffies更新为当时的jiffies值。全局变量wall_jiffies定义在kernel/timer.c文件中: /* jiffies at the most recent update of wall time */ unsigned long wall_jiffies; 全局变量sys_tz:它是一个timezone结构类型的全局变量,表示系统当前的时区信息。结构类型timezone定义在include/linux/time.h头文件中,如下所示: struct timezone int tz_minuteswest; /* minutes west of Greenwich */ int tz_dsttime; /* type of dst correction */ ; 基于上述结构,Linux在kernel/time.c文件中定义了全局变量sys_tz表示系统当前所处的时区信息,如下所示: struct timezone sys_tz; 23 Linux对TSC的编程实现 Linux用定义在arch/i386/kernel/time.c文件中的全局变量use_tsc来表示内核是否使用CPU的TSC寄存器,use_tsc1表示使用TSC,use_tsc0表示不使用TSC。该变量的值是在time_init()初始化函数中被初始化的(详见下一节)。该变量的定义如下: static int use_tsc; 宏cpu_has_tsc可以确定当前系统的CPU是否配置有TSC寄存器。此外,宏CONFIG_X86_TSC也表示是否存在TSC寄存器。 1 读TSC寄存器的宏操作 x86 CPU的rdtsc指令将TSC寄存器的高32位值读到EDX寄存器中、低32位读到EAX寄存器中。Linux根据不同的需要,在rdtsc指令的基础上封装几个高层宏操作,以读取TSC寄存器的值。它们均定义在include/asm-i386/msr.h头文件中,如下: #define rdtsc(low,high) _asm_ _volatile_(rdtsc : =a (low), =d (high) #define rdtscl(low) _asm_ _volatile_ (rdtsc : =a (low) : : edx) #define rdtscll(val) _asm_ _volatile_ (rdtsc : =A (val) 宏rdtsc()同时读取TSC的LSB与MSB,并分别保存到宏参数low和high中。宏rdtscl则只读取TSC寄存器的LSB,并保存到宏参数low中。宏rdtscll读取TSC的当前64位值,并将其保存到宏参数val这个64位变量中。 2 校准TSC 与可编程定时器PIT相比,用TSC寄存器可以获得更精确的时间度量。但是在可以使用TSC之前,它必须精确地确定1个TSC计数值到底代表多长的时间间隔,也即到底要过多长时间间隔TSC寄存器才会加1。Linux内核用全局变量fast_gettimeoffset_quotient来表示这个值,其定义如下(arch/i386/kernel/time.c): /* Cached *multiplier* to convert TSC counts to microseconds. * (see the equation below). * Equal to 232 * (1 / (clocks per usec) ). * Initialized in time_init. */ unsigned long fast_gettimeoffset_quotient; 根据上述定义的注释我们可以看出,这个变量的值是通过下述公式来计算的: fast_gettimeoffset_quotient (232) / (每微秒内的时钟周期个数) 定义在arch/i386/kernel/time.c文件中的函数calibrate_tsc()就是根据上述公式来计算fast_gettimeoffset_quotient的值的。显然这个计算过程必须在内核启动时完成,因此,函数calibrate_tsc()只被初始化函数time_init()所调用。 3 用TSC实现高精度的时间服务 在拥有TSC(TimeStamp Counter)的x86 CPU上,Linux内核可以实现微秒级的高精度定时服务,也即可以确定两次时钟中断之间的某个时刻的微秒级时间值。要确定时刻x的微秒级时间值,就必须确定时刻x距上一次时钟中断产生时刻的时间间隔偏移offset_usec的值(以微秒为单位)。为此,内核定义了以下两个变量: (1)中断服务执行延迟delay_at_last_interrupt:由于从产生时钟中断的那个时刻到内核时钟中断服务函数timer_interrupt真正在CPU上执行的那个时刻之间是有一段延迟间隔的,因此,Linux内核用变量delay_at_last_interrupt来表示这一段时间延迟间隔,其定义如下(arch/i386/kernel/time.c): /* Number of usecs that the last interrupt was delayed */ static int delay_at_last_interrupt; 关于delay_at_last_interrupt的计算步骤我们将在分析timer_interrupt()函数时讨论。 (2)全局变量last_tsc_low:它表示中断服务timer_interrupt真正在CPU上执行时刻的TSC寄存器值的低32位(LSB)。 显然,通过delay_at_last_interrupt、last_tsc_low和时刻x处的TSC寄存器值,我们就可以完全确定时刻x距上一次时钟中断产生时刻的时间间隔偏移offset_usec的值。实现在arch/i386/kernel/time.c中的函数do_fast_gettimeoffset()就是这样计算时间间隔偏移的,当然它仅在CPU配置有TSC寄存器时才被使用,后面我们会详细分析这个函数。 3 时钟中断的驱动如前所述,82538254 PIT的通道0通常被用来在IRQ0上产生周期性的时钟中断。对时钟中断的驱动是绝大数操作系统内核实现time-keeping的关键所在。不同的OS对时钟驱动的要求也不同,但是一般都包含下列要求内容: 1. 维护系统的当前时间与日期。 2. 防止进程运行时间超出其允许的时间。 3. 对CPU的使用情况进行记帐统计。 4. 处理用户进程发出的时间系统调用。 5. 对系统某些部分提供监视定时器。 其中,第一项功能是所有OS都必须实现的基础功能,它是OS内核的运行基础。31 Linux对时钟中断的初始化 Linux对时钟中断的初始化是分为几个步骤来进行的:(1)首先,由init_IRQ()函数通过调用init_ISA_IRQ()函数对中断向量32256所对应的中断向量描述符进行初始化设置。显然,这其中也就把IRQ0(也即中断向量32)的中断向量描述符初始化了。(2)然后,init_IRQ()函数设置中断向量32256相对应的中断门。(3)init_IRQ()函数对PIT进行初始化编程;(4)sched_init()函数对计数器、时间中断的Bottom Half进行初始化。(5)最后,由time_init()函数对Linux内核的时钟中断机制进行初始化。这三个初始化函数都是由init/main.c文件中的start_kernel()函数调用的,如下: asmlinkage void _init start_kernel() trap_init(); init_IRQ(); sched_init(); time_init(); softirq_init(); (1)init_IRQ()函数对8254 PIT的初始化编程 函数init_IRQ()函数在完成中断门的初始化后,就对8254 PIT进行初始化编程设置,设置的步骤如下:(1)设置8254 PIT的控制寄存器(端口0x43)的值为“01100100”,也即选择通道0、先读写LSB再读写MSB、工作模式2、二进制存储格式。(2)将宏LATCH的值写入通道0的计数器中(端口0x40),注意要先写LATCH的LSB,再写LATCH的高字节。其源码如下所示(arch/i386/kernel/i8259.c): void _init init_IRQ(void) /* * Set the clock to HZ Hz, we already have a valid * vector now: */ outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ outb_p(LATCH & 0xff , 0x40); /* LSB */ outb(LATCH 8 , 0x40); /* MSB */ (2)sched_init()对定时器机制和时钟中断的Bottom Half的初始化 函数sched_init()中与时间相关的初始化过程主要有两步:(1)调用init_timervecs()函数初始化内核定时器机制;(2)调用init_bh()函数将BH向量TIMER_BH、TQUEUE_BH和IMMEDIATE_BH所对应的BH函数分别设置成timer_bh()、tqueue_bh()和immediate_bh()函数。如下所示(kernel/sched.c): void _init sched_init(void) init_timervecs(); init_bh(TIMER_BH, timer_bh); init_bh(TQUEUE_BH, tqueue_bh); init_bh(IMMEDIATE_BH, immediate_bh); 4详细设计4.1 基本操作(1)date 042612492005(2)hwclock w第一步的意思是设置时间,设置完了可以用date命令查看对不对.格式是月日时分年第二步的意思是写入主板的rtc芯片.=su -c date -s 月/日/年su -c date -s 时:分:秒=1.在虚拟终端中使用date命令来查看和设置系统时间查看系统时钟的操作:# date设置系统时钟的操作:# date 091713272003.30通用的设置格式:# date 月日时分年.秒2.使用hwclock或clock命令查看和设置硬件时钟查看硬件时钟的操作:# hwclock -show 或# clock -show2003年09月17日 星期三 13时24分11秒 -0.482735 seconds设置硬件时钟的操作:# hwclock -set -date=09/17/2003 13:26:00或者# clock -set -date=09/17/2003 13:26:00通用的设置格式:hwclock/clock -set -date=“月/日/年 时:分:秒”。3.同步系统时钟和硬件时钟Linux系统(使用的是Red hat Enterprise linux 5)默认重启后,硬件时钟和系统时钟同步。如果不大方便重新启动的话(服务器通常很少重启),使用clock或hwclock命令来同步系统时钟和硬件时钟。硬件时钟与系统时钟同步:# hwclock -hctosys或者# clock -hctosys上面命令中,-hctosys表示Hardware Clock to SYStem clock。系统时钟和硬件时钟同步:# hwclock -systohc或者# clock -systohc还可以使用图形化系统设置

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论