Fork vs Clone sur le noyau Linux 2.6

37

J'ai une certaine confusion en ce qui concerne fork et clone. J'ai vu ça:

  • fork est pour les processus et le clone est pour les threads

  • fork appelle simplement clone, le clone est utilisé pour tous les processus et threads

Est-ce que l'un ou l'autre est exact? Quelle est la distinction entre ces 2 appels système avec un noyau Linux 2.6?

Gregg Leventhal
la source

Réponses:

52

fork()était l'appel système UNIX d'origine. Il ne peut être utilisé que pour créer de nouveaux processus, pas de threads. En outre, il est portable.

Sous Linux, clone()un nouvel appel système polyvalent peut être utilisé pour créer un nouveau thread d’exécution. En fonction des options passées, le nouveau thread d'exécution peut adhérer à la sémantique d'un processus UNIX, d'un thread POSIX, d'un élément intermédiaire ou de quelque chose de complètement différent (comme un conteneur différent). Vous pouvez spécifier toutes sortes d'options pour déterminer si la mémoire, les descripteurs de fichier, les différents espaces de noms, les gestionnaires de signaux, etc. sont partagés ou copiés.

Puisque clone()est l'appel système superset, l'implémentation du fork()wrapper d'appel système dans glibc appelle en réalité clone(), mais il s'agit d'un détail d'implémentation que les programmeurs n'ont pas besoin de connaître. L' fork()appel système réel existe toujours dans le noyau Linux pour des raisons de compatibilité ascendante, même s'il est devenu redondant, car les programmes qui utilisent des versions très anciennes de libc, ou une autre libc que glibc, pourraient l'utiliser.

clone()est également utilisé pour implémenter la pthread_create()fonction POSIX pour la création de threads.

Les programmes portables doivent appeler fork()et pthread_create()non clone().

Celada
la source
2
posix_spawn est une autre fonction pertinente, à la fois plus et moins portable que fork.
Random832
10

Il semble que deux clone()éléments flottent dans Linux 2.6

Il y a un appel système:

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

C'est le "clone ()" décrit en faisant man 2 clone.

Si vous lisez suffisamment cette page de manuel, vous verrez ceci:

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

Apparemment, vous êtes censé implémenter le threading en utilisant la "fonction de bibliothèque" superposée à l'appel système qui porte un nom identique.

J'ai écrit un programme court:

#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;
}

Compilez-le avec: c99 -Wall -Wextraet parcourez-le strace -fpour voir ce que font réellement les appels système faisant appel à la fourchette. Je l'ai obtenu stracesur une machine Linux 2.6.18 (CPU x86_64):

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)

Aucun appel "fork" n'apparaît dans la stracesortie. L' clone()appel qui apparaît dans la stracesortie a des arguments très différents de ceux du clone man-page. child_stack=0comme le premier argument est différent de int (*fn)(void *).

Il semble que l' fork(2)appel système soit implémenté en termes réels clone() , tout comme la "fonction de bibliothèque" clone()est implémentée. Le réel clone() a un ensemble d'arguments différent du clone man-page.

De manière simpliste, vos deux déclarations apparemment contradictoires à propos de fork()et clone()sont correctes. Le "clone" impliqué est différent, cependant.

Bruce Ediger
la source
9
"C’est en fait une fonction de bibliothèque superposée à l’appel système sous-jacent clone ()." - en général, cela s'applique à tout appel système. En réalité, les programmeurs appellent presque toujours dans libc des fonctions nommées d'après l'appel système. Cela est dû au fait que passer un appel système réel directement à partir de C nécessite une magie spécifique à la plate-forme (généralement en forçant une sorte d’interruption du processeur, dépend de l’architecture ABI) et du code machine préférable de le déléguer à libc.
Celada
1
@Celada - oui, d'accord. C'est juste que les man 2 clonemots sont formulés exactement de cette façon, ce qui, je pensais, confondait le problème et empêchait le questionneur d'obtenir une bonne réponse.
Bruce Ediger
2
Je crois que la page de manuel signifie que la liste d'arguments de la clonefonction de bibliothèque diffère considérablement de la liste d'arguments acceptée par l'appel système sous-jacent. En particulier, l’appel système revient toujours deux fois sur la même pile, comme le fait habituellement fork; tous les arguments liés à la pile enfant sont gérés strictement dans l'espace utilisateur. Voir, par exemple, sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/…
zwol le
1
Je voulais donner à votre réponse la meilleure réponse parce que ça déchire, mais le troupeau m'a influencé et je suis parti avec la première réponse. Elle obtient des points pour le temps de réponse. Merci pour votre explication.
Gregg Leventhal
6

fork()est juste un ensemble particulier d'indicateurs à l'appel système clone(). clone()est assez général pour créer un "processus" ou un "fil" ou même des choses étranges qui se situent quelque part entre les processus et les threads (par exemple, des "processus" différents qui partagent la même table de descripteur de fichier).

Essentiellement, pour chaque "type" d'informations associé à un contexte d'exécution dans le noyau, clone()vous pouvez choisir d'aliaser ces informations ou de les copier. Les threads correspondent aux alias, les processus correspondent à la copie. En spécifiant des combinaisons intermédiaires de drapeaux à clone(), vous pouvez créer des choses étranges qui ne sont ni des threads ni des processus. Vous ne devriez normalement pas faire cela, et j'imagine que le développement du noyau Linux a fait l’objet d’un débat sur la possibilité de prévoir un mécanisme aussi général que clone().

Atsby
la source