virli/tutorial/4/timens.md

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 :

```bash 42sh$ cat /proc/uptime 123456.78 987654.32 ```

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 :

```c #include #include

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 :

``` ```

<clock-id> peut être :

  • monotonic pour CLOCK_MONOTONIC
  • boottime pour CLOCK_BOOTTIME

Voici un script shell pour créer un conteneur avec un temps décalé de 10 jours dans le futur :

```bash 42sh# unshare --time /bin/bash intimens# echo "monotonic 864000 0" > /proc/self/timens_offsets intimens# echo "boottime 864000 0" > /proc/self/timens_offsets intimens# exec /bin/bash intimens-child# ./show_clocks CLOCK_MONOTONIC: 987654.123456789 CLOCK_BOOTTIME: 987654.123456789 ```

::::: {.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 :

```c #define _GNU_SOURCE #include #include #include #include <sys/wait.h> #include #include #include #include

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_MONOTONIC et CLOCK_BOOTTIME peuvent ê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 :