获得iOS系统正常运行时间,睡眠时不会暂停



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

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

我能找到的所有方法要么在设备休眠时暂停(CACurrentMediaTime[NSProcessInfo systemUptime]mach_absolute_time),要么在系统日期更改时更改(sysctl/KERN_BOOTTIME)。

有什么想法吗?

我想我已经解决了。

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;
}

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

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

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

CLOCK_MONOTONIC时钟,它单调递增,跟踪自任意点以来的时间,并将在系统休眠时继续递增。

CLOCK_MONOTONIC_RAW单调递增的时钟,跟踪从任意点开始的时间,如CLOCK_MON。但是,此时钟不受频率或时间调整的影响。不应将其与其他系统时间源进行比较。

CLOCK_MONOTONIC_RAW_APPROX类似于CLOCK_MON TONIC_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)

对于那些仍然有兴趣在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)

列出的所有示例中都有一个竞争条件:如果发生时间更改(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;
}

如果需要更高的精度,那么下面是已接受答案的修改版本。

#include <sys/sysctl.h>
- (NSTimeInterval)uptime
{
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
double uptime = -1;
if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0)
{
uptime = now.tv_sec - boottime.tv_sec;
uptime += (double)(now.tv_usec - boottime.tv_usec) / 1000000.0;
}
return uptime;
}

我想对Leszek的答案发表评论,但没有足够的代表…Leszek解决方案返回秒。

如果有人需要毫秒精度,下面是Leszek答案的修改版本。

#include <sys/sysctl.h>
+ (long long int)uptime 
{
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
struct timeval now;
struct timezone tz;
gettimeofday(&now, &tz);
long long int uptime = -1;
if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0)
{
uptime = ((long long int)(now.tv_sec - boottime.tv_sec)) * 1000;
uptime += (now.tv_usec - boottime.tv_usec) / 1000;
}
return uptime;
}

为什么不使用systemUptime?

系统正常运行时间返回自计算机重新启动以来的时间。

  • (NSTimeInterval)系统正常运行时间返回值NSTimeInterval,指示计算机重新启动后的时间

可用性在iOS 4.0及更高版本中可用。声明于NSProcessInfo.h

我已经测试并证明,至少在iOS 7.1.1的iPhone 5上,当你的手机被锁定时,系统正常运行时间不会停止。所以任何人都不相信这可以自己测试

最新更新