Les threads sont-ils copiés lors de l'appel de fork?

31

Si j'ai un programme fonctionnant avec des threads et que j'appelle fork()sur un système basé sur Unix, les threads sont-ils copiés? Je sais que la mémoire virtuelle du processus actuel est copiée 1: 1 dans le nouveau processus généré. Je sais que les threads ont leur propre pile dans la mémoire virtuelle d'un processus. Ainsi, au moins la pile de threads doit également être copiée. Cependant, je ne sais pas s'il y a quelque chose de plus dans les threads qui ne réside pas dans la mémoire virtuelle et n'est donc PAS copié. Si ce n'est pas le cas, les deux processus partagent-ils les threads ou sont-ils des copies indépendantes?

Jean-Baptiste Yunès
la source

Réponses:

29

Non.

Les threads ne sont pas copiés fork(). La spécification POSIX dit (c'est moi qui souligne):

fork - créer un nouveau processus

Un processus doit être créé avec un seul thread . Si un processus multithread appelle fork (), le nouveau processus doit contenir une réplique du thread appelant et tout son espace d'adressage, incluant éventuellement les états des mutex et d'autres ressources. Par conséquent, pour éviter les erreurs, le processus enfant ne peut exécuter que des opérations async-signal-safe jusqu'à ce que l'une des fonctions exec soit appelée.

Pour contourner ce problème, il existe une pthread_atfork()fonction pour vous aider.

Jean-Baptiste Yunès
la source
7

fourchette homme :

Le processus enfant est créé avec un seul thread, celui qui a appelé fork (). L'espace d'adressage virtuel complet du parent est répliqué dans l'enfant, y compris les états des mutex, des variables de condition et d'autres objets pthreads; l'utilisation de pthread_atfork (3) peut être utile pour traiter les problèmes que cela peut provoquer.

kaylum
la source
Mais cela semble étrange: pourquoi la pile pour les threads du processus appelant fork serait-elle copiée si les threads réels (que je ne connais pas contiennent du stockage ailleurs que la mémoire virtuelle) ne le sont pas?
Eh bien, le pourquoi est une question complètement différente. Je ne connais pas les décisions de conception originales qui ont conduit à cette mise en œuvre. Si vous êtes intéressé, vous devriez poser cette question séparément.
kaylum
@dip mais la pile d'autres threads n'est pas copiée, qui l'a dit?
Jean-Baptiste Yunès
1
@ Jean-BaptisteYunès Dans les systèmes Unix, il existe une structure représentant la mémoire virtuelle d'un processus. C'est celui copié. Pas seulement le tas et le bss
6
vous obtenez tout l'espace mémoire - et donc les piles de tous les threads. vous en avez besoin car il n'y a aucune limitation sur l'endroit où les pointeurs vivant dans la pile (ou la mémoire statique) accessibles au thread restant pointent - ils pourraient très bien pointer vers des données qui vivaient dans la pile de certains threads dans le processus d'origine
davidbak
4

De la base Open Group Spécifications Numéro 7, édition de 2018 fourche :

Un processus doit être créé avec un seul thread. Si un processus multithread appelle fork () , le nouveau processus doit contenir une réplique du thread appelant et tout son espace d'adressage, incluant éventuellement les états des mutex et d'autres ressources. Par conséquent, pour éviter les erreurs, le processus enfant ne peut exécuter que des opérations de sécurité du signal asynchrone jusqu'à ce qu'une des fonctions exec soit appelée.

Lorsque l'application appelle fork () à partir d'un gestionnaire de signaux et que l'un des gestionnaires de fork enregistrés par pthread_atfork () appelle une fonction qui n'est pas async-signal-safe, le comportement n'est pas défini.

Ian Abbott
la source
-2

A l'origine, "fork" a été atteint en écrivant la tâche sur le disque puis, plutôt que de lire dans un thread différent (ce qui serait fait si vous échangiez la tâche avec une autre), en modifiant l'ID de tâche de l'image toujours en mémoire et en continuant avec son exécution (comme la nouvelle tâche). Il s'agissait d'une modification très simple du mécanisme de commutation de tâche de base, où une seule tâche occupait la mémoire RAM à la fois.

Bien sûr, à mesure que la gestion de la mémoire devenait plus élaborée, ce schéma a été modifié pour s'adapter au nouvel environnement.

Hot Licks
la source
Curieux de savoir pourquoi cela a été rejeté. C'est ainsi qu'Unix l'a fait.
Hot Licks
C'est un aperçu intéressant, mais où mentionne-t-il les fils? Ça ne ressemble pas à une réponse pour moi.
wastl