web-dev-qa-db-fra.com

La différence entre fork (), vfork (), exec () et clone ()

Je cherchais à trouver la différence entre ces quatre sites sur Google et je m'attendais à une quantité considérable d'informations à ce sujet, mais il n'y avait pas vraiment de comparaison solide entre les quatre appels.

Je me suis efforcé d’essayer de compiler en quelque sorte un aperçu de base des différences entre ces appels système et voici ce que j’ai obtenu. Toutes ces informations sont-elles correctes ou manque-t-il quelque chose d'important?

Fork: l'appel fork crée essentiellement un duplicata du processus en cours, identique dans presque tous les sens (tout n'est pas copié, par exemple, les ressources sont limitées dans certaines implémentations, mais l'idée est de créer une copie aussi proche que possible. ).

Le nouveau processus (enfant) obtient un ID de processus différent (PID) et utilise le PID de l'ancien processus (parent) comme PID parent (PPID). Étant donné que les deux processus exécutent maintenant exactement le même code, ils peuvent déterminer lequel correspond au code de retour de fork: l'enfant obtient 0, le parent obtient le PID de l'enfant. C’est tout, bien sûr, en supposant que l’appel fork fonctionne - sinon, aucun enfant n’est créé et le parent obtient un code d’erreur.

Vfork: La différence fondamentale entre vfork et fork est que lorsqu'un nouveau processus est créé avec vfork (), le processus parent est temporairement suspendu et le processus enfant peut emprunter l'espace d'adressage du parent. Cet état de fait étrange se poursuit jusqu'à ce que le processus enfant se termine ou appelle execve (), moment auquel le processus parent se poursuit.

Cela signifie que le processus enfant d'un vfork () doit être prudent pour éviter de modifier de manière inattendue les variables du processus parent. En particulier, le processus enfant ne doit pas renvoyer de la fonction contenant l'appel vfork () et ne doit pas appeler exit () (s'il doit quitter, il devrait utiliser _exit (); en fait, cela est également vrai pour l'enfant d'une fourche normale ()).

Exec : L'appel exec est un moyen de remplacer fondamentalement l'ensemble du processus en cours par un nouveau programme. Il charge le programme dans l'espace de processus actuel et l'exécute à partir du point d'entrée. exec () remplace le processus en cours par un exécutable pointé par la fonction. Control ne retourne jamais au programme d'origine sauf en cas d'erreur exec ().

Clone : Clone, en tant que fork, crée un nouveau processus. Contrairement à fork, ces appels permettent au processus enfant de partager des parties de son contexte d'exécution avec le processus appelant, telles que l'espace mémoire, la table des descripteurs de fichier et la table des gestionnaires de signaux.

Lorsque le processus enfant est créé avec clone, il exécute la fonction application fn (arg). (Cela diffère de fork, où l'exécution continue dans l'enfant à partir du point de l'appel fork initial.) L'argument fn est un pointeur sur une fonction appelée par le processus enfant au début de son exécution. L'argument arg est passé à la fonction fn.

Lorsque l'application de la fonction fn (arg) est renvoyée, le processus enfant se termine. Le nombre entier renvoyé par fn est le code de sortie du processus enfant. Le processus enfant peut également se terminer explicitement en appelant exit (2) ou après avoir reçu un signal fatal.

Informations obtenues sous forme:

Merci d'avoir pris le temps de lire ceci ! :)

186
user476033
  • vfork() est une optimisation obsolète. Avant une bonne gestion de la mémoire, fork() créait une copie complète de la mémoire du parent, ce qui coûtait donc cher. dans la plupart des cas, une fork() a été suivie de exec(), qui supprime la mappe de mémoire actuelle et en crée une nouvelle, ce fut une dépense inutile. De nos jours, fork() ne copie pas la mémoire; il est simplement défini comme "copie sur écriture", donc fork() + exec() est aussi efficace que vfork() + exec().

  • clone() est l'appel système utilisé par fork(). avec certains paramètres, cela crée un nouveau processus, avec d'autres, cela crée un fil. la différence entre elles réside simplement dans les structures de données (espace mémoire, état du processeur, pile, PID, fichiers ouverts, etc.) partagées ou non.

147
Javier
  • execve() remplace l'image exécutable actuelle par une autre chargée à partir d'un fichier exécutable.
  • fork() crée un processus enfant.
  • vfork() est une version historique optimisée de fork(), destinée à être utilisée lorsque execve() est appelé directement après fork(). Cela s’est avéré bien fonctionner dans les systèmes non-MMU (où fork() ne peut pas fonctionner de manière efficace) et lorsque fork()ing traite des processus avec une énorme empreinte mémoire pour exécuter un petit programme (pensez à Java Runtime.exec()). POSIX a normalisé la posix_spawn() pour remplacer ces deux dernières utilisations plus modernes de vfork().
  • posix_spawn() fait l'équivalent d'un fork()/execve(), et permet également à certains joueurs de jongler entre eux. Il est supposé remplacer fork()/execve(), principalement pour les plates-formes non-MMU.
  • pthread_create() crée un nouveau fil.
  • clone() est un appel spécifique à Linux, qui peut être utilisé pour implémenter n'importe quoi de fork() à pthread_create(). Cela donne beaucoup de contrôle. Inspiré sur rfork().
  • rfork() est un appel spécifique à Plan-9. Il est supposé être un appel générique, permettant plusieurs degrés de partage, entre processus complets et threads.
74
ninjalj
  1. fork() - crée un nouveau processus enfant, qui est une copie complète du processus parent. Les processus enfants et parents utilisent des espaces d'adressage virtuels différents, qui sont initialement renseignés par les mêmes pages de mémoire. Ensuite, à mesure que les deux processus sont exécutés, les espaces d’adresse virtuels commencent à différer de plus en plus, car le système d’exploitation copie les pages de mémoire écrites par l’un de ces deux processus et attribue des copies indépendantes des pages modifiées. mémoire pour chaque processus. Cette technique s'appelle Copy-On-Write (COW).
  2. vfork() - crée un nouveau processus enfant, qui est une copie "rapide" du processus parent. Contrairement à l'appel système fork(), les processus enfants et parents partagent le même espace d'adressage virtuel. REMARQUE! En utilisant le même espace d'adressage virtuel, le parent et l'enfant utilisent la même pile, le pointeur de pile et le pointeur d'instruction, comme dans le cas du classique fork()! Pour éviter les interférences indésirables entre le parent et l'enfant, qui utilisent la même pile, l'exécution du processus parent est gelée jusqu'à ce que l'enfant appelle soit exec() (crée un nouvel espace d'adressage virtuel et une transition vers une pile différente) ou _exit() (fin de l'exécution du processus). vfork() est l'optimisation de fork() pour le modèle "fork-and-exec". Il peut être exécuté 4 à 5 fois plus rapidement que la fork(), car contrairement à la fork() (même avec COW gardé à l'esprit), la mise en œuvre de vfork() l'appel système n'inclut pas la création de un nouvel espace d'adressage (l'allocation et la mise en place de nouveaux annuaires de pages).
  3. clone() - crée un nouveau processus enfant. Différents paramètres de cet appel système spécifient quelles parties du processus parent doivent être copiées dans le processus enfant et quelles parties seront partagées entre elles. En conséquence, cet appel système peut être utilisé pour créer toutes sortes d'entités d'exécution, à partir de threads et se terminant par des processus complètement indépendants. En fait, clone() appel système est la base utilisée pour la mise en oeuvre de pthread_create() et de toute la famille des fork() appels système.
  4. exec() - réinitialise toute la mémoire du processus, charge et analyse le binaire de l'exécutable spécifié, configure une nouvelle pile et passe le contrôle au point d'entrée de l'exécutable chargé. Cet appel système ne renvoie jamais le contrôle à l'appelant et sert au chargement d'un nouveau programme dans le processus existant. Cet appel système avec fork() appel système forme ensemble un modèle classique de gestion de processus UNIX appelé "fork-and-exec".
39
ZarathustrA

Fork (), vfork () et clone () appellent tous do_fork () pour effectuer le travail réel, mais avec des paramètres différents.

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 0);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
    unsigned long clone_flags;
    unsigned long newsp;

    clone_flags = regs.ebx;
    newsp = regs.ecx;
    if (!newsp)
        newsp = regs.esp;
    return do_fork(clone_flags, newsp, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0);
}
#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM    0x00000100  /* set if VM shared between processes */

SIGCHLD means the child should send this signal to its father when exit.

Pour fork, l'enfant et le père ont la table de pages indépendante VM, mais depuis l'efficacité, fork ne copie aucune page, il définit simplement toutes les pages enregistrables en lecture seule pour le processus enfant. Ainsi, lorsqu'un processus enfant souhaite écrire quelque chose sur cette page, une exception de page se produit et le noyau allouera une nouvelle page clonée à partir de l'ancienne page avec le droit d'écriture. Cela s'appelle "copie sur écriture".

Pour vfork, la mémoire virtuelle est exactement par enfant et père - juste à cause de cela, père et enfant ne peuvent pas être éveillés simultanément car ils vont s'influencer mutuellement. Ainsi, le père dormira à la fin de "do_fork ()" et se réveillera lorsque l'enfant appellera exit () ou execve () depuis lors, il possédera une nouvelle table de pages. Voici le code (dans do_fork ()) que le père dort.

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

Voici le code (dans mm_release () appelé par exit () et execve ()) qui réveille le père.

up(tsk->p_opptr->vfork_sem);

Pour sys_clone (), il est plus flexible car vous pouvez y entrer n'importe quel clone_flags. Donc pthread_create () appelle cet appel système avec plusieurs clone_flags:

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

Résumé: les fonctions fork (), vfork () et clone () créeront des processus enfants avec différents montages de ressources de partage avec le processus père. Nous pouvons également dire que vfork () et clone () peuvent créer des threads (en réalité, ce sont des processus puisqu'ils ont une tâche_struct indépendante) car ils partagent la table de page VM avec le processus père.

6
user991800

Différences entre fork () et vfork () Difference-between-fork-vfork the difference between fork and vfork

Le comportement de Vfork () expliqué plus en détail dans le programme ci-dessous.

shashi@linuxtechi ~}$ cat vfork_advanced.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
    int n =10;
    pid_t pid = vfork(); //creating the child process
    if (pid == 0)          //if this is a chile process
    {
        printf("Child process started\n");
    }
    else//parent process execution
    {
        printf("Now i am coming back to parent process\n");
    }
    printf("value of n: %d \n",n); //sample printing to check "n" value
    return 0;
}
shashi@linuxtechi ~}$ cc vfork_advanced.c
shashi@linuxtechi ~}$ ./a.out
Child process started
value of n: 10
Now i am coming back to parent process
value of n: 594325573
a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.
Aborted

Remarque: à nouveau si vous observez que le résultat de vfork n’est pas défini. La valeur de "n" a été imprimée pour la première fois en tant que 10, ce qui est attendu. Mais la prochaine fois dans le processus parent, il affichera une valeur erronée.