` 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
#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;
}
```
::::: {.code}
Pour compiler :
```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 :
- `time_namespaces(7)` - page de manuel
- [Time Namespace support](https://lwn.net/Articles/766089/) :
- [Checkpoint/Restore and Time Namespace](https://lwn.net/Articles/801652/) :