Getting OS Information - The KUSER_SHARED_DATA Structure

Windows Research Kernel @ HPI

Ever asked how Windows API retrieves the current time, the version of the OS, or whether an evaluation period has expired? This structure will answers some of these questions.

Recently, a colleague of mine, Martin von Löwis, found an article in Google about Windows timing and this article used an artifact of the KUSER_SHARED_DATA structure. Well, as we have the sources, let's have a closer look to that structure (see public\sdk\inc\ntexapi.h). (As the WRK license agreement limits the number of lines of code that can be shown in one piece to 50 lines, I left out the comments.)

02229 typedef struct _KUSER_SHARED_DATA {
02237     ULONG TickCountLowDeprecated;
02238     ULONG TickCountMultiplier;
02244     volatile KSYSTEM_TIME InterruptTime;
02250     volatile KSYSTEM_TIME SystemTime;
02256     volatile KSYSTEM_TIME TimeZoneBias;
02264     USHORT ImageNumberLow;
02265     USHORT ImageNumberHigh;
02271     WCHAR NtSystemRoot[260];
02277     ULONG MaxStackTraceDepth;
02283     ULONG CryptoExponent;
02289     ULONG TimeZoneId;
02290     ULONG LargePageMinimum;
02291     ULONG Reserved2[7];
02297     NT_PRODUCT_TYPE NtProductType;
02298     BOOLEAN ProductTypeIsValid;
02309     ULONG NtMajorVersion;
02310     ULONG NtMinorVersion;
02316     BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX];
02322     ULONG Reserved1;
02323     ULONG Reserved3;
02329     volatile ULONG TimeSlip;
02335     ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture;
02344     LARGE_INTEGER SystemExpirationDate;
02350     ULONG SuiteMask;
02356     BOOLEAN KdDebuggerEnabled;
02362     UCHAR NXSupportPolicy;
02368     volatile ULONG ActiveConsoleId;
02376     volatile ULONG DismountCount;
02384     ULONG ComPlusPackage;
02392     ULONG LastSystemRITEventTickCount;
02399     ULONG NumberOfPhysicalPages;
02405     BOOLEAN SafeBootMode;
02413     ULONG TraceLogging;
02422     ULONGLONG TestRetInstruction;
02423     ULONG SystemCall;
02424     ULONG SystemCallReturn;
02425     ULONGLONG SystemCallPad[3];
02431     union {
02432         volatile KSYSTEM_TIME TickCount;
02433         volatile ULONG64 TickCountQuad;
02434     };
02440     ULONG Cookie;
02446     ULONG Wow64SharedInformation[MAX_WOW64_SHARED_ENTRIES];
02447
02448 } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;

While I cannot say the meaning and usage of all fields, I will concentrate on the most interesting ones.

Windows Times

Windows maintains three types of time: The interrupt time (line 2244), the system time (line 2250), and the system's tick count (lines 2431 thru 2434). While the first ones represent a real time value in units of 100 ns, the latter is just a counter starting at zero when the machine boots. In tests using the KUSER_SHARED_DATA we figured out that the tick count is incremented each 15.625 ms.

The interrupt time is the only Windows clock that guarantees to be monotonous that is, its value only increases over timer. Its value represents the time in units of 100 ns since the system was booted. The interrupt time is the base clock for all timers in Windows (see my recent article A Bug in Windows Timer Management). It is updated every clock interrupt.
In contrast, the system time represents the time in units of 100 ns since January 1, 1601. It always represents your local time, not UTC (Universal Coordinated Time). The system time does not guarantee to be monotonous, which means it might "jump" backwards. (Just think of the Daylight Saving time adjustment or NTP.)

Now, let's have a closer look at the data structure that is used to hold the interrupt time and the system time (see public\sdk\inc\ntkeapi.h):

00048 typedef struct _KSYSTEM_TIME {
00049     ULONG LowPart;
00050     LONG High2Time;
00051     LONG High3Time;
00052 } KSYSTEM_TIME, *PKSYSTEM_TIME;

As you can see, the time is stored as a 64-bit value: High2Time:LowPart. But why is there another HighPart (High3Time) field in the structure (line 51)? To be short, it is for synchronization purposes. The three time values are updated directly by the clock interrupt service routine (ISR). An ISR must not acquire any lock because it must complete as fast as possible and thus must not block. However, to ensure applications (Win32) to read a consistent time value, the order in which the members of KSYSTEM_TIME are updated has a very strict manner. The ISR first updates High3Time, then LowPart, and finally High2Time. A consuming application must read the structure the same strict but inverse order, that is, it first reads High2Time, then LowPart, and finally High3Time. If High2Time and High3Time are equal, the High2Time:LowPart is a consistent value. Otherwise the application was interrupted by the clock interrupt and needs to read the structure again.

Access the Structure in Your Application

The most important thing I need to tell you about the KUSER_SHARED_DATA structure is that it is exported to user address space. See files base\ntos\inc\i386.h or base\ntos\inc\amd64.h, respectively, depending on your target architecture. Refer to the definition of KI_USER_SHARED_DATA. This constant holds the virtual address, where the single instance of KUSER_SHARE_DATA is mapped to. For example, on an x86 architecture, it is mapped to 0xFFDF0000. But be careful with using this structure! It is subject to change and it might have a completely different structure on Windows XP or Windows Vista. Although I think this is pretty improbable because of its strong usage inside the kernel, you must be aware of that fact.

If you already have experiences with KUSER_SHARED_DATA, don't hesitate to comment and share your knowledge among the community.

Comments

5 Responses to "Getting OS Information - The KUSER_SHARED_DATA Structure"

  1. Alex Ionescu on September 24th, 2007 19:53

    This structure is documented in the WDK, along with the full structure and comments up to Vista… there's no use in using the WRK for this or hiding any details.

  2. Detecting scheduler interference? - Page 2 | keyongtech on March 5th, 2009 14:07

    [...] For details on how to access USER_SHARED_DATA check this Post , for example. >>>>>>PASTE>>>>>>>>>> // // Define [...]

  3. systemcall on April 1st, 2010 13:30

    [...] it already: http://afatkulin.blogspot.com/ He has some good Oracle internals information in there,Windows Research Kernel @ HPI - news, howtos and …Windows 7 and Server 2008 R2 Kernel Changes (TechEd Europe 2009) Resolved: A Performance … [...]

  4. Dan on June 25th, 2010 03:33

    "The interrupt time is the only Windows clock that guarantees to be monotonous that is, its value only increases over time"

    Shouldn't the tick count also be a monotonically increasing value?

  5. Paul Whittemore on March 9th, 2011 01:25

    @Dan: If the tick count is based on the same data as GetTickCount (or the other way around, as I believe it to be), then there can be a separate tick count per processor, and they are not guaranteed to be synchronized (and in fact usually are not). If two calls are made to obtain tick counts from different processors, the second one can have a (slightly) smaller value than the first call, i.e. tick counts can go backwards.

    This is a serious problem for code that needs a simple incrementing tick count, such as the code I write. The only workable solution I have found so far is to use a separate timer thread that sleeps for 1 ms, then wakes and updates a global variable with the current tick count. The key here is to then limit the processor affinity of that thread. However, some environments may not support the affinity calls. (I have been told that the Windows affinity calls in apps running under Amazon S3 may not be ignored, but I have not confirmed this yet.) So I have been looking for a Windows-supported simple tick count for some time. I was unaware of this KUSER_SHARED_DATA structure and I'm hoping the InterruptTime can be used instead of GetTickCount without requiring processor affinity limitations in order for it to be a simple increment.