GNU/Linux >> Tutoriels Linux >  >> Linux

Perte de temps avec execv() et fork()

Pas plus longtemps. Il y a quelque chose qui s'appelle COW (Copy On Write), uniquement lorsque l'un des deux processus (Parent/Child) essaie d'écrire dans une donnée partagée, celle-ci est copiée.

Dans le passé :
Le fork() l'appel système a copié l'espace d'adressage du processus appelant (le parent) pour créer un nouveau processus (l'enfant). La copie de l'espace d'adressage du parent dans l'enfant était la partie la plus coûteuse du fork() opération.

Maintenant :
Un appel au fork() est fréquemment suivi presque immédiatement d'un appel à exec() dans le processus enfant, qui remplace la mémoire de l'enfant par un nouveau programme. C'est ce que fait généralement le shell, par exemple. Dans ce cas, le temps passé à copier l'espace d'adressage du parent est largement perdu, car le processus enfant utilisera très peu de sa mémoire avant d'appeler exec() .

Pour cette raison, les versions ultérieures d'Unix ont tiré parti du matériel de mémoire virtuelle pour permettre au parent et à l'enfant de partager la mémoire mappée dans leurs espaces d'adressage respectifs jusqu'à ce que l'un des processus la modifie réellement. Cette technique est connue sous le nom de copie sur écriture . Pour cela, le fork() le noyau copierait les mappages d'espace d'adressage du parent vers l'enfant au lieu du contenu des pages mappées, et en même temps marquerait les pages désormais partagées en lecture seule. Lorsque l'un des deux processus essaie d'écrire sur l'une de ces pages partagées, le processus prend un défaut de page. À ce stade, le noyau Unix se rend compte que la page était en réalité une copie "virtuelle" ou "copie sur écriture", et il crée donc une nouvelle copie privée et inscriptible de la page pour le processus défaillant. De cette manière, le contenu des pages individuelles n'est pas réellement copié tant qu'il n'est pas réellement écrit. Cette optimisation fait un fork() suivi d'un exec() dans l'enfant beaucoup moins cher :l'enfant n'aura probablement besoin de copier qu'une seule page (la page actuelle de sa pile) avant d'appeler exec() .


Quel est l'avantage obtenu en utilisant ce combo (au lieu d'une autre solution) qui fait que les gens l'utilisent encore même si nous avons des déchets ?

Vous devez créer un nouveau processus d'une manière ou d'une autre. Il y a très peu de façons pour un programme en espace utilisateur d'accomplir cela. POSIX avait vfork() parallèlement au fork() , et certains systèmes peuvent avoir leurs propres mécanismes, tels que clone() spécifique à Linux , mais depuis 2008, POSIX ne spécifie que fork() et le posix_spawn() famille. Le fork + exec L'itinéraire est plus traditionnel, est bien compris et présente peu d'inconvénients (voir ci-dessous). Le posix_spawn la famille est conçue comme un but spécial substitut à utiliser dans des contextes qui présentent des difficultés pour fork(); vous pouvez trouver des détails dans la section "Justification" de sa spécification.

Cet extrait de la page de manuel Linux pour vfork() peut être éclairant :

Sous Linux, fork (2) est implémenté en utilisant des pages de copie sur écriture, donc la seule pénalité encourue par fork (2) est le temps et la mémoire nécessaires pour dupliquer les tables de pages du parent et pour créer une structure de tâches unique pour l'enfant . Cependant, au mauvais vieux temps, un fork (2) nécessiterait de faire une copie complète de l'espace de données de l'appelant, souvent inutilement, car généralement immédiatement après un exec (3) est terminé. Ainsi, pour plus d'efficacité, BSD a introduit le vfork () appel système, qui ne copie pas entièrement l'espace d'adressage du processus parent, mais emprunte la mémoire et le fil de contrôle du parent jusqu'à un appel à execve (2) ou une sortie s'est produite. Le processus parent a été suspendu pendant que l'enfant utilisait ses ressources. L'utilisation de vfork () était délicate :par exemple, ne pas modifier les données dans le processus parent dépendait de la connaissance des variables contenues dans un registre.

(Soulignement ajouté)

Ainsi, votre préoccupation concernant le gaspillage n'est pas fondée pour les systèmes modernes (pas limités à Linux), mais c'était en effet un problème historiquement, et il y avait en effet des mécanismes conçus pour l'éviter. De nos jours, la plupart de ces mécanismes sont obsolètes.


Une autre réponse indique :

Cependant, dans le mauvais vieux temps, un fork(2) nécessitait de faire une copie complète de l'espace de données de l'appelant, souvent inutilement, car généralement immédiatement après, un exec(3) est fait.

De toute évidence, les mauvais jours d'une personne sont beaucoup plus jeunes que les autres ne s'en souviennent.

Les systèmes UNIX d'origine n'avaient pas la mémoire pour exécuter plusieurs processus et ils n'avaient pas de MMU pour garder plusieurs processus en mémoire physique prêts à fonctionner dans le même espace d'adressage logique :ils ont échangé les processus sur le disque qui n'étaient pas en cours d'exécution.

L'appel système fork était presque entièrement identique à l'échange du processus actuel sur le disque, à l'exception de la valeur de retour et de not remplacement de la copie restante en mémoire par échange dans un autre processus. Comme vous deviez de toute façon échanger le processus parent pour exécuter l'enfant, fork+exec n'entraînait aucune surcharge.

Il est vrai qu'il y a eu une période où fork+exec était gênant :quand il y avait des MMU qui fournissaient un mappage entre l'espace d'adressage logique et physique mais que les défauts de page ne retenaient pas assez d'informations que la copie sur écriture et un certain nombre d'autres virtuels -les schémas de mémoire/demande de pagination étaient réalisables.

Cette situation était suffisamment pénible, pas seulement pour UNIX, pour que la gestion des défauts de page du matériel ait été adaptée pour devenir "rejouable" assez rapidement.


Linux
  1. Obtenir l'heure de l'utilisateur et du noyau d'un processus en cours d'exécution ?

  2. Heure de début du processus avec fuseau horaire ?

  3. Processus Linux - ID de processus, fork, execv, wait, waitpid Fonctions C

  4. États de processus Linux

  5. Différence entre CLOCK_REALTIME et CLOCK_MONOTONIC ?

Serveur NTP et meilleures pratiques

Comment suspendre un processus et le reprendre plus tard sous Linux

Comment définir la date, l'heure et le fuseau horaire dans RHEL 8

Comment trouver la date et l'heure d'installation du système d'exploitation Linux

Comment définir la date et l'heure sous Linux

horodatage, heure de modification et heure de création d'un fichier