PID_NAMESPACES(7) Linux-Programmierhandbuch PID_NAMESPACES(7)

pid_namespaces - Überblick über PID-Namensräume

Für einen Überblick über Namensräume, siehe namespaces(7).

PID-Namensräume isolieren den Raum der Prozesskennungen. Das bedeutet, dass Prozesse in verschiedenen PID-Namensräumen die gleiche PID haben können. PID-Namensräume erlauben Containern, Funktionalitäten wie Suspendierung/Wiederaufnahme der Gruppe an Prozessen in dem Container und der Migration des Containers auf einen neuen Rechner bereitzustellen, bei denen die Prozesse innerhalb des Containers die gleiche PID behalten.

PIDs in einem neuen PID-Namensraum beginnen bei 1, ähnlich wie in autonomen Systemen. Aufrufe von fork(2), vfork(2) oder clone(2) werden Prozesse mit PIDs erstellen, die innerhalb des Namensraums eindeutig sind.

Die Verwendung von PID-Namensräumen benötigt einen Kernel, der mit der Option CONFIG_PID_NS konfiguriert wurde.

Der erste in einem neuen Namensraum erstellte Prozess (d.h. der mittels clone(2) mit dem Schalter CLONE_NEWPID erstellte Prozess oder der erste Prozess, der durch einen Prozess nach einem Aufruf von unshare(2) mittels des Schalters CLONE_NEWPID erstellt wurde) hat die PID 1 und ist der »Init«-Prozess für den Namensraum (siehe init(1)). Dieser Prozess wird der Elternprozess jedes Kindprozesses, die verwaist wurden, da sich ein Prozess, der sich in diesem Namensraum befindet, beendet hat (weitere Details finden Sie weiter unten).

Falls sich der »Init«-Prozess eines PID-Namensraums beendet, beendet der Kernel mittels des Signals SIGKILL alle Prozesse in dem Namensraum. Dieses Verhalten spiegelt die Tatsache wieder, dass der »Init«-Prozess für das korrekte Funktionieren eines PID-Namensraums wesentlich ist. In diesem Fall wird ein nachfolgender fork(2) in den Namensraum mit dem Fehler ENOMEM fehlschlagen; es ist nicht möglich, einen neuen Prozess in einem PID-Namensraum zu erstellen, dessen »Init«-Prozess sich beendet hat. Solche Szenarien können auftreten, wenn beispielsweise ein Prozess einen offenen Dateideskriptor für eine Datei /proc/[PID]/ns/pid verwendet, der einem Prozess entspricht, der in einem Namensraum war und der mit setns(2) in einen Namensraum soll, nachdem sich der »Init«-Prozess beendet hat. Ein anderes mögliches Szenario kann direkt nach dem Aufruf von unshare(2) auftreten: Falls sich der erste Kindprozess, der nach einem fork(2) nachfolgend erstellt wurde, beendet, dann schlagen nachfolgende Aufrufe von fork(2) mit ENOMEM fehl.

Nur Signale, für die der »Init«-Prozess einen Signal-Handler etabliert hat, können durch andere Mitglieder des PID-Namensraums an den »Init«-Prozess gesandt werden. Diese Einschränkung gilt sogar für privilegierte Prozesse und verhindert, dass andere Mitglieder des PID-Namensraums versehentlich den »Init«-Prozess töten.

Entsprechend kann ein Prozess in einem Vorgängernamensraum – gemäß der gewöhnlichen, in kill(2) beschriebenen Berechtigungsprüfungen – nur Signale an den »Init«-Prozess eines Nachfolge-PID-Namensraums senden, falls der »Init«-Prozess einen Handler für das Signal etabliert hat. (Innerhalb des Handlers wird das sigaction(2) siginfo_t-Feld si_pid Null sein.) SIGKILL oder SIGSTOP werden besonders behandelt: diese Signale werden zwangsweise zugestellt, wenn sie von einem Vorgänger-PID-Namensraum gesandt werden. Keines dieser Signale kann durch den »Init«-Prozess abgefangen werden. Daher führen sie zu den gewöhnlichen Aktionen, die diesen Signalen zugeordnet sind (beenden bzw. stoppen des Prozesses).

Seit Linux 3.4 führt der Systemaufruf reboot(2) zum Senden eines Signales an den »Init«-Prozess des Namensraumes. Siehe reboot(2) für weitere Details.

PID-Namensräume können verschachtelt werden: jeder PID-Namensraum hat einen Vorgänger, außer für den anfänglichen (»Wurzel-«) PID-Namensraum. Der Vorgänger eines PID-Namensraums ist der PID-Namensraum des Prozesses, der den Namensraum mittels clone(2) oder unshare(2) erstellte. PID-Namensräume formen somit einen Baum, wobei alle Namensräume in letzter Instanz ihren Vorgänger auf den Wurzelnamensraum zurückführen. Seit Linux 3.7 begrenzt der Kernel die maximale Schachtelungstiefe für PID-Namensräume auf 32.

Ein Prozess ist für andere Prozesse in seinem PID-Namensraum sichtbar und für Prozesse in jedem direkten Vorgänger-PID-Namensraum, direkt zurück bis zum Wurzel-PID-Namensraum. In diesem Zusammenhang bedeutet »sichtbar«, dass ein Prozess das Ziel von Aktionen eines anderen Prozesses durch Verwendung von Systemaufrufen sein kann, die eine Prozesskennung angeben können. Umgekehrt kann ein Prozess in einem Nachfolge-PID-Namensraum die Prozesse in dem Vorgänger und weiter entfernten Vorgänger-Namensräumen nicht sehen. Kurz gesagt: Ein Prozess kann nur Prozesse, die in seinem eigenen PID-Namensraum und in Nachfolgern dieses Namensraums sind, sehen (z.B. ihnen Signale mit kill(2) senden, ihren Nice-Wert mit setpriority(2) ändern usw.).

Ein Prozess hat eine Prozesskennung in jedem der Ebenen der PID-Namensraum-Hierarchie, in der er sichtbar ist, sowie rückwärts durch jeden direkten Vorgängernamensraum bis zum Wurzel-PID-Namensraum. Systemaufrufe, die auf Prozesskennungen agieren, agieren immer auf Prozesskennungen, die in dem PID-Namensraum des aufrufenden Prozesses sichtbar sind. Ein Aufruf von getpid(2) liefert immer die PID zurück, die dem Namensraum zugeordnet ist, in dem der Prozess erstellt wurde.

Einige Prozesse in einem PID-Namensraum können Elternprozesse haben, die sich außerhalb des Namensraums befinden. Der Elternprozess des anfänglichen Prozesses in dem Namensraum (d.h. der init(1)-Prozess mit der PID 1) befindet sich beispielsweise notwendigerweise in einem anderen Namensraum. Entsprechend sind die direkten Kindprozesses eines Prozesses, der setns(2) verwendet, damit seine Kindprozesse einem PID-Namensraum beitreten, in einem anderen PID-Nemsraum als der Aufrufende von setns(2). Wird für solche Prozesse getppid(2) aufgerufen, dann wird 0 zurückgeliefert.

Während Prozesse frei in Nachfolge-PID-Namensräume absteigen können (z.B. mittels setns(2) mit einem PID-Namensraum-Dateideskriptor), können sie sich nicht in die andere Richtung bewegen. Das bedeutet, Prozesse dürfen keine Vorgängernamensräume (direkte, zweiter Stufe, usw.) betreten. Das Ändern von PID-Namensräumen ist eine Einwegaktion.

Die Aktion NS_GET_PARENT ioctl(2) kann zum Erkennen der hierarchischen Beziehung zwischen PID-Namensräumen verwandt werden; siehe ioctl_ns(2).

Aufrufe von setns(2), die einen PID-Namensraum-Dateideskriptor festlegen und Aufrufe von unshare(2) mit dem Schalter CLONE_NEWPID führen dazu, dass nachfolgend durch den Aufrufenden erstellte Kindprozesse in einem anderen PID-Namensraum als dem des Aufrufenden abgelegt werden. (Seit Linux 4.12 wird dieser PID-Namensraum über die Datei /proc/[PID]/ns/pid_for_children gezeigt, wie in namespaces(7) beschrieben.) Diese Aufrufe ändern allerdings nicht den PID-Namensraum des aufrufenden Prozesses, da dies das Verständnis des Aufrufenden über seine eigene PID (wie sie mit getpid() berichtet wird) ändern würde, wodurch viele Anwendungen und Bibliotheken beschädigt würden.

Um es anders zu sagen: die Mitgliedschaft eines Prozesses in einem PID-Namensraum wird bei der Erstellung des Prozesses bestimmt und kann danach nicht mehr geändert werden. Unter anderem bedeutet dies, dass die Eltern-Kind-Beziehung zwischen Prozessen die Vorgänger-Nachfolger-Beziehung zwischen PID-Namensräumen spiegelt: der Elternprozess ist entweder im gleichen Namensraum oder befindet sich im direkten Vorgänger-PID-Namensraum.

Ein Prozess kann unshare(2) mit dem Schalter CLONE_NEWPID nur einmal aufrufen. Nachdem er diese Aktion durchgeführt hat, wird sein symbolischer Link /proc/PID/ns/pid_for_children leer sein, bis der erste Kindprozess in dem Namensraum erstellt wurde.

Wenn ein Kindprozess verwaist wird, wird der »Init«-Prozess in dem PID-Namensraum seines Elternprozesses sein neuer Elternprozess (außer einer der Vorfahrprozesse des Elternprozesses, der näher dran ist, setzt den Befehl prctl(2) PR_SET_CHILD_SUBREAPER ein, um sich selbst als Auffänger des verwaisten Nachfahrprozesses zu markieren.) Beachten Sie, dass aufgrund der oben beschriebenen Semantik von setns(2) und unshare(2) dies der »Init«-Prozess in dem PID-Namensraum sein kann, der Vorgänger des PID-Namensraums des Kindprozesses sein kann, statt des »Init«-Prozesses in dem eigenen PID-Namensraum des Kindprozesses.

In den aktuellen Versionen von Linux kann CLONE_NEWPID nicht mit CLONE_THREAD kombiniert werden. Threads müssen im gleichen PID-Namensraum sein, damit die Threads in einem Prozess sich gegenseitig Signale senden können. Entsprechend muss es möglich sein, alle Threads eines Prozesses in dem Dateisystem proc(5) zu sehen. Ergänzend kommt hinzu, dass die Prozesskennung des Prozesses, der ein Signal sendet, nicht beim Senden eines Signals aussagekräftig kodiert werden könnte, falls die zwei Threads in verschiedenen PID-Namensräumen wären (siehe die Beschreibung des Typs siginfo_t in sigaction(2)). Da diese berechnet wird, wenn ein Signal in die Warteschlange gestellt wird, würde eine Signalwarteschlange, die von Prozessen in mehreren PID-Namensräumen gemeinsam benutzt würde, dies vereiteln.

In früheren Versionen von Linux war zusätzlich CLONE_NEWPID verboten (schlug mit dem Fehler EINVAL fehl) zusammen mit CLONE_SIGHAND (vor Linux 4.3) sowie CLONE_VM (vor Linux 3.12). Die Änderungen, die diese Beschränkungen aufhoben, wurden auch in ältere stabile Kernel portiert.

Ein Dateisystem /proc zeigt (in den Verzeichnissen /proc/[PID]) nur Prozesse, die in dem PID-Namensraum des Prozesses sichtbar sind, der die Einhängung durchführte, selbst falls das Dateisystem /proc von Prozessen in anderen Namensräumen betrachtet wird.

Nach dem Erstellen eines neuen PID-Namensraumes ist es für den Kindprozess nützlich, sein Wurzelverzeichnis zu ändern und eine neue Procfs-Instanz unter /proc einzuhängen, so dass Werkzeuge wie ps(1) korrekt funktionieren. Falls ein neuer Einhängenamensraum gleichzeitig durch Aufnahme von CLONE_NEWNS in dem Argument flags von clone(2) oder unshare(2) erstellt wird, dann ist es nicht notwendig, das Wurzelverzeichnis zu ändern: eine neue Procfs-Instanz kann direkt über /proc eingehängt werden.

In einer Shell lautet der Einhängebefehl für /proc:


$ mount -t proc proc /proc

Der Aufruf von readlink(2) auf dem Pfad /proc/self liefert die Prozesskennung des Aufrufenden in dem PID-Namensraum der Procfs-Einhängung (d.h. des PID-Namensraums, der Procfs einhängte). Dies kann zu Untersuchungszwecken nützlich sein, wenn ein Prozess seine PID in anderen Namensräumen herausfinden möchte.

/proc/sys/kernel/ns_last_pid (seit Linux 3.3)
Diese Datei (die pro PID-Namensraum virtualisiert ist) zeigt die letzte PID, die in diesem PID-Namensraum zugewiesen wurde. Wenn die nächste PID zugewiesen wird, wird der Kernel nach der kleinsten nicht zugewiesenen PID suchen, die größer als dieser Wert ist, und wenn danach diese Datei gelesen wird, wird diese PID dann gezeigt.
Diese Datei ist für einen Prozess, der über die Capability CAP_SYS_ADMIN oder (seit Linux 5.9) CAP_CHECKPOINT_RESTORE innerhalb des Benutzernamensraums, der den PID-Namensraum besitzt, verfügt, schreibbar. Dies ermöglicht es, die PID zu bestimmen, die dem nächsten Prozess, der innerhalb des PID-Namensraums erstellt wird, zugewiesen wird.

Wenn eine Prozesskennung über einen UNIX-Domain-Socket an einen Prozess in einem anderen PID-Namensraum übergeben wird (siehe die Beschreibung von SCM_CREDENTIALS in unix(7)), dann wird sie in den entsprechenden PID-Wert in dem PID-Namensraum des empfangenen Prozesses übersetzt.

Namensräume sind eine Linux-spezifische Funktionalität.

Siehe user_namespaces(7).

clone(2), reboot(2), setns(2), unshare(2), proc(5), capabilities(7), credentials(7), mount_namespaces(7), namespaces(7), user_namespaces(7), switch_root(8)

Diese Seite ist Teil der Veröffentlichung 5.13 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 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.

1. November 2020 Linux