让iOS系统正常运行,在睡眠时不会暂停

Rus*_*inn 40 kernel clock uptime ios

我正在寻找一种方法来在iOS上获得绝对的,始终递增的系统正常运行时间.

它应该返回自设备上次重新启动以来的时间,并且不受系统日期更改的影响.

当设备处于休眠状态(所有我能找到任何方法暂停CACurrentMediaTime,[NSProcessInfo systemUptime],mach_absolute_time),或改变系统日期变更时(sysctl/KERN_BOOTTIME).

有任何想法吗?

Rus*_*inn 49

我想我已经解决了.

time()当设备处于睡眠状态时继续递增,但当然可以由操作系统或用户操纵.但是,内核启动时间(系统上次启动时的时间戳)也会在系统时钟更改时发生更改,因此即使这两个值都不固定,它们之间的偏移也是如此.

#include <sys/sysctl.h>

- (time_t)uptime
{
    struct timeval boottime;
    int mib[2] = {CTL_KERN, KERN_BOOTTIME};
    size_t size = sizeof(boottime);
    time_t now;
    time_t uptime = -1;

    (void)time(&now);

    if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) {
        uptime = now - boottime.tv_sec;
    }

    return uptime;
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,有竞争条件."从未见过竞争条件问题"是无效的,因为:1.您的应用实际上可能并不依赖于时钟2的单调性.您可能没有实际捕获现场问题的机制(即您的用户是点击它,你只是不知道)3.你没有大量的用户安装基础或你的安装基础不代表其他用户(例如你的用户永远不会改变他们的手动时钟).你的用户可能只是很幸运,从来没有打过这个. (3认同)

nyg*_*nyg 17

正如其中一条评论所述,POSIX clock_gettime()是在iOS 10和macOS 10.12中实现的.当与CLOCK_MONOTONIC参数一起使用时,它似乎确实返回正常运行时间值.但是,文档不保证这一点:

对于此时钟,clock_gettime()返回的值表示自过去未指定的点(例如,系统启动时间或Epoch)以来的时间量(以秒和纳秒为单位).系统启动时间后,这一点不会改变.

以及相应macOS手册页的摘录:

CLOCK_MONOTONIC时钟单调递增,跟踪任意点以来的时间,并在系统处于睡眠状态时继续递增.

CLOCK_MONOTONIC_RAW时钟单调递增,跟踪自CLOCK_MONOTONIC等任意点以来的时间.但是,此时钟不受频率或时间调整的影响.不应将其与其他系统时间源进行比较.

CLOCK_MONOTONIC_RAW_APPROXCLOCK_MONOTONIC_RAW类似,但在上下文切换时读取系统缓存的值.这可以更快地读取,但是会丢失准确性,因为它可能返回毫秒级的值.

CLOCK_UPTIME_RAW时钟以与CLOCK_MONOTONIC_RAW相同的方式单调递增,但在系统处于睡眠状态时不会递增.在应用适当的mach_timebase转换后,返回的值与mach_absolute_time()的结果相同.

请注意,CLOCK_MONOTONIC返回精度为微秒CLOCK_MONOTONIC_RAW级,精度为纳秒级.

SWIFT代码:

func uptime() -> timespec {

    var uptime = timespec()
    if 0 != clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) {
        fatalError("Could not execute clock_gettime, errno: \(errno)")
    }

    return uptime
}

print(uptime()) // timespec(tv_sec: 636705, tv_nsec: 750700397)
Run Code Online (Sandbox Code Playgroud)

对于那些仍然对在Swift中获取内核启动时间感兴趣的人:

func kernelBootTime() -> timeval {

    var mib = [ CTL_KERN, KERN_BOOTTIME ]
    var bootTime = timeval()
    var bootTimeSize = MemoryLayout<timeval>.size

    if 0 != sysctl(&mib, UInt32(mib.count), &bootTime, &bootTimeSize, nil, 0) {
        fatalError("Could not get boot time, errno: \(errno)")
    }

    return bootTime
}

print(kernelBootTime()) // timeval(tv_sec: 1499259206, tv_usec: 122778)
Run Code Online (Sandbox Code Playgroud)


Vit*_*ali 14

列出的所有示例中都存在竞争条件:如果时间有变化(NTP或用户修改),代码将竞争,时钟不再单调.

正确的实施是:

#include <sys/sysctl.h>

static int64_t us_since_boot() {
    struct timeval boottime;
    int mib[2] = {CTL_KERN, KERN_BOOTTIME};
    size_t size = sizeof(boottime);
    int rc = sysctl(mib, 2, &boottime, &size, NULL, 0);
    if (rc != 0) {
      return 0;
    }
    return (int64_t)boottime.tv_sec * 1000000 + (int64_t)boottime.tv_usec;
}

- (int64_t)us_uptime
{
    int64_t before_now;
    int64_t after_now;
    struct timeval now;

    after_now = us_since_boot();
    do {
        before_now = after_now;
        gettimeofday(&now, NULL);
        after_now = us_since_boot();
    } while (after_now != before_now);

    return (int64_t)now.tv_sec * 1000000 + (int64_t)now.tv_usec - before_now;
}
Run Code Online (Sandbox Code Playgroud)

  • 我测试了这个,看起来像一个魅力.我找不到任何瑕疵.谢谢!对于记录,最后有一个非尴尬的解决方案OS10及以上.clock_gettime API终于可用,并且使用CLOCK_MONOTONIC应该支持单调定时器的要求,该定时器以挂钟速率增加并具有相当高的准确度. (4认同)