查找将时间调回 1 小时的 Windows 进程

查找将时间调回 1 小时的 Windows 进程

我有一台 Windows 机器,它会定期更改系统时间,原因不明。它似乎每小时发生一次。

此 Windows 计算机是虚拟机(Parallels Desktop 9、Win7 客户机、OSX 主机)。它具有 NTP 服务(网络时间) 运行可以迅速纠正错误,但在改变和纠正之间的短短几秒钟内,它会导致问题。

我检查过:

  • VM 时间同步已禁用
  • Windows“Internet 时间”已禁用
  • Windows 时间服务已禁用
  • 我只运行一个 NTP 客户端,每 15 分钟更新一次

有一个复杂因素。我们运行夜间天文学服务。为了避免自动 DST 更改引起的问题,我们禁用自动 DST 更改,并在当天晚些时候手动将机器时区设置为具有正确偏移的时区。例如,在西班牙,现在是 DST。标准时间为 UTC+1,DST 为 UTC+2。DST 更改后的早晨,我们将机器时区设置为希腊 UTC+2。主机配置正常(正确的时区,自动 DST 更改)。复杂之处在于时钟在 UTC+1(DST 之前的时间)更改回当前时间。

某些进程正在改变这一点。可能它有自己的时区设置。但我无法追踪它。更改记录在系统日志中。有两个关键条目:时间设置错误的位置以及更正时间的时间: 事件日志时间戳
(全面披露,事件日志按事件 ID = 1 进行过滤,但其他事件似乎毫无意义)。

有趣的是,这些事件发生的频率非常高(每小时,精确到秒)。更有趣的是,这些事件是正常运行时间。我可以在任务管理器中查看系统启动时间,当时间超过一个小时时,时钟就会发生变化。

查看事件详情也很有趣:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"> 
    - <System> 
        <Provider Name="Microsoft-Windows-Kernel-General" Guid="{GUID}" /> 
        <EventID>1</EventID> 
        <Version>0</Version> 
        <Level>4</Level> 
        <Task>0</Task> 
        <Opcode>0</Opcode> 
        <Keywords>0x8000000000000010</Keywords> 
        <TimeCreated SystemTime="2018-04-18T00:31:28.500000000Z" /> 
        <EventRecordID>500706</EventRecordID> 
        <Correlation /> 
        <Execution ProcessID="4" ThreadID="56" /> 
        <Channel>System</Channel> 
        <Computer>T07-VM-GUEST</Computer> 
        <Security UserID="SID" /> 
    </System> 
    - <EventData> 
        <Data Name="NewTime">2018-04-18T00:31:28.500000000Z</Data> 
        <Data Name="OldTime">2018-04-18T01:31:28.861800000Z</Data> 
    </EventData> 
</Event>

我们可以看到此事件从 01:31 变为 00:31(UTC 时间,事件日志中显示的当地时间为 03:31 至 02:31)。特别有趣的是这一行:

<Execution ProcessID="4" ThreadID="56" /> 

PID 4 是System进程:
任务管理器进程列表

使用 ProcessExplorer 我可以检查系统进程(PID 4),并且可以看到有关 ThreadId 56 的一些详细信息(假设它们没有被回收,而我正在查看正确的进程):
进程探索器

但对我来说,这都是胡言乱语。我在这里能看到的唯一有意义的东西是开始时间,以及它与时钟变化事件时间的关系(正如我上面所说,每小时与正常运行时间同步)。

这个答案讨论了在安全日志中查找时间更改,并且 NetTime 服务发起的所有更改都在那里。但有问题的更改可疑地缺失了: 安全日志

我的分析正确吗?如果正确,为什么系统进程每小时都会更改我的系统时钟?

答案1

好吧,正如所有最严重类型的问题一样,这个问题分为两部分。

  1. Windows 每小时将系统时间更新为硬件时钟。(请注意,它在启动时也会执行此操作,从而加快测试速度)
  2. Parallels 错误地虚拟化了硬件时钟。我不得不说,我使用的是旧版本的 Parallels (PD9),我希望他们现在已经修复了这个问题。

我还没有找到关于这一点的明确说法,但我看到很多和我一样的人证实了同样的事情:Windows 每小时读取实时时钟 (RTC,或 BIOS 中的硬件时钟) 并与其重新同步。请参阅参考资料:#1#2#3#4

显然,其中大部分与 RTC 故障、BIOS 电池电量不足或与 *nix OS(在 RTC 中存储 UTC,而不是本地时间)双启动有关。但事实是 Windows 每小时都会这样做。我还没有找到禁用此功能的方法。

此外,Parallels 本身并不保留硬件时钟,而是在 VM 配置文件中保留偏移量(与系统时间的偏移量)。问题是,这个偏移量没有正确考虑 DST。例如,我在西班牙马德里有一台主机 Mac,通常为 UTC+1,但目前在夏令时为 UTC+2。当我在客户机中设置时间时,Parallels 会计算我的客户机时间和主机时区之间的差异,而不考虑 DST。

我们来举个例子:
当前时间为 UTC 00:00。
马德里标准时间为 UTC+1,即 01:00。但目前是 DST,UTC+2,02:00。我将客户机设置为 02:00,Windows 尝试将其写入 RTC,Parallels 计算客户机时间与马德里标准时间 (01:00) 之间的差异,并保存<TimeShift>-3600</TimeShift>到配置文件中(文件仅在重新启动时更新,我猜想此变量在运行时会在内存中跟踪)。因此,每次 Windows 读取 RTC(Parallels 读取主机系统时间)时,它都会认为 RTC 设置为 HostTime-3600s(-1 小时)并更新时间。

我知道我的客户机设置很复杂(手动设置为开罗以找到没有夏令时的时区),我想我应该相信 Parallels,看看在客户机和主机都设置为正确时区(马德里,夏令时)的情况下它是否正常工作。不,它仍然搞砸了。

解决方案:
我找不到禁止 Windows 每小时读取 RTC 的方法,因此暂时我强制主机使用不使用 DST 的时区(例如开罗,UTC+2)。这有效。当我将客户机时间保存为 02:00,而开罗时间(UTC+2)为 02:00 时,Parallels 会保存<TimeShift>0</TimeShift>

丑陋的。

答案2

软件可以使用两种时间:系统时间和本地时间。无论 DST 设置如何,都不应更新系统时间。本地时间是经过调整的时间。如果不是这种情况,则您的操作系统或更可能是应用程序存在编程错误。可能是应用程序正在使用 GetLocalTime(),而它应该使用 GetSystemTime(),并且 UI 错误地指出它是系统时间。

答案3

几天来我一直在为这个系统事件而苦苦挣扎。多次尝试禁用或配置时间同步服务都失败了。我最终找到了一种解决方法,可以防止系统每 3600 秒调整一次系统时间。

Win32 API 提供了SetSystemTime函数可将系统时间设置为任意所需时间。PowerShell 提供了更方便的函数Set-Date包装 API 调用。通过调用,Set-Date -Adjust 0系统时间保持不变,但系统看起来还不错。但缺点是,需要每 3600 秒重复一次调用,以防止系统事件持续发生。因此,我运行 PowerShell 脚本来注册每 59 分钟触发一次的计划任务:

$taskName = "Mock System Time Synchronization"
$taskDescription = "This task prevents the system to synchronize the system time with the hardware clock every 3600 seconds."

$startTime = (Get-Date) + (New-TimeSpan -Minutes 1)
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-WindowStyle Hidden -command "& Set-Date -Adjust 0"'
$trigger =  New-ScheduledTaskTrigger -Once -At $startTime -RepetitionInterval (New-TimeSpan -Minutes 59)

Register-ScheduledTask -Action $action -Trigger $trigger -TaskName $taskName -Description $taskDescription -User "System"

相关内容