clone, __clone2, clone3 - Créer un processus enfant
(child)
Bibliothèque C standard (libc, -lc)
/* Prototype de la fonction enveloppe de la glibc */
#define _GNU_SOURCE
#include <sched.h>
int clone(int (*fn)(void *_Nullable), void *stack, int flags,
void *_Nullable arg, ... /* pid_t *_Nullable parent_tid,
void *_Nullable tls,
pid_t *_Nullable child_tid */ );
/* Pour le prototype de l'appel système clone() brut, voir REMARQUES */
#include <linux/sched.h> /* Définition de struct clone_args */
#include <sched.h> /* Définition des constantes CLONE_* */
#include <sys/syscall.h> /* Définition des constantes SYS_* */
#include <unistd.h>
long syscall(SYS_clone3, struct clone_args *cl_args, size_t size);
Remarque : La glibc ne fournit pas d'enveloppe pour
clone3() ; appelez-la en utilisant syscall(2).
Ces appels système créent un nouveau processus
« enfant », de façon analogue à
fork(2).
Contrairement à fork(2), ces appels système
offrent un contrôle plus précis du contexte d'exécution
partagé entre le processus appelant et son enfant. Par exemple, en
utilisant ces appels système, l'appelant peut contrôler si les
deux processus partagent ou non l'espace d'adresse virtuel, la table des
descripteurs de fichier et celle des gestionnaires de signal. Ces appels
système permettent également au nouveau processus enfant
d'aller dans un namespaces(7) à part.
Remarquez que dans cette page de manuel, le
« processus appelant » correspond en principe au
« processus parent ». Mais voir les descriptions
de CLONE_PARENT et de CLONE_THREAD ci-dessous.
Cette page décrit les interfaces suivantes :
- •
- Cette page présente à la fois la fonction enveloppe
clone() de la glibc et l'appel système sous-jacent sur
lequel elle s'appuie. Le texte principal décrit la fonction
enveloppe ; les différences avec l'appel système brut
sont précisées vers la fin de cette page.
- •
- Le nouvel appel système clone3().
Dans la suite de cette page, le terme « appel
clone » est utilisé pour évoquer les
détails applicables à toutes ces interfaces,
Quand le processus enfant est créé par la fonction
enveloppe clone(), il débute son exécution par un appel
à la fonction vers laquelle pointe l'argument fn (cela est
différent de fork(2), pour lequel l'exécution continue
dans le processus enfant à partir du moment de l'appel de
fork(2)). L'argument arg est passé comme argument de la
fonction fn.
Quand la fonction fn(arg) renvoie, le processus
enfant se termine. La valeur entière renvoyée par fn
est utilisée comme code de retour du processus enfant. Ce dernier
peut également se terminer de manière explicite en invoquant
la fonction exit(2) ou après la réception d'un signal
fatal.
L'argument stack indique l'emplacement de la pile
utilisée par le processus enfant. Comme les processus enfant et
appelant peuvent partager de la mémoire, il n'est
généralement pas possible pour l'enfant d'utiliser la
même pile que son parent. Le processus appelant doit donc
préparer un espace mémoire pour stocker la pile de son enfant,
et transmettre à clone un pointeur sur cet emplacement. Les
piles croissent vers le bas sur tous les processeurs implémentant
Linux (sauf le HP PA), donc stack doit pointer sur la plus haute
adresse de l'espace mémoire prévu pour la pile du processus
enfant. Remarquez que clone() ne fournit aucun moyen pour que
l'appelant puisse informer le noyau de la taille de la zone de la pile.
Les paramètres restants de clone() sont
décrits ci-dessous.
L'appel système clone3() fournit un sur-ensemble de
la fonctionnalité de l'ancienne interface de clone(). Il offre
également un certain nombre d'améliorations de l'API
dont : un espace pour des bits d'attributs supplémentaires,
une séparation plus propre dans l'utilisation de plusieurs
paramètres et la possibilité d'indiquer la taille de la zone
de la pile de l'enfant.
Comme avec fork(2), clone3() renvoie à la
fois au parent et à l'enfant. Il renvoie 0 dans le processus
enfant et il renvoie le PID de l'enfant dans le parent.
Le paramètre cl_args de clone3() est une
structure ayant la forme suivante :
struct clone_args {
u64 flags; /* Masque de bit d'attribut */
u64 pidfd; /* Où stocker le descripteur de fichier du PID
(int *) */
u64 child_tid; /* Où stocker le TID enfant,
dans la mémoire de l'enfant (pid_t *) */
u64 parent_tid; /* Où stocker le TID enfant,
dans la mémoire du parent (pid_t *) */
u64 exit_signal; /* Signal à envoyer au parent quand
l'enfant se termine */
u64 stack; /* Pointeur vers l'octet le plus faible de la pile */
u64 stack_size; /* Taille de la pile */
u64 tls; /* Emplacement du nouveau TLS */
u64 set_tid; /* Pointeur vers un tableau pid_t
(depuis Linux 5.5) */
u64 set_tid_size; /* Nombre d'éléments dans set_tid
(depuis Linux 5.5) */
u64 cgroup; /* Descripteur de fichier du cgroup cible
de l'enfant (depuis Linux 5.7) */
};
Le paramètre size fourni à clone3()
doit être initialisé à la taille de cette structure
(l'existence du paramètre size autorise des extensions futures
de la structure clone_args).
La pile du processus enfant est indiquée avec
cl_args.stack, qui pointe vers l'octet le plus faible de la zone de
la pile, et avec cl_args.stack_size, qui indique la taille de la pile
en octets. Si l'attribut CLONE_VM est indiqué (voir
ci-dessous), une pile doit être explicitement allouée et
indiquée. Sinon, ces deux champs peuvent valoir NULL et 0, ce
qui amène l'enfant à utiliser la même zone de pile que
son parent (dans l'espace d'adressage virtuel de son propre enfant).
Les autres champs du paramètre cl_args sont
abordés ci-dessous.
Équivalence entre les paramètres de clone() et de
clone3()
Contrairement à l'ancienne interface clone(),
où les paramètres sont passés individuellement, ceux de
la nouvelle interface clone3() sont empaquetés dans la
structure clone_args présentée ci-dessus. Cette
structure permet de passer un ensemble d'informations à l’aide
des arguments de clone().
Le tableau suivant montre l'équivalence entre les
paramètres de clone() et les champs du paramètre
clone_args fournis à clone3() :
clone() |
clone3() |
Notes |
|
Champ cl_args |
attributs & ~0xff |
attributs |
Pour la plupart des attributs, détails ci-dessous |
parent_tid |
pidfd |
Voir CLONE_PIDFD |
child_tid |
child_tid |
Voir CLONE_CHILD_SETTID |
parent_tid |
parent_tid |
Voir CLONE_PARENT_SETTID |
attributs & 0xff |
exit_signal |
stack |
stack |
--- |
stack_size |
tls |
tls |
Voir CLONE_SETTLS |
--- |
set_tid |
Voir ci-dessous pour des détails |
--- |
set_tid_size |
--- |
cgroup |
Voir CLONE_INTO_CGROUP |
Quand le processus enfant se termine, un signal peut être
envoyé au parent. Le signal de fin est indiqué dans l'octet de
poids faible de flags (clone()) ou dans
cl_args.exit_signal (clone3()). Si ce signal est
différent de SIGCHLD, le processus parent doit
également spécifier les options __WALL ou
__WCLONE lorsqu'il attend la fin de l'enfant avec wait(2). Si
aucun signal n'est indiqué (donc zéro), le processus parent ne
sera pas notifié de la terminaison de l'enfant.
Par défaut, le noyau choisit le PID séquentiel
suivant pour le nouveau processus dans chacun des espaces de noms de PID
où il est présent. Lors de la création d'un processus
avec clone3(), le tableau set_tid (depuis Linux 5.5)
peut être utilisé pour sélectionner des PID
spécifiques pour le processus dans tout ou partie des espaces de noms
où il est présent. Si le PID du processus nouvellement
créé ne doit être positionné que dans l'espace
de noms du processus actuel ou dans celui du PID nouvellement
créé (si flags contient CLONE_NEWPID), le
premier élément du tableau set_tid doit être le
PID souhaité et set_tid_size doit valoir 1.
Si le PID du processus nouvellement créé doit avoir
une certaine valeur dans plusieurs espaces de noms de PID, le tableau
set_tid peut avoir plusieurs entrées. La première
entrée définit le PID de l'espace de noms le plus
imbriqué, puis chacune des entrées suivantes contient le PID
de l'espace de noms supérieur correspondant. Le nombre d'espaces de
noms de PID où un PID doit être positionné est
défini par set_tid_size, qui ne peut pas être plus
grand que le nombre d'espaces de noms de PID imbriqués.
Pour créer un processus dont les PID suivants s'inscrivent
dans la hiérarchie de l'espace de noms de PID :
Niveau esp. noms du PID |
PID demandé |
Notes |
0 |
31496 |
Espace de noms du PID le plus à l'extérieur |
1 |
42 |
2 |
7 |
Espace de noms du PID le plus à l'intérieur |
Positionner le tableau sur :
set_tid[0] = 7;
set_tid[1] = 42;
set_tid[2] = 31496;
set_tid_size = 3;
Si seuls les PID des deux espaces de noms de PID les plus à
l’intérieur doivent être indiqués, positionnez
le tableau sur :
set_tid[0] = 7;
set_tid[1] = 42;
set_tid_size = 2;
Le PID dans les espaces de noms de PID en dehors des deux espaces
de noms les plus à l’intérieur sera
sélectionné de la même manière qu'on
sélectionne n'importe quel autre PID.
La fonctionnalité set_tid exige CAP_SYS_ADMIN
ou (depuis Linux 5.9) CAP_CHECKPOINT_RESTORE dans tous les espaces de
noms appartenant à l'utilisateur des espaces de noms du processus
cible.
Les appelants ne peuvent choisir qu'un PID supérieur
à 1 dans un espace de noms de PID donné si un processus
init (à savoir un processus dont le PID est 1) existe
déjà dans cet espace de noms. Sinon, l'entrée du PID
dans cet espace de noms de PID doit valoir 1.
Tant clone() que clone3() permettent d'utiliser un
masque de bit flags pour modifier leur comportement, et elles permettent
à l'appelant d'indiquer ce qui est partagé entre le processus
appelant et son enfant. Ce masque de bit – le paramètre
flags de clone() ou le champ cl_args.flags passé
à clone3() – est désigné comme le
masque flags dans le reste de cette page.
Le masque flags est indiqué comme un OU bit à
bit de zéro ou plus des constantes ci-dessous. Sauf explicitement
indiqués, ces attributs sont disponibles (et ont le même
effet) dans clone() et dans clone3().
- CLONE_CHILD_CLEARTID
(depuis Linux 2.5.49)
- Effacer (zéro) l'ID du thread enfant situé là
où pointe child_tid (clone()) ou
cl_args.child_tid (clone3()) dans la mémoire de
l'enfant lorsqu'il se termine, et provoquer le réveil avec le futex
à cette adresse. L'adresse concernée peut être
modifiée par l'appel système set_tid_address(2). Cela
est utilisé dans les bibliothèques de gestion de
threads.
- CLONE_CHILD_SETTID
(depuis Linux 2.5.49)
- Enregistrer l'ID du thread de l'enfant là où pointe
child_tid ((clone()) ou cl_args.child_tid
(clone3()) dans la mémoire de l'enfant. L'opération
d'enregistrement se termine avant que l'appel clone ne redonne le
contrôle à l'espace utilisateur dans le processus enfant
(remarquez que l'opération d'enregistrement peut ne pas être
terminée avant que l'appel clone ne renvoie au processus parent, ce
qui sera pertinent si l'attribut CLONE_VM est également
utilisé).
- CLONE_CLEAR_SIGHAND
(depuis Linux 5.5)
- Par défaut, l'état des signaux du thread de l'enfant est le
même que celui du parent. Si cet attribut est positionné,
tous les signaux gérés par le parent sont
réinitialisés à leur état par défaut
(SIG_DFL) dans l'enfant.
- Indiquer cet attribut avec CLONE_SIGHAND n'a pas de sens et n'est
pas autorisé.
- CLONE_DETACHED
(historique)
- Pendant un moment (pendant la série de versions au cours du
développement de Linux 2.5), il y a eu un attribut
CLONE_DETACHED, avec lequel le parent ne recevait pas de signal
quand l'enfant se terminait. Au final, l'effet de cet attribut a
été inhibé par l'attribut CLONE_THREAD et
quand Linux 2.6.0 a été publié, cet attribut n'avait
pas d'effet. À partir de Linux 2.6.2, il n'a plus été
nécessaire de fournir cet attribut avec CLONE_THREAD.
- Cet attribut est toujours défini, mais il est
généralement ignoré lors d'un appel clone().
Toutefois, voir la description de CLONE_PIDFD pour certaines
exceptions.
- CLONE_FILES
(depuis Linux 2.0)
- Si l'attribut CLONE_FILES est positionné, le processus
appelant et le processus enfant partagent la même table de
descripteurs de fichier. Tout descripteur créé par un
processus est également valable pour l'autre processus. De
même si un processus ferme un descripteur, ou modifie ses attributs
(en utilisant l'opération fcntl(2) F_SETFD), l'autre
processus en est aussi affecté. Si un processus qui partage une
table de descripteurs de fichier appelle execve(2), sa table est
dupliquée (non partagée).
- Si CLONE_FILES n'est pas positionné, le processus enfant
hérite d'une copie des descripteurs de fichier ouverts par
l'appelant au moment de l'appel clone(). Les opérations
d'ouverture et de fermeture ou de modification d'attributs du descripteur
de fichier subséquentes, effectuées par le processus
appelant ou son enfant, ne concernent pas l'autre processus. Remarquez
toutefois que les copies des descripteurs de fichier dans l'enfant sont
associées aux mêmes descriptions de fichiers ouverts que les
descripteurs de fichier correspondants dans le processus appelant,
partageant ainsi les attributs de position et d’états du
fichier (consultez open(2)).
- CLONE_FS
(depuis Linux 2.0)
- Si l'attribut CLONE_FS est positionné, le processus appelant
et le processus enfant partagent les mêmes informations concernant
le système de fichiers. Cela inclut la racine du système de
fichiers, le répertoire de travail, et l'umask. Tout appel à
chroot(2), chdir(2) ou umask(2) effectué par
un processus aura également une influence sur l'autre
processus.
- Si CLONE_FS n'est pas positionné, le processus enfant
travaille sur une copie des informations de l'appelant concernant le
système de fichiers au moment de l'appel clone. Les appels à
chroot(2), chdir(2), umask(2) effectués
ensuite par un processus n'affectent pas l'autre processus.
- CLONE_INTO_CGROUP
(depuis Linux 5.7)
- Par défaut, un processus enfant est mis dans le même cgroup
version 2 que son parent. L'attribut CLONE_INTO_CGROUP
permet au processus enfant d'être créé dans un cgroup
version 2 différent (remarquez que CLONE_INTO_CGROUP
n'a d'effet que sur les cgroup version 2).
- Pour mettre le processus enfant dans un cgroup différent,
l'appelant indique CLONE_INTO_CGROUP dans cl_args.flags et
passe un descripteur de fichier qui se rapporte à un cgroup
version 2 du champ cl_args.cgroup (le descripteur de fichier
peut être obtenu en ouvrant un répertoire cgroup v2, en
utilisant l'attribut O_RDONLY ou O_PATH). Remarquez que
toutes les restrictions habituelles (décrites dans
cgroups(7)) quant au positionnement d'un processus dans un cgroup
version 2 s'appliquent.
- Voici certains des cas d'utilisation possibles de
CLONE_INTO_CGROUP :
- •
- Créer un processus dans un autre cgroup que celui du parent permet
au gestionnaire de service de placer directement de nouveaux services dans
des cgroup dédiés. Cela élimine les contraintes
comptables qui existeraient si le processus enfant était
créé d'abord dans le même cgroup que le parent puis
déplacé dans un cgroup cible. De plus, la création
d'un processus enfant directement dans un cgroup cible coûte
beaucoup moins cher que de déplacer le processus enfant dans le
cgroup cible après l'avoir créé.
- •
- L'attribut CLONE_INTO_CGROUP permet également la
création de processus enfants gelés en les créant
dans un cgroup gelé (voir cgroups(7) pour une description
des contrôleurs de gel).
- •
- Pour les applications threadées (voire même les
implémentations de thread qui utilisent des cgroup pour limiter les
threads individuels), il est possible d'établir une couche de
cgroup fixe avant de créer chaque thread directement dans son
cgroup cible.
- CLONE_IO
(depuis Linux 2.6.25)
- Si CLONE_IO est défini, alors le nouveau processus partage
un contexte d'entrées-sorties avec le processus appelant. Si cet
attribut n'est pas défini, alors (comme pour fork(2)) le
nouveau processus a son propre contexte d'entrées-sorties.
- Le contexte d'entrées-sorties correspond à la
visibilité que l'ordonnanceur de disques a des
entrées-sorties (c'est-à-dire, ce que l'ordonnanceur
d'entrées-sorties utilise pour modéliser l'ordonnancement
des entrées-sorties d'un processus). Si des processus partagent le
même contexte d'entrées-sorties, ils sont traités
comme un seul par l'ordonnanceur d'entrées-sorties. Par
conséquent, ils partagent le même temps d'accès aux
disques. Pour certains ordonnanceurs d'entrées-sorties, si deux
processus partagent un contexte d'entrées-sorties, ils seront
autorisés à intercaler leurs accès disque. Si
plusieurs threads utilisent des entrées-sorties pour le même
processus (aio_read(3), par exemple), ils devraient utiliser
CLONE_IO pour obtenir de meilleures performances
d'entrées-sorties.
- Si le noyau n'a pas été configuré avec l'option
CONFIG_BLOCK, cet attribut n'a aucun effet.
- CLONE_NEWCGROUP
(depuis Linux 4.6)
- Créer le processus dans un nouvel espace de noms cgroup. Si cet
attribut n'est pas invoqué, alors (comme pour fork(2)) le
processus est créé dans le même espace de noms cgroup
que le processus appelant.
- Pour plus d'informations sur les espaces de noms cgroup, consultez
cgroup_namespaces(7).
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN)
peut utiliser CLONE_NEWCGROUP.
- CLONE_NEWIPC
(depuis Linux 2.6.19)
- Si CLONE_NEWIPC est invoqué, alors le processus est
créé dans un nouvel espace de noms utilisateur IPC. Si cet
attribut n'est pas invoqué, alors (comme pour fork(2)) le
processus est créé dans le même espace de noms
utilisateur IPC que le processus appelant.
- Pour plus d'informations sur les espaces de noms IPC, reportez vous
à ipc_namespaces(7).
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN)
peut utiliser CLONE_NEWIPC. Cet attribut ne peut pas être
employé en association avec CLONE_SYSVSEM.
- CLONE_NEWNET
(depuis Linux 2.6.24)
- (L'implémentation de cet attribut n'est complète que depuis
Linux 2.6.29.)
- Si CLONE_NEWNET est invoqué, alors le processus est
créé dans un nouvel espace de noms réseau. Si cet
attribut n'est pas invoqué, alors (comme pour fork(2)) le
processus est créé dans le même espace de noms
réseau que le processus appelant.
- Pour plus d'informations sur les espaces de noms réseau, reportez
vous à network_namespaces(7).
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN)
peut appeler CLONE_NEWNET.
- CLONE_NEWNS
(depuis Linux 2.4.19)
- Si l'attribut CLONE_NEWNS est invoqué, l'enfant cloné
démarre dans un nouvel espace de noms de montage, initialisé
avec une copie de l'espace de noms du parent. Si CLONE_NEWNS n'est
pas invoqué, alors l'enfant existe dans le même espace de
noms de montage que le parent.
- Pour plus d'informations sur les espaces de noms de montage, consultez
namespaces(7) et mount_namespaces(7).
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN)
peut utiliser l'attribut CLONE_NEWNS. Il n'est pas possible de
spécifier à la fois CLONE_NEWNS et CLONE_FS
pour le même appel clone.
- CLONE_NEWPID
(depuis Linux 2.6.24)
- Si CLONE_NEWPID est invoqué, alors le processus est
créé dans un nouvel espace de noms PID. Si cet attribut
n'est pas invoqué, alors (comme pour fork(2)) le processus
est créé dans le même espace de noms PID que le
processus appelant.
- Pour plus d'informations sur les espaces de noms PID, consultez
namespaces(7) et pid_namespaces(7).
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN)
peut utiliser CLONE_NEWPID. Cet attribut ne peut pas être
utilisé en association avec CLONE_THREAD ou avec
CLONE_PARENT.
- CLONE_NEWUSER
- (Cet attribut est apparu dans clone() pour la première fois
dans Linux 2.6.23, les sémantiques actuelles de
clone() ont été ajoutées dans
Linux 3.5, et les derniers modules rendant les espaces de noms
utilisateur complètement opérationnels sont apparus dans
Linux 3.8.)
- Si CLONE_NEWUSER est invoqué, alors le processus est
créé dans un nouvel espace de noms utilisateur. Si cet
attribut n'est pas invoqué, alors (comme pour fork(2)) le
processus est créé dans le même espace de noms
utilisateur que le processus appelant.
- Pour plus d'informations sur les espaces de noms utilisateur, consultez
namespaces(7) et user_namespaces(7).
- Avant Linux 3.8, les processus appelant devaient disposer de trois
capacités pour utiliser CLONE_NEWUSER :
CAP_SYS_ADMIN, CAP_SETUID et CAP_SETGID. À
partir de Linux 3.8, il n'est plus nécessaire de disposer de
privilèges pour créer des espaces de noms utilisateur.
- Cet attribut ne peut pas être utilisé en association avec
CLONE_THREAD ou avec CLONE_PARENT. Pour des raisons de
sécurité, CLONE_NEWUSER ne peut pas être
utilisé en association avec CLONE_FS.
- CLONE_NEWUTS
(depuis Linux 2.6.19)
- Si CLONE_NEWUTS est défini, créez le processus dans
un nouvel espace de noms UTS, dont les identifiants sont
initialisés en dupliquant les identifiants de l'espace de noms UTS
du processus appelant. Si cet attribut n'est pas défini, alors
(comme pour fork(2)) le processus est créé dans le
même espace de noms UTS que le processus appelant.
- Pour obtenir plus d'informations sur les espaces de noms UTS, consultez
namespaces(7).
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN)
peut utiliser CLONE_NEWUTS.
- CLONE_PARENT
(depuis Linux 2.3.12)
- Si CLONE_PARENT est présent, le parent du nouvel enfant
(comme il est indiqué par getppid(2)) sera le même
que celui du processus appelant.
- Si CLONE_PARENT n'est pas fourni, alors (comme pour fork(2))
le parent du processus enfant sera le processus appelant.
- Remarquez que c'est le processus parent, tel qu'indiqué par
getppid(2), qui est notifié lors de la fin de l'enfant.
Ainsi, si CLONE_PARENT est présent, alors c'est le parent du
processus appelant, et non ce dernier, qui sera notifié.
- L'attribut CLONE_PARENT ne peut pas être utilisé dans
des appels clone par le processus d'initialisation global (PID 1
dans l'espace de noms PID initial) et par les processus initiaux dans les
autres espaces de noms PID. Cette restriction empêche la
création d'arbres de processus à plusieurs racines ou de
zombies non récupérables dans l'espace de noms PID
initial.
- CLONE_PARENT_SETTID
(depuis Linux 2.5.49)
- Enregistrer l'ID du thread enfant à l'endroit vers lequel pointe
parent_tid (clone()) ou cl_args.parent_tid
(clone3()) dans la mémoire du parent (dans
Linux 2.5.32-2.5.48 il y a un attribut CLONE_SETTID qui fait
cela). L'opération d'enregistrement s'achève avant que
l'opération clone ne donne le contrôle à l'espace
utilisateur.
- CLONE_PID (de
Linux 2.0 à Linux 2.5.15)
- Si l'attribut CLONE_PID est positionné, les processus
appelant et enfant ont le même numéro de processus. C'est
bien pour bidouiller le système, mais autrement il n'est plus
utilisé. Depuis Linux 2.3.21, cet attribut ne peut
être utilisé que par le processus de démarrage du
système (PID 0). Il a disparu dans Linux 2.5.16. Si bien que
le noyau ignorait silencieusement le bit s'il était indiqué
dans le masque flags. Bien plus tard, le même bit a
été recyclé pour être utilisé comme
attribut de CLONE_PIDFD.
- CLONE_PIDFD
(depuis Linux 5.2)
- Si cet attribut est indiqué, un descripteur de fichier PID
renvoyant au processus enfant est alloué et placé à
un endroit donné dans la mémoire du parent. L'attribut
close-on-exec est positionné sur ce nouveau descripteur de fichier.
Les descripteurs de fichier PID peuvent être utilisés pour
des objectifs décrits dans pidfd_open(2).
- •
- Quand on utilise clone3(), le descripteur de fichier PID est
placé à un endroit vers lequel pointe
cl_args.pidfd.
- •
- Quand on utilise clone(), le descripteur de fichier PID est
placé à un endroit vers lequel pointe parent_tid.
Comme le paramètre parent_tid est utilisé pour
renvoyer le descripteur de fichier PID, CLONE_PIDFD ne peut pas
être utilisé avec CLONE_PARENT_SETTID lors d'un appel
clone().
- Il n'est pas possible actuellement d'utiliser cet attribut avec
CLONE_THREAD. Cela veut dire que le processus identifié par
le descripteur de fichier PID sera toujours un leader dans le groupe de
threads.
- Si l'attribut obsolète CLONE_DETACHED est indiqué
avec CLONE_PIDFD lors d'un appel à clone(), une
erreur est renvoyée. Une erreur se produit aussi si
CLONE_DETACHED est spécifié lors d'un appel à
clone3(). Ce comportement garantit que le bit qui correspond
à CLONE_DETACHED pourra être utilisé à
l'avenir pour des fonctionnalités supplémentaires du
descripteur de fichier PID.
- CLONE_PTRACE
(depuis Linux 2.2)
- Si l'attribut CLONE_PTRACE est positionné et si l'appelant
est suivi par un débogueur, alors l'enfant sera également
suivi (consultez ptrace(2)).
- CLONE_SETTLS
(depuis Linux 2.5.32)
- Le descripteur TLS (Thread Local Storage) est positionné sur
tls.
- L'interprétation de tls et les effets qui en
découlent dépendent de l'architecture. Sur x86, tls
est interprété comme une struct user_desc *
(voir set_thread_area(2)). Sur x86-64, il s'agit de la nouvelle
valeur à positionner pour le registre de base %fs (voir le
paramètre ARCH_SET_FS de arch_prctl(2)). Sur les
architectures ayant un registre TLS dédié, il s'agit de la
nouvelle valeur de ce registre.
- L'utilisation de cet attribut exige une connaissance
détaillée et n'est généralement pas
souhaitable, sauf dans l'implémentation de bibliothèques de
gestion des threads.
- CLONE_SIGHAND
(depuis Linux 2.0)
- Si l'attribut CLONE_SIGHAND est positionné, le processus
appelant et le processus enfant partagent la même table de
gestionnaires de signaux. Si l'appelant, ou l'enfant, appelle
sigaction(2) pour modifier le comportement associé à
un signal, ce comportement est également changé pour l'autre
processus. Néanmoins, l'appelant et l'enfant ont toujours des
masques de signaux distincts, et leurs ensembles de signaux bloqués
sont indépendants. L'un des processus peut donc bloquer ou
débloquer un signal en utilisant sigprocmask(2) sans
affecter l'autre processus.
- Si CLONE_SIGHAND n'est pas utilisé, le processus enfant
hérite d'une copie des gestionnaires de signaux de l'appelant lors
de l'invocation de clone(). Les appels à sigaction(2)
effectués ensuite depuis l'un des processus n'ont pas d'effets sur
l'autre processus.
- Depuis Linux 2.6.0, le masque flags doit aussi inclure
CLONE_VM si CLONE_SIGHAND est spécifié
- CLONE_STOPPED
(depuis Linux 2.6.0)
- Si l'attribut CLONE_STOPPED est positionné, l'enfant est
initialement stoppé (comme s'il avait reçu le signal
SIGSTOP), et doit être relancé en lui envoyant le
signal SIGCONT.
- Cet attribut a été rendu obsolète par
Linux 2.6.25, puis il a été supprimé
dans Linux 2.6.38. Depuis lors, le noyau l'ignore silencieusement
sans erreur. À partir de Linux 4.6, le même bit a
été réutilisé comme attribut de
CLONE_NEWCGROUP.
- CLONE_SYSVSEM
(depuis Linux 2.5.10)
- Si CLONE_SYSVSEM est positionné, l'enfant et le processus
appelant partagent une même liste de valeurs d’ajustement de
sémaphores System V (consultez semop(2)). Dans ce
cas, cette liste regroupe toutes les valeurs semadj des processus
partageant cette liste, et les modifications des sémaphores sont
effectuées seulement lorsque le dernier processus de la liste se
termine (ou cesse de partager la liste en invoquant unshare(2)). Si
cet attribut n'est pas utilisé, l'enfant a une liste semadj
séparée, initialement vide.
- CLONE_THREAD
(depuis Linux 2.4.0)
- Si CLONE_THREAD est présent, l'enfant est placé dans
le même groupe de threads que le processus appelant. Afin de rendre
l'explication de CLONE_THREAD plus lisible, le terme
« thread » est utilisé pour parler des
processus dans un même groupe de threads.
- Les groupes de threads sont une fonctionnalité ajoutée dans
Linux 2.4 pour gérer la notion POSIX d'ensemble de threads
partageant un même PID. En interne, ce PID partagé est
appelé identifiant de groupe de threads (TGID). Depuis
Linux 2.4, l'appel getpid(2) renvoie l'identifiant du groupe
de threads de l'appelant.
- Les threads dans un groupe peuvent être distingués par leur
identifiant de thread (TID, unique sur le système). Le TID d'un
nouveau thread est disponible sous la forme du résultat d'une
fonction renvoyé à l'appelant et un thread peut obtenir son
propre TID en utilisant gettid(2).
- Quand clone est appelé sans positionner CLONE_THREAD, le
nouveau thread est placé dans un nouveau groupe de threads dont le
TGID est identique au TID du nouveau thread. Ce thread est le
leader du nouveau groupe.
- Un nouveau thread créé en utilisant CLONE_THREAD a le
même processus parent que le processus réalisant l'appel
clone (de même qu'avec CLONE_PARENT), ainsi les appels
à getppid(2) renvoient la même valeur à tous
les threads dans un même groupe. Lorsqu'un thread
créé avec CLONE_THREAD termine, le thread qui
l’a créé ne reçoit pas le signal
SIGCHLD (ou autre notification de terminaison) ; de
même, l'état d'un tel thread ne peut pas être obtenu
par wait(2). Le thread est dit détaché.
- Lorsque tous les threads d'un groupe de threads terminent, le processus
parent du groupe reçoit un signal SIGCHLD (ou un autre
indicateur de terminaison).
- Si l'un des threads dans un groupe de threads appelle execve(2),
tous les threads sauf le leader sont tués, et le nouveau programme
est exécuté dans le leader du groupe de threads.
- Si l'un des threads dans un groupe crée un enfant avec
fork(2), n'importe lequel des threads du groupe peut utiliser
wait(2) sur cet enfant.
- Depuis Linux 2.5.35, le masque flags doit aussi inclure
CLONE_SIGHAND si CLONE_THREAD est spécifié (et
remarquez que depuis Linux 2.6.0, CLONE_SIGHAND a
également besoin de CLONE_VM).
- Les gestions de signaux sont définies au niveau des
processus : si un signal sans gestionnaire est reçu par un
thread, il affectera (tuera, stoppera, relancera, ou sera ignoré
par) tous les membres du groupe de threads.
- Chaque thread a son propre masque de signal, tel que défini par
sigprocmask(2).
- Un signal peut être adressé à un processus ou
à un thread. S'il s'adresse à un processus, il cible un
groupe de threads (c'est-à-dire un TGID), et il est envoyé
à un thread choisi arbitrairement parmi ceux ne bloquant pas les
signaux. Un signal peut s'adresser à un processus car il est
généré par le noyau pour d'autres raisons qu'une
exception matérielle, ou parce qu'il a été
envoyé en utilisant kill(2) ou sigqueue(3). Si un
signal s'adresse à un thread, il cible (donc est envoyé)
à un thread spécifique. Un signal peut s'adresser à
un thread du fait d'un envoi par tgkill(2) ou
pthread_sigqueue(3), ou parce que le thread a exécuté
une instruction en langage machine qui a provoqué une exception
matérielle (comme un accès non valable en mémoire,
provoquant SIGSEGV, ou une exception de virgule flottante
provoquant un SIGFPE).
- Un appel à sigpending(2) renvoie un jeu de signaux qui
réunit les signaux en attente adressés au processus et ceux
en attente pour le thread appelant.
- Si un signal adressé à un processus est envoyé
à un groupe de threads, et si le groupe a installé un
gestionnaire pour ce signal, alors le gestionnaire sera
exécuté exactement dans un des membres du groupe de threads,
choisi de façon arbitraire parmi ceux qui n'ont pas bloqué
ce signal. Si plusieurs threads dans un groupe attendent le même
signal en utilisant sigwaitinfo(2), le noyau choisira
arbitrairement l'un d'entre eux pour recevoir le signal.
- CLONE_UNTRACED
(depuis Linux 2.5.46)
- Si l'attribut CLONE_UNTRACED est positionné, alors un
processus traçant le parent ne peut pas forcer CLONE_PTRACE
pour cet enfant.
- CLONE_VFORK
(depuis Linux 2.2)
- Si le bit CLONE_VFORK est actif, l'exécution du processus
appelant est suspendue jusqu'à ce que l'enfant libère ses
ressources de mémoire virtuelle par un appel execve(2) ou
_exit(2) (comme avec vfork(2)).
- Si CLONE_VFORK n'est pas indiqué, alors les deux processus
sont ordonnancés à partir de la fin de l'appel, et
l'application ne devrait pas considérer que l'ordre
d'exécution est déterminé dans un ordre
particulier.
- CLONE_VM
(depuis Linux 2.0)
- Si le bit CLONE_VM est actif, le processus appelant et le processus
enfant s'exécutent dans le même espace mémoire. En
particulier, les écritures en mémoire effectuées par
l'un des processus sont visibles par l'autre. De même toute
projection en mémoire, ou toute suppression de projection,
effectuée avec mmap(2) ou munmap(2) par l'un des
processus affectera également l'autre processus.
- Si CLONE_VM n'est pas actif, le processus enfant utilisera une
copie distincte de l'espace mémoire de l'appelant au moment de
l'appel clone. Les écritures ou les
associations/désassociations de fichiers en mémoire
effectuées par un processus n'affectent pas l'autre processus,
comme cela se passe avec fork(2).
- Si l'attribut CLONE_VM est indiqué et si l'attribut
CLONE_VFORK ne l'est pas, toute autre pile de signal mise en place
par sigaltstack(2) sera vidée dans le processus enfant.
En cas de réussite, le TID du processus enfant est
renvoyé dans le thread d'exécution de l'appelant. En cas
d'échec, -1 est renvoyé dans le contexte de l'appelant,
aucun enfant n'est créé, et errno sera
positionné pour indiquer l'erreur.
- EACCES
(clone3() seulement)
- CLONE_INTO_CGROUP était indiqué dans
cl_args.flags, mais les restrictions à la mise en place d'un
processus enfant dans un cgroup version 2 auquel se rapporte
cl_args.cgroup (décrites dans cgroups(7)) ne sont pas
respectées.
- EAGAIN
- Trop de processus en cours d'exécution. Consultez
fork(2).
- EBUSY
(clone3() seulement)
- CLONE_INTO_CGROUP était indiqué dans
cl_args.flags, mais le descripteur de fichier indiqué dans
cl_args.cgroup se rapporte à un
cgroup version 2 où un contrôleur de domaine
est activé.
- EEXIST
(clone3() seulement)
- Un (ou plusieurs) PID indiqué dans le set_tid existe
déjà dans l'espace de noms PID correspondant.
- EINVAL
- Tant CLONE_SIGHAND que CLONE_CLEAR_SIGHAND ont
été indiqués dans le masque flags.
- EINVAL
- CLONE_SIGHAND a été spécifié dans le
masque flags, mais pas CLONE_VM (depuis
Linux 2.6.0).
- EINVAL
- CLONE_THREAD a été spécifié dans le
masque flags, mais pas CLONE_SIGHAND (depuis
Linux 2.5.35).
- EINVAL
- CLONE_THREAD a été indiqué dans le masque
flags mais le processus actuel avait appelé
unshare(2) avec l'attribut CLONE_NEWPID ou il utilisait
setns(2) pour se réassocier à l'espace de noms
PID.
- EINVAL
- Tant CLONE_FS que CLONE_NEWNS ont été
indiqués dans le masque flags.
- EINVAL (depuis
Linux 3.9)
- Tant CLONE_NEWUSER que CLONE_FS ont été
indiqués dans le masque flags.
- EINVAL
- Tant CLONE_NEWIPC que CLONE_SYSVSEM ont été
indiqués dans le masque flags.
- EINVAL
- CLONE_NEWPID ou CLONE_NEWUSER, et CLONE_THREAD ou
CLONE_PARENT, ont été indiqués, seuls ou
ensemble, dans le masque flags.
- EINVAL (depuis
Linux 2.6.32)
- CLONE_PARENT a été spécifié et
l'appelant est un processus d'initialisation.
- EINVAL
- Renvoyée par l'enveloppe glibc de clone() quand fn ou
stack valent NULL.
- EINVAL
- CLONE_NEWIPC a été spécifié dans le
masque flags, mais le noyau n'a pas été
configuré avec les options CONFIG_SYSVIPC et
CONFIG_IPC_NS.
- EINVAL
- CLONE_NEWNET a été spécifié dans le
masque flags, mais le noyau n'a pas été
configuré avec l'option CONFIG_NET_NS.
- EINVAL
- CLONE_NEWPID a été spécifié dans le
masque flags, mais le noyau n'a pas été
configuré avec l'option CONFIG_PID_NS.
- EINVAL
- CLONE_NEWUSER a été spécifié dans le
masque flags, mais le noyau n'a pas été
configuré avec l'option CONFIG_USER_NS.
- EINVAL
- CLONE_NEWUTS a été spécifié dans le
masque flags, mais le noyau n'a pas été
configuré avec l'option CONFIG_UTS_NS.
- EINVAL
- stack n'est pas alignée sur une limite adaptée
à cette architecture. Par exemple, sur aarch64, stack doit
être un multiple de 16.
- EINVAL
(clone3() seulement)
- CLONE_DETACHED a été spécifié dans le
masque flags.
- EINVAL
(clone() seulement)
- CLONE_PIDFD a été indiqué avec
CLONE_DETACHED dans le masque flags.
- EINVAL
- CLONE_PIDFD a été indiqué avec
CLONE_THREAD dans le masque flags.
- EINVAL
(clone() seulement)
- CLONE_PIDFD a été indiqué avec
CLONE_PARENT_SETTID dans le masque flags.
- EINVAL
(clone3() seulement)
- set_tid_size est supérieur au nombre de niveaux dans
l'espace de noms PID.
- EINVAL
(clone3() seulement)
- Un des PID indiqué dans set_tid n'était pas
valable.
- EINVAL
(AArch64 seulement, Linux 4.6 et antérieur)
- stack n'était pas aligné sur une limite de 128
bits.
- ENOMEM
- Pas assez de mémoire pour copier les parties du contexte du
processus appelant qui doivent être dupliquées, ou pour
allouer une structure de tâche pour le processus enfant.
- ENOSPC (depuis
Linux 3.7)
- CLONE_NEWPID a été spécifié dans le
masque flags, et l'appel provoquerait un dépassement de la
limite du nombre maximal d'espaces de noms utilisateur imbriqués.
Consultez pid_namespaces(7).
- ENOSPC (depuis
Linux 4.9 ; auparavant EUSERS)
- CLONE_NEWUSER a été spécifié dans le
masque flags, et l'appel provoquerait un dépassement de la
limite du nombre maximal d'espaces de noms utilisateur imbriqués.
Consultez user_namespaces(7).
- De Linux 3.11 à Linux 4.8, l'erreur indiquée dans ce cas
était EUSERS.
- ENOSPC (depuis
Linux 4.9)
- Une des valeurs dans le masque flags indiquait de créer un
nouvel espace de noms utilisateur, mais cela aurait provoqué un
dépassement de la limite définie par le fichier
correspondant dans /proc/sys/user. Pour plus de détails,
voir namespaces(7).
- EOPNOTSUPP
(clone3() seulement)
- CLONE_INTO_CGROUP était indiqué dans
cl_args.flags, mais le descripteur de fichier indiqué dans
cl_args.cgroup se rapporte à un
cgroup version 2 dont l'état est domain
invalid.
- EPERM
- CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET,
CLONE_NEWNS, CLONE_NEWPID ou CLONE_NEWUTS a
été spécifié par un processus non
privilégié (processus sans CAP_SYS_ADMIN).
- EPERM
- CLONE_PID a été spécifié par un
processus autre que le processus 0 (cette erreur n'arrive que sur
Linux 2.5.15 et antérieurs).
- EPERM
- CLONE_NEWUSER a été spécifié dans le
masque flags, mais l'identifiant utilisateur effectif ou
l'identifiant de groupe effectif de l'appelant n'a pas de correspondance
dans l'espace de noms parent (consultez user_namespaces(7)).
- EPERM (depuis
Linux 3.9)
- CLONE_NEWUSER a été spécifié dans le
masque flags et l'appelant se trouve dans un environnement chroot
(c'est-à-dire que le répertoire racine de l'appelant ne
correspond pas au répertoire racine de l'espace de noms de montage
dans lequel il se trouve).
- EPERM
(clone3() seulement)
- set_tid_size était supérieur à zéro et
l'appelant n'a pas la capacité CAP_SYS_ADMIN dans un ou
plusieurs des espaces de noms utilisateur qui possèdent les espaces
de noms PID correspondants.
- ERESTARTNOINTR
(depuis Linux 2.6.17)
- L'appel système a été interrompu par un signal et va
être redémarré (cela n'est visible qu'à
l'occasion d'un trace()).
- EUSERS (Linux 3.11
à Linux 4.8)
- CLONE_NEWUSER a été spécifié dans le
masque flags, et l'appel provoquerait un dépassement de la
limite du nombre maximal d'espaces de noms utilisateur imbriqués.
Voir le point sur l'erreur ENOSPC ci-dessus.
L'appel système clone3() est apparu pour la
première fois dans Linux 5.3.
Ces appels système sont spécifiques à Linux
et ne doivent pas être utilisés dans des programmes
conçus pour être portables.
Une utilisation de ces appels système consiste à
implémenter des threads : un programme est scindé en
plusieurs lignes de contrôle, s'exécutant simultanément
dans un espace mémoire partagée.
Remarquez que la fonction enveloppe clone() de la glibc
effectue des changements dans la mémoire vers laquelle pointe
stack (ce sont des changements nécessaires pour positionner
correctement la pile pour l'enfant) avant de recourir à
l'appel système clone(). Dès lors, lorsque
clone() est utilisé pour créer des enfants de
manière récursive, n'utilisez pas le tampon servant à
la pile du parent en tant que pile de l'enfant.
L'appel système kcmp(2) peut être
utilisé pour vérifier si deux processus partagent des
ressources, telles qu'une table de descripteurs de fichier, des
opérations Annuler le sémaphore sur System V, ou un
espace d'adressage virtuel.
Les gestionnaires enregistrés en utilisant
pthread_atfork(3) ne sont pas exécutés pendant un appel
clone.
Dans les séries 2.4.x de Linux, CLONE_THREAD ne fait
en général pas du processus parent du nouveau thread un
processus identique au parent du processus appelant. Cependant, de
Linux 2.4.7 à Linux 2.4.18, l'attribut
CLONE_THREAD impliquait CLONE_PARENT (de même que dans
Linux 2.6.0 et supérieurs).
Sur i386, clone() ne devrait pas être appelé
à l’aide de vsyscall, mais directement en utilisant int
$0x80.
L'appel système clone brut ressemble plus à
fork(2), en ceci que l'exécution dans le processus enfant
continue à partir du point d'appel. À ce titre, les arguments
fn et arg de la fonction enveloppe de clone() sont
omis.
Contrairement à l'enveloppe de la glibc, l'appel
système brut clone() accepte NULL en paramètre de
stack (et de même, clone3() permet à
cl_args.stack d'être NULL). Dans ce cas l'enfant utilise une
copie de la pile du parent (la sémantique de copie-en-écriture
assure que l'enfant recevra une copie indépendante des pages de la
pile dès qu'un des deux processus la modifiera). Pour que cela
fonctionne, il faut naturellement que CLONE_VM ne soit pas
présent (si l'enfant partage la mémoire du parent du
fait d'une utilisation de CLONE_VM, aucune duplication à
l’aide de la copie-en-écriture ne se produit et il peut
s'ensuivre probablement un grand chaos).
L'ordre des paramètres change aussi dans l'appel
système brut et des variations existent dans les paramètres en
fonction des architectures, comme indiqué dans les paragraphes
suivants.
L'interface de l'appel système brut sur des architectures
x86-64 et quelques autres (dont sh, tile et alpha), est :
long clone(unsigned long flags, void *stack,
int *parent_tid, int *child_tid,
unsigned long tls);
Sur x86-32 et d'autres architectures classiques (dont score, ARM,
ARM 64, PA-RISC, arc, Power PC, xtensa et MIPS), l'ordre des deux derniers
paramètres est inversé :
long clone(unsigned long flags, void *stack,
int *parent_tid, unsigned long tls,
int *child_tid);
Sur les architectures cris et s390, l'ordre des deux premiers
paramètres est inversé :
long clone(void *stack, unsigned long flags,
int *parent_tid, int *child_tid,
unsigned long tls);
Sur l'architecture microblaze, il existe un paramètre
supplémentaire :
long clone(unsigned long flags, void *stack,
int stack_size, /* Taille de la pile */
int *parent_tid, int *child_tid,
unsigned long tls);
Les conventions de passage des arguments sur blackfin, m68k et
sparc sont différentes de celles décrites
précédemment. Pour plus de détails, se
référer aux sources du noyau (et de la glibc).
Sur ia64, une interface différente est
utilisée :
int __clone2(int (*fn)(void *),
void *stack_base, size_t stack_size,
int flags, void *arg, ...
/* pid_t *parent_tid, struct user_desc *tls,
pid_t *child_tid */ );
Le prototype présenté ci-dessus vaut pour la
fonction enveloppe de la glibc ; pour l'appel système
lui-même, il peut être décrit comme suit (il est
identique au prototype clone() sur microblaze) :
long clone2(unsigned long flags, void *stack_base,
int stack_size, /* Taille de la pile */
int *parent_tid, int *child_tid,
unsigned long tls);
__clone2() fonctionne comme clone(), sauf que
stack_base pointe sur la plus petite adresse de la pile de l'enfant
et que stack_size indique la taille de la pile sur laquelle pointe
stack_base.
Sous Linux 2.4 et plus anciens, clone() ne prend pas
les paramètres parent_tid, tls, et
child_tid.
Les versions de la bibliothèque C GNU
jusqu'à la 2.24 comprise contenaient une fonction enveloppe
pour getpid(2) qui effectuait un cache des PID. Ce cache
nécessitait une prise en charge par l'enveloppe de clone() de
la glibc, mais des limites dans l'implémentation faisaient que le
cache pouvait ne pas être à jour sous certaines circonstances.
En particulier, si un signal était distribué à un
enfant juste après l'appel à clone(), alors un appel
à getpid(2) dans le gestionnaire de signaux du signal pouvait
renvoyer le PID du processus appelant (le parent), si l'enveloppe de clone
n'avait toujours pas eu le temps de mettre le cache de PID à jour
pour l'enfant. (Ce point ignore le cas où l'enfant a
été créé en utilisant CLONE_THREAD, quand
getpid(2) doit renvoyer la même valeur pour l'enfant et
pour le processus qui a appelé clone(), puisque l'appelant et
l'enfant se trouvent dans le même groupe de threads. Ce
problème de cache n'apparaît pas non plus si le
paramètre flags contient CLONE_VM.) Pour obtenir la
véritable valeur, il peut être nécessaire d'utiliser
quelque chose comme ceci :
#include <syscall.h>
pid_t mypid;
mypid = syscall(SYS_getpid);
Suite à un problème de cache ancien, ainsi
qu'à d'autres problèmes traités dans getpid(2),
la fonctionnalité de mise en cache du PID a été
supprimée de la glibc 2.25.
Le programme suivant décrit l'usage de clone() dans
le but de créer un processus enfant qui s'exécute dans un
espace de noms UTS distinct. Le processus enfant change le nom d'hôte
(hostname) dans son propre espace UTS. Les processus parent et enfant
affichent chacun le nom d'hôte qui leur correspond, permettant ainsi
de constater la différence des noms d'hôtes dans leurs espaces
de noms UTS respectifs. Pour un exemple d’utilisation de ce
programme, consultez setns(2).
Dans le programme d'exemple, nous allouons la mémoire qui
doit être utilisée pour la pile de l'enfant en utilisant
mmap(2) au lieu de malloc(3) pour les raisons
suivantes :
- •
- mmap(2) alloue un bloc de mémoire commençant à
la limite d'une page et qui est un multiple de la taille de la page. Cela
est utile si on veut établir une page de protection (avec
PROT_NONE) à la fin de la pile en utilisant
mprotect(2).
- •
- On peut indiquer l'attribut MAP_STACK pour demander une association
adaptée à une pile. Pour le moment, cet attribut n'est pas
opérationnel sur Linux, mais il existe et a des effets sur d'autres
systèmes, donc on doit l'inclure pour la portabilité.
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <unistd.h>
static int /* Commencer la fonction pour l'enfant cloné */
childFunc(void *arg)
{
struct utsname uts;
/* Modifier le nom d'hôte dans l'espace de noms UTS de l'enfant. */
if (sethostname(arg, strlen(arg)) == -1)
err(EXIT_FAILURE, "sethostname");
/* Récupérer et afficher le nom d'hôte. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename dans l'enfant : %s\n", uts.nodename);
/* Rester en sommeil (fonction sleep) pour conserver l'espace
de noms ouvert pendant un moment. Cela permet de réaliser
quelques expérimentations — par exemple, un autre processus
pourrait rejoindre l'espace de noms. */
sleep(200);
return 0; /* Le processus enfant se termine à ce moment */
}
#define STACK_SIZE (1024 * 1024) /* Taille de la pile pour
l'enfant cloné */
int
main(int argc, char *argv[])
{
char *stack; /* Début du tampon de la pile */
char *stackTop; /* Fin du tampon de la pile */
pid_t pid;
struct utsname uts;
if (argc < 2) {
fprintf(stderr, "Utilisation : %s <nom_d_hôte-enfant>\n", argv[0]);
exit(EXIT_SUCCESS);
}
/* Allouer la mémoire à utiliser pour la pile du processus enfant. */
stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (stack == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
stackTop = stack + STACK_SIZE; /* On suppose que la pile grandit vers
le bas */
/* Créer un processus enfant disposant de son propre
espace de noms UTS ; le processus enfant débute
son exécution dans childFunc(). */
pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]);
if (pid == -1)
err(EXIT_FAILURE, "clone");
printf("clone() a renvoyé %jd\n", (intmax_t) pid);
/* Le parent se retrouve ici */
sleep(1); /* Laisser le temps au processus enfant de
changer son nom d'hôte */
/* Afficher le nom d'hôte pour l'espace de noms UTS du processus parent.
Celui-ci sera différent du nom d'hôte pour l'espace de noms UTS du
processus enfant. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename dans le parent : %s\n", uts.nodename);
if (waitpid(pid, NULL, 0) == -1) /* Attendre le processus enfant */
err(EXIT_FAILURE, "waitpid");
printf("Fin du processus enfant\n");
exit(EXIT_SUCCESS);
}
fork(2), futex(2), getpid(2),
gettid(2), kcmp(2), mmap(2), pidfd_open(2),
set_thread_area(2), set_tid_address(2), setns(2),
tkill(2), unshare(2), wait(2), capabilities(7),
namespaces(7), pthreads(7)
La traduction française de cette page de manuel a
été créée par Christophe Blaess
<https://www.blaess.fr/christophe/>, Stéphan Rafin
<stephan.rafin@laposte.net>, Thierry Vignaud
<tvignaud@mandriva.com>, François Micaux, Alain Portal
<aportal@univ-montp2.fr>, Jean-Philippe Guérard
<fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh)
<jean-luc.coulon@wanadoo.fr>, Julien Cristau
<jcristau@debian.org>, Thomas Huriaux
<thomas.huriaux@gmail.com>, Nicolas François
<nicolas.francois@centraliens.net>, Florentin Duneau
<fduneau@gmail.com>, Simon Paillard
<simon.paillard@resel.enst-bretagne.fr>, Denis Barbier
<barbier@debian.org>, David Prévot <david@tilapin.org>,
Cédric Boutillier <cedric.boutillier@gmail.com>,
Frédéric Hantrais <fhantrais@gmail.com> et Jean-Philippe
MENGUAL <jpmengual@debian.org>
Cette traduction est une documentation libre ; veuillez
vous reporter à la
GNU General
Public License version 3 concernant les conditions de copie et de
distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page
de manuel, veuillez envoyer un message à
debian-l10n-french@lists.debian.org.