2012-09-19 21 views
31

Estoy buscando una forma de obtener un tiempo de actividad del sistema absoluto y en constante aumento en iOS.Cómo obtener el tiempo de actividad del sistema iOS, que no se detiene cuando está dormido

Debe devolver el tiempo transcurrido desde que se reinició por última vez el dispositivo, y no verse afectado por cambios en la fecha del sistema.

Todos los métodos que puedo encontrar se pausan cuando el dispositivo está dormido (CACurrentMediaTime, [NSProcessInfo systemUptime], mach_absolute_time) o cambian cuando la fecha del sistema cambia (sysctl/KERN_BOOTTIME).

¿Alguna idea?

Respuesta

45

Creo que lo resolví.

time() continúa incrementándose mientras el dispositivo está dormido, pero, por supuesto, puede ser manipulado por el sistema operativo o el usuario. Sin embargo, el Kernel boottime (una marca de tiempo de cuando el sistema arrancó por última vez) también cambia cuando se cambia el reloj del sistema, por lo tanto, aunque ambos valores no son fijos, la diferencia entre ellos es.

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

} 
+0

compañero Bien hecho. Iba a tener que buscar una comparación entre fecha de servidor/fecha local antes de esto ... Muy apreciada. – Magoo

+1

Creo que esto no funciona para el simulador. – mkeremkeskin

+0

Eso es correcto. –

-6

¿Por qué no utilizar systemUptime?

systemUptime Devuelve cuánto tiempo ha pasado desde que se reinició la computadora.

  • (NSTimeInterval) systemUptime Valor de retorno Un NSTimeInterval que indica cuánto tiempo ya que el ordenador se ha reiniciado.

Disponibilidad Disponible en iOS 4.0 y posterior. declarados en NSProcessInfo.h

he probado y demostrado que al menos en el iPhone 5 con iOS 7.1.1, systemUptime no se detiene cuando el teléfono está bloqueado. Entonces, cualquiera no cree que esto pueda probarlo usted mismo.

+0

En mi prueba con iOS 7.1.x. No se detendrá cuando el dispositivo esté bloqueado y la pantalla apagada. –

+0

se ve afectado por el sueño/despierto, realmente. Tal vez lo probó con un cable conectado que evita el sueño. O alguna otra razón bloquee el dispositivo mientras duerme ... – Speakus

+0

Sí, volví a probarlo y se detiene, lo siento. –

3

Si se necesita una mayor precisión, a continuación se muestra una versión modificada de la respuesta aceptada.

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

Hay una condición de carrera en todos los ejemplos que figuran: si hay un cambio de hora (NTP o modificados por el usuario), el código correrá y el reloj ya no es monótona.

La aplicación correcta es:

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

He probado esto y parece funcionar como un encanto. No puedo encontrar ningún defecto. ¡Gracias! Para el registro, finalmente hay una solución no incómoda para OS10 en adelante. La API clock_gettime finalmente está disponible, y el uso de CLOCK_MONOTONIC debe ser compatible con los requisitos de un temporizador monotónico que aumenta a la velocidad del reloj de pared con una precisión decente. –

+0

Esto es genial excepto por el nombre de función 'us_since_boot'. Es solo un tiempo de arranque, no el tiempo transcurrido desde el arranque. –

+0

Tenga en cuenta que no es posible desbordamiento cuando se hace 'boottime.tv_sec * 1000000' o' now.tv_sec * 1000000' (en plataformas de 32 bits que es fácil de coger). Para evitar este problema, debes realizar un lanzamiento directo. – d12frosted

2

me gustaría comentar sobre la respuesta de Leszek, pero no lo suficiente rep ... La solución de Leszek retornos segundos.

La siguiente es una versión modificada de la respuesta de Leszek si alguien necesita una precisión de milisegundos.

#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

Como se ha dicho en uno de los comentarios, POSIX'sclock_gettime() se implementó en IOS 10 y MacOS 10.12. Cuando se usa con el argumento CLOCK_MONOTONIC, parece devolver el valor de tiempo de actividad.Sin embargo, esto no está garantizado por la documentación:

Para este reloj, el valor devuelto por clock_gettime() representa la cantidad de tiempo (en segundos y nanosegundos) desde un punto no especificado en el pasado (por ejemplo , el tiempo de inicio del sistema o la Época). Este punto no cambia después del tiempo de inicio del sistema.

y un extracto de la página man correspondiente macOS:

CLOCK_MONOTONIC reloj que se incrementa de forma monótona, el seguimiento del tiempo desde un punto arbitrario, y continuará aumentando mientras el sistema está dormido.

CLOCK_MONOTONIC_RAW reloj que se incrementa de forma monótona, siguiendo el tiempo desde un punto arbitrario como CLOCK_MONOTONIC. Sin embargo, este reloj no se ve afectado por los ajustes de frecuencia o tiempo. No se debe comparar con otras fuentes de tiempo del sistema.

CLOCK_MONOTONIC_RAW_APPROX como CLOCK_MONOTONIC_RAW, pero lee un valor almacenado en caché por el sistema al cambio de contexto . Esto se puede leer más rápido, pero con una pérdida de precisión, ya que puede devolver valores que son milisegundos anteriores.

CLOCK_UPTIME_RAW reloj que se incrementa de forma monótona, de la misma manera como CLOCK_MONOTONIC_RAW, pero que no incremento mientras el sistema está dormido. El valor devuelto es idéntico al resultado de mach_absolute_time() después de aplicar la conversión mach_timebase apropiada.

en cuenta que CLOCK_MONOTONIC regresa con una precisión de microsegundos, mientras CLOCK_MONOTONIC_RAW con una precisión de nanosegundos.

Código 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) 

Para aquellos que todavía están interesados ​​por obtener el tiempo de arranque del kernel en 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) 
Cuestiones relacionadas