Получение работоспособности системы iOS, которая не приостанавливается при спящем режиме

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

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

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

Любые идеи?

Ответ 1

Думаю, я это проработал.

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;

}

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

И выдержка из соответствующей справочной страницы 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 с точностью до наносекунд.

Быстрый код:

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)

Ответ 5

Я бы прокомментировал 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;
}

Ответ 6

Почему бы не использовать systemUptime?

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

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

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

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