x86处理器中的时间源

7

有人知道可以用哪些x86指令来测量时间吗?引发任务切换的计时器是否可被软件访问?


1
你想在机器码层面上做这个吗?有一个硬件中断和一些端口地址可以访问计时器。然而,我最后一次读到这个已经是15年前了。 - JohnB
最小PIT示例:https://github.com/cirosantilli/x86-bare-metal-examples/blob/9a24f92f36a45abb3f8c37aafc0c3ee9b15563ab/in_pit.S#L2,最小RTC示例:https://github.com/cirosantilli/x86-bare-metal-examples/blob/9a24f92f36a45abb3f8c37aafc0c3ee9b15563ab/in_rtc.S - Ciro Santilli OurBigBook.com
3个回答

14

在x86平台上测量时间的方法:

  • 实时时钟 - 操作系统中时间和日期的来源。精确到1秒。标准PC中唯一可以用于测量绝对时间的时间源。

  • 8254计数器/定时器 - 一种标准计数器/定时器芯片,自PC产生以来一直存在于主板上(现在是芯片组内的一个功能块)。这是IRQ0的传统来源,它在大多数旧操作系统中执行任务切换。

  • RDTSC汇编指令 - 计算CPU时钟周期数。详情请见Anon ymous发布的答案。这是x86上可找到的最高级别的时间源。但是,它在准确性方面有一些注意事项。如果您正在写汇编语言,这也是最方便的选项。

  • RDTSCP汇编指令 - 类似于RDTSC,但是是串行化的,这解决了RDTSC的一些准确性问题。只能在最新的处理器上找到。

  • HPET - 在Core Duo时代的PC上引入。旨在替代老旧的8254。现代操作系统将使用它作为任务调度中断(Vista及更高版本)。

  • 芯片组中的专有定时器 - 一些芯片组内置有用于电源管理和多媒体功能的特殊定时器。如果您正在处理固定功能嵌入式系统而不是通用PC,则有时可以占用这些定时器。

请注意,这些选项并非都适用于您,具体取决于您的操作系统和硬件。如果您正在运行现代操作系统(Windows、Linux),它将控制8254/HPET以满足其自身的定时需求,因此它们对您来说将不可用。

在现代操作系统下,最好使用操作系统提供的定时功能。如果您尝试自己编写定时功能,您可能会遇到许多问题,而操作系统开发人员已经解决了这些问题。(注意,操作系统可能提供多个定时功能。请选择适合您应用程序的那一个。)


2
你可以使用rdtsc指令。只需将当前值减去先前值即可计算时间差。
引用块: 将处理器时间戳计数器(64位MSR)的当前值加载到EDX:EAX寄存器中。 EDX寄存器加载MSR的高32位,EAX寄存器加载低32位。(在支持英特尔64架构的处理器上,清除RAX和RDX的每个寄存器的高32位。)
这是一个实现此指令的C代码:- 链接
unsigned long long int rdtsc(void)
{
   unsigned long long int x;
   unsigned a, d;

   __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));

   return ((unsigned long long)a) | (((unsigned long long)d) << 32);;
}

1
请注意,RDTSC 有一些限制可能会影响准确性:(1)它不是串行化的,并且可能会受到 CPU 管道内容的影响。 (2)由于电源管理、英特尔 Turbo Boost 或热节流,CPU 频率可能会波动。 (3)在多 CPU/多核系统中,该指令在哪个核心上执行? - myron-semack
@msemack 目前(2016年)带有rdtscp的处理器可以处理情况(1)。 - JAB

0

这里有一个可调用FORTRAN例程:

汇编指令 RDTSC 返回一个64位整数,该整数表示自某个时刻以来CPU时钟计数的数量。如果你的FORTRAN拥有64位整数,则参数KOUNT只需声明为INTEGER*8。否则,将其声明为两个32位INTEGER*4数组。

因此,在您的FORTRAN程序中,您可以这样写:

CALL TIMERR(KOUNT)

在开始时,保存KOUNT的值,然后在结束时重复。然后减去这两个64位值以确定经过的时间。 通常我只会减去两个较低的字,因为我计时的时间通常少于2^32个系统时钟

它也可以从C中调用,但我不会说C。

; C This assembler routine looks to FORTRAN like this:
;       SUBROUTINE TIMERR(KOUNT)
;       INTEGER*4 KOUNT(2);  or INTEGER*8 KOUNT
;       ...get a 64-but system time value into KOUNT......
;       RETURN
;       END
;
.Code
_TIMERR@4: RDTSC
         Push Eax
    Push Ecx
    Push Edx
    Mov Ecx, [Esp + 16]
    Mov [Ecx], Eax
    Mov [Ecx + 4], Edx
    Pop Edx
    Pop Ecx
    Pop Eax
    Ret 4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接