clone, __clone2 - erzeugt einen Kindprozess
ÜBERSICHT
/* Prototyp für die Glibc-Wrapper-Funktion */
#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 */ );
/* Für den Prototyp des den rohen Systemaufrufs siehe ANMERKUNGEN */
clone() erzeugt auf eine ähnliche Weise wie
fork(2) einen neuen Prozess.
Diese Seite beschreibt sowohl 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.
Im Gegensatz zu fork(2) erlaubt clone(), dass der
Kindprozess Teile seines Kontextes mit dem aufrufenden Prozess teilt. Dazu
zählen der virtuelle Adressraum, die Tabelle der Dateideskriptoren
und die Tabelle der Signal-Handler. (Beachten Sie, dass »aufrufender
Prozess« auf dieser Handbuchseite »Elternprozess«
entspricht. Aber lesen Sie im Folgenden die Beschreibung von
CLONE_PARENT.)
clone() wird benutzt, um Threads zu implementieren: mehrere
Steuerflüsse in einem Programm, die gleichzeitig in einem gemeinsamen
Speicherbereich ausgeführt werden.
Wird mit clone() 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 child_stack 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 child_stack
für gewöhnlich auf die oberste Adresse im bereitgehaltenen
Speicherbereich zeigt.
Das niederwertige Byte von flags enthält die Nummer
des Beendigungssignals, das an den Elternprozess gesandt wird, wenn
der Kindprozess endet. 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 angegeben wurde, wird dem
Elternprozess nicht signalisiert, wenn der Kindprozess endet.
flags kann darüber hinaus noch durch bitweises
»ODER« mit keiner oder mehreren der folgenden Konstanten
verknüpft werden. Dadurch wird festgelegt, welche Ressourcen sich
Eltern- und Kindprozess teilen:
- CLONE_CHILD_CLEARTID
(seit Linux 2.5.49)
- Die Kind-Thread-Kennung an der Stelle ctid im Kindspeicher
bereinigen (nullen), wenn das Kind existiert 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.
- CLONE_CHILD_SETTID
(seit Linux 2.5.49)
- Speichert die Kind-Thread-Kennung an der Stelle ctid im
Kindspeicher. Die Speicheraktion wird abgeschlossen, bevor clone()
die Steuerung an den Benutzerraum zurückgibt.
- CLONE_FILES
(since Linux 2.0)
- 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)).
- CLONE_FS (seit
Linux 2.0)
- 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 von
clone() 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.
- CLONE_IO (seit
Linux 2.6.25)
- 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-Steuerprogramms, d.h., welches das E/A-Steuerprogramm zur
Modellplanung für E/As des Prozesses benutzt. Falls sich Prozesse
den gleichen E/A-Kontext teilen, werden sie vom E/A-Steuerprogramm als ein
einziger betrachtet. Als Konsequenz daraus müssen sie sich die
gleiche Plattenzeitzugriffzeit teilen. Einige E/A-Steuerprogramme
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.
- CLONE_NEWCGROUP
(seit Linux 4.6)
- Erstellt den Prozess in einem neuen cgroup-Namensraum. Falls dieser
Schalter nicht gesetzt ist, dann wird (wie mit fork(2)) der Prozess
in den gleichen cgroup-Namensräumen wie der aufrufende Prozess
erstellt. Der Schalter ist für die Implementierung von Containern
gedacht.
- Weitere Informationen über cgroup-Namensräume finden Sie
unter cgroup_namespaces(7).
- Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann
CLONE_NEWCGROUP angeben.
- CLONE_NEWIPC
(seit Linux 2.6.19)
- Ist CLONE_NEWIPC gesetzt, dann wird der Prozess in einem neuen
IPC-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist, dann
wird der Prozess (wie bei fork(2)) im gleichen IPC-Namensraum wie
der aufrufende Prozess erstellt. Dieser Schalter ist für die
Implementierung von Containern gedacht.
- Ein IPC-Namensraum stellt eine isolierte Ansicht von System-V-IPC-Objekten
(siehe svipc(7)) und (seit 2.6.30) POSIX-Nachrichtenwarteschlangen
(siehe mq_overview(7)) bereit. Das gemeinsame Merkmal dieser
IPC-Mechanismen ist, dass IPC-Objekte durch andere Mechanismen als
Dateisystempfadnamen identifiziert werden.
- Objekte, die in einem IPC-Namensraum erstellt wurden, sind für alle
anderen Prozesse sichtbar, die Mitglieder des Namensraums sind. Die
Objekte sind jedoch nicht für Prozesse in anderen
Namensräumen sichtbar.
- Wenn ein IPC-Namensraum zerstört wird, d.h. wenn der letzte Prozess
im Namensraum beendet wird, werden alle IPC-Objekte im Namensraum
automatisch zerstört.
- Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann
CLONE_NEWIPC angeben. Dieser Schalter darf nicht zusammen mit
CLONE_SYSVSEM angegeben werden.
- Weitere Informationen zu IPC-Namensräumen finden Sie in
namespaces(7).
- CLONE_NEWNET
(seit Linux 2.6.24)
- (Die Implementierung dieses Schalters wurde erst ungefähr mit der
Kernel-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. Dieser Schalter
ist für die Implementierung von Containern gedacht.
- Ein Netzwerk-Namensraum stellt eine isolierte Ansicht des
Netzwerk-Stapelspeichers (Netzwerkgeräteschnittstellen, IPv4- und
IPv6-Protokoll-Stapelspeicher, IP-Routing-Tabellen, Firewall-Regeln, die
Verzeichnisbäume /proc/net und /sys/class/net,
Sockets, etc.) bereit. Ein physisches Netzwerkgerät kann in genau
einem Netzwerknamensraum bestehen. Ein virtuelles
Netzwerkgerätepaar (veth(4)) stellt eine einer Pipe
ähnliche Abstraktion bereit, die benutzt werden kann, um Tunnel
zwischen Netzwerk-Namensräumen aufzubauen und eine Brücke in
ein physisches Netzwerkgerät in einem anderen Namensraum zu
erstellen.
- Wenn ein Netzwerk-Namensraum freigegeben wird, d.h. wenn der letzte
Prozess im Namensraum beendet wird, werden seine physischen
Netzwerkgeräte zurück in den ursprünglichen
Namensraum verschoben (nicht zum Elternprozess). Weitere Informationen zu
Netzwerk-Namensräumen finden Sie in namespaces(7).
- Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann
CLONE_NEWNET angeben.
- CLONE_NEWNS
(seit Linux 2.4.19)
- 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.
- 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.
- Für weitere Informationen über
Einhängenamensräume lesen Sie namespaces(7) und
mount_namespaces(7)
- CLONE_NEWPID
(seit Linux 2.6.24)
- Wenn CLONE_NEWPID gesetzt ist, dann wird der Prozess in einem neuen
PID-Namensraum erstellt. Falls dieser Schalter nicht gesetzt ist (wie mit
fork(2)), dann wird der Prozess in dem gleichen PID-Namensraum wie
der aufrufende Prozess erstellt. Der Schalter ist für die
Implementierung von Containern gedacht.
- 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.
- CLONE_NEWUSER
- (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.
- 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.
- Für weitere Informationen über Benutzernamensräume
lesen Sie namespaces(7) und user_namespaces(7).
- CLONE_NEWUTS
(seit Linux 2.6.19)
- 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. Dieser
Schalter ist für die Implementierung von Containern gedacht.
- Ein UTS-Namensraum ist eine Zusammenstellung von Bezeichnern, die von
uname(2) zurückgegeben werden; von denen können der
Domain-Name und der Rechnername durch setdomainname(2)
beziehungsweise sethostname(2) geändert werden.
Änderungen, die an Bezeichnern in einem UTS-Namensraum vorgenommen
werden, sind für alle anderen Prozesse im gleichen Namensraum
sichtbar, nicht jedoch für Prozesse in anderen
UTS-Namensräumen.
- Nur ein privilegierter Prozess (CAP_SYS_ADMIN) kann
CLONE_NEWUTS angeben.
- Weitere Informationen zu UTS-Namensräumen finden Sie in
namespaces(7).
- CLONE_PARENT
(seit Linux 2.3.12)
- 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.
- CLONE_PARENT_SETTID
(seit Linux 2.5.49)
- Die Kindprozess-Thread-Kennung an der Stelle ptid im Elternspeicher
ablegen. (In Linux 2.5.32-2.5.48 gab es einen Schalter
CLONE_SETTID, der das tat.) Die Speicheraktion wird abgeschlossen,
bevor clone() die Steuerung an den Benutzerraum
zurückgibt.
- CLONE_PID
(Linux 2.0 bis 2.5.15)
- 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 angegeben
werden (PID 0). Dieser Schalter verschwand in Linux 2.5.16 komplett aus
den Kernelquellen. Seitdem ignoriert der Kernel dieses Bit, falls es in
flags festgelegt ist.
- CLONE_PTRACE
(seit Linux 2.2)
- Falls CLONE_PTRACE angegeben ist und der aufrufende Prozess
verfolgt wird, dann wird der Kindprozess ebenfalls verfolgt (siehe
ptrace(2)).
- CLONE_SETTLS
(seit Linux 2.5.32)
- Der TLS (Thread Local Storage)-Deskriptor ist auf newtls
gesetzt.
- Die Interpretation von newtls und der resultierende Effekt ist
architekturabhängig. Auf X86 ist newtls 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.
- CLONE_SIGHAND
(seit Linux 2.0)
- 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 durch den
clone-Aufruf 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-test6 müssen die flags außerdem
CLONE_VM enthalten, falls CLONE_SIGHAND angegeben
wurde.
- CLONE_STOPPED
(seit Linux 2.6.0-test2)
- 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.
- CLONE_SYSVSEM
(seit Linux 2.5.10)
- 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.
- CLONE_THREAD
(seit Linux 2.4.0-test8)
- 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 von clone() 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() aufruft (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 per
clone() 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 müssen die flags auch CLONE_SIGHAND
enthalten, wenn CLONE_THREAD angegeben wurde. Beachten Sie auch,
dass seit Linux 2.6.0-test6 CLONE_SIGHAND auch CLONE_VM
enthalten muss.
- Signale können an eine Thread-Gruppe als Ganzes geschickt werden
(d.h. einer TGID) unter Benutzung von kill(2) oder an einen
bestimmten Thread unter Benutzung von tgkill(2).
- 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 sie von
sigprocmask(2) gesetzt wird, Signale können aber entweder
für den ganzen Prozess anstehen (d.h. an jedes Mitglied der
Thread-Gruppe zu liefern sein), wenn sie mit kill(2) gesandt wurden
oder für einen einzelnen Thread, wenn sie mit tgkill(2)
gesandt wurden. Ein Aufruf von sigpending(2) gibt eine
Signalzusammenstellung zurück, die eine Verbindung ausstehender
Signale für den ganzen Prozess und der Signale ist, die für
den aufrufenden Prozess anstehen.
- Falls kill(2) benutzt wird, um ein Signal an eine Thread-Gruppe zu
senden 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 per kill(2) gesandt Signal zu
empfangen.
- CLONE_UNTRACED
(seit Linux 2.5.46)
- Falls CLONE_UNTRACED angegeben ist, kann ein verfolgender Prozess
kein CLONE_PTRACE auf diesem Kindprozess erzwingen.
- CLONE_VFORK
(seit Linux 2.2)
- 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.
- CLONE_VM (seit
Linux 2.0)
- 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 zur Zeit 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).
Beachten Sie, dass die Glibc-Wrapperfunktion clone() einige
Änderungen am Speicher, auf den child_stack zeigt, vornimmt
(Änderungen, um den Stack 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
Stack der Eltern eingesetzt wird, als Stack der Kinder.
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.
Ein weiterer Unterschied für den rohen Systemaufruf
clone() besteht darin, dass das Argument child_stack NULL sein
könnte, so dass in diesem Fall das Kind eine Dublette des Stacks des
Elternprozesses verwendet. (»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 flags, void *child_stack,
int *ptid, int *ctid,
unsigned long newtls);
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 flags, void *child_stack,
int *ptid, unsigned long newtls,
int *ctid);
Auf der Cris- und S30-Architektur ist die Reihenfolge der ersten
zwei Argumente gedreht:
long clone(void *child_stack, unsigned long flags,
int *ptid, int *ctid,
unsigned long newtls);
Auf der Microblaze-Architektur wird ein zusätzliches
Argument übergeben:
long clone(unsigned long flags, void *child_stack,
int stack_size, /* Größe des Stacks */
int *ptid, int *ctid,
unsigned long newtls);
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.
Auf ia64 wird eine andere Schnittstelle benutzt:
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 */ );
Der oben gezeigte Prototyp ist für die
Glibc-Wrapper-Funktion. Die rohe Systemaufrufschnittstelle hat kein
fn- oder arg-Argument und ändert die Reihenfolge der
Argumente, so dass flags das erste und tls das letzte Argument
ist.
__clone2() arbeitet auf die gleiche Weise wie
clone(), außer dass child_stack_base auf die niedrigste
Adresse im Stapelspeicherbereich des Kindprozesses zeigt und
stack_size die Größe des Stapelspeichers angibt, auf
die child_stack_base zeigt.
Unter Linux 2.4 und früher gab es die Argumente
ptid, tls und ctid noch nicht.
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 entsprechend gesetzt.
- EAGAIN
- Es laufen bereits zu viele Prozesse; siehe fork(2).
- EINVAL
- CLONE_SIGHAND wurde angegeben, aber nicht CLONE_VM. (Seit
Linux 2.6.0-test6.)
- EINVAL
- CLONE_THREAD wurde angegeben, aber nicht CLONE_SIGHAND.
(Seit Linux 2.5.35.)
- EINVAL
- In flags wurden sowohl CLONE_FS als auch CLONE_NEWNS
angegeben.
- EINVAL (seit
Linux 3.9)
- In flags wurden sowohl CLONE_NEWUSER als auch
CLONE_FS angegeben.
- EINVAL
- In flags wurden sowohl CLONE_NEWIPC als auch
CLONE_SYSVSEM angegeben.
- EINVAL
- Eines (oder beides) von CLONE_NEWPID oder CLONE_NEWUSER und
eines (oder beides) von CLONE_THREAD oder CLONE_PARENT wurde
in flags angegeben.
- EINVAL
- Wird von der Glibc-Wrapper-Funktion clone() zurückgegeben,
wenn ein Wert von Null für fn oder child_stack
angegeben wurde.
- EINVAL
- In flags wurde CLONE_NEWIPC angegeben, der Kernel wurde
jedoch nicht mit den Optionen CONFIG_SYSVIPC und
CONFIG_IPC_NS konfiguriert.
- EINVAL
- In flags wurde CLONE_NEWNET angegeben, der Kernel wurde
jedoch nicht mit der Option CONFIG_NET_NS konfiguriert.
- EINVAL
- In flags wurde CLONE_NEWPID angegeben, der Kernel wurde
jedoch nicht mit der Option CONFIG_PID_NS konfiguriert.
- EINVAL
- In flags wurde CLONE_NEWUTS angegeben, der Kernel wurde
jedoch nicht mit der Option CONFIG_UTS konfiguriert.
- EINVAL
- child_stack ist nicht an einer geeigneten Grenze für diese
Architektur ausgerichtet. Beispielsweise muss child_stack auf
Aarch64 ein Vielfaches von 16 sein.
- ENOMEM
- Es kann nicht ausreichend Speicher für eine Aufgabenstruktur des
Kindprozesses reserviert werden oder um benötigte Teile vom Kontext
des Aufrufenden zu kopieren.
- ENOSPC (seit Linux
3.7)
- CLONE_NEWPID wurde in flags angegeben, aber die Begrenzung
der geschachtelten Tiefe der PID-Namensräume würde
überschritten; siehe pid_namespaces(7).
- ENOSPC (seit
Linux 4.9; vorher EUSERS)
- CLONE_NEWUSER wurde in flags angegeben und der Aufruf
würde dazu führen, dass die Anzahl der geschachtelten
Benutzernamensräume überschritten würde. Siehe
user_namespaces(7).
- Von Linux 3.11 bis Linux 4.8 war der in diesem Fall diagnostizierte Fehler
EUSERS.
- ENOSPC (seit
Linux 4.9)
- Einer der Werte in flags 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).
- EPERM
- CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET,
CLONE_NEWNS, CLONE_NEWPID oder CLONE_NEWUTS wurde von
einem nicht privilegierten Prozess angegeben (Prozess ohne
CAP_SYS_ADMIN).
- EPERM
- CLONE_PID wurde von einem anderen Prozess als Prozess 0 angegeben.
(Dieser Fehler tritt nur unter Linux 2.5.15 und früheren Versionen
auf.)
- EPERM
- CLONE_NEWUSER wurde in flags festgelegt, aber weder die
effektive Benutzerkennung noch die effektive Gruppenkennung des
Aufrufenden hat eine Abbildung in den Elternnamensraum (siehe
user_namespaces(7)).
- EPERM (seit Linux
3.9)
- CLONE_NEWUSER wurde in flags angegeben und der Aufrufende
ist in einer Chroot-Umgebung (d.h. das Wurzelverzeichnis des Aufrufenden
passt nicht zum Wurzelverzeichnis des Einhängenahmensraums, in dem
es liegt).
- ERESTARTNOINTR
(seit Linux 2.6.17)
- Ein Systemaufruf wurde durch ein Signal unterbrochen und wird neu
gestartet. (Dies wird nur während einer Verfolgung sichtbar
sein.)
- EUSERS (Linux 3.11
bis Linux 4.8)
- CLONE_NEWUSER wurde in flags angegeben und die Anzahl der
geschachtelten Benutzernamensräume würde
überschritten. Siehe die Diskussion von ENOSPC weiter
oben.
clone() ist Linux-spezifisch und sollte nicht in
portierbaren Programmen benutzt 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 Aufrufs von clone() nicht
ausgeführt.
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 Kernel-Versionen 2.4.7 bis 2.4.18 implizierte der Schalter
CLONE_THREAD jedoch den Schalter CLONE_PARENT (wie in Kernel
2.6.0 und neuer).
Für eine Weile gab es CLONE_DETACHED
(eingeführt in 2.5.32): Elternprozesse wollen kein Ende-Signal des
Kindprozesses. In Linux 2.6.2 verschwand die Notwendigkeit, dies zusammen
mit CLONE_THREAD zu übergeben. Dieser Schalter ist immer noch
definiert, hat aber keine Auswirkungen.
Auf i386-Architekturen sollte clone() nicht durch vsyscall
aufgerufen werden, sondern direkt durch int $0x80.
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 flags 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.
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).
#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 /* Startfunktion für geklonten Kindprozess */
childFunc(void *arg)
{
struct utsname uts;
/* Rechnername im UTS-Namensraum des Kindprozesses ändern */
if (sethostname(arg, strlen(arg)) == -1)
errExit("sethostname");
/* Rechnernamen abfragen und anzeigen */
if (uname(&uts) == -1)
errExit("uname");
printf("uts.nodename im Kindprozess: %s\n", uts.nodename);
/* Der Namensraum wird für eine Weile durch Schlafen offen gehalten.
Dies ermöglicht etwas zu experimentieren – zum Beispiel
kann ein weiterer Prozess dem Namensraum beitreten. */
sleep(200);
return 0; /* Kindprozess wird nun beendet */
}
#define STACK_SIZE (1024 * 1024) /* Stapelspeichergröße für geklonten
Kindprozess */
int
main(int argc, char *argv[])
{
char *stack; /* Start des Stapelspeicherpuffers */
char *stackTop; /* Ende des Stapelspeicherpuffers */
pid_t pid;
struct utsname uts;
if (argc < 2) {
fprintf(stderr, "Aufruf: %s <Kindprozess-Rechnername>\n", argv[0]);
exit(EXIT_SUCCESS);
}
/* Stapelspeicher für Kindprozess reservieren */
stack = malloc(STACK_SIZE);
if (stack == NULL)
errExit("malloc");
stackTop = stack + STACK_SIZE; /* Annahme, dass Stapelspeicher nach
unten wächst */
/* Es wird ein Kindprozess erzeugt, der seinen eigenen Namensraum hat.
Der Kindprozess beginnt die Ausführung in childFunc() */
pid = clone(childFunc, stackTop, CLONE_NEWUTS | SIGCHLD, argv[1]);
if (pid == -1)
errExit("clone");
printf("clone() gab %ld zurück\n", (long) pid);
/* Elternprozess fällt bis hierher durch */
sleep(1); /* gibt dem Kindprozess Zeit zum Ändern des Rechnernamens */
/* Den Rechnernamen im UTS-Namensraum des Elternprozesses anzeigen.
Dieser wird sich vom Rechnernamen im UTS-Namensraum des Kindprozesses
unterscheiden. */
if (uname(&uts) == -1)
errExit("uname");
printf("uts.nodename im Elternprozess: %s\n", uts.nodename);
if (waitpid(pid, NULL, 0) == -1) /* Warten auf Kindprozess */
errExit("waitpid");
printf("Kindprozess wurde beendet\n");
exit(EXIT_SUCCESS);
}
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)
Diese Seite ist Teil der Veröffentlichung 4.16 des Projekts
Linux-man-pages. Eine Beschreibung des Projekts, Informationen, wie
Fehler gemeldet werden können sowie die aktuelle Version dieser Seite
finden sich unter https://www.kernel.org/doc/man-pages/.
Ü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
<debian-l10n-german@lists.debian.org>.