6.4 KiB
Le namespace time
Introduction
L'espace de noms time, introduit dans Linux 5.6, permet de virtualiser les
horloges CLOCK_MONOTONIC et CLOCK_BOOTTIME pour des processus. Cela est
particulièrement utile pour la migration de conteneurs ou pour tester des
applications sensibles au temps.
::::: {.warning}
Contrairement à ce que son nom pourrait suggérer, le namespace time ne
permet pas de virtualiser l'heure système (l'horloge temps réel). Il ne
virtualise que les compteurs monotones.
:::::
Utilisation pratique
Voyons comment utiliser le namespace time pour modifier le temps
d'exécution perçu par un processus.
Lire les horloges monotones\
Commençons par observer les valeurs actuelles de nos horloges :
Le premier nombre représente le temps depuis le démarrage du système (équivalent
à CLOCK_BOOTTIME), et le second le temps CPU cumulé en mode idle.
On peut également utiliser un petit programme C pour afficher les horloges :
int main(void) { struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("CLOCK_MONOTONIC: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_BOOTTIME, &ts);
printf("CLOCK_BOOTTIME: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);
return 0;
}
</div>
::::: {.code}
Pour compiler ce programme :
<div lang="en-US">
```bash
42sh$ gcc -o show_clocks show_clocks.c
:::::
Créer un namespace time avec décalage\
Pour créer un nouveau namespace time, il faut d'abord le dissocier puis
configurer les décalages (offsets) avant de lancer un processus enfant.
Le fichier /proc/<PID>/timens_offsets permet de configurer les décalages pour
les horloges. Le format est le suivant :
Où <clock-id> peut être :
monotonicpourCLOCK_MONOTONICboottimepourCLOCK_BOOTTIME
Voici un script shell pour créer un conteneur avec un temps décalé de 10 jours dans le futur :
::::: {.warning}
Important : Les décalages doivent être configurés avant de créer le
premier processus enfant. Une fois qu'un processus enfant existe dans le
namespace, les offsets ne peuvent plus être modifiés. C'est pourquoi nous
utilisons exec pour remplacer le shell actuel.
:::::
Exemple en C\
Voici un exemple complet en C pour créer un namespace time avec un décalage :
static int child_func(void *arg) { struct timespec ts;
// Attendre que le parent configure les offsets
sleep(1);
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("Child - CLOCK_MONOTONIC: %ld.%09ld\n",
ts.tv_sec, ts.tv_nsec);
clock_gettime(CLOCK_BOOTTIME, &ts);
printf("Child - CLOCK_BOOTTIME: %ld.%09ld\n",
ts.tv_sec, ts.tv_nsec);
return 0;
}
#define STACK_SIZE (1024 * 1024) static char child_stack[STACK_SIZE];
int main(void) { pid_t pid; int fd; char path[256]; struct timespec ts;
// Afficher les horloges du parent
clock_gettime(CLOCK_MONOTONIC, &ts);
printf("Parent - CLOCK_MONOTONIC: %ld.%09ld\n",
ts.tv_sec, ts.tv_nsec);
// Se dissocier du namespace time
if (unshare(CLONE_NEWTIME) == -1) {
perror("unshare");
exit(EXIT_FAILURE);
}
// Configurer les offsets (décalage de 1000 secondes)
fd = open("/proc/self/timens_offsets", O_WRONLY);
if (fd == -1) {
perror("open timens_offsets");
exit(EXIT_FAILURE);
}
if (write(fd, "monotonic 1000 0\n", 17) != 17) {
perror("write monotonic offset");
exit(EXIT_FAILURE);
}
if (write(fd, "boottime 1000 0\n", 16) != 16) {
perror("write boottime offset");
exit(EXIT_FAILURE);
}
close(fd);
// Créer un processus enfant qui verra les nouvelles horloges
pid = clone(child_func, child_stack + STACK_SIZE,
SIGCHLD, NULL);
if (pid == -1) {
perror("clone");
exit(EXIT_FAILURE);
}
waitpid(pid, NULL, 0);
return 0;
}
</div>
::::: {.code}
Pour compiler :
<div lang="en-US">
```bash
42sh$ gcc -o time_ns_demo time_ns_demo.c
42sh$ ./time_ns_demo
Parent - CLOCK_MONOTONIC: 123456.789012345
Child - CLOCK_MONOTONIC: 124456.789012345
Child - CLOCK_BOOTTIME: 124456.789012345
On voit bien le décalage de 1000 secondes entre le parent et l'enfant.
:::::
Cas d'usage pratiques
Migration de conteneurs\
Lors de la migration d'un conteneur d'une machine à une autre, les processus
peuvent avoir des timers ou des alarmes basées sur CLOCK_MONOTONIC. Si on ne
virtualise pas ces horloges, tous ces timers seraient incorrects après la
migration.
Le namespace time permet de restaurer l'état des horloges monotones lors
de la restauration d'un conteneur.
Tests et simulation\
Pour tester des applications qui utilisent des timeouts ou des mécanismes de retry avec backoff exponentiel, il peut être utile de virtualiser le temps pour accélérer les tests sans modifier le code de l'application.
Limitations
- Seules les horloges
CLOCK_MONOTONICetCLOCK_BOOTTIMEpeuvent être virtualisées - L'heure système (
CLOCK_REALTIME) reste identique pour tous les processus - Les offsets ne peuvent être configurés qu'une seule fois, avant la création du premier processus enfant
- Les décalages ne peuvent pas être négatifs (on ne peut pas remonter dans le temps)
Aller plus loin {-}
Pour plus d'informations sur le namespace time, consultez :
time_namespaces(7)- page de manuel- Time Namespace support : https://lwn.net/Articles/766089/
- Checkpoint/Restore and Time Namespace : https://lwn.net/Articles/801652/