当前位置: 首页 > 图文教程 > 操作系统 > Unix/Linux > linux中的时间流

Unix/Linux
MX 10
使用Apache&花生壳架设Web服务器
请教dns!有关于密码的问题!
Linux下各种服务器的架设详解
用init玩转 Linux 运行级别
修改/etc/inittab后如何保存退出
emacs进行文件编辑后如何保存退出
linux怎样用普通用户关机
Linux下备份恢复技术的应用
Linux下使用磁带机的常用命令 tar
vmware中linux如何设置网卡
vmware中linux怎么设置网络
怎么在redhat linux下挂载光驱软驱???
redhat linux 下载大全 完全整理
用vmware安装linux系统过程
在linux如何查看IP
linux下如何修改ip地址,主机名等信息
vmware 网络配置
Linux系统中如何查看运行级别
Slackware Linux init 进程

Unix/Linux 中的 linux中的时间流


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-11-01   浏览: 46 ::
收藏到网摘: n/a

《linux设备驱动程序》中对时间流的一段分析
我们来看看内核代码是如何对时间问题进行处理的。按复杂程度递增排列,该问题包括: 理解内核时间机制 如何获得当前时间 如何将操作延迟指定的一段时间 如何调度异步函数到指定的时间后执行 内核中的时间间隔 我们首先要涉及的是时钟中断,操作系统通过时钟中断来确定时间间隔。中断是异步事件,通常由外部硬件触发。中断发生时,CPU停止正在进行的任务,转而执行另一段特殊的代码(即中断服务例程,又称ISR)来响应这个中断。中断和 ISR 的实现将在第9章讨论。 时钟中断由系统计时硬件以周期性的间隔产生,这个间隔由内核根据 HZ 的值设定,HZ 是一个与体系结构有关的常数,在文件 中定义。当前的Linux版本为大多数平台定义的 HZ 的值是100,某些平台上是 1024,IA-64 仿真器上是 20。驱动程序开发者不应使用任何特定的 HZ 值来计数,不管你的平台使用的是哪一个值。 当时钟中断发生时,变量jiffies的值就增加。jiffies在系统启动时初始化为0,因此,jiffies 值就是自操作系统启动以来的时钟滴答的数目,jiffies在头文件 中被定义为数据类型为 unsigned long volatile型变量,这个变量在经过长时间的连续运行后有可能溢出(不过现在还没有哪种平台会在运行不到 16 个月就使jiffies溢出)。为了保证 jiffies 溢出时内核仍能正常工作,人们已做了很多努力。驱动程序开发人员通常不用考虑 jiffies 的溢出问题,知道有这种可能性就行了。 如果想改变系统时钟中断发生的频率,可以修改HZ值。有人使用Linux处理硬实时任务,他们增加了HZ值以获得更快的响应时间,为此情愿忍受额外的时钟中断产生的系统开销。总而言之,时钟中断的最好方法是保留 HZ 的缺省值,因为我们可以完全相信内核的开发者们,他们一定已经为我们挑选了最佳值。 处理器特有的寄存器 如果需要度量非常短的时间,或是需要极高的时间精度,可以使用与特定平台相关的资源,这是将时间精度的重要性凌驾于代码的可移植性之上的做法。 大多数较新的CPU都包含一个高精度的计数器,它每个时钟周期递增一次。这个计数器可用于精确地度量时间。由于大多数系统中的指令执行时间具有不可预测性(由于指令调度、分支预测、缓存等等),在运行具有很小时间粒度的任务时,使用这个时钟计数器是唯一可靠的计时方法。为适应现代处理器的高速度,满足衡量性能指标的紧迫需求,同时由于CPU设计中的多层缓存引起的指令时间的不可预测性,CPU 的制造商们引入了记录时钟周期这一测量时间的简单可靠的方法。所以绝大多数现代处理器都包含一个随时钟周期不断递增的计数寄存器。 基于不同的平台,在用户空间,这个寄存器可能是可读的,也可能不可读;可能是可写的,也可能不可写;可能是64位的也可能是32位的。如果是 32 位的,还得注意处理溢出的问题。无论该寄存器是否可以置0,我们都强烈建议不要重置它,即使硬件允许这么做。因为总可以通过多次读取该寄存器并比较读出数值的差异来完成要做的事,我们无须要求独占该寄存器并修改它的当前值。 最有名的计数器寄存器就是TSC(timestamp counter,时间戳计数器),从x86的Pentium处理器开始提供该寄存器,并包括在以后的所有 CPU 中。它是一个64位寄存器,记录 CPU时钟周期数,内核空间和用户空间都可以读取它。 包含了头文件 (意指“machine-specific registers,机器特有的寄存器”)之后,就可以使用如下的宏: rdtsc(low,high); rdtscl(low); 前一个宏原子性地把 64 位的数值读到两个 32 位变量中;后一个只把寄存器的低半部分读入一个 32 位变量,在大多数情况,这已经够用了。举例来说,一个 500MHz 的系统使一个 32 位计数器溢出需 8.5 秒,如果要处理的时间肯定比这短的话,那就没有必要读出整个寄存器。 下面这段代码可以测量该指令自身的运行时间: unsigned long ini, end; rdtscl(ini); rdtscl(end); printk("time lapse: %li\en", end - ini); 其他一些平台也提供了类似的功能,在内核头文件中还有一个与体系结构无关的函数可以代替rdtsc,它就是get_cycles,是在2.1版的开发过程中引入的。其原型是: #include cycles_t get_cycles(void); 在各种平台上都可以使用这个函数,在没有时钟周期记数寄存器的平台上它总是返回0。cycles_t 类型是能装入对应 CPU 单个寄存器的合适的无符号类型。选择能装入单个寄存器的类型意味着,举例来说,get_cycles 用于 Pentium 的时钟周期计数器时只返回低32位。这种选择是明智的,它避免了多寄存器操作的问题,与此同时并未阻碍对该计数器的正常用法,即用来度量很短的时间间隔。 除了这个与体系结构无关的函数外,我们还将示例使用一段内嵌的汇编代码。为此,我们来给 MIPS 处理器实现一个rdtscl函数,功能就象x86的一样。 这个例子之所以基于 MIPS,是因为大多数MIPS处理器都有一个32位的计数器,在它们的内部“coprocessor 0”中命名为register 9寄存器。为了从内核空间读取该寄存器,可以定义下面的宏,它执行“从coprocessor 0读取”的汇编指令: =======footnote begins============= nop 指令是必需的,防