Récemment, on m'a posé une question dans une interview quelle est la différence entre un processus et un fil. Vraiment, je ne connaissais pas la réponse. J'ai réfléchi une minute et j'ai donné une réponse très bizarre.
Les threads partagent la même mémoire, pas les processus. Après avoir répondu à cela, l'intervieweur m'a fait un sourire diabolique et m'a tiré les questions suivantes:
Q. Connaissez-vous les segments dans lesquels un programme est divisé?
Ma réponse: oui (je pensais que c'était facile) Stack, Data, Code, Heap
Q. Alors, dites-moi: quels segments les threads partagent-ils?
Je n'ai pas pu répondre à cette question et j'ai fini par les dire tous.
S'il vous plaît, quelqu'un peut-il présenter les réponses correctes et impressionnantes pour la différence entre un processus et un fil?
Réponses:
Vous avez à peu près raison, mais les threads partagent tous les segments sauf la pile. Les threads ont des piles d'appels indépendantes, mais la mémoire des autres piles de threads est toujours accessible et en théorie, vous pouvez conserver un pointeur sur la mémoire dans le cadre de pile local d'un autre thread (bien que vous devriez probablement trouver un meilleur endroit pour mettre cette mémoire!).
la source
De Wikipedia (je pense que cela ferait une très bonne réponse pour l'intervieweur: P)
la source
Ce qu'il faut vraiment souligner, c'est qu'il y a vraiment deux aspects à cette question - l'aspect théorique et l'aspect des implémentations.
Voyons d'abord l'aspect théorique. Vous devez comprendre ce qu'est un processus sur le plan conceptuel pour comprendre la différence entre un processus et un thread et ce qui est partagé entre eux.
Nous avons ce qui suit de la section 2.2.2 Le modèle de fil classique dans les systèmes d'exploitation modernes 3e par Tanenbaum:
Il continue:
Plus loin, il fournit le tableau suivant:
Ce qui précède est ce dont vous avez besoin pour que les threads fonctionnent. Comme d'autres l'ont souligné, des éléments comme les segments sont des détails d'implémentation dépendants du système d'exploitation.
la source
Dites à l'intervieweur que cela dépend entièrement de la mise en œuvre du système d'exploitation.
Prenez Windows x86 par exemple. Il n'y a que 2 segments [1], Code et Data. Et ils sont tous deux mappés à tout l'espace d'adressage de 2 Go (linéaire, utilisateur). Base = 0, limite = 2 Go. Ils en auraient fait un mais x86 ne permet pas qu'un segment soit à la fois en lecture / écriture et en exécution. Ils en ont donc créé deux et ont défini CS pour pointer vers le descripteur de code, et le reste (DS, ES, SS, etc.) pour pointer vers l'autre [2]. Mais les deux pointent vers les mêmes choses!
La personne qui vous a interviewé a fait une supposition cachée qu'elle n'a pas déclaré, et c'est une astuce stupide à tirer.
Donc en ce qui concerne
Les segments ne sont pas pertinents pour la question, au moins sous Windows. Les threads partagent tout l'espace d'adressage. Il n'y a qu'un seul segment de pile, SS, et il pointe exactement vers les mêmes choses que DS, ES et CS [2]. C'est-à-dire tout l'espace utilisateur sanglant . 0-2 Go. Bien sûr, cela ne signifie pas que les threads n'ont qu'une seule pile. Naturellement, chacun a sa propre pile, mais les segments x86 ne sont pas utilisés à cette fin.
Peut-être que * nix fait quelque chose de différent. Qui sait. La prémisse sur laquelle la question était fondée était brisée.
ntsd notepad
:cs=001b ss=0023 ds=0023 es=0023
la source
Généralement, les threads sont appelés processus légers. Si nous divisons la mémoire en trois sections, ce sera: Code, données et pile. Chaque processus a ses propres sections de code, de données et de pile et, en raison de ce contexte, le temps de changement de contexte est un peu élevé. Pour réduire le temps de changement de contexte, les gens ont trouvé un concept de thread, qui partage le segment de données et de code avec d'autres threads / processus et il a son propre segment STACK.
la source
Un processus a des segments de code, de données, de tas et de pile. Maintenant, le pointeur d'instruction (IP) d'un thread OU threads pointe vers le segment de code du processus. Les segments de données et de segment de mémoire sont partagés par tous les threads. Qu'en est-il maintenant de la zone de pile? Quelle est en réalité la zone de pile? C'est une zone créée par le processus uniquement pour son thread à utiliser ... car les piles peuvent être utilisées de manière beaucoup plus rapide que les tas, etc. La zone de pile du processus est divisée en threads, c'est-à-dire s'il y a 3 threads, alors le la zone de pile du processus est divisée en 3 parties et chacune est donnée aux 3 fils. En d'autres termes, lorsque nous disons que chaque thread a sa propre pile, cette pile fait en fait partie de la zone de pile de processus allouée à chaque thread. Lorsqu'un thread termine son exécution, la pile du thread est récupérée par le processus. En réalité, non seulement la pile d'un processus est divisée entre les threads, mais l'ensemble des registres qu'un thread utilise comme SP, PC et les registres d'état sont les registres du processus. Ainsi, en matière de partage, le code, les données et les zones de tas sont partagés, tandis que la zone de pile est simplement divisée entre les threads.
la source
Les threads partagent le code et les segments de données et le tas, mais ils ne partagent pas la pile.
la source
Les threads partagent des données et du code, contrairement aux processus. La pile n'est pas partagée pour les deux.
Les processus peuvent également partager de la mémoire, plus précisément du code, par exemple après un
Fork()
, mais il s'agit d'un détail d'implémentation et d'une optimisation (du système d'exploitation). Le code partagé par plusieurs processus sera (espérons-le) dupliqué lors de la première écriture dans le code - c'est ce qu'on appelle la copie sur écriture . Je ne suis pas sûr de la sémantique exacte du code des threads, mais je suppose que le code est partagé.1 Le code est logiquement privé mais peut être partagé pour des raisons de performances. 2 Je ne suis pas sûr à 100%.
la source
Les discussions partagent tout [1]. Il existe un espace d'adressage pour l'ensemble du processus.
Chaque thread a sa propre pile et ses propres registres, mais les piles de tous les threads sont visibles dans l'espace d'adressage partagé.
Si un thread alloue un objet sur sa pile et envoie l'adresse à un autre thread, ils auront tous deux un accès égal à cet objet.
En fait, je viens de remarquer un problème plus large: je pense que vous confondez deux utilisations du segment de mot .
Le format de fichier d'un exécutable (par exemple, ELF) comporte des sections distinctes, qui peuvent être appelées segments, contenant du code compilé (texte), des données initialisées, des symboles de l'éditeur de liens, des informations de débogage, etc. Il n'y a pas de segments de tas ou de pile. ici, car ce sont des constructions uniquement à l'exécution.
Ces segments de fichiers binaires peuvent être mappés séparément dans l'espace d'adressage du processus, avec des autorisations différentes (par exemple, un exécutable en lecture seule pour le code / texte et un non exécutable en copie sur écriture pour les données initialisées).
Les zones de cet espace d'adressage sont utilisées à des fins différentes, comme l'allocation de segments de mémoire et les piles de threads, par convention (appliquées par vos bibliothèques d'exécution de langage). Cependant, il ne s'agit que de mémoire et probablement pas segmenté, sauf si vous exécutez en mode 8086 virtuel. La pile de chaque thread est un bloc de mémoire alloué au moment de la création du thread, l'adresse de la pile actuelle étant stockée dans un registre de pointeur de pile, et chaque thread conserve son propre pointeur de pile avec ses autres registres.
[1] D'accord, je sais: masques de signaux, TSS / TSD etc. L'espace d'adressage, y compris tous ses segments de programme mappés, est cependant toujours partagé.
la source
Dans un framework x86, on peut diviser autant de segments (jusqu'à 2 ^ 16-1). Les directives ASM SEGMENT / ENDS le permettent, et les opérateurs SEG et OFFSET permettent l'initialisation des registres de segments. CS: IP sont généralement initialisés par le chargeur, mais pour DS, ES, SS, l'application est responsable de l'initialisation. De nombreux environnements permettent les soi-disant "définitions de segment simplifiées" comme .code, .data, .bss, .stack etc. et, en fonction également du "modèle de mémoire" (petit, grand, compact, etc.) le chargeur initialise les registres de segment en conséquence. Habituellement .data, .bss, .stack et autres segments habituels (je ne l'ai pas fait depuis 20 ans donc je ne me souviens pas de tout) sont regroupés en un seul groupe - c'est pourquoi généralement DS, ES et SS pointent vers le même domaine, mais ce n'est que pour simplifier les choses.
En général, tous les registres de segments peuvent avoir des valeurs différentes lors de l'exécution. Donc, la question de l'entretien était juste: lequel des CODE, DATA et STACK est partagé entre les threads. La gestion de tas est autre chose - c'est simplement une séquence d'appels au système d'exploitation. Mais que se passe-t-il si vous n'avez pas du tout d'OS, comme dans un système embarqué - pouvez-vous toujours avoir new / delete dans votre code?
Mon conseil aux jeunes - lisez un bon livre de programmation d'assemblage. Il semble que les programmes universitaires soient assez pauvres à cet égard.
la source
Extrait de: The Linux Programming Interface: A Linux and UNIX System Programming Handbook, Michael Kerrisk , page 619
la source
Le thread partage le tas (il existe des recherches sur le tas spécifique au thread) mais l'implémentation actuelle partage le tas. (et bien sûr le code)
la source
En cours, tous les threads partagent des ressources système comme la mémoire de tas, etc. tandis que le thread a sa propre pile
Ainsi, votre ans doit être une mémoire de tas que tous les threads partagent pour un processus.
la source