Uzyskanie czasu pracy systemu iOS, który nie zatrzymuje się podczas snu

Szukam sposobu, aby uzyskać absolutny, zawsze zwiększający się czas pracy systemu na iOS.

Powinien zwracać czas od ostatniego ponownego uruchomienia urządzenia i nie podlegać zmianom daty systemowej.

Wszystkie metody, które mogę znaleźć albo Pauza, gdy urządzenie jest w stanie uśpienia (CACurrentMediaTime, [NSProcessInfo systemUptime], mach_absolute_time) lub są zmieniane po zmianie daty systemowej (sysctl/KERN_BOOTTIME).

Jakieś pomysły?
Author: ricardopereira, 2012-09-19

6 answers

Chyba to rozgryzłem.

time() kontynuuje inkrementację, gdy urządzenie jest w stanie uśpienia, ale oczywiście może być manipulowane przez system operacyjny lub użytkownika. Jednak czas rozruchu Jądra (znacznik czasu, kiedy system ostatnio uruchomiony) również zmienia się, gdy zegar systemowy jest zmieniony, dlatego nawet jeśli obie te wartości nie są stałe, przesunięcie między nimi jest.

#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;
}
 53
Author: Russell Quinn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-17 16:06:58

Jak napisano w jednym z komentarzy, POSIX ' s clock_gettime() został zaimplementowany w systemach iOS 10 i macOS 10.12. Gdy jest używany z argumentem CLOCK_MONOTONIC, wydaje się zwracać wartość uptime. Jednak nie jest to gwarantowane przez dokumentację:

Dla tego zegara wartość zwracana przez clock_gettime () reprezentuje ilość czasu (w sekundach i nanosekundach) od nieokreślonego punktu w przeszłości (na przykład czas uruchomienia systemu lub Epoka). To punkt nie zmienia się po czas rozruchu systemu.

I Wyciąg z odpowiedniej strony podręcznika macOS:

CLOCK_MONOTONIC Zegar, który zwiększa się monotonicznie, śledząc czas od dowolnego punktu i będzie nadal zwiększać, gdy system jest w stanie uśpienia.

CLOCK_MONOTONIC_RAW Zegar, który zwiększa monotonicznie, śledząc czas od dowolnego punktu, takiego jak CLOCK_MONOTONIC. Jednak zegar ten nie ma wpływu na częstotliwość ani korekty czasu. Nie należy go porównywać z innymi źródłami czasu systemowego.

CLOCK_MONOTONIC_RAW_APPROX podobnie jak CLOCK_MONOTONIC_RAW, ale odczytuje wartość buforowaną przez system w kontekście Zamiana. Można to odczytać szybciej, ale z utratą dokładności, ponieważ może zwracać wartości stare Na milisekundy.

CLOCK_UPTIME_RAW Zegar, który zwiększa się monotonicznie, w taki sam sposób jak CLOCK_MONOTONIC_RAW, ale że nie zwiększa się podczas snu systemu. Na zwracana wartość jest identyczna z wynikiem mach_absolute_time () po zastosowaniu odpowiedniej konwersji mach_timebase.

Zauważ, że CLOCK_MONOTONIC zwraca z mikrosekundową precyzją, podczas gdy CLOCK_MONOTONIC_RAW z nanosekundową precyzją.

Kod 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)

Dla tych, którzy nadal są zainteresowani uzyskaniem czasu rozruchu jądra w języku 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)
 23
Author: nyg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-08 15:16:36

Istnieje warunek wyścigu we wszystkich wymienionych przykładach: jeśli nastąpi zmiana czasu (NTP lub zmodyfikowany przez użytkownika), kod będzie ścigał się, a zegar nie jest już monotoniczny.

Prawidłowa implementacja to:

#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;
}
 15
Author: Vitali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-10 16:01:46

Jeśli wymagana jest większa precyzja, poniżej znajduje się zmodyfikowana wersja zaakceptowanej odpowiedzi.

#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
Author: Leszek Szary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-18 17:57:36

Ja bym skomentował odpowiedź Leszka, ale za mało rep... Rozwiązanie Leszka zwraca sekundy.

Poniżej znajduje się zmodyfikowana wersja odpowiedzi Leszka, jeśli ktoś potrzebuje milisekundowej precyzji.
#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;
}
 3
Author: Boris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-30 15:10:34

Dlaczego nie używać systemUptime?

SystemUptime Zwraca czas, jaki upłynął od ponownego uruchomienia komputera.

  • (NSTimeInterval)systemUptime Return Value Wartość NSTimeInterval wskazująca, jak długo komputer został ponownie uruchomiony.

Dostępność Dostępne w systemie iOS 4.0 i nowszych. Zadeklarowane W NSProcessInfo.h

Przetestowałem i udowodniłem, że przynajmniej na iPhone 5 z iOS 7.1.1, systemUptime nie zatrzymuje się, gdy telefon jest zamknięte. Więc każdy nie wierzy, że może to przetestować samodzielnie.

 -6
Author: Owen Zhao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-15 02:03:52