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?
Réponses:
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
:
fork
pour exécuter les programmes que vous appelez à partir de la ligne de commande.fork
pour 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.fork
pour 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.fork
est 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.fork
indirectement pour démarrer les processus enfants. Par exemple, chaque fois que vous utilisez une commande comme subprocess.Popen
en Python, vous fork
un processus enfant et lisez sa sortie. Cela permet aux programmes de fonctionner ensemble.L'utilisation typique de fork
dans 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 exec
et 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.
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()
.
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.
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éé)
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.
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.
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.
system()
. J'étais en train de lire fork()
parce que je veux que mon code C exécute un script python.
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.
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.
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
Il faut d'abord comprendre ce qu'est l'appel système fork (). Laissez-moi expliquer
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.
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 ():
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.
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.
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.
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]