Je cherche un moyen d'obtenir une disponibilité du système absolue et toujours incrémentielle sur iOS.
Il doit renvoyer le temps écoulé depuis le dernier redémarrage de l'appareil et ne pas être affecté par les modifications de la date du système.
Toutes les méthodes que je peux trouver font une pause lorsque l'appareil est endormi (CACurrentMediaTime
, [NSProcessInfo systemUptime]
, mach_absolute_time
) ou sont modifiés lorsque la date système change (sysctl/KERN_BOOTTIME
).
Des idées?
Je pense que je l'ai résolu.
time()
continue à incrémenter pendant que l'appareil est en veille, mais peut bien sûr être manipulé par le système d'exploitation ou l'utilisateur. Cependant, le bootime du noyau (horodatage du dernier démarrage du système) change également lorsque l'horloge système est modifiée, donc même si ces deux valeurs ne sont pas fixes, le décalage entre elles l'est.
#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;
}
Comme indiqué dans l'un des commentaires, POSIX'sclock_gettime()
a été implémenté dans iOS 10 et macOS 10.12. Lorsqu'il est utilisé avec le CLOCK_MONOTONIC
argument, il semble renvoyer la valeur de disponibilité. Cependant, cela n'est pas garanti par la documentation:
Pour cette horloge, la valeur renvoyée par clock_gettime () représente la durée (en secondes et nanosecondes) depuis un point non spécifié dans le passé (par exemple, l'heure de démarrage du système ou l'Epoch). Ce point ne change pas après l'heure de démarrage du système.
Et un extrait de la page de manuel macOS correspondante:
CLOCK_MONOTONIC horloge qui s'incrémente de façon monotone, suivant le temps depuis un point arbitraire, et continuera à incrémenter pendant que le système est en veille.
CLOCK_MONOTONIC_RAW horloge qui s'incrémente de façon monotone, suivant le temps depuis un point arbitraire comme CLOCK_MONOTONIC. Cependant, cette horloge n'est pas affectée par les ajustements de fréquence ou de temps. Il ne doit pas être comparé à d'autres sources de temps système.
CLOCK_MONOTONIC_RAW_APPROX comme CLOCK_MONOTONIC_RAW, mais lit une valeur mise en cache par le système lors du changement de contexte. Cela peut être lu plus rapidement, mais avec une perte de précision car il peut renvoyer des valeurs vieilles de quelques millisecondes.
CLOCK_UPTIME_RAW horloge qui s'incrémente de façon monotone, de la même manière que CLOCK_MONOTONIC_RAW, mais que ne fait pas incrémenter pendant que le système est endormi. La valeur renvoyée est identique au résultat de mach_absolute_time () après l'application de la conversion mach_timebase appropriée.
Notez que CLOCK_MONOTONIC
retourne avec une précision en microsecondes tandis que CLOCK_MONOTONIC_RAW
avec une précision en nanosecondes.
Code rapide:
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)
Pour ceux qui souhaitent toujours obtenir le temps de démarrage du noyau dans 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)
Il y a une condition de concurrence dans tous les exemples répertoriés: s'il y a un changement d'heure (NTP ou modifié par l'utilisateur), le code va courir et l'horloge n'est plus monotone.
L'implémentation correcte est:
#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;
}
Si une précision plus élevée est nécessaire, voici une version modifiée de la réponse acceptée.
#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;
}
Je commenterais la réponse de Leszek, mais pas assez de représentants ... La solution de Leszek retourne quelques secondes.
Ce qui suit est une version modifiée de la réponse de Leszek si quelqu'un a besoin d'une précision en millisecondes.
#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;
}