Le *namespace* `PID`  {#pid-ns}
--------------------

### Introduction {#pid-ns-intro}

L'espace de noms `PID` est celui qui va nous permettre d'isoler un sous-arbre
de processus en créant un nouvel arbre, qui aura son propre processus considéré
comme l'`init`.

Contrairement aux autres *namespaces* où l'on peut demander à se séparer du
*namespace* en question à n'importe quel moment de l'exécution du processus,
via `unshare(2)` ou `setns(2)` par exemple, ici, le changement ne sera valable
qu'après le prochain `fork(2)` (ou similaire).
En effet, l'espace de noms n'est pas changé, afin que le processus ne change
pas de PID en cours de route, puisqu'il dépend du *namespace* dans lequel il se
trouve.


### Isolons !

Première étape s'isoler :

<div lang="en-US">
```
42sh# unshare --pid --fork /bin/bash
    inpidns# echo $$
    1
```
</div>

::::: {.question}

#### Qu'est-ce qu'il se passe sans l'option `--fork` ? {-}
\

Nous utilisons ici l'option `--fork`, pour que le passage dans le nouvel espace
de noms des PID soit effectif (cf. [Introduction](#pid-ns-intro)). Si on
l'omet, voici ce qu'il se passe :

<div lang="en-US">
```
42sh# unshare --pid /bin/sh
    inpidns# echo $$
    23456
```
</div>

Ce n'est apparemment pas ce que l'on souhaitait : on est toujours dans l'ancien
*namespace*, puisqu'on a juste `unshare(2)`, sans `fork(2)`. Si l'on va plus
loin et que l'on `fork` en demandant à `sh` d'exécuter un nouveau processus :

<div lang="en-US">
```
42sh# unshare --pid /bin/sh
    inpidns# echo $$
    34567
    inpidns# sh
      inpidns# echo $$
      1
```
</div>

C'est d'ailleurs le bon moment pour regarder le contenu de notre fichier
`pid_for_children` pour le processus `34567` :

<div lang="en-US">
```
42sh# ls -l /proc/34567/ns/pid*
lrwxrwxrwx 1 nemunaire 1 oct. 23:42 pid    -> 'pid:[4026531836]'
lrwxrwxrwx 1 nemunaire 1 oct. 23:42 pid_for_children -> 'pid:[4026532993]'
```
</div>

On remarque bien que l'on se trouve dans un entre-deux : `pid` pointe toujours
sur l'*inode* de l'arbre de PID initial, tandis que `pid_for_children` est prêt
à transmettre le nouvel *inode* aux nouveaux processus.
\

#### Pourquoi ça ne fonctionne pas du tout avec `bash` ? {-}
\

Si on utilise `bash` à la place de `sh` dans les exemples précédents, toujours
sans l'option `--fork`, on obtient une erreur plutôt étrange :

<div lang="en-US">
```
42sh# unshare --pid /bin/bash
    inpidns# echo $$
    65432
    inpidns# sh
    -bash: fork: Cannot allocate memory
```
</div>

Il se trouve que `bash` commence par `fork(2)` lui-même afin de réaliser un
certain nombre d'opérations. Notre PID 1, le premier PID de notre conteneur, a
donc été alloué à un processus d'initialisation de `bash`, qui s'est terminé
depuis.\

Le comportement du noyau, lorsque le PID 1 se termine, est de lancer un *kernel
panic* (car c'est un processus indispensable, notamment de part son rôle de
parent pour tous les processus orphelin). Au sein d'un *namespace* `PID` qui
n'est pas le *namespace* racine, le noyau appelle la fonction
`disable_pid_allocation` qui retire le *flag* `PIDNS_HASH_ADDING` de l'espace
de nom. Le fait de ne pas avoir `PIDNS_HASH_ADDING` fait retourner `ENOMEM` à
la fonction `alloc_pid` appelée par `fork(2)` et `clone(2)`. On n'a donc pas
d'autre choix que de quitter ce *namespace* pour en recréer un nouveau.

:::::

Un coup d'œil à `top` ou `ps aux` devrait nous montrer que l'on est maintenant
le seul processus ... pourtant, il n'en est rien, ces deux commandes continuent
d'afficher la liste complète des processus de notre système.

Cela est dû au fait que ces deux programmes, sous Linux, se basent sur le
contenu de `/proc`. D'ailleurs, si l'on affiche le PID du processus courant
`echo $$`, on obtient bien 1.

En l'état, beaucoup d'informations sont divulguées. Mais il n'est pas possible
de monter le bon `/proc` car il serait également monté pour les processus de
notre système initial. Pour s'en sortir, il est nécessaire de s'isoler dans un
*namespace* `mount` séparé.


### Double isolation : ajout du *namespace* `mount`

Voici la nouvelle ligne de commande que l'on va utiliser :

<div lang="en-US">
```
42sh# unshare --pid --mount --fork --mount-proc /bin/bash
```
</div>

Avec l'option `--mount-proc`, `unshare` va s'occuper de monter le nouveau
`/proc`.

Cette fois, `top` et `ps` nous rapportent bien que l'on est seul dans notre
*namespace*.


### Arborescence à l'extérieur du *namespace*

Lors de notre première tentative de `top`, lorsque `/proc` était encore monté
sur le `procfs` de l'espace de noms initial : notre processus (au PID 1 dans
son nouveau *namespace*) était présent dans l'arborescence de l'espace initial
avec un PID dans la continuité des autres processus, étonnant !

En fait, l'isolation consiste en une virtualisation des numéros du processus :
la plupart des processus du système initial ne sont pas accessibles, et ceux qui
font partie de l'espace de noms créé disposent d'une nouvelle numérotation. Et
c'est cette nouvelle numérotation qui est montrée au processus.

Si l'on veut interagir avec ce processus depuis un de ses espaces de noms
parent, il faut le faire avec son identifiant de processus du même *namespace*
que le processus appelant.


### Processus orphelins et `nsenter`

Au sein d'un *namespace*, le processus au PID 1 est considéré comme le
programme `init`, les mêmes propriétés s'appliquent donc.

Si un processus est orphelin, il est donc affiché comme étant fils du PID 1
dans son *namespace*[^PR_SET_CHILD_SUBREAPER] ; il n'est pas sorti de l'espace
de noms.

[^PR_SET_CHILD_SUBREAPER]: en réalité, ce comportement est lié à la propriété
  `PR_SET_CHILD_SUBREAPER`, qui peut être définie pour n'importe quel processus
  de l'arborescence. Le processus au PID 1 hérite forcément de cette propriété ;
  il va donc récupérer tous les orphelins, si aucun de leurs parents n'a la
  propriété définie.

Lorsqu'on lance un processus via `nsenter(1)` ou `setns(2)`, cela crée un
processus qui n'est sans doute pas un fils direct du processus d'`init` de
notre conteneur. Malgré tout, même s'il est affiché comme n'étant pas un fils à
l'extérieur du conteneur, les propriétés d'`init` sont biens appliquées à
l'intérieur pour conserver un comportement cohérent.


### Aller plus loin  {-}

N'hésitez pas à jeter un œil à la page de manuel consacrée à cet espace de
noms : `pid_namespaces(7)`.
