Получение iOS system uptime, который не приостанавливается во время сна

Я ищу способ получить абсолютное, всегда увеличивающееся время работы системы на iOS.

Он должен возвращать время с момента последней перезагрузки устройства и не затрагиваться изменениями системной даты.

все методы, которые я могу найти, либо приостанавливаются, когда устройство спит (CACurrentMediaTime, [nsprocessinfo systemUptime], mach_absolute_time), либо изменяются при изменении системной даты (sysctl/KERN_BOOTTIME).

какие идеи?

6 ответов


Я думаю, я понял.

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;

}

во всех приведенных примерах есть условие гонки: если есть изменение времени (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;
}

как сказано в одном из комментариев, в POSIX это clock_gettime() был реализован в iOS 10 и macOS 10.12. При использовании CLOCK_MONOTONIC аргумент, похоже, возвращает значение времени безотказной работы. Однако это не гарантируется документацией:

для этих часов значение, возвращаемое clock_gettime (), представляет количество времени (в секундах и наносекундах), так как неопределенный момент в прошлом (например, время запуска системы или эпоха). Этот точка не изменяется после запуска системы.

и выписка из соответствующей man-страницы macOS:

CLOCK_MONOTONIC часы, которые монотонно увеличиваются, отслеживая время с произвольной точки, и будут продолжать увеличиваться, пока система спит.

CLOCK_MONOTONIC_RAW часы, которые монотонно увеличиваются, отслеживая время с произвольной точки, такой как CLOCK_MONOTONIC. Однако на эти часы не влияют настройки частоты или времени. Его не следует сравнивать с другими источниками системного времени.

CLOCK_MONOTONIC_RAW_APPROX как CLOCK_MONOTONIC_RAW, но считывает значение, кэшированное системой в контексте переключатель. Это можно прочитать быстрее,но с потерей точности, поскольку это может возвращать значения, которые являются миллисекундами.

CLOCK_UPTIME_RAW часы, которые увеличиваются монотонно, таким же образом, как CLOCK_MONOTONIC_RAW, но это не инкремент пока система спит. Возвращаемое значение идентично результату mach_absolute_time () после применения соответствующего преобразования mach_timebase.

отметим, что 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)

Если требуется более высокая точность, ниже приведена измененная версия принятого ответа.

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

Я бы прокомментировал ответ Лешека, но недостаточно репутации... Решение Лешека возвращает секунды.

ниже приведена модифицированная версия ответа Лешека, если кому-то нужна миллисекундная точность.

#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?

параметра systemuptime Возвращает время, прошедшее с момента перезагрузки компьютера.

  • (NSTimeInterval)systemUptime значение NSTimeInterval указывает, как долго после перезагрузки компьютера.

в наличии Доступно в iOS 4.0 и более поздних версиях. Объявлено В NSProcessInfo.h

Я проверил и доказал, что по крайней мере на iPhone 5 с iOS 7.1.1, systemUptime не останавливается, когда ваш телефон заблокирован. Так что никто не верит, что это можно проверить самостоятельно.