Scroll to navigation

clone(2) System Calls Manual clone(2)

BEZEICHNUNG

clone, __clone2, clone3 - erzeugt einen Kindprozess

BIBLIOTHEK

Standard-C-Bibliothek (libc, -lc)

ÜBERSICHT

/* Prototyp für die Glibc-Wrapper-Funktion */
#define _GNU_SOURCE
#include <sched.h>
int clone(int (*fn)(void *_Nullable), void *Stapel, int Schalter,
          void *_Nullable Arg, ...  /* pid_t *_Nullable Eltern_tid,
                                       void *_Nullable tls,
                                       pid_t *_Nullable Kind_tid */ );
/* Für den Prototyp des rohen clone()-Systemaufrufs siehe ANMERKUNGEN */
#include <linux/sched.h>    /* Definition von struct clone_args */
#include <sched.h>          /* Definition der CLONE_*-Konstanten */
#include <sys/syscall.h>    /* Definition der SYS_*-Konstanten */
#include <unistd.h>
long syscall(SYS_clone3, struct clone_args *cl_args, size_t groesse);

Hinweis: Glibc stellt keinen Wrapper für clone3() bereit; rufen Sie ihn mittels syscall(2) auf.

BESCHREIBUNG

Diese Systemaufrufe erzeugen auf eine ähnliche Weise wie fork(2) einen neuen Prozess (»Kind«).

Im Gegensatz zu fork(2) bieten diese Systemaufrufe eine genauere Kontrolle darüber, welche Teile des Ausführungskontextes vom aufrufenden und vom Kindprozess gemeinsam benutzt werden. Beispielsweise kann der Aufrufende mittels dieser Systemaufrufe steuern, ob die zwei Prozesse den virtuellen Adressraum, die Tabelle der Dateideskriptoren und die Tabelle der Signal-Handler gemeinsam benutzen. Diese Systemaufrufe ermöglichen es auch, den neuen Kindprozess in einen separaten Namensraum (siehe namespaces(7)) abzulegen.

Beachten Sie, dass in dieser Handbuchseite der »aufrufende Prozess« normalerweise der »Elternprozess« ist. Siehe aber auch die nachfolgende Beschreibung von CLONE_PARENT und CLONE_THREAD.

Diese Seite beschreibt die folgenden Schnittstellen:

Die clone()-Wrapper-Funktion von Glibc als auch den darunterliegenden Systemaufruf, auf dem sie basiert. Der Haupttext erklärt die Wrapper-Funktion. Die Unterschiede zum rohen Systemaufruf werden gegen Ende dieser Seite erläutert.
Der neuere Systemaufruf clone3().

Im Rest der Seite wird die Terminologie »der Clone-Aufruf« verwandt, wenn Details erklärt werden, die auf alle diese Schnittstellen zutreffen.

Die clone()-Wrapper-Funktion

Wird mit der clone()-Wrapper-Funktion ein Kindprozess erzeugt, beginnt es die Ausführung durch Aufruf der Funktion, auf die das Argument fn zeigt. (Dies ist ein Unterschied zu fork(2), wo die Ausführung im Kindprozess vom Punkt des fork(2)-Aufrufs fortfährt.) Das Argument arg wird als Argument der Funktion fn übergeben.

Kehrt die Funktion fn(arg) zurück, so beendet sich der Kindprozess. Der Ganzzahlwert, der von fn zurückgeliefert wird, entspricht dem Exit-Status des Kindprozesses. Der Kindprozess kann auch durch den expliziten Aufruf von exit(2) oder durch den Empfang eines fatalen Signals beendet werden.

Das Argument Stapel bestimmt den Ort des Stapelspeichers, der vom Kindprozess verwendet wird. Da der aufrufende und der Kindprozess sich Speicherbereiche teilen können, kann der Kindprozess nicht auf dem selben Stapelspeicher wie der aufrufende Prozess laufen. Der aufrufende Prozess muss daher einen Speicherbereich als Stapelspeicher für den Kindprozess bereithalten und per clone einen Zeiger darauf an den Kindprozess übergeben. Der Stapelspeicher wächst (mit Ausnahme der PA-Prozessoren von HP) auf allen von Linux unterstützten Prozessoren nach unten, so dass Stapel für gewöhnlich auf die oberste Adresse im bereitgehaltenen Speicherbereich zeigt. Beachten Sie, dass clone() keine Möglichkeit bereitstellt, mit der der Aufrufende den Kernel über die Größe des Stapel-Bereichs informieren könnte.

Die verbliebenen Argumente für clone() werden unten behandelt.

clone3()

Der Systemaufruf clone3() stellt eine Obermenge der Funktionalität der älteren Schnittstelle clone() bereit. Er stellt auch eine Reihe von API-Verbesserungen bereit, einschließlich: Platz für zusätzliche Schalter-Bits; deutlichere Trennung beim Einsatz der verschiedenen Argumente, die Möglichkeit, die Größe des Stapel-Bereichs des Kindprozesses festzulegen.

Wie bei fork(2) kehrt clone3() sowohl im Eltern- als auch im Kindprozess zurück. Er liefert 0 im Kindprozess und die PID des Kindprozesses im Elternprozess zurück.

Das Argument cl_args von clone3() ist eine Struktur der folgenden Form:


struct clone_args {

u64 flags; /* Schalter-Bit-Maske */
u64 pidfd; /* Wo der PID-Dateideskriptor gespeichert
werden soll (int *) */
u64 child_tid; /* Wo die Kind-TID gespeichert werden soll,
im Speicher des Kindes (pid_t *) */
u64 parent_tid; /* Wo die Kind-TID gespeichert werden soll,
im Speicher des Elternprozesses (pid_t *) */
u64 exit_signal; /* Beim Beenden des Kindprozesses an den Elternprozess
zu sendendes Signal */
u64 stack; /* Zeiger auf das niedrigste Byte des Stapels */
u64 stack_size; /* Größe des Stapels */
u64 tls; /* Ort eines neuen TLS */
u64 set_tid; /* Zeiger auf ein pid_t-Feld
(seit Linux 5.5) */
u64 set_tid_size; /* Anzahl von Elementen in set_tid
(seit Linux 5.5) */
u64 cgroup; /* Dateideskriptor für Ziel-Cgroup
eines Kindes (seit Linux 5.7) */ };

Das an clone3() übergebene Argument groesse sollte auf die Größe dieser Struktur initialisiert werden. (Die Existenz des Arguments groesse ermöglicht zukünftige Erweiterungen der clone_args-Struktur.)

Der Stapel für den Kindprozess wird in cl_args.stack, der auf das niedrigste Byte des Stapel-Bereichs zeigt, und cl_args.stack_size, der die Größe des Stapel-Bereichs in Byte festlegt, angegeben. Falls der Schalter CLONE_VM (siehe unten) angegeben ist, muss ein Stapel explizit reserviert und festgelegt werden. Andernfalls können diese Felder als NULL und 0 angegeben werden, wodurch der Kindprozess den gleichen Stapel-Bereich wie der Elternprozess verwendet (im eigenen virtuellen Adressraum des Kindprozesses).

Die verbliebenen Felder im Argument cl_args werden unten behandelt.

Äquivalenz zwischen den Argumenten von clone() und clone3()

Anders als die ältere clone()-Schnittstelle, bei der die Argumente individuell übergeben werden, werden die Argumente bei der neueren clone3()-Schnittstelle in die oben gezeigte Struktur clone_args gepackt. Diese Struktur erlaubt es, dass eine Obermenge an Informationen über die clone()-Argumente übergeben wird.

Die folgende Tabelle zeigt die Äquivalenz zwischen den Argumenten von clone() und den Feldern in den an clone3() übergebenen clone_args:

clone() clone3() Hinweise
Feld cl_args
Schalter & ~0xff flags Für die meisten Schalter; Details unten
parent_tid pidfd Siehe CLONE_PIDFD
child_tid child_tid Siehe CLONE_CHILD_SETTID
parent_tid parent_tid Siehe CLONE_PARENT_SETTID
Schalter & 0xff exit_signal
Stapel Stapel
--- stack_size
tls tls Siehe CLONE_SETTLS
--- set_tid Siehe weiter unten für Details.
--- set_tid_size
--- cgroup Siehe CLONE_INTO_CGROUP

Das Kind-Beendigungssignal

Wenn sich der Kindprozess beendet, kann ein Signal an den Elternprozess gesandt werden. Das Beendigungssignal wird in den niedrigen Bytes von Schalter (clone()) oder in cl_args.exit_signal (clone3()) angegeben. Falls dieses Signal als etwas anderes als SIGCHLD angegeben wurde, dann muss der Elternprozess die Optionen __WALL oder __WCLONE angeben, wenn er mit wait(2) auf den Kindprozess wartet. Falls kein Signal (d.h. Null) angegeben wurde, wird dem Elternprozess nicht signalisiert, wenn der Kindprozess endet.

Das Feld set_tid

Standardmäßig wählt der Kernel die nächste sequenzielle PID für den neuen Prozess in jedem der PID-Namensräume, in denen er vorhanden ist. Beim Erstellen eines Prozesses mit clone3() kann das (seit Linux 5.5 verfügbare) Feld set_tid zur Auswahl bestimmter PIDs für den Prozess in einem oder allen der PID-Namensräume, in denen er vorhanden ist, eingesetzt werden. Falls die PID des neu erstellten Prozesses nur im aktuellen PID-Namensraum oder in dem neu erstellten PID-Namensraum (falls Schalter CLONE_NEWPID enthält) gesetzt werden soll, dann muss das erste Element in dem Feld set_tid auf die gewünschte PID gesetzt werden und set_tid_size muss 1 sein.

Falls die PID des neu erstellten Prozesses einen bestimmten Wert in mehreren PID-Namensräumen haben soll, dann kann dass Feld set_tid über mehrere Einträge verfügen. Der erste Eintrag definiert die PID im am tiefsten verschachtelten PID-Namensraum und jeder der nachfolgenden Einträge enthält die PID in dem entsprechenden Vorfahren-PID-Namensraum. Die Anzahl der PID-Namensräume in denen eine PID gesetzt werden soll, wird mit set_tid_size gesetzt; dieser Wert kann nicht größer als die Anzahl der derzeit verschachtelten PID-Namensräume sein.

Um einen Prozess zu erzeugen, der die nachfolgenden PIDs in einem PID-Namensraum hat:

PID-NS-Stufe Angeforderte PID Hinweise
0 31496 Äußerster PID-Namensraum
1 42
2 7 Innerster PID-Namensraum

Setzen Sie das Feld auf:


set_tid[0] = 7;
set_tid[1] = 42;
set_tid[2] = 31496;
set_tid_size = 3;

Falls nur die PIDs in den zwei innersten PID-Namensräumen festgelegt werden müssen, setzen Sie das Feld auf:


set_tid[0] = 7;
set_tid[1] = 42;
set_tid_size = 2;

Die PID in den PID-Namensräumen außerhalb der zwei innersten PID-Namensräume ist genauso wie jede andere PID ausgewählt.

Die Funktionalität set_tid benötigt CAP_SYS_ADMIN oder (seit Linux 5.9) CAP_CHECKPOINT_RESTORE in allen Benutzernamensräumen, die dem des Ziel-PID-Namensraumes gehören.

Aufrufende dürfen in einem gegebenen PID-Namensraum nur eine PID größer als 1 auswählen, falls ein init-Prozess (d.h. ein Prozess mit der PID 1) in diesem Namensraum bereits existiert. Andernfalls muss der PID-Eintrag für diesen PID-Namensraum 1 sein.

Die Schaltermaske

Sowohl clone() als auch clone3() erlauben eine Schalter-Bit-Maske, die das Verhalten verändert und dem Aufrufenden festzulegen erlaubt, was von dem aufrufenden Prozess und dem Kindprozess gemeinsam benutzt wird. Diese Bitmaske—das Argument Schalter von clone() oder das an clone3() übergebene Feld cl_args.flags—wird im Rest dieser Handbuchseite als die Schalter-Maske bezeichnet.

Die Schalter-Maske wird als bitweises ODER von Null oder mehreren der oben aufgeführten Konstanten angegeben. Falls nicht unten anders angegeben, sind diese Schalter sowohl in clone() als auch clone3() verfügbar (und haben die gleiche Wirkung).

Die Kind-Thread-Kennung an der durch Kind_tid gezeigten Stelle (clone()) oder cl_args.child_tid (clone3()) im Kindspeicher bereinigen (nullen), wenn das Kind sich beendet und beim Futex (»fast userspace mutual exclusion«/schneller gegenseitiger Ausschluss im Userspace) an dieser Adresse aufwachen lassen. Die betroffene Adresse könnte durch den Systemaufruf set_tid_address(2) geändert werden. Dies wird von Threading-Bibliotheken benutzt.
Speichert die Kind-Thread-Kennung an der Stelle, auf die Kind_tid (clone()) oder cl_args.child_tid (clone3()) zeigt, im Kindspeicher. Die Speicheraktion wird abgeschlossen, bevor der Clone-Aufruf die Steuerung an den Benutzerraum im Kindprozess zurückgibt. (Beachten Sie, dass die Speicheraktion noch nicht abgeschlossen sein könnte, bevor der Clone-Aufruf den Elternprozess zurückliefert, was relevant ist, wenn auch der Schalter CLONE_VM eingesetzt wird.)
Standardmäßig sind die Signal-Zuordnungen im Kind-Thread identisch zu denen im Eltern-Prozess. Falls dieser Schalter angegeben ist, dann werden alle Signale, die im Eltern-Prozess gehandhabt werden, im Kind-Thread auf ihre Standardzuordnung (SIG_DFL) zurückgesetzt.
Es ergibt keinen Sinn, diesen Schalter zusammen mit CLONE_SIGHAND anzugeben; daher ist diese Kombination nicht erlaubt.
Eine Zeit lang (während der Linux-2.5-Entwicklungsserie) gab es einen Schalter CLONE_DETACHED, der dazu führte, dass der Elternprozess kein Signal empfing, wenn sich das Kind beendete. Schließlich wurde die Auswirkung dieses Schalters in dem Schalter CLONE_THREAD mit aufgenommen und zum Zeitpunkt der Veröffentlichung von Linux 2.6.0 hatte dieser Schalter keine Auswirkung. Beginnend mit Linux 2.6.2 verschwand die Notwendigkeit, diesen Schalter mit CLONE_THREAD zusammen anzugeben.
Dieser Schalter ist noch definiert, wird aber beim Aufruf von clone() normalerweise ignoriert. Siehe allerdings die Beschreibung von CLONE_PIDFD für einige Ausnahmen.
Ist CLONE_FILES gesetzt, teilen sich der aufrufende und der Kindprozess ihre Dateideskriptor-Tabellen. Jeder Dateideskriptor, der im aufrufenden Prozess oder vom Kindprozess erzeugt wird, ist auch im anderen Prozess gültig. Ebenso wirkt sich das Schließen eines Dateideskriptors oder das Ändern der zugehörigen Schalter (benutzen der F_SETFD-Operation von fcntl(2)) auf den anderen Prozess aus. Falls sich ein Prozess eine Dateideskriptor-Tabelle teilt und execve(2) aufruft, wird seine Dateideskriptor-Tabelle dupliziert (nicht länger geteilt).
Ist CLONE_FILES nicht gesetzt, erbt der Kindprozess zur Ausführungszeit von Clone eine Kopie der aktuell geöffneten Dateideskriptoren. Anschließende Aktionen, die Dateideskriptoren öffnen oder schließen bzw. deren Schalter ändern, werden entweder vom aufrufenden Prozess oder dem Kindprozess durchgeführt und betreffen nicht den jeweils anderen Prozess. Beachten Sie aber, dass sich die duplizierten Dateideskriptoren im Kind auf die gleiche offene Dateideskription wie der korrespondierende Dateideskriptor im aufrufenden Prozess bezieht und sich daher den Dateiversatz und die Dateistatusschalter mit diesem teilt (siehe open(2)).
Ist CLONE_FS gesetzt, teilen sich aufrufender Prozess und Kindprozess ihre Informationen über das Dateisystem. Dazu zählen der Ort des Wurzelverzeichnisses, das aktuelle Arbeitsverzeichnis und die Maske der Dateizugriffsrechte (umask). Jeder Aufruf von chroot(2), chdir(2) oder umask(2), entweder durch den aufrufenden Prozess oder den Kindprozess, beeinflusst auch den jeweils anderen Prozess.
Ist CLONE_FS nicht gesetzt, arbeitet der Kindprozess mit einer Kopie der Dateisysteminformationen des aufrufenden Prozesses zur Zeit des Clone-Aufrufs. Spätere Aufrufe von chroot(2), chdir(2) oder umask(2) beeinflussen den anderen Prozess nicht.
Standardmäßig wird ein Kindprozess in die gleiche Version-2-Cgroup wie sein Elternprozess abgelegt. Der Schalter CLONE_INTO_CGROUP ermöglicht es, den Kindprozess in einer anderen Version-2-Cgroup zu erstellen. (Beachten Sie, dass CLONE_INTO_CGROUP nur für Version-2-Cgroups wirksam wird.)
Um den Kindprozess in eine andere Cgroup abzulegen, legt der Aufrufende CLONE_INTO_CGROUP in cl_args.flags fest und übergibt im Feld cl_args.cgroup einen Dateideskriptor, der sich auf eine Version-2-Cgroup bezieht. (Dieser Dateideskriptor kann erhalten werden, indem ein Cgroup-v2-Verzeichnis mittels des Schalters O_RDONLY oder O_PATH geöffnet wird.) Beachten Sie, dass sämtliche übliche Einschränkungen (beschrieben in cgroups(7)) über das Ablegen eines Prozesses in einer Version-2-Cgroup gültig bleiben.
Folgende Anwendungsfälle für CLONE_INTO_CGROUP sind unter anderen möglich:
Das Erzeugen eines Prozesses in einer Cgroup, die sich von der des Elternprozesses unterscheidet, ermöglicht es einem Diensteverwalter, neue Dienste direkt in dedizierte Cgroups zu erzeugen. Dies beseitigt das Flackern bei der Buchführung, das erzeugt würde, falls der Kindprozess erst in der gleichen Cgroup wie der Elternprozess erzeugt und dann in die Ziel-Cgroup verschoben würde. Desweiteren ist die Erzeugung des Kindprozesses direkt in der Ziel-Cgroup deutlich billiger als das Verschieben des Kindprozesses in die Ziel-Cgroup, nachdem er erstellt wurde.
Der Schalter CLONE_INTO_CGROUP erlaubt auch die Erstellung eingefrorener Kindprozesse durch Erzeugung dieser in einer eingefrorenen Cgroup. (Siehe cgroups(7) für eine Beschreibung des Einfrier-Controllers.)
Für Anwendungen mit Threads (oder sogar Thread-Implementierungen, die Cgroups verwenden, um einzelne Threads zu begrenzen) ist es möglich, ein festes Cgroup-Layout zu errichten, bevor jeder Thread direkt in seine Ziel-Cgroup erzeugt wird.
Ist CLONE_FS gesetzt, teilt sich der neue Prozess einen E/A-Kontext mit dem aufrufenden Prozess. Falls dieser Schalter nicht gesetzt ist (wie bei fork(2)), hat der neue Prozess seinen eigenen E/A-Kontext.
Der E/A-Kontext entspricht dem E/A-Gültigkeitsbereich des Platten-Schedulers, d.h. welches der E/A-Scheduler zur Modellplanung für E/As des Prozesses benutzt. Falls sich Prozesse den gleichen E/A-Kontext teilen, werden sie vom E/A-Scheduler als ein einziger betrachtet. Als Konsequenz daraus müssen sie sich die gleiche Plattenzeitzugriffzeit teilen. Einige E/A-Scheduler ermöglichen zwei Prozessen, die einen E/A-Kontext teilen, ihren Plattenzugriff zu verzahnen. Falls mehrere Prozesse E/A im Auftrag des gleichen Prozesses durchführen (aio_read(3) zum Beispiel), sollten sie für eine bessere E/A-Leistung CLONE_IO verwenden.
Falls der Kernel nicht mit der Option CONFIG_BLOCK konfiguriert wurde, bewirkt dieser Schalter nichts.
Der Prozess wird in einem neuen cgroup-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist, dann wird der Prozess (wie mit fork(2)) im gleichen cgroup-Namensraum wie der aufrufende Prozess erstellt.
Weitere Informationen über cgroup-Namensräume finden Sie unter cgroup_namespaces(7).
Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann CLONE_NEWCGROUP angeben.
Wenn CLONE_NEWIPC gesetzt ist, dann wird der Prozess in einem neuen IPC-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist, dann wird der Prozess (wie mit fork(2)) im gleichen IPC-Namensraum wie der aufrufende Prozess erstellt.
Weitere Informationen zu IPC-Namensräumen finden Sie in ipc_namespaces(7).
Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann CLONE_NEWIPC angeben. Dieser Schalter darf nicht zusammen mit CLONE_SYSVSEM angegeben werden.
(Die Implementierung dieses Schalters wurde erst ungefähr mit der Linux-Version 2.6.29 abgeschlossen.)
Wenn CLONE_NEWNET gesetzt ist, dann wird der Prozess in einem neuen Netzwerk-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist, dann wird der Prozess (wie mit fork(2)) im gleichen Netzwerk-Namensraum wie der aufrufende Prozess erstellt.
Weitere Informationen zu Netzwerk-Namensräumen finden Sie in network_namespaces(7).
Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann CLONE_NEWNET angeben.
Wenn der Schalter CLONE_NEWNS gesetzt ist, wird der geklonte Kindprozess in einem neuen, eingehängten Namensraum gestartet, der mit einer Kopie des Namensraums des Elternprozesses initialisiert wurde. Wenn CLONE_NEWNS nicht gesetzt ist, bleibt der Kindprozess im gleichen Namensraum wie der Elternprozess.
Für weitere Informationen über Einhängenamensräume lesen Sie namespaces(7) und mount_namespaces(7)
Nur ein privilegierter Prozess (einer der die Fähigkeit CAP_SYS_ADMIN hat) kann den Schalter CLONE_NEWNS angeben. Es ist nicht erlaubt, sowohl CLONE_NEWNS als auch CLONE_FS im gleichen Aufruf von Clone anzugeben.
Wenn CLONE_NEWPID gesetzt ist, dann wird der Prozess in einem neuen PID-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist, dann wird der Prozess (wie mit fork(2)) im gleichen PID-Namensraum wie der aufrufende Prozess erstellt.
Weitere Informationen zu PID-Namensräumen finden Sie in namespaces(7) und pid_namespaces(7).
Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann CLONE_NEWPID angeben. Dieser Schalter darf nicht zusammen mit CLONE_THREAD oder CLONE_PARENT angegeben werden.
(Dieser Schalter hatte für clone() erstmals in Linux 2.6.23 eine Bedeutung, die aktuelle clone()-Semantik wurde in Linux 3.5 aufgenommen und die letzten Anteile, um Benutzernamensräume komplett nutzbar zu bekommen, wurden in Linux 3.8 aufgenommen.)
Wenn CLONE_NEWUSER gesetzt ist, dann wird der Prozess in einem neuen Benutzer-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist, dann wird der Prozess (wie mit fork(2)) im gleichen Benutzer-Namensraum wie der aufrufende Prozess erstellt.
Für weitere Informationen über Benutzernamensräume lesen Sie namespaces(7) und user_namespaces(7).
Vor Linux 3.8 verlangte die Verwendung von CLONE_NEWUSER, dass der Aufrufende drei Capabilities hatte: CAP_SYS_ADMIN, CAP_SETUID und CAP_SETGID. Seit Linux 3.8 werden für die Erstellung eines Benutzernamensraums keine Privilegien benötigt.
Dieser Schalter kann nicht zusammen mit CLONE_THREAD oder CLONE_PARENT angegeben werden. Aus Sicherheitsgründen darf CLONE_NEWUSER nicht zusammen mit CLONE_FS angegeben werden.
Falls CLONE_NEWUTS gesetzt ist, erzeugt der Prozess einen neuen UTS-Namensraum, dessen Bezeichner durch Duplizieren der Bezeichner aus dem UTS-Namensraum des aufrufenden Prozesses initialisiert werden. Wenn dieser Schalter nicht gesetzt ist (wie mit fork(2)), dann wird der Prozess im gleichen UTS-Namensraum wie der aufrufende Prozess erzeugt.
Weitere Informationen zu UTS-Namensräumen finden Sie in uts_namespaces(7).
Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann CLONE_NEWUTS angeben.
Falls CLONE_PARENT gesetzt ist, dann wird der Elternprozess des neuen Kindprozesses (wie er von getppid(2) zurückgegeben wird) der gleiche wie der aufrufende Prozess sein.
Falls CLONE_PARENT nicht gesetzt ist (wie bei fork(2)), dann ist der Elternprozess des Kindprozesses der aufrufende Prozess.
Beachten Sie, dass dem Elternprozess, wie er von getppid(2) zurückgegeben wird, signalisiert wird wenn der Kindprozess endet. Wenn also CLONE_PARENT gesetzt ist, wird dem Elternprozess des aufrufenden Prozesses anstatt dem aufrufenden Prozess selbst das Signal gesandt.
Der Schalter CLONE_PARENT kann in Clone-Aufrufen durch den globalen Init-Prozess (PID 1 im anfänglichen PID-Namensraum) und in Init-Prozessen in anderen PID-Namensräumen nicht verwandt werden. Diese Einschränkung verhindert die Erstellung von Prozessbäumen mit mehreren Wurzeln sowie die Erstellung von nicht zerstörbaren Zombies im anfänglichen PID-Namensraum.
Die Kindprozess-Thread-Kennung an der Stelle im Elternspeicher ablegen, auf die Eltern_tid (clone()) oder cl_args.parent_tid (clone3()) zeigt. (In Linux 2.5.32-2.5.48 gab es einen Schalter CLONE_SETTID, der das tat.) Die Speicheraktion wird abgeschlossen, bevor der Clone-Aufruf die Steuerung an den Benutzerraum zurückgibt.
Falls CLONE_PID gesetzt ist, wird der Kindprozess mit der gleichen Prozesskennung wie der aufrufende Prozess erstellt. Dies ist gut, um das System zu hacken, aber andererseits zu nicht viel mehr zu gebrauchen. Seit Linux 2.3.21 konnte dieser Schalter nur durch den Boot-Prozess festgelegt werden (PID 0). Dieser Schalter verschwand in Linux 2.5.16 komplett aus den Kernelquellen. In der Folge ignorierte der Kernel dieses Bit, falls es in der Schalter-Maske angegeben wurde. Viel später wurde das Bit für die Verwendung als Schalter CLONE_PIDFD recyclet.
Falls dieser Schalter angegeben ist, wird ein PID-Dateideskriptor, der sich auf einen Kindprozess bezieht, reserviert und an dem angegebenen Ort im Speicher des Elternprozesses abgelegt. Der Schalter »close-on-exec« wird bei diesem neuen Dateideskriptor gesetzt. PID-Dateideskriptoren können für die in pidfd_open(2) beschriebenen Zwecke verwandt werden.
Bei der Verwendung von clone3() wird der PID-Dateideskriptor an dem durch cl_args.pidfd angezeigten Ort abgelegt.
Bei der Verwendung von clone() wird der PID-Dateideskriptor an dem Ort abgelegt, auf den Eltern_tid zeigt. Da das Argument Eltern_tid zur Rückgabe des PID-Dateideskriptors verwandt wird, kann CLONE_PIDFD beim Aufruf von clone() nicht mit CLONE_PARENT_SETTID benutzt werden.
Es ist derzeit nicht möglich, diesen Schalter zusammen mit CLONE_THREAD zu verwenden. Das bedeutet, dass ein durch den PID-Dateideskriptor identifizierter Prozess immer der Prozessgruppenleiter sein wird.
Falls der veraltete Schalter CLONE_DETACHED beim Aufruf von clone() zusammen mit CLONE_PIDFD angegeben wird, wird ein Fehler zurückgeliefert. Falls CLONE_DETACHED beim Aufruf von clone3() angegeben wird, wird auch ein Fehler zurückgeliefert. Dieses Fehlerverhalten stellt sicher, dass das CLONE_DETACHED entsprechende Bit für weitere PID-Dateideskriptorenfunktionalitäten in der Zukunft recyclet werden kann.
Falls CLONE_PTRACE angegeben ist und der aufrufende Prozess verfolgt wird, dann wird der Kindprozess ebenfalls verfolgt (siehe ptrace(2)).
Der TLS (Thread Local Storage)-Deskriptor ist auf tls gesetzt.
Die Interpretation von tls und der resultierende Effekt ist architekturabhängig. Auf X86 ist tls als ein struct user_desc * interpretiert (siehe set_thread_area(2)). Auf X86-64 ist es der neue für das Basisregister %fs zu setzende Wert (siehe das Argument ARCH_SET_FS von arch_prctl(2)). Auf Architekturen mit einem dedizierten TLS-Register ist es der neue Wert dieses Registers.
Der Einsatz dieses Schalters verlangt detaillierte Kenntnisse und sollte im Allgemeinen nicht erfolgen, außer in einigen Bibliotheken, die Threading implementieren.
Ist CLONE_SIGHAND gesetzt, teilen sich der aufrufende Prozess und der Kindprozess die Tabelle der Signal-Handler. Ruft einer der beiden Prozesse sigaction(2) auf, um das Antwortverhalten auf ein Signal zu verändern, so betrifft dies auch den anderen Prozess. Jedoch besitzen aufrufender Prozess und Kindprozess nach wie vor getrennte Signalmasken und getrennte Listen der noch ausstehenden Signale. Daher könnten Signale durch Aufruf von sigprocmask(2) für einen Prozess geblockt oder zugelassen werden ohne den anderen Prozess zu beeinflussen.
Ist CLONE_SIGHAND nicht gesetzt, erbt der Kindprozess zum Zeitpunkt des Clone-Aufrufs eine Kopie des Signal-Handlers vom aufrufenden Prozess. Spätere Aufrufe von sigaction(2) durch einen der Prozesse hat dann keine Auswirkung auf den anderen Prozess.
Seit Linux 2.6.0 muss die Schalter-Maske außerdem CLONE_VM enthalten, falls CLONE_SIGHAND angegeben wurde.
Falls CLONE_STOPPED gesetzt ist, ist der Kindprozess anfangs gestoppt (als ob ein SIGSTOP-Signal gesendet worden wäre) und muss durch Senden eines SIGCONT-Signals wieder aufgenommen werden.
Dieser Schalter war ab Linux 2.6.25 missbilligt und wurde in Linux 2.6.38 vollständig entfernt. Seitdem ignoriert der Kernel ihn ohne Fehler. Seit Linux 4.6 wird dasselbe Bit für den Schalter CLONE_NEWCGROUP wiederverwendet.
Wenn CLONE_SYSVSEM gesetzt ist, dann teilen sich der Kindprozess und der aufrufende Prozess eine einzige Liste von System-V-Semaphore-Anpassungswerten, (siehe semop(2)). In diesem Fall sammelt die gemeinsame Liste semadj Werte über alle Prozesse, die die Liste gemeinsam nutzen und Semaphore-Anpassungen werden nur durchgeführt, wenn der letzte Prozess, der die Liste gemeinsam nutzt, sich beendet (oder mittels unshare(2) aufhört, die Liste mitzunutzen). Falls dieser Schalter nicht gesetzt ist, besitzt der Kindprozess eine separate semadj-Liste, die anfangs leer ist.
Falls CLONE_THREAD gesetzt ist, wird der Kindprozess in die gleiche Thread-Gruppe wie der aufrufende Prozess platziert. Um den Rest der Diskussion von CLONE_THREAD leserlicher zu machen, wird der Begriff »Thread« benutzt, um Bezug auf Prozesse innerhalb einer Thread-Gruppe zu nehmen.
Thread-Gruppen waren ein Leistungsmerkmal, das in Linux 2.4 hinzugefügt wurde, um den POSIX-Thread-Gedanken von einer Thread-Zusammenstellung zu unterstützen, die sich eine einzelne PID teilt. Intern ist diese gemeinsame PID ein sogenannter Thread-Gruppen-Bezeichner (TGID) für die Thread-Gruppe. Seit Linux 2.4 geben Aufrufe von getpid(2) die TGID des Aufrufers zurück.
Die Threads innerhalb einer Gruppe können durch ihre (systemweit) einheitliche Thread-Kennung (TID) unterschieden werden. Die TID eines neuen Threads ist als Funktionsergebnis verfügbar, das an den Aufrufenden zurückgegeben wird. Ein Thread kann durch Benutzen von gettid(2) seine eigene TID erhalten.
Wenn Clone ohne Angabe von CLONE_THREAD aufgerufen wurde, dann wird der resultierende Thread in eine neue Thread-Gruppe platziert, deren TGID der TID des Threads entspricht. Dieser Thread ist der Führer der neuen Thread-Gruppe.
Ein neuer mit CLONE_THREAD erzeugter Thread hat den gleichen Elternprozess wie der, der Clone aufrufen hat (d.h. wie CLONE_PARENT), so dass Aufrufe von getppid(2) den gleichen Wert für alle Threads in der Thread-Gruppe zurückliefern. Wenn ein CLONE_THREAD-Thread endet, wird dem Thread, der ihn erstellt hat, weder ein SIGCHLD-Signal (oder ein anderes Ende-Signal) gesandt, noch kann der Status eines solchen Threads per wait(2) abgefragt werden. (Der Thread wird als losgelöst bezeichnet.)
Nachdem alle Threads in einer Thread-Gruppe beendet sind, wird dem Elternprozess ein SIGCHLD-Signal (oder ein anderes Ende-Signal) gesandt.
Falls einige der Threads in einer Thread-Gruppe ein execve(2) durchführen, dann werden alle Threads außer dem Thread-Führer beendet und das neue Programm wird im Thread-Gruppenführer ausgeführt.
Falls einer der Threads in einer Thread-Gruppe per fork(2) einen Kindprozess erzeugt, dann kann jeder Thread in der Gruppe wait(2) für diesen Kindprozess ausführen.
Seit Linux 2.5.35 muss die Schalter-Maske auch CLONE_SIGHAND enthalten, wenn CLONE_THREAD angegeben wurde. Beachten Sie auch, dass seit Linux 2.6.0 CLONE_SIGHAND auch CLONE_VM enthalten muss.
Signalzuordnungen und -aktionen sind prozessweit: Falls ein nicht abgefangenes Signal an den Thread geschickt wird, dann wird es alle Mitglieder in der Thread-Gruppe beeinflussen (beenden, stoppen, fortfahren, darin ignoriert werden).
Jeder Thread hat seine eigene Signalmaske, wie von sigprocmask(2) gesetzt.
Ein Signal kann Prozess-orientiert oder Thread-orientiert sein. Ein Prozess-orientiertes Signal kann auf eine Thread-Gruppe (d.h. einer TGID) abzielen und wird an einen beliebig ausgewählten Thread innerhalb dieser, der das Signal nicht blockiert, ausgeliefert. Ein Signal kann Prozess-orientiert sein, da es vom Kernel aus anderen Gründen (neben Hardware-Ausnahmebehandlungen) erstellt wurde oder da mittels kill(2) oder sigqueue(3) gesandt wurde. Ein Thread-orientiertes Signal zielt auf ein bestimmten Thread (d.h. wird an ihn ausgeliefert). Ein Signal kann Thread-orientiert sein, da es mittels tgkill(2) oder pthread_sigqueue(3) gesandt wurde oder da der Thread einen Maschinensprachenbefehl ausführte, der eine Hardware-Ausnahmebehandlung auslöste (z.B. löst ein ungültiger Speicherzugriff SIGSEGV oder eine Fließkommaausnahmebehandlung SIGFPE aus).
Ein Aufruf von sigpending(2) liefert eine Signalmenge zurück, die die Vereinigung der anhängigen Prozess-orientierten Signale und der Signale, die für den aufrufenden Thread anhängig sind, ist.
Falls ein Prozess-orientiertes Signal an eine Thread-Gruppe ausgeliefert wird und die Thread-Gruppe einen Handler für dieses Signal installiert hat, dann dann wird der Handler in exakt einem willkürlich ausgewählten Mitglied der Thread-Gruppe aufrufen, das das Signal nicht blockiert hat. Falls mehrere Threads in einer Gruppe darauf warten das gleiche Signal per sigwaitinfo(2) zu akzeptieren, wird der Kernel einen dieser Threads willkürlich auswählen, um das Signal zu empfangen.
Falls CLONE_UNTRACED angegeben ist, kann ein verfolgender Prozess kein CLONE_PTRACE auf diesem Kindprozess erzwingen.
Falls CLONE_VFORK gesetzt ist, wird die Ausführung des aufrufenden Prozesses aufgeschoben bis der Kindprozess seine virtuellen Speicherressourcen durch Aufrufen von execve(2) oder _exit(2) (wie bei vfork(2)) freigibt.
Falls CLONE_VFORK nicht gesetzt ist, dann werden sowohl der aufrufende Prozess, als auch der Kindprozess nach dem Aufruf planbar und eine Anwendung sollte sich nicht darauf verlassen, dass die Ausführung in einer speziellen Reihenfolge erfolgt.
Ist CLONE_VM gesetzt, laufen aufrufender Prozess und Kindprozess im selben Speicherbereich. Insbesondere sind Schreibzugriffe des aufrufenden Prozesses oder des Kindprozesses in den gemeinsamen Speicher auch vom anderen Prozess aus sichtbar. Zudem beeinflusst jede Veränderung der Speicher-Mappings mit mmap(2) oder munmap(2) durch den Kindprozess oder den aufrufenden Prozess auch den jeweils anderen Prozess.
Ist CLONE_VM nicht gesetzt, erhält der Kindprozess eine eigene Kopie des Speicherbereichs des aufrufenden Prozesses zum Zeitpunkt des Clone-Aufrufs. Führt ein Prozess Schreibzugriffe auf den Speicher oder Änderungen am Dateispeicher-Mapping aus, beeinflussen diese Operationen nicht den jeweils anderen, wie bei fork(2).
Falls der Schalter CLONE_VM angegeben und der Schalter CLONE_VFORK nicht angegeben ist, dann wird jeder alternative Stapel, der durch sigaltstack(2) etabliert wurde, im Kindprozess bereinigt.

RÜCKGABEWERT

Bei Erfolg wird im ausgeführten Thread des Aufrufenden die Thread-Kennung des Kindprozesses zurückgegeben. Im Fehlerfall wird im Kontext des Aufrufenden -1 zurückgegeben, kein Kindprozess erzeugt und errno gesetzt, um den Fehler anzuzeigen.

FEHLER

CLONE_INTO_CGROUP wurde in cl_args.flags angegeben, aber die (in cgroups(7) beschriebenen) Beschränkungen zum Ablegen des Kindprozesses in die Cgroup der Version 2, auf die sich cl_args.cgroup bezieht, werden nicht erfüllt.
Es laufen bereits zu viele Prozesse; siehe fork(2).
CLONE_INTO_CGROUP wurde in cl_args.flags angegeben, aber der in cl_args.cgroup angegebene Dateideskriptor bezieht sich auf eine Version-2-Cgroup, in der ein Domain-Controller aktiviert wurde.
Eine (oder mehrere) der in set_tid festgelegten PIDs existiert im entsprechenden PID-Namensraum bereits.
In der Schalter-Maske wurden sowohl CLONE_SIGHAND als auch CLONE_CLEAR_SIGHAND festgelegt.
CLONE_SIGHAND wurde in der Schalter-Maske festgelegt, aber nicht CLONE_VM. (Seit Linux 2.6.0.)
CLONE_THREAD wurde in der Schalter-Maske festgelegt, aber nicht CLONE_SIGHAND. (Seit Linux 2.5.35.)
CLONE_THREAD wurde in der Schalter-Maske festgelegt, aber der aktuelle Prozess hatte vorher unshare(2) mit dem Schalter CLONE_NEWPID aufgerufen oder setns(2) verwandt, um sich wieder einem PID-Namensraum zuzuordnen.
In der Schalter-Maske wurden sowohl CLONE_FS als auch CLONE_NEWNS festgelegt.
In der Schalter-Maske wurden sowohl CLONE_NEWUSER als auch CLONE_FS festgelegt.
In der Schalter-Maske wurden sowohl CLONE_NEWIPC als auch CLONE_SYSVSEM festgelegt.
Eines (oder beides) von CLONE_NEWPID oder CLONE_NEWUSER und eines (oder beides) von CLONE_THREAD oder CLONE_PARENT wurde in der Schalter-Maske festgelegt.
CLONE_PARENT wurde angegeben und der Aufrufende ist ein Init-Prozess.
Wird von der Glibc-Wrapper-Funktion clone() zurückgegeben, wenn ein Wert von NULL für fn oder Stapel festgelegt wurde.
CLONE_NEWIPC wurde in der Schalter-Maske festgelegt, aber der Kernel ist nicht mit den Optionen CONFIG_SYSVIPC und CONFIG_IPC_NS konfiguriert.
CLONE_NEWNET wurde in der Schalter-Maske festgelegt, aber der Kernel ist nicht mit der Option CONFIG_NET_NS konfiguriert.
CLONE_NEWPID wurde in der Schalter-Maske festgelegt, aber der Kernel ist nicht mit der Option CONFIG_PID_NS konfiguriert.
CLONE_NEWUSER wurde in der Schalter-Maske festgelegt, aber der Kernel ist nicht mit der Option CONFIG_USER_NS konfiguriert.
CLONE_NEWUTS wurde in der Schalter-Maske festgelegt, aber der Kernel ist nicht mit der Option CONFIG_UTS_NS konfiguriert.
Stapel ist nicht an einer geeigneten Grenze für diese Architektur ausgerichtet. Beispielsweise muss Stapel auf Aarch64 ein Vielfaches von 16 sein.
In der Schalter-Maske wurden CLONE_DETACHED festgelegt.
CLONE_PIDFD wurde zusammen mit CLONE_DETACHED in der Schalter-Maske festgelegt.
CLONE_PIDFD wurde zusammen mit CLONE_THREAD in der Schalter-Maske festgelegt.
CLONE_PIDFD wurde zusammen mit CLONE_PARENT_SETTID in der Schalter-Maske festgelegt.
set_tid_size ist größer als die Anzahl der geschachtelten PID-Namensräume.
Eine der in set_tid festgelegten PIDs war ungültig.
CLONE_THREAD oder CLONE_PARENT wurde in der Schalter-Maske festgelegt, aber in exit_signal wurde ein Signal angegeben.
Stapel war nicht an einer 128-Bit-Grenze ausgerichtet.
Es kann nicht ausreichend Speicher für eine Aufgabenstruktur des Kindprozesses reserviert werden oder um benötigte Teile vom Kontext des Aufrufenden zu kopieren.
CLONE_NEWPID wurde in der Schalter-Maske festgelegt, aber die Begrenzung der Verschachtelungstiefe von PID-Namensräumen würde überschritten; siehe pid_namespaces(7).
CLONE_NEWUSER wurde in der Schalter-Maske festgelegt und der Aufruf würde zu einer Überschreitung der Begrenzung für die Anzahl von verschachtelten Benutzernamensräumen führen. Siehe user_namespaces(7).
Von Linux 3.11 bis Linux 4.8 war der in diesem Fall diagnostizierte Fehler EUSERS.
Einer der Werte in der Schalter-Maske legte die Erstellung eines neuen Benutzer-Namensraums fest, dadurch würde aber die in der enstprechenden Datei in /proc/sys/user festgelegte Begrenzung überschritten. Für weitere Details siehe namespaces(7).
CLONE_INTO_CGROUP wurde in cl_args.flags angegeben, aber der in cl_args.cgroup angegebene Dateideskriptor bezieht sich auf eine Version-2-Cgroup, die im Zustand Domain ungültig ist.
CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID oder CLONE_NEWUTS wurde von einem nicht privilegierten Prozess festgelegt (Prozess ohne CAP_SYS_ADMIN).
CLONE_PID wurde von einem anderen Prozess als Prozess 0 festgelegt. (Dieser Fehler tritt nur unter Linux 2.5.15 und früheren Versionen auf.)
CLONE_NEWUSER wurde in der Schalter-Maske festgelegt, aber weder die effektive Benutzerkennung noch die effektive Gruppenkennung des Aufrufenden hat eine Abbildung in den Namensraum der Eltern (siehe user_namespaces(7)).
CLONE_NEWUSER wurde in der Schalter-Maske festgelegt und der Aufrufende ist in einer Chroot-Umgebung (d.h. das Wurzelverzeichnis des Aufrufenden passt nicht zum Wurzelverzeichnis des Einhängenamensraums, in dem er sich befindet).
set_tid_size war größer als Null und dem Aufrufenden fehlt in einem oder mehreren Benutzernamensräumen, dem die entsprechenden PID-Namensräume gehören, die Capability CAP_SYS_ADMIN.
Ein Systemaufruf wurde durch ein Signal unterbrochen und wird neu gestartet. (Dies wird nur während einer Verfolgung sichtbar sein.)
CLONE_NEWUSER wurde in der Schalter-Maske festgelegt und die Begrenzung für die Anzahl von verschachtelten Benutzernamensräumen würde überschritten. Siehe die Diskussion des Fehlers ENOSPC oben.

VERSIONEN

Die Glibc-Wrapperfunktion clone() nimmt einige Änderungen am Speicher vor, auf den Stapel zeigt (Änderungen, um den Stapel korrekt für das Kind einzurichten), bevor der Systemaufruf clone() ausgelöst wird. Verwenden Sie daher in Fällen, in denen clone() zur rekursiven Erstellung von Kindern verwandt wird, nicht den Puffer, der für den Stapel der Eltern eingesetzt wird, als Stapel der Kinder.

Auf i386-Architekturen sollte clone() nicht durch vsyscall aufgerufen werden, sondern direkt durch int $0x80.

Unterschiede C-Bibliothek/Kernel

Der rohe sys_clone-Systemaufruf entspricht eher fork(2), da er mit der Ausführung des Kindprozesses am Zeitpunkt des Aufrufs fortfährt. Von daher werden die Argumente fn und arg der clone()-Wrapper-Funktion weggelassen.

Im Gegensatz zum Glibc-Wrapper akzeptiert der rohe Systemaufruf clone() NULL als Stapel-Argument (und clone3() erlaubt entsprechend cl_args.stack NULL zu sein). In diesem Fall verwendet das Kind eine Dublette des Stapels des Elternprozesses. (»Copy-on-write«-Semantik stellt sicher, dass der Kindprozess getrennte Kopien des Stapelspeichers erhält, wenn einer der beiden Prozesse den Stapelspeicher verändert.) In diesem Fall sollte die Option CLONE_VM nicht angegeben werden, damit es korrekt funktioniert. (Falls das Kind sich aufgrund des Schalters CLONE_VM mit dem Elternprozess den Speicher teilt, dann tritt keine copy-on-write-Duplizierung auf und wahrscheinlich tritt Chaos ein.

Die Reihenfolge der Argumente unterscheidet sich auch im rohen Systemaufruf und es gibt über die Architekturen hinweg Variationen in den Argumenten, wie dies in den folgenden Absätzen dargestellt wird.

Die rohe Schnittstelle für Systemaufrufe auf x86-64 und einigen anderen Architekturen (darunter Sh, Tile und Alpha) sieht so aus:


long clone(unsigned long Schalter, void *Stapel,
           int *Eltern_tid, int *Kind_tid,
           unsigned long tls);

Auf x86-32 und mehreren anderen häufigen Architekturen (darunter Score, ARM, ARM 64, PA-RISC, Arc, Power PC, Xtensa und MIPS) ist die Reihenfolge der letzten zwei Argumente gedreht:


long clone(unsigned long Schalter, void *Stapel,
          int *Eltern_tid, unsigned long tls,
          int *Kind_tid);

Auf der Cris- und S30-Architektur ist die Reihenfolge der ersten zwei Argumente gedreht:


long clone(void *Stapel, unsigned long Schalter,
           int *Eltern_tid, int *Kind_tid,
           unsigned long tls);

Auf der Microblaze-Architektur wird ein zusätzliches Argument übergeben:


long clone(unsigned long Schalter, void *Stapel,
           int Stapelgröße,         /* Größe des Stapels */
           int *Eltern_tid, int *Kind_tid,
           unsigned long tls);

Blackfin, M68k und Sparc

Die Konventionen der Argumentübergabe weichen auf Blackfin, M68k und Sparc von der obigen Beschreibung ab. Einzelheiten finden Sie in der Kernel- (und Glibc-) Quelle.

Ia64

Auf ia64 wird eine andere Schnittstelle benutzt:


int __clone2(int (*fn)(void *),
             void *Stapelbasis, size_t Stapelgröße,
             int Schalter, void *arg, …
          /* pid_t *Eltern_tid, struct user_desc *tls,
             pid_t *Kind_tid */ );

Der oben gezeigte Prototyp ist für die Glibc-Wrapper-Funktion; für den Systemaufruf selbst wird der Prototyp wie folgt beschrieben (er ist identisch zum clone()-Prototyp auf Microblaze):


long clone2(unsigned long Schalter, void *Stapelbasis,
            int Stapelgröße,         /* Größe des Stapels */
            int *Eltern_tid, int *Kind_tid,
            unsigned long tls);

__clone2() arbeitet auf die gleiche Weise wie clone(), außer dass Stapelbasis auf die niedrigste Adresse im Stapelspeicherbereich des Kindprozesses zeigt und Stapelgröße die Größe des Stapelspeichers angibt, auf die Stapelbasis zeigt.

STANDARDS

Linux.

GESCHICHTE

Linux 5.3.

Linux 2.4 und älter

In der Linux 2.4.x-Serie gibt CLONE_THREAD generell dem neuen Prozess nicht den gleichen Elternprozess, wie dem aufrufenden Prozess. Für die Linux-Versionen 2.4.7 bis 2.4.18 implizierte der Schalter CLONE_THREAD jedoch den Schalter CLONE_PARENT (wie in Linux 2.6.0 und neuer).

Unter Linux 2.4 und früher gab es die Argumente Eltern_tid, tls und Kind_tid noch nicht.

ANMERKUNGEN

Diese Systemaufrufe werden benutzt, um Threads zu implementieren: mehrere Steuerflüsse in einem Programm, die gleichzeitig in einem gemeinsamen Speicherbereich ausgeführt werden.

Der Systemaufruf kcmp(2) kann zum Testen, ob zwei Prozesse sich verschiedene Ressourcen, wie die Dateideskriptortabelle, die Rücksetz-Aktionen der System-V-Semaphoren oder einen virtuellen Adressraum, teilen, verwandt werden.

Handler, die mittels pthread_atfork(3) registriert sind, werden während eines Clone-Aufrufs nicht ausgeführt.

FEHLER

GNU-C-Bibliotheksversionen 2.3.4 bis einschließlich 2.24 enthielten eine Wrapper-Funktion für getpid(2), die Zwischenspeichern von PIDs vornahm. Dieses Zwischenspeichern beruhte auf der Unterstützung in dem Glibc-Wrapper von clone(), aber Einschränkungen in der Implementierung bedeuteten, dass unter einigen Umständen der Zwischenspeicher nicht aktuell war. Insbesondere wenn ein Signal sofort nach dem clone()-Aufruf an den Kindprozess gesandt wurde, konnte ein Aufruf von getpid(2) in einem Signal-Handler die PID des aufrufenden Prozesses (des »Elternprozesses«) zurückgeben, falls der Clone-Wrapper noch keine Chance hatte den PID-Zwischenspeicher im Kindprozess zu aktualisieren. (Diese Diskussion ignoriert den Fall, dass der Kindprozess mit CLONE_THREAD erstellt wurde, in dem getpid(2) den gleichen Wert im Kindprozess zurückgeben sollte und im Prozess, der clone() aufrief, wie sich der Aufrufende und der Kindprozess in der gleichen Thread-Gruppe befinden. Das Problem des nicht mehr frischen Zwischenspeichers tritt auch auf, wenn das Argument Schalter CLONE_VM enthält.) Um die Wahrheit zu erfahren, war es manchmal notwendig gewesen, Code wie den folgenden zu verwenden:


#include <syscall.h>
pid_t mypid;
mypid = syscall(SYS_getpid);

Aufgrund des Problems mit dem nicht mehr frischem Zwischenspeicher sowie anderen in getpid(2) bemerkten Problemen, wurde die Funktionalität des PID-Zwischenspeicherns in Glibc 2.25 entfernt.

BEISPIELE

Das folgende Programm demonstriert die Benutzung von clone() zum Erzeugen eines Kindprozesses, der in einem separaten UTS-Namensraum ausgeführt wird. Der Kindprozess ändert in seinem UTS-Namensraum den Rechnernamen. Dann zeigen sowohl Eltern- als auch Kindprozess den Rechnernamen des Systems an, wodurch sichtbar wird, dass der Rechnername sich im UTS-Namensraum von Eltern- und Kindprozess unterscheidet. Ein Beispiel für die Verwendung dieses Programms finden Sie in setns(2).

Innerhalb des Beispielprogramms reservieren wir Speicher, der für den Stapel des Kindprogramms verwandt werden soll. Dabei verwenden wir aus den folgenden Gründen mmap(2) statt malloc(3):

mmap(2) reserviert einen Speicherblock, der an einer Seitengrenze beginnt und ein Vielfaches der Seitengröße groß ist. Dies ist nützlich, um am Ende des Stapels mittels mprotect(2) eine Wächterseite (eine Seite mit dem Schutz PROT_NONE) einzurichten.
Wir können den Schalter MAP_STACK angeben, um ein für den Stapel geeignetes Mapping festzulegen. Derzeit führt dieser Schalter unter Linux zu keiner Aktion, aber er existiert und hat auf anderen Systemen Auswirkungen, daher sollten wir ihn zwecks Portabilität aufnehmen.

Programmquelltext

#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              /* Funktion für geklontes Kind starten */
childFunc(void *arg)
{

struct utsname uts;
/* Rechnername im UTS-Namensraum des Kindes ändern. */
if (sethostname(arg, strlen(arg)) == -1)
err(EXIT_FAILURE, "sethostname");
/* Rechnername ermitteln und anzeigen. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename in child: %s\n", uts.nodename);
/* Mittels »sleep« den Namensraum eine Weile offen halten.
Dies erlaubt einige Experimente -- beispielsweise könnte
ein anderer Prozess den Namensraum betreten. */
sleep(200);
return 0; /* Kind wird nun beendet */ } #define STACK_SIZE (1024 * 1024) /* Stapelgröße für geklontes Kind */ int main(int argc, char *argv[]) {
char *stack; /* Beginn des Stapelpuffers */
char *stackTop; /* Ende des Stapelpuffers */
pid_t pid;
struct utsname uts;
if (argc < 2) {
fprintf(stderr, "Usage: %s <child-hostname>\n", argv[0]);
exit(EXIT_SUCCESS);
}
/* Speicher zuweisen, der für den Stapel des Kindes verwendet wird. */
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; /* Annehmen, das der Stapel schrumpft */
/* Kind erzeugen, das seinen eigenen UTS-Namensraum hat;
Kind beginnt Ausführung in childFunc(). */
pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]);
if (pid == -1)
err(EXIT_FAILURE, "clone");
printf("clone() returned %jd\n", (intmax_t) pid);
/* Elternprozess fällt bis hierher durch */
sleep(1); /* Dem Kind Zeit geben, seinen
Rechnernamen zu ändern */
/* Rechnernamen im UTS-Namensraum des Elternprozesses
anzeigen. Dieser wird sich vom Rechnernamen im
UTS-Namensraum des Kindes unterscheiden. */
if (uname(&uts) == -1)
err(EXIT_FAILURE, "uname");
printf("uts.nodename in parent: %s\n", uts.nodename);
if (waitpid(pid, NULL, 0) == -1) /* Wait for child */
err(EXIT_FAILURE, "waitpid");
printf("child has terminated\n");
exit(EXIT_SUCCESS); }

SIEHE AUCH

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)

ÜBERSETZUNG

Die deutsche Übersetzung dieser Handbuchseite wurde von Daniel Kobras <kobras@linux.de>, Chris Leick <c.leick@vollbio.de>, Mario Blättermann <mario.blaettermann@gmail.com>, Dr. Tobias Quathamer <toddy@debian.org> und Helge Kreutzmann <debian@helgefjell.de> erstellt.

Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 oder neuer bezüglich der Copyright-Bedingungen. Es wird KEINE HAFTUNG übernommen.

Wenn Sie Fehler in der Übersetzung dieser Handbuchseite finden, schicken Sie bitte eine E-Mail an die Mailingliste der Übersetzer.

3. Mai 2023 Linux man-pages 6.05.01