iOS 启动时间可以漂移吗?

Can iOS boot time drift?

我正在使用此代码来确定我的 iOS 设备上次重启的时间:

int mib[MIB_SIZE];
size_t size;
struct timeval boottime;

mib[0] = CTL_KERN;
mib[1] = KERN_BOOTTIME;
size = sizeof(boottime);
if (sysctl(mib, MIB_SIZE, &boottime, &size, NULL, 0) != -1) {
    return boottime.tv_sec;
}

return 0;

我发现这次有些异常。特别是,我保存了 long 和 days 和 weeks 之后再次检查保存的 long 上面代码返回的值。

我不确定,但我认为我看到了一些偏差。这对我来说没有任何意义。我不会转换为 NSDate 以防止漂移。我认为启动时间是内核在启动时记录的,不会再次计算,它只是被存储了。但是 iOS 是否可以将启动时间作为 NSDate 来节省,这会带来任何固有的漂移问题吗?

虽然 iOS 内核是闭源的,但可以合理地假设其中大部分与 OSX 内核相同,即 open-source

osfmk/kern/clock.c 中有函数:

/*
 *  clock_get_boottime_nanotime:
 *
 *  Return the boottime, used by sysctl.
 */
void
clock_get_boottime_nanotime(
    clock_sec_t         *secs,
    clock_nsec_t        *nanosecs)
{
    spl_t   s;

    s = splclock();
    clock_lock();

    *secs = (clock_sec_t)clock_boottime;
    *nanosecs = 0;

    clock_unlock();
    splx(s);
}

clock_boottime 声明为:

static uint64_t     clock_boottime;             /* Seconds boottime epoch */

最后这个函数的注释表明它确实可以改变:

/*
 *  clock_set_calendar_microtime:
 *
 *  Sets the current calendar value by
 *  recalculating the epoch and offset
 *  from the system clock.
 *
 *  Also adjusts the boottime to keep the
 *  value consistent, writes the new
 *  calendar value to the platform clock,
 *  and sends calendar change notifications.
 */
void
clock_set_calendar_microtime(
    clock_sec_t         secs,
    clock_usec_t        microsecs)
{
    ...

更新以回答来自 OP

的查询

我不确定 clock_set_calendar_microtime() 的调用频率,因为我不熟悉内核的内部工作原理;但是它调整了 clock_boottime 值并且 clock_bootime 值在 clock_initialize_calendar() 中被初始化,所以我会说它可以被调用不止一次。我一直找不到任何调用它的方法:

$ find . -type f -exec grep -l clock_set_calendar_microtime {} \;

回复我上面的评论...

"to my understanding, when the user goes into settings and changes the time manually, the boot time is changed by the delta to the new time to keep the interval between boot time and system time, equal. but it does not "drift" as it is a timestamp, only the system clock itself drifts."

我在我的 iOS 应用程序上使用 运行 NTP,并与 Google 的时间服务器通话。

我向 NTP 提供自启动以来的正常运行时间(如果某些恶意用户开始扰乱系统时间,它不会暂停并且会正确调整......这首先是整个要点),然后添加自启动以来的正常运行时间和纪元时间与我的正常运行时间之间的偏移量。

inline static struct timeval uptime(void) {

   struct timeval before_now, now, after_now;

   after_now = since_boot();

   do {
      before_now = after_now;
      gettimeofday(&now, NULL);
      after_now = since_boot();

   } while (after_now.tv_sec != before_now.tv_sec && after_now.tv_usec != before_now.tv_usec);


   struct timeval systemUptime;
   systemUptime.tv_sec = now.tv_sec - before_now.tv_sec;
   systemUptime.tv_usec = now.tv_usec - before_now.tv_usec;

   return systemUptime;
 }

我每 15 分钟与时间服务器同步一次,并且每次都计算偏移漂移(又名系统时钟漂移)。

static void calculateOffsetDrift(void) {

   static dispatch_queue_t offsetDriftQueue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
   static double lastOffset;

   dispatch_barrier_sync(offsetDriftQueue, ^{

      double newOffset = networkOffset();

      if (lastOffset != 0.0f) printf("offset difference = %f \n", lastOffset - newOffset);

      lastOffset = newOffset;
   });
 } 

在我的 iPhone Xs Max 上,系统时钟通常在 15 分钟后运行大约 30 毫秒。

这是我刚刚 运行 在纽约使用 LTE 进行的测试的一些数据..

+47.381592 ms
+43.325684 ms
-67.654541 ms
+24.860107 ms
+5.940674  ms
+25.395264 ms
-34.969971 ms