Quel est le but de fork ()?


87

Dans de nombreux programmes et pages de manuel de Linux, j'ai vu du code utiliser fork(). Pourquoi devons-nous utiliser fork()et quel est son but?


150
Pour que tous ces philosophes de la cuisine ne meurent pas de faim.
kenj0418

Réponses:


106

fork()est la façon dont vous créez de nouveaux processus sous Unix. Lorsque vous appelez fork, vous créez une copie de votre propre processus qui possède son propre espace d'adressage . Cela permet à plusieurs tâches de s'exécuter indépendamment les unes des autres, comme si elles disposaient chacune de la mémoire complète de la machine.

Voici quelques exemples d'utilisation de fork:

  1. Votre shell utilise forkpour exécuter les programmes que vous appelez à partir de la ligne de commande.
  2. Les serveurs Web comme Apache sont utilisés forkpour créer plusieurs processus serveur, chacun d'entre eux traitant les demandes dans son propre espace d'adressage. Si l'un meurt ou perd de la mémoire, les autres ne sont pas affectés, il fonctionne donc comme un mécanisme de tolérance aux pannes.
  3. Google Chrome utilise forkpour gérer chaque page dans un processus distinct. Cela empêchera le code côté client sur une page de faire tomber tout votre navigateur.
  4. forkest utilisé pour générer des processus dans certains programmes parallèles (comme ceux écrits avec MPI ). Notez que cela diffère de l'utilisation de threads , qui n'ont pas leur propre espace d'adressage et existent dans un processus.
  5. Les langages de script utilisent forkindirectement pour démarrer les processus enfants. Par exemple, chaque fois que vous utilisez une commande comme subprocess.Popenen Python, vous forkun processus enfant et lisez sa sortie. Cela permet aux programmes de fonctionner ensemble.

L'utilisation typique de forkdans un shell pourrait ressembler à ceci:

int child_process_id = fork();
if (child_process_id) {
    // Fork returns a valid pid in the parent process.  Parent executes this.

    // wait for the child process to complete
    waitpid(child_process_id, ...);  // omitted extra args for brevity

    // child process finished!
} else {
    // Fork returns 0 in the child process.  Child executes this.

    // new argv array for the child process
    const char *argv[] = {"arg1", "arg2", "arg3", NULL};

    // now start executing some other program
    exec("/path/to/a/program", argv);
}

Le shell génère un processus enfant à l'aide de execet attend qu'il se termine, puis continue sa propre exécution. Notez que vous n'êtes pas obligé d'utiliser fork de cette façon. Vous pouvez toujours générer de nombreux processus enfants, comme le ferait un programme parallèle, et chacun pourrait exécuter un programme simultanément. En gros, chaque fois que vous créez de nouveaux processus dans un système Unix, vous utilisez fork(). Pour l'équivalent Windows, jetez un œil à CreateProcess.

Si vous voulez plus d'exemples et une explication plus longue, Wikipedia a un résumé décent. Et voici quelques diapositives sur le fonctionnement des processus, des threads et de la concurrence dans les systèmes d'exploitation modernes.


Puce 5: «souvent»? Seulement «souvent»? Lesquels ne l'utilisent pas, ou dans quelles circonstances fork () n'est pas utilisé - sur les systèmes qui supportent fork (), c'est-à-dire.
Jonathan Leffler

19
Curieusement, ça s'appelle CreateProcess () - ces fous de Windows :-)
paxdiablo

2
jamais réalisé jusqu'à présent que "le shell utilise fork pour exécuter les programmes que vous appelez depuis la ligne de commande"!
Lazer

1
Le lien de Slides est rompu
piertoni

1
Toutes les réponses disent que fork()est le moyen de créer un nouveau processus sous UNIX, mais pour être pédant, il y a au moins un autre: posix_spawn().
Davislor

15

fork () est la façon dont Unix crée de nouveaux processus. Au moment où vous avez appelé fork (), votre processus est cloné et deux processus différents continuent l'exécution à partir de là. L'un d'eux, l'enfant, aura fork () return 0. L'autre, le parent, aura fork () retournera le PID (process ID) de l'enfant.

Par exemple, si vous tapez ce qui suit dans un shell, le programme shell appellera fork (), puis exécutera la commande que vous avez passée (telnetd, dans ce cas) dans l'enfant, tandis que le parent affichera à nouveau l'invite, ainsi sous forme de message indiquant le PID du processus d'arrière-plan.

$ telnetd &

Quant à la raison pour laquelle vous créez de nouveaux processus, c'est ainsi que votre système d'exploitation peut faire beaucoup de choses en même temps. C'est pourquoi vous pouvez exécuter un programme et, pendant qu'il est en cours d'exécution, basculer vers une autre fenêtre et faire autre chose.


@varDumper Bonne prise!
Daniel C.Sobral

9

fork () est utilisé pour créer un processus enfant. Quand une fonction fork () est appelée, un nouveau processus sera généré et l'appel de la fonction fork () retournera une valeur différente pour l'enfant et le parent.

Si la valeur de retour est 0, vous savez que vous êtes le processus enfant et si la valeur de retour est un nombre (qui se trouve être l'ID de processus enfant), vous savez que vous êtes le parent. (et si c'est un nombre négatif, le fork a échoué et aucun processus enfant n'a été créé)

http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html


1
Sauf si la valeur de retour est -1, auquel cas le fork () a échoué.
Jonathan Leffler

8

fork () est essentiellement utilisé pour créer un processus enfant pour le processus dans lequel vous appelez cette fonction. Chaque fois que vous appelez un fork (), il renvoie un zéro pour l'ID enfant.

pid=fork()
if pid==0
//this is the child process
else if pid!=0
//this is the parent process

par cela, vous pouvez fournir différentes actions pour le parent et l'enfant et utiliser la fonction multithreading.


6

fork () créera un nouveau processus enfant identique au parent. Donc, tout ce que vous exécutez dans le code après cela sera exécuté par les deux processus - très utile si vous avez par exemple un serveur et que vous souhaitez gérer plusieurs requêtes.


pourquoi créer un enfant identique au parent à quoi ça sert?

1
C'est comme construire une armée contre un seul soldat. Vous bifurquez pour que votre programme puisse gérer plus de requêtes en même temps, au lieu d'une par une.
cloudhead

fork () renvoie 0 sur l'enfant et le pid de l'enfant sur le parent. L'enfant peut alors utiliser un appel comme exec () pour remplacer son état par un nouveau programme. C'est ainsi que les programmes sont lancés.
Todd Gamblin le

Les processus sont très proches de l'identité, mais il existe de nombreuses différences subtiles. Les différences flagrantes sont le PID actuel et le PID parent. Il y a des problèmes liés aux écluses et aux sémaphores détenus. La page de manuel fork () de POSIX répertorie 25 différences entre le parent et l'enfant.
Jonathan Leffler

2
@kar: Une fois que vous avez deux processus, ils peuvent continuer séparément de là et l'un d'entre eux pourrait se remplacer (exex ()) par un autre programme entièrement.
Vatine

4

Vous n'avez probablement pas besoin d'utiliser fork dans la programmation quotidienne si vous écrivez des applications.

Même si vous voulez que votre programme démarre un autre programme pour effectuer une tâche, il existe d'autres interfaces plus simples qui utilisent fork dans les coulisses, telles que «système» en C et perl.

Par exemple, si vous souhaitez que votre application lance un autre programme tel que bc pour effectuer des calculs à votre place, vous pouvez utiliser «système» pour l'exécuter. Le système fait un «fork» pour créer un nouveau processus, puis un «exec» pour transformer ce processus en bc. Une fois bc terminé, le système reprend le contrôle de votre programme.

Vous pouvez également exécuter d'autres programmes de manière asynchrone, mais je ne me souviens pas comment.

Si vous écrivez des serveurs, des shells, des virus ou des systèmes d'exploitation, vous êtes plus susceptible d'utiliser fork.


Merci pour system(). J'étais en train de lire fork()parce que je veux que mon code C exécute un script python.
Bean Taxi

4

Fork crée de nouveaux processus. Sans fork, vous auriez un système unix qui ne pourrait exécuter que init.


4

L'appel système fork () est utilisé pour créer des processus. Il ne prend aucun argument et renvoie un ID de processus. Le but de fork () est de créer un nouveau processus, qui devient le processus enfant de l'appelant. Après la création d'un nouveau processus enfant, les deux processus exécuteront l'instruction suivante après l'appel système fork (). Par conséquent, nous devons distinguer le parent de l'enfant. Cela peut être fait en testant la valeur retournée de fork ():

Si fork () renvoie une valeur négative, la création d'un processus enfant a échoué. fork () renvoie un zéro au processus enfant nouvellement créé. fork () renvoie une valeur positive, l'ID de processus du processus enfant, au parent. L'ID de processus renvoyé est de type pid_t défini dans sys / types.h. Normalement, l'ID de processus est un entier. De plus, un processus peut utiliser la fonction getpid () pour récupérer l'ID de processus attribué à ce processus. Par conséquent, après l'appel système à fork (), un simple test peut dire quel processus est l'enfant. Veuillez noter qu'Unix fera une copie exacte de l'espace d'adressage du parent et la remettra à l'enfant. Par conséquent, les processus parent et enfant ont des espaces d'adressage séparés.

Comprenons-le avec un exemple pour clarifier les points ci-dessus. Cet exemple ne distingue pas les processus parents et enfants.

#include  <stdio.h>
#include  <string.h>
#include  <sys/types.h>

#define   MAX_COUNT  200
#define   BUF_SIZE   100

void  main(void)
{
     pid_t  pid;
     int    i;
     char   buf[BUF_SIZE];

     fork();
     pid = getpid();
     for (i = 1; i <= MAX_COUNT; i++) {
          sprintf(buf, "This line is from pid %d, value = %d\n", pid, i);
          write(1, buf, strlen(buf));
     } 
}

Supposons que le programme ci-dessus s'exécute jusqu'au point de l'appel à fork ().

Si l'appel à fork () est exécuté avec succès, Unix fera deux copies identiques des espaces d'adressage, une pour le parent et l'autre pour l'enfant. Les deux processus commenceront leur exécution à la prochaine instruction suivant l'appel à fork (). Dans ce cas, les deux processus commenceront leur exécution à l'affectation

pid = .....;

Les deux processus démarrent leur exécution juste après l'appel système fork (). Puisque les deux processus ont des espaces d'adressage identiques mais séparés, ces variables initialisées avant l'appel de fork () ont les mêmes valeurs dans les deux espaces d'adressage. Comme chaque processus a son propre espace d'adressage, toutes les modifications seront indépendantes des autres. En d'autres termes, si le parent change la valeur de sa variable, la modification n'affectera que la variable dans l'espace d'adressage du processus parent. Les autres espaces d'adressage créés par les appels fork () ne seront pas affectés même s'ils ont des noms de variables identiques.

Quelle est la raison d'utiliser write plutôt que printf? C'est parce que printf () est "tamponné", ce qui signifie que printf () regroupera la sortie d'un processus. Lors de la mise en mémoire tampon de la sortie du processus parent, l'enfant peut également utiliser printf pour imprimer certaines informations, qui seront également mises en mémoire tampon. En conséquence, puisque la sortie ne sera pas envoyée à l'écran immédiatement, vous risquez de ne pas obtenir le bon ordre du résultat attendu. Pire encore, la sortie des deux processus peut être mélangée de manière étrange. Pour résoudre ce problème, vous pouvez envisager d'utiliser l'écriture «sans tampon».

Si vous exécutez ce programme, vous pouvez voir ce qui suit à l'écran:

................
This line is from pid 3456, value 13
This line is from pid 3456, value 14
     ................
This line is from pid 3456, value 20
This line is from pid 4617, value 100
This line is from pid 4617, value 101
     ................
This line is from pid 3456, value 21
This line is from pid 3456, value 22
     ................

L'ID de processus 3456 peut être celui attribué au parent ou à l'enfant. En raison du fait que ces processus sont exécutés simultanément, leurs lignes de sortie sont mélangées d'une manière plutôt imprévisible. De plus, l'ordre de ces lignes est déterminé par l'ordonnanceur du processeur. Par conséquent, si vous exécutez à nouveau ce programme, vous pouvez obtenir un résultat totalement différent.


3
Au lieu de copier-coller le texte, vous auriez pu commenter un lien: csl.mtu.edu/cs4411.ck/www/NOTES/process/fork/create.html
chaitanya lakkundi

3

Le multitraitement est au cœur de l'informatique. Par exemple, votre IE ou Firefox peut créer un processus pour télécharger un fichier pour vous pendant que vous naviguez encore sur Internet. Ou, pendant que vous imprimez un document dans un traitement de texte, vous pouvez toujours consulter différentes pages et continuer à effectuer des modifications avec.


3

Fork () est utilisé pour créer de nouveaux processus comme chaque corps l'a écrit.

Voici mon code qui crée des processus sous forme d'arbre binaire ....... Il vous demandera de scanner le nombre de niveaux jusqu'à lequel vous souhaitez créer des processus dans l'arbre binaire

#include<unistd.h> 
#include<fcntl.h> 
#include<stdlib.h>   
int main() 
{
int t1,t2,p,i,n,ab;
p=getpid();                
printf("enter the number of levels\n");fflush(stdout);
scanf("%d",&n);                
printf("root %d\n",p);fflush(stdout);
for(i=1;i<n;i++)    
{        
    t1=fork();

    if(t1!=0)
        t2=fork();        
    if(t1!=0 && t2!=0)        
        break;            
    printf("child pid %d   parent pid %d\n",getpid(),getppid());fflush(stdout);
}   
    waitpid(t1,&ab,0);
    waitpid(t2,&ab,0);
return 0;
}

PRODUCTION

  enter the number of levels
  3
  root 20665
  child pid 20670   parent pid 20665
  child pid 20669   parent pid 20665
  child pid 20672   parent pid 20670
  child pid 20671   parent pid 20670
  child pid 20674   parent pid 20669
  child pid 20673   parent pid 20669

2

Il faut d'abord comprendre ce qu'est l'appel système fork (). Laissez-moi expliquer

  1. L'appel système fork () crée le double exact du processus parent, il fait le double de la pile parent, du tas, des données initialisées, des données non initialisées et partage le code en mode lecture seule avec le processus parent.

  2. L'appel système Fork copie la mémoire sur la base de la copie sur écriture, ce qui signifie que l'enfant crée dans la page de mémoire virtuelle lorsqu'il y a besoin de copier.

Maintenant le but de fork ():

  1. Fork () peut être utilisé à l'endroit où il y a division du travail comme un serveur doit gérer plusieurs clients, donc le parent doit accepter la connexion sur une base régulière, donc le serveur fait un fork pour que chaque client effectue la lecture-écriture.

1

fork()est utilisé pour générer un processus enfant. Il est généralement utilisé dans des situations similaires à celles du threading, mais il existe des différences. Contrairement aux threads, fork()crée des processus entiers séparés, ce qui signifie que l'enfant et le parent alors qu'ils sont des copies directes l'un de l'autre au point quefork() appelé, ils sont complètement séparés, aucun ne peut accéder à l'espace mémoire de l'autre (sans aller aux problèmes normaux vous allez accéder à la mémoire d'un autre programme).

fork()est toujours utilisé par certaines applications serveur, principalement celles qui s'exécutent en tant que root sur une machine * NIX qui abandonnent les autorisations avant de traiter les demandes des utilisateurs. Il existe encore d'autres cas d'utilisation, mais la plupart des gens sont maintenant passés au multithreading.


2
Je ne comprends pas la perception que «la plupart des gens» sont passés au multithreading. Les processus sont là pour rester, tout comme les threads. Personne n'a "déménagé" de l'un ou l'autre. Dans la programmation parallèle, les codes les plus grands et les plus simultanés sont des programmes multiprocessus à mémoire distribuée (par exemple MapReduce et MPI). Pourtant, la plupart des gens opteraient pour OpenMP ou un paradigme de mémoire partagée pour une machine multicœur, et les GPU utilisent des threads ces jours-ci, mais il y en a beaucoup au-delà. Je parie, cependant, que plus de codeurs sur ce site rencontrent un parallélisme de processus côté serveur que tout ce qui est multithread.
Todd Gamblin

1

Le raisonnement derrière fork () par rapport au simple fait d'avoir une fonction exec () pour lancer un nouveau processus est expliqué dans une réponse à une question similaire sur l'échange de pile unix .

Essentiellement, puisque fork copie le processus courant, toutes les différentes options possibles pour un processus sont établies par défaut, de sorte que le programmeur ne les a pas fournies.

Dans le système d'exploitation Windows, en revanche, les programmeurs doivent utiliser la fonction CreateProcess qui est BEAUCOUP plus compliquée et nécessite de remplir une structure multiforme pour définir les paramètres du nouveau processus.

Donc, pour résumer, la raison de la fourche (par rapport à l'exécution) est la simplicité dans la création de nouveaux processus.


0

Appel système Fork () utilisé pour créer un processus enfant. C'est le double exact du processus parent. Fork copie la section de pile, la section de tas, la section de données, la variable d'environnement, les arguments de ligne de commande du parent.

voir: http://man7.org/linux/man-pages/man2/fork.2.html


0

La fonction fork () est utilisée pour créer un nouveau processus en dupliquant le processus existant à partir duquel il est appelé. Le processus existant à partir duquel cette fonction est appelée devient le processus parent et le processus nouvellement créé devient le processus enfant. Comme déjà indiqué, l'enfant est une copie du parent mais il y a quelques exceptions à cela.

  • L'enfant a un PID unique comme tout autre processus en cours d'exécution dans le système d'exploitation.

  • L'enfant a un ID de processus parent qui est le même que le PID du
    processus qui l'a créé.

  • Les compteurs d'utilisation des ressources et de temps CPU sont remis à zéro dans le processus enfant.

  • L'ensemble des signaux en attente dans l'enfant est vide.

  • L'enfant n'hérite d'aucun minuteur de son parent

Exemple :

    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <stdio.h>
    #include <sys/wait.h>
    #include <stdlib.h>

    int var_glb; /* A global variable*/

int main(void)
{
    pid_t childPID;
    int var_lcl = 0;

    childPID = fork();

    if(childPID >= 0) // fork was successful
    {
        if(childPID == 0) // child process
        {
            var_lcl++;
            var_glb++;
            printf("\n Child Process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
        }
        else //Parent process
        {
            var_lcl = 10;
            var_glb = 20;
            printf("\n Parent process :: var_lcl = [%d], var_glb[%d]\n", var_lcl, var_glb);
        }
    }
    else // fork failed
    {
        printf("\n Fork failed, quitting!!!!!!\n");
        return 1;
    }

    return 0;
}

Maintenant, lorsque le code ci-dessus est compilé et exécuté:

$ ./fork

Parent process :: var_lcl = [10], var_glb[20]

Child Process :: var_lcl = [1], var_glb[1]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.