table of contents
CLONE(2) | Manuel du programmeur Linux | CLONE(2) |
NOM¶
clone, __clone2 - Créer un processus enfant (child)
SYNOPSIS¶
/* Prototype de la fonction enveloppe de la glibc */
#define _GNU_SOURCE #include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, void *newtls, pid_t *ctid */ );
/* Pour le prototype de l'appel système brut, voir REMARQUES */
DESCRIPTION¶
clone() crée un nouveau processus, de façon analogue à fork(2).
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.
Contrairement à fork(2), clone() autorise le processus enfant à partager des éléments de son contexte d'exécution avec le processus appelant, tels que son espace d’adressage virtuel, le tableau des descripteurs de fichiers et celui des gestionnaires de signaux (remarquez que dans cette page de manuel, le « processus appelant » correspond normalement au « processus parent ». Mais voir la description de CLONE_PARENT ci-dessous).
Une utilisation de clone() consiste à implémenter des threads : un programme est scindé en plusieurs lignes de contrôle, s'exécutant simultanément dans un espace d’adressage partagé.
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 child_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 le processus appelant. 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 child_stack pointe en général vers la plus haute adresse de l'espace mémoire prévu pour la pile du processus enfant.
L'octet faible de flags contient le numéro du signal de terminaison envoyé au parent quand l'enfant meurt. 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é, le processus parent ne sera pas notifié de la terminaison de l'enfant.
flags peut être aussi associé (OU bit à bit) à zéro ou plusieurs des constantes suivantes pour indiquer ce qui est partagé entre le processus appelant et celui enfant :
- CLONE_CHILD_CLEARTID (depuis Linux 2.5.49)
- Effacer (zéro) l'ID du thread enfant à l'emplacement ctid 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 enfant à l'endroit ctid 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.
- 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_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. Cet attribut a été conçu pour l'implémentation des conteneurs.
- 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 IPC. Si cet attribut n'est pas invoqué, alors (comme pour fork(2)) le processus est créé dans le même espace de noms IPC que le processus appelant. Cet attribut a été créé pour l'implémentation de conteneurs.
- Un espace de noms IPC offre un aperçu isolé des objets IPC System V (voir svipc(7)) et (depuis Linux 2.6.30) des files d'attente de messages POSIX (voir mq_overview(7)). La caractéristique commune à ces mécanismes IPC est que les objets sont identifiés par des mécanismes différents des chemins des systèmes de fichiers.
- Des objets créés dans un espace de noms IPC sont visibles par tous les autres processus membres de cet espace de noms, mais ils sont invisibles pour ceux d'autres espaces de noms IPC.
- Lors de la destruction d’un espace de noms IPC (c’est-à-dire quand le dernier processus membre de cet espace de noms se termine), tous les objets IPC de cet espace de noms sont détruits automatiquement.
- 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.
- Pour plus d'informations sur les espaces de noms IPC, reportez vous à namespaces(7).
- CLONE_NEWNET (depuis Linux 2.6.24)
- (L'implémentation de cet attribut n'est complète que depuis le noyau 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. Cet attribut a été conçu pour l'implémentation de conteneurs.
- Un espace de noms réseau fournit un aperçu isolé de la pile réseau (interfaces de périphérique réseau, piles du protocole IPV4 et IPV6, tables de routage IP, règles de pare-feu, arborescences du répertoire /proc/net et /sys/class/net, sockets, etc.). Un périphérique réseau physique peut vivre dans exactement un espace de noms réseau. Un pair de périphérique réseau virtuel (veth(4)) offre une abstraction similaire à un tuyau, utilisable pour créer des tunnels entre des espaces de noms réseau ou pour créer une passerelle (bridge) vers le périphérique réseau physique d'un autre espace de noms.
- Lorsqu'un espace de noms réseau est libéré (à savoir quand le dernier processus de l'espace de noms se termine), ses périphériques réseaux physiques sont ramenés dans leur espace de noms initial (et non au parent du processus). Pour plus d'informations sur les espaces de noms réseau, voir 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.
- 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().
- Pour plus d'informations sur les espaces de noms de montage, consultez namespaces(7) et mount_namespaces(7).
- 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. Cet attribut a été conçu pour l'implémentation de conteneurs.
- 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.
- 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.
- Pour plus d'informations sur les espaces de noms utilisateur, consultez namespaces(7) et user_namespaces(7).
- CLONE_NEWUTS (depuis Linux 2.6.19)
- Si CLONE_NEWUTS est défini, créer 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. Cet attribut a été conçu pour l'implémentation de conteneurs.
- Un espace de noms UTS est l'ensemble des identifiants renvoyés par uname(2) ; parmi eux, le nom de domaine et le nom d'hôte peuvent être modifiés respectivement par setdomainname(2) et sethostname(2). Les modifications apportées aux identifiants dans un espace de noms UTS sont visibles par tous les autres processus du même espace de noms, pas par ceux d'autres espaces de noms.
- Seul un processus disposant de privilèges (CAP_SYS_ADMIN) peut utiliser CLONE_NEWUTS.
- Pour obtenir plus d'informations sur les espaces de noms UTS, consultez namespaces(7).
- 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é.
- CLONE_PARENT_SETTID (depuis Linux 2.5.49)
- Enregistrer l'ID du thread enfant à l'endroit ptid situé dans la mémoire du parent. (Dans Linux 2.5.32-2.5.48 il y a un attribut CLONE_SETTID qui faisait cela). L'opération d'enregistrement se termine avant que l'appel clone() ne redonne le contrôle à l'espace utilisateur.
- CLONE_PID (Linux 2.0 à 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 sinon c'est peu utile. À partir de Linux 2.3.21, cet attribut ne pouvait être utilisé que par le processus d'amorçage du système (PID 0). Il a disparu des sources du noyau depuis Linux 2.5.16. Si bien que le noyau ignore silencieusement le bit s'il est indiqué dans flags.
- 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 newtls.
- L'interprétation de newtls et les effets qui en découlent dépendent de l'architecture. Sur x86, newtls 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.
- 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 un processus n'ont pas d'effets sur l'autre processus.
- Depuis Linux 2.6.0-test6, flags doit aussi inclure CLONE_VM si CLONE_SIGHAND est spécifié
- CLONE_STOPPED (depuis Linux 2.6.0-test2)
- 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-test8)
- 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 de clone(), 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 l'appelant de 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 a appelé clone() pour le créer ne reçoit pas le signal SIGCHLD (ou une 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, flags doit aussi inclure CLONE_SIGHAND si CLONE_THREAD est spécifié (et remarquez que depuis Linux 2.6.0-test6, CLONE_SIGHAND a également besoin de CLONE_VM).
- Des signaux peuvent être envoyés à un groupe de threads entier (c'est-à-dire un TGID) en utilisant kill(2), ou bien à un thread spécifique (donc un TID) en utilisant tgkill(2).
- 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, défini par sigprocmask(2), mais des signaux peuvent être en attente : soit pour tout le processus (à savoir envoyable à n'importe quel membre du groupe de threads), lorsqu'il est envoyé avec kill(2) ; soit envoyable pour un thread individuel, s'il est envoyé avec tgkill(2). Un appel à sigpending(2) renvoie un ensemble de signaux qui est l'union de tous les signaux en attente pour tous le processus et les signaux en attente pour le thread appelant.
- Si kill(2) est utilisé pour adresser un signal à un groupe de threads et si le groupe de threads a installé un gestionnaire pour ce signal, alors le gestionnaire sera exécuté dans exactement 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).
NOTES¶
Remarquez que la fonction enveloppe clone() de la glibc effectue des changements dans la mémoire vers laquelle pointe child_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.
Différences entre bibliothèque C et noyau¶
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.
Une autre différence de l'appel système brut est que clone() accepte NULL en paramètre de child_stack. 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 *child_stack, int *ptid, int *ctid, unsigned long newtls);
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 *child_stack, int *ptid, unsigned long newtls, int *ctid);
Sur les architectures cris et s390, l'ordre des deux premiers paramètres est inversé :
long clone(void *child_stack, unsigned long flags, int *ptid, int *ctid, unsigned long newtls);
Sur l'architecture microblaze, il existe un paramètre supplémentaire :
long clone(unsigned long flags, void *child_stack, int stack_size, /* Taille de la pile */ int *ptid, int *ctid, unsigned long newtls);
blackfin, m68k, et sparc¶
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).
ia64¶
Sur ia64, une interface différente est utilisée :
int __clone2(int (*fn)(void *), void *child_stack_base, size_t stack_size, int flags, void *arg, ... /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
Le prototype ci-dessus vaut pour la fonction enveloppe de la glibc ; l'appel système brut n'a pas de paramètres fn ou arg et modifie l'ordre des paramètres pour que flags soit le premier paramètre et tls le dernier.
__clone2() fonctionne comme clone(), sauf que child_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 child_stack_base.
Linux 2.4 et antérieurs¶
Sous Linux 2.4 et plus anciens, clone() ne prend pas les paramètres ptid, tls et ctid.
VALEUR RENVOYÉE¶
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 contiendra le code d'erreur.
ERREURS¶
- EAGAIN
- Trop de processus en cours d'exécution. Consultez fork(2).
- EINVAL
- CLONE_SIGHAND a été spécifié mais pas CLONE_VM (depuis Linux 2.6.0-test6).
- EINVAL
- CLONE_THREAD a été spécifié mais pas CLONE_SIGHAND (depuis Linux 2.5.35).
- EINVAL
- Tant CLONE_FS que CLONE_NEWNS ont été indiqués dans 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 voire les deux, et CLONE_THREAD ou CLONE_PARENT voire les deux ont été indiqués dans flags.
- EINVAL
- Renvoyée par la fonction enveloppe clone() de la glibc quand fn ou child_stack valent NULL.
- EINVAL
- CLONE_NEWIPC a été spécifié dans 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 flags, mais le noyau n'a pas été configuré avec l'option CONFIG_NET_NS.
- EINVAL
- CLONE_NEWPID a été spécifié dans le flags, mais le noyau n'a pas été configuré avec l'option CONFIG_PID_NS.
- EINVAL
- CLONE_NEWUTS a été spécifié dans flags, mais le noyau n'a pas été configuré avec l'option CONFIG_UTS_NS.
- EINVAL
- child_stack n'est pas alignée sur une limite adaptée à cette architecture. Par exemple, sur aarch64, child_stack doit être un multiple de 16.
- 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 les attributs 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 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 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).
- 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 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 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).
- 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 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.
CONFORMITɶ
clone() est spécifique à Linux et ne devrait pas être employé dans des programmes destinés à être portables.
NOTES¶
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 noyaux 2.4.x, CLONE_THREAD ne fait en général pas du processus parent du nouveau thread un processus identique au parent du processus appelant. Cependant, pour les versions 2.4.7 à 2.4.18 du noyau, l'attribut CLONE_THREAD impliquait CLONE_PARENT (de même qu'avec les noyaux 2.6.0 et supérieurs).
Longtemps, il y avait un CLONE_DETACHED (introduite dans 2.5.32) : le parent ne veut pas de signal de fin de l'enfant. Dans Linux 2.6.2, la nécessité de fournir cet attribut avec CLONE_THREAD a disparu. Cet attribut est toujours défini mais n'a aucun effet.
Sur i386, clone() ne devrait pas être appelé à l’aide de vsyscall, mais directement en utilisant int $0x80.
BOGUES¶
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.
EXEMPLE¶
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).
Source du programme¶
#define _GNU_SOURCE #include <sys/wait.h> #include <sys/utsname.h> #include <sched.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0) 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 du
processus enfant */
if (sethostname(arg, strlen(arg)) == -1)
errExit("sethostname");
/* Récupérer et afficher le nom d'hôte */
if (uname(&uts) == -1)
errExit("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 pile pour le processus enfant */
stack = malloc(STACK_SIZE);
if (stack == NULL)
errExit("malloc");
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)
errExit("clone");
printf("clone() a renvoyé %ld\n", (long) 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)
errExit("uname");
printf("uts.nodename dans le parent : %s\n", uts.nodename);
if (waitpid(pid, NULL, 0) == -1) /* Attendre le processus enfant */
errExit("waitpid");
printf("Fin du processus enfant\n");
exit(EXIT_SUCCESS); }
VOIR AUSSI¶
fork(2), futex(2), getpid(2), gettid(2), kcmp(2), set_thread_area(2), set_tid_address(2), setns(2), tkill(2), unshare(2), wait(2), capabilities(7), namespaces(7), pthreads(7)
COLOPHON¶
Cette page fait partie de la publication 4.16 du projet man-pages Linux. Une description du projet et des instructions pour signaler des anomalies et la dernière version de cette page peuvent être trouvées à l'adresse https://www.kernel.org/doc/man-pages/.
TRADUCTION¶
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.
15 septembre 2017 | Linux |