用户态中断定时器访问,例如通过 KeQueryInterruptTime (或类似)
是否有“Nt
”或类似的(即非内核模式驱动程序)函数相当于KeQueryInterruptTime
或类似的东西?好像没有NtQueryInterruptTime
这样的东西,至少我没有发现。
我想要的是某种相当准确、可靠、单调的计时器(因此不是 QPC),它相当有效,并且不会像溢出的 32 位计数器那样带来意外,并且没有不必要的“智能” ,没有时区,也没有复杂的结构。
因此,理想情况下,我想要类似 timeGetTime
的 64 位值。它甚至不必是同一个计时器。
从 Vista 开始就存在 GetTickCount64
,这本身是可以接受的,但我不想仅仅因为这样一个愚蠢的原因就破坏 XP 支持。
读取 0x7FFE0008
处的四字,如此处所示...好吧,有效 ...并且它证明实际的内部计数器在 XP 下确实是 64 位(它也是尽可能快的),但是呃……我们先不讨论读取某些未知的硬编码内存位置是一种多么令人讨厌的黑客行为。
在调用人为愚弄的(将 64 位计数器缩小到 32 位)高级 API 函数和读取原始内存地址之间肯定存在某些东西?
Is there a "Nt
" or similar (i.e. non-kernelmode-driver) function equivalent for KeQueryInterruptTime
or anything similar? There seems to be no such thing as NtQueryInterruptTime
, at least I've not found it.
What I want is some kind of reasonably accurate and reliable, monotonic timer (thus not QPC) which is reasonably efficient and doesn't have surprises as an overflowing 32-bit counter, and no unnecessary "smartness", no time zones, or complicated structures.
So ideally, I want something like timeGetTime
with a 64 bit value. It doesn't even have to be the same timer.
There exists GetTickCount64
starting with Vista, which would be acceptable as such, but I'd not like to break XP support only for such a stupid reason.
Reading the quadword at 0x7FFE0008
as indicated here ... well, works ... and it proves that indeed the actual internal counter is 64 bits under XP (it's also as fast as it could possibly get), but meh... let's not talk about what a kind of nasty hack it is to read some unknown, hardcoded memory location.
There must certainly be something in between calling an artificially stupefied (scaling a 64 bit counter down to 32 bits) high-level API function and reading a raw memory address?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
下面是 GetTickCount() 的线程安全包装器示例,它将刻度计数值扩展到 64 位,并且等效于 GetTickCount64()。
为了避免意外的计数器翻转,请确保每 49.7 天调用此函数几次。您甚至可以有一个专用线程,其唯一目的是调用此函数,然后在无限循环中休眠大约 20 天。
编辑:这是一个测试 MyGetTickCount64() 的完整应用程序。
作为测试,我在 Windows XP 下在一台具有 2 个 CPU 的闲置计算机上运行此测试应用程序 5 个多小时(闲置,以避免潜在的长时间饥饿时间,从而避免丢失每 5 秒发生一次的计数器溢出),它是仍然表现良好。
以下是控制台的最新输出:
如您所见,
MyGetTickCount64()
已观察到 3824 个 32 位溢出,并且无法使用其第二个更新
110858 次。因此,确实发生了溢出,最后一个数字意味着该变量实际上是由两个线程同时更新的。Count
的值>InterlockedCompareExchange64()您还可以看到,两个线程从
TickUserThread()
中的MyGetTickCount64()
接收的 64 位刻度计数在前 32 位中没有丢失任何内容,并且非常接近SimulatedTickCount
中的实际 64 位刻度计数,其 32 个低位由SimulatedGetTickCount()
返回。由于线程调度和不频繁的统计打印,0x00000E1BC8800000 在视觉上落后于 0x00000E1BFA800000,它落后了 100*TICK_COUNT_10MS_INCREMENT,即 1 秒。当然,在内部,差异要小得多。现在,关于 InterlockedCompareExchange64() 的可用性...有点奇怪,它是 自 Windows Vista 和 Windows Server 2003 起正式提供。 Server 2003 实际上是基于与 Windows XP 相同的代码库构建的。
但这里最重要的是,该函数建立在 Pentium
CMPXCHG8B
指令之上,该指令自 1998 年或更早以来就可用 (1), (2)。我可以在 Windows XP (SP3) 二进制文件中看到这条指令。它位于 ntkrnlpa.exe/ntoskrnl.exe(内核)和 ntdll.dll(导出内核的 NtXxxx() 函数的 DLL)中。建立在)。查找 0xF0、0x0F、0xC7 字节序列,并反汇编该位置周围的代码,看看这些字节是否巧合存在。您可以通过
CPUID
指令(CPUID函数0x00000001和函数0x80000001的EDX位8)检查该指令的可用性,如果该指令不存在,则拒绝运行而不是崩溃,但现在您'不太可能找到不支持该指令的机器。如果您这样做,那么它就不是一台适用于 Windows XP 的好机器,而且可能也不适用于您的应用程序。Here's an example of a thread-safe wrapper for GetTickCount() extending the tick count value to 64 bits and in that being equivalent to GetTickCount64().
To avoid undesired counter roll overs, make sure to call this function a few times every 49.7 days. You can even have a dedicated thread whose only purpose would be to call this function and then sleep some 20 days in an infinite loop.
EDIT: And here's a complete application that tests MyGetTickCount64().
As a test I've been running this test app under Windows XP for 5+ hours on an otherwise idle machine that has 2 CPUs (idle, to avoid potential long starvation times and therefore avoid missing counter overflows that occur every 5 seconds) and it's still doing well.
Here's the latest output from the console:
As you can see,
MyGetTickCount64()
has observed 3824 32-bit overflows and failed to update the value ofCount
with its secondInterlockedCompareExchange64()
110858 times. So, overflows indeed occur and the last number means that the variable is, in fact, being concurrently updated by the two threads.You can also see that the 64-bit tick counts that the two threads receive from
MyGetTickCount64()
inTickUserThread()
don't have anything missing in the top 32 bits and are pretty close to the actual 64-bit tick count inSimulatedTickCount
, whose 32 low bits are returned bySimulatedGetTickCount()
. 0x00000E1BC8800000 is visually behind 0x00000E1BFA800000 due to thread scheduling and infrequent stat prints, it's behind by exactly 100*TICK_COUNT_10MS_INCREMENT, or 1 second. Internally, of course, the difference is much smaller.Now, on availability of
InterlockedCompareExchange64()
... It's a bit odd that it's officially available since Windows Vista and Windows Server 2003. Server 2003 is in fact build from the same code base as Windows XP.But the most important thing here is that this function is built on top of the Pentium
CMPXCHG8B
instruction that's been available since 1998 or earlier (1), (2). And I can see this instruction in my Windows XP's (SP3) binaries. It's in ntkrnlpa.exe/ntoskrnl.exe (the kernel) and ntdll.dll (the DLL that exports kernel's NtXxxx() functions that everything's built upon). Look for a byte sequence of 0xF0, 0x0F, 0xC7 and disassemble the code around that place to see that these bytes aren't there coincidentally.You can check availability of this instruction through the
CPUID
instruction (EDX bit 8 of CPUID function 0x00000001 and function 0x80000001) and refuse to run instead of crashing if the instruction isn't there, but these days you're unlikely to find a machine that doesn't support this instruction. If you do, it won't be a good machine for Windows XP and probably your application as well anyways.感谢 Google 图书免费提供相关文献,我想出了一个简单快速的
GetTickCount64
实现,它在 Vista 之前的系统上也运行得很好(而且它仍然比从硬编码内存地址读取值)。事实上,它就像调用中断 0x2A 一样简单,它映射到
KiGetTickCount
。在 GCC 内联汇编中,这给出:由于
KiGetTickCount
的工作方式,该函数可能最好称为GetTickCount46
,因为它执行右移 18,返回 46 位,而不是 64。尽管原始 Vista 版本也是如此。请注意,
KiGetTickCount
会破坏edx
,如果您打算实现自己的 32 位版本的更快实现(必须将edx
添加到在这种情况下就是破坏列表!)。Thanks to Google Books which kindly offered the relevant literature for free, I came up with an easy and fast implementation of
GetTickCount64
which works perfectly well on pre-Vista systems too (and it still is somewhat less nasty than reading a value from a hardcoded memory address).It is in fact as easy as calling interrupt 0x2A, which maps to
KiGetTickCount
. In GCC inline assembly, this gives:Due to the way
KiGetTickCount
works, the function should probably better be calledGetTickCount46
, as it performs a right shift by 18, returning 46 bits, not 64. Though the same is true for the original Vista version, too.Note that
KiGetTickCount
clobbersedx
, this is relevant if you plan to implement your own faster implementation of the 32-bit version (must addedx
to the clobber list in that case!).这是另一种方法,Alex 包装器的变体,但仅使用 32 位互锁。它实际上只返回一个 60 位数字,但这在大约三千六百万年里仍然有效。 :-)
确实需要更频繁地调用它,至少每三天一次。这通常不应该是一个主要缺点。
Here's another approach, a variant of Alex's wrapper but using only 32-bit interlocks. It only actually returns a 60-bit number, but that's still good for about thirty-six million years. :-)
It does need to be called more often, at least once every three days. That shouldn't normally be a major drawback.