Fork vs Clone auf 2.6 Kernel Linux

37

Ich habe einige Verwirrung in Bezug auf Gabel und Klon. Das habe ich gesehen:

  • fork ist für Prozesse und clone ist für Threads

  • fork ruft nur clone auf, clone wird für alle Prozesse und Threads verwendet

Sind beide genau? Was ist der Unterschied zwischen diesen beiden Systemaufrufen mit einem 2.6 Linux-Kernel?

Gregg Leventhal
quelle

Antworten:

52

fork()war der ursprüngliche UNIX-Systemaufruf. Es können nur neue Prozesse erstellt werden, keine Threads. Auch ist es tragbar.

Unter Linux gibt clone()es einen neuen, vielseitigen Systemaufruf, mit dem ein neuer Ausführungsthread erstellt werden kann. Abhängig von den übergebenen Optionen kann sich der neue Ausführungsthread an die Semantik eines UNIX-Prozesses, eines POSIX-Threads, an etwas dazwischen oder an etwas völlig anderes (wie einen anderen Container) halten. Sie können alle Arten von Optionen angeben, die festlegen, ob Speicher, Dateideskriptoren, verschiedene Namespaces, Signalhandler usw. gemeinsam genutzt oder kopiert werden.

Da clone()es sich um den Superset-Systemaufruf handelt, ruft die Implementierung des fork()Systemaufruf-Wrappers in glibc tatsächlich auf clone(), dies ist jedoch ein Implementierungsdetail, das Programmierer nicht kennen müssen. Der eigentliche fork()Systemaufruf ist aus Gründen der Abwärtskompatibilität im Linux-Kernel noch vorhanden, obwohl er überflüssig geworden ist, da Programme, die sehr alte Versionen von libc oder eine andere libc als glibc verwenden, ihn möglicherweise verwenden.

clone()wird auch verwendet, um die pthread_create()POSIX-Funktion zum Erstellen von Threads zu implementieren .

Portable Programme sollten fork()und pthread_create()nicht aufrufen clone().

Celada
quelle
2
posix_spawn ist eine weitere relevante Funktion - in mancher Hinsicht mehr oder weniger portabel als fork.
Random832
10

Es scheint, dass clone()in Linux 2.6 zwei Dinge im Umlauf sind

Es gibt einen Systemaufruf:

int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

Dies ist der "clone ()", der durch "doing" beschrieben wird man 2 clone.

Wenn Sie diese Manpage nah genug gelesen haben, sehen Sie Folgendes:

It is actually a library function layered on top of the
underlying clone() system call.

Anscheinend soll das Threading mithilfe der "Bibliotheksfunktion" implementiert werden, die auf dem verwirrend identisch benannten Systemaufruf liegt.

Ich habe ein kurzes Programm geschrieben:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int ac, char **av)
{
    pid_t cpid;
    switch (cpid = fork()) {
    case 0:   // Child process
        break;
    case -1:  // Error
        break;
    default:  // parent process
        break;
    }
    return 0;
}

Kompilierte es mit: c99 -Wall -Wextraund ließ es unter strace -flaufen, um zu sehen, was Systemaufrufe tatsächlich tun. Ich habe dies straceauf einem Linux 2.6.18-Rechner (x86_64-CPU) herausgefunden:

20097 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x2b4ee9213770) = 20098
20097 exit_group(0)                     = ?
20098 exit_group(0)

In der straceAusgabe erscheint kein "Fork" -Aufruf. Der clone()in der straceAusgabe angezeigte Aufruf hat ganz andere Argumente als der Manpage-Klon. child_stack=0als erstes argument ist anders als int (*fn)(void *).

Es scheint, dass der fork(2)Systemaufruf in Bezug auf den Real implementiert ist clone(), genau wie die "Bibliotheksfunktion" clone()implementiert ist. Der Real clone() hat andere Argumente als der Manpage-Klon.

Vereinfacht gesagt, sind beide Ihrer scheinbar widersprüchlichen Aussagen zu fork()und clone()richtig. Der "Klon" ist jedoch anders.

Bruce Ediger
quelle
9
"Es ist eigentlich eine Bibliotheksfunktion, die über dem zugrunde liegenden clone () - Systemaufruf liegt." - Dies gilt im Allgemeinen für jeden Systemaufruf. Programmierer rufen in libc praktisch immer Funktionen auf, die nach dem Systemaufruf benannt sind. Dies liegt daran, dass ein tatsächlicher realer Systemaufruf direkt von C aus plattformspezifische Magie erfordert (in der Regel durch Erzwingen einer CPU-Falle, abhängig von der ABI-Architektur) und Computercode, die am besten an libc delegiert wird.
Celada
1
@ Celada - ja, einverstanden. Es ist nur so, dass man 2 clonees genau so ausdrückt, was meiner Meinung nach das Problem verwirrt und den Fragesteller daran hindert, eine gute Antwort zu erhalten.
Bruce Ediger
2
Ich glaube, die Manpage zeigt an, dass sich die Argumentliste der cloneBibliotheksfunktion wesentlich von der vom zugrunde liegenden Systemaufruf akzeptierten Argumentliste unterscheidet. Insbesondere gibt der Systemaufruf immer zweimal auf demselben Stapel zurück, wie dies bei herkömmlichen Methoden der Fall forkist. Alle Argumente, die sich auf den untergeordneten Stapel beziehen, werden streng im Benutzerbereich behandelt. Siehe zum Beispiel sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/…
zwol 30.04.15
1
Ich wollte deine Antwort am besten beantworten, weil es rockt, aber ich wurde von der Herde beeinflusst und ging mit der ersten Antwort. Sie bekommt Punkte für die Reaktionszeit. Danke für Ihre Erklärung.
Gregg Leventhal
6

fork()ist nur ein bestimmter Satz von Flags für den Systemaufruf clone(). clone()ist allgemein genug, um entweder einen "Prozess" oder einen "Thread" oder sogar seltsame Dinge zu erstellen, die irgendwo zwischen Prozessen und Threads liegen (z. B. verschiedene "Prozesse", die dieselbe Dateideskriptortabelle verwenden).

Im Wesentlichen können Sie für jeden "Informationstyp", der einem Ausführungskontext im Kernel zugeordnet ist, clone()auswählen, ob diese Informationen als Alias ​​verwendet oder kopiert werden sollen. Themen entsprechen Aliasing, Prozesse dem Kopieren. Durch die Angabe von Zwischenkombinationen von Flags für clone()können Sie seltsame Dinge erstellen, die keine Threads oder Prozesse sind. Normalerweise sollte man das nicht tun, und ich stelle mir vor, dass während der Entwicklung des Linux-Kernels eine Debatte darüber stattgefunden hat, ob ein so allgemeiner Mechanismus wie dieser möglich sein sollte clone().

Atsby
quelle