Add content for missing namespaces
This commit is contained in:
parent
ab3341bc54
commit
822dc619b8
7 changed files with 1650 additions and 6 deletions
263
tutorial/4/timens.md
Normal file
263
tutorial/4/timens.md
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
Le *namespace* `time` {#time-ns}
|
||||
---------------------
|
||||
|
||||
### 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 :
|
||||
|
||||
<div lang="en-US">
|
||||
```bash
|
||||
42sh$ cat /proc/uptime
|
||||
123456.78 987654.32
|
||||
```
|
||||
</div>
|
||||
|
||||
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 :
|
||||
|
||||
<div lang="en-US">
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
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
|
||||
```
|
||||
</div>
|
||||
|
||||
:::::
|
||||
|
||||
|
||||
#### 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 :
|
||||
|
||||
<div lang="en-US">
|
||||
```
|
||||
<clock-id> <offset-secs> <offset-nanosecs>
|
||||
```
|
||||
</div>
|
||||
|
||||
Où `<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 :
|
||||
|
||||
<div lang="en-US">
|
||||
```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
|
||||
```
|
||||
</div>
|
||||
|
||||
::::: {.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 :
|
||||
|
||||
<div lang="en-US">
|
||||
```c
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
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
|
||||
```
|
||||
</div>
|
||||
|
||||
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 :
|
||||
|
||||
- `time_namespaces(7)` - page de manuel
|
||||
- [Time Namespace support](https://lwn.net/Articles/766089/) : <https://lwn.net/Articles/766089/>
|
||||
- [Checkpoint/Restore and Time Namespace](https://lwn.net/Articles/801652/) : <https://lwn.net/Articles/801652/>
|
||||
Loading…
Add table
Add a link
Reference in a new issue