Quels threads partagent en général?

20

Eh bien, c'est une question générale. Et si quelqu'un veut rendre l'implémentation spécifique, je préférerai les choses liées à Unix. Mais il faut d'abord connaître les problèmes suivants en général:

Je lis qu'un seul processus peut avoir plusieurs threads. Plusieurs threads du même processus partagent des choses entre eux. Je veux savoir ce qu'ils partagent et ce qui ne l'est pas. Considérant que le processus comprend l'espace d'adressage, la pile, le tas, les variables globales, le code, les données, les ressources du système d'exploitation, qu'est-ce qui est partagé par les threads? J'ai les suppositions suivantes:

  1. Variables globales - J'ai lu la variable globale de partage de thread. De plus, lors de la programmation en Java et C #, j'ai créé des threads pour partager des variables de niveau classe. Je pense donc que les threads partagent des variables globales (bien que je ne sois pas sûr que les concepts des langages de programmation de haut niveau se traduisent tels quels par des faits de bas niveau de système d'exploitation).

  2. Tas - Puisque la variable globale est stockée dans le tas, le tas est partagé entre les threads.

  3. Pile - Puisque chaque thread peut avoir sa propre séquence / code d'exécution, il doit avoir sa propre pile sur laquelle il peut pousser / éclater le contenu de son compteur de programme (quand les appels de fonction et les retours se produisent). Les threads du même processus ne partagent donc pas la pile.

Maintenant, je ne suis pas sûr du partage des choses suivantes

  1. Espace d'adressage - Je ne sais pas exactement ce qui compte dans l'espace d'adressage. Mais je suppose que l'espace d'adressage est généralement utilisé dans le contexte des processus, pas des threads. Et puisque tous les threads du même processus résident dans le même espace d'adressage que le processus parent, il est dit que les threads partagent l'espace d'adressage. (Mais alors, ils conservent une pile différente dans le même espace d'adressage?)

  2. Ressources OS - Je suppose que cela peut être très spécifique à l'implémentation. Par exemple, le processus parent peut donner de manière sélective le handle du même fichier à certains de ses threads et pas à tous. Ou je me trompe et les ressources du système d'exploitation signifient autre chose que des fichiers?

  3. Code - Les threads peuvent avoir un code différent, donc le partage de code n'est pas toujours le cas.

  4. Données - Je ne sais pas quoi considérer sous les données. Mais assurez-vous que les variables globales sont partagées entre les threads. Et bien sûr, les variables locales ne sont pas partagées de la même manière.

Dans l'ensemble, je suis considérablement confus en raison des termes vagues, des super-généralisations effectuées dans les livres sur les systèmes d'exploitation et des détails spécifiques à l'implémentation fournis en ligne. J'essaie donc de trouver une réponse qui puisse me satisfaire.

Maha
la source

Réponses:

13

En général, chaque thread a ses propres registres (y compris son propre compteur de programme), son propre pointeur de pile et sa propre pile. Tout le reste est partagé entre les threads partageant un processus.

En particulier, un processus est généralement considéré comme consistant en un ensemble de threads partageant un espace d'adressage, un tas, des données statiques, des segments de code et des descripteurs de fichier * .

Un espace d'adressage est simplement le mappage d'adresses logiques à des morceaux spécifiques de mémoire physique. Ainsi, lorsque nous disons que tous les threads d'un processus partagent le même espace d'adressage, nous voulons dire que lors de l'accès à une variable foode portée globale, tous les threads verront la même variable. De même, les threads peuvent tous exécuter un point différent dans le code à un moment donné, mais ils sont tous autorisés à appeler la fonction globale bar(), qui correspondra à la même fonction pour chaque thread du processus.

La plupart des systèmes d'exploitation modernes ont ajouté une notion de stockage local de threads , qui sont des variables de portée globale qui ne sont pas partagées. L'exemple habituel d'utilisation de ceci est pour la variable errno. Il s'agit d'une variable unique de portée globale, mais dans la plupart des systèmes d'exploitation modernes, chaque thread reçoit sa propre copie locale, de sorte qu'une erreur dans un appel de bibliothèque sur un thread n'affectera pas le comportement des autres threads.

* Il y a un état de processus supplémentaire partagé par tous les threads d'un processus, des choses comme l'ID de processus, la gestion du signal et les verrous de fichiers. Pour une liste complète de l'état du processus partagé par les threads, vous devez consulter la documentation de l'implémentation de threading spécifique. Par exemple, la page de manuel pthreads .

Logique errante
la source
4

Les discussions se présentent sous deux angles: les systèmes d'exploitation et les langages de programmation. Dans les deux cas, les attributs d'un thread varient.

Une définition minimale d'un thread est que ce sont des choses qui se passent en séquence, une chose après l'autre.

Dans un modèle d'exécution de machine typique, chaque thread a son propre ensemble de registres à usage général et son propre compteur de programme. Si la machine définit un registre spécifique en tant que pointeur de pile, il y a une copie par thread.

Du point de vue du système d'exploitation, le minimum qu'un système d'exploitation doit faire pour prendre en charge les threads est de fournir un moyen de basculer entre eux. Cela peut se produire automatiquement ( multitâche prémptif ou uniquement lorsque le thread fait une demande explicite (multitâche coopératif; dans ce cas, les threads sont parfois appelés fibres ). Il existe également des modèles hybrides avec à la fois des rendements de préemption et de coopération, par exemple une préemption entre les threads de différents groupes. ou des tâches mais des rendements explicites entre les threads du même groupe / tâche. La commutation entre les threads implique au minimum la sauvegarde des valeurs de registre de l'ancien thread et la restauration des valeurs de registre du nouveau thread.

Dans un système d'exploitation multitâche qui assure l' isolement entre les tâches (ou processus , vous pouvez traiter ces termes comme des synonymes dans un contexte de système d'exploitation), chaque tâche a ses propres ressources, en particulier l'espace d'adressage, mais aussi des fichiers ouverts, des privilèges, etc. à fournir par le noyau du système d' exploitation , une entité qui est au-dessus des processus. Chaque tâche a normalement au moins un thread - une tâche qui n'exécute pas de code n'est pas très utile. Le système d'exploitation peut ou non prendre en charge plusieurs threads dans la même tâche; par exemple, l'Unix d'origine ne l'a pas fait. Une tâche peut toujours exécuter plusieurs threads en s'arrangeant pour basculer entre eux - cela ne nécessite aucun privilège spécial. C'est ce qu'on appelle les « threads utilisateur», En particulier dans un contexte Unix. De nos jours, la plupart des systèmes Unix fournissent des threads du noyau, en particulier parce que c'est la seule façon d'avoir plusieurs threads du même processus s'exécutant sur différents processeurs.

La plupart des ressources du système d'exploitation en dehors du temps de calcul sont attachées aux tâches, pas aux threads. Certains systèmes d'exploitation (par exemple, Linux) délimitent explicitement les piles, auquel cas chaque thread a le sien; mais il y a des OS où le noyau ne sait rien des piles, ils ne sont qu'une partie du tas en ce qui le concerne. Le noyau gère également généralement un contexte de noyau pour chaque thread, qui est une structure de données contenant des informations sur ce que le thread fait actuellement; cela permet au noyau de gérer simultanément plusieurs threads bloqués dans un appel système.

En ce qui concerne le système d'exploitation, les threads d'une tâche exécutent le même code, mais sont à des positions différentes dans ce code (valeurs de compteur de programme différentes). Il peut ou non arriver que certaines parties du code d'un programme soient toujours exécutées dans des threads spécifiques, mais il existe généralement du code commun (par exemple des fonctions utilitaires) qui peut être appelé à partir de n'importe quel thread. Tous les threads voient les mêmes données, sinon ils seraient considérés comme des tâches différentes; si certaines données ne sont accessibles que par un thread particulier, il s'agit généralement uniquement de la compétence du langage de programmation, et non du système d'exploitation.

Dans la plupart des langages de programmation, le stockage est partagé entre les threads du même programme. Il s'agit d'un modèle de mémoire partagée de programmation simultanée; c'est très populaire, mais aussi très sujet aux erreurs, car le programmeur doit être prudent lorsque les mêmes données sont accessibles par plusieurs threads car des conditions de concurrence peuvent se produire. Notez que même les variables locales peuvent être partagées entre les threads: «variable locale» (généralement) signifie une variable dont le nom n'est valide que pendant une exécution d'une fonction, mais un autre thread peut obtenir un pointeur sur cette variable et y accéder.

Il existe également des langages de programmation où chaque thread a son propre stockage, et la communication entre eux se fait en envoyant des messages sur les canaux de communication. Il s'agit du modèle de transmission de messages de programmation simultanée. Erlangest le principal langage de programmation qui se concentre sur le passage de messages; son environnement d'exécution a une gestion très légère des threads, et il encourage les programmes écrits avec de nombreux threads de courte durée, contrairement à la plupart des autres langages de programmation où la création d'un thread est une opération relativement coûteuse et l'environnement d'exécution ne peut pas prendre en charge un très grand nombre de threads en même temps. Le sous-ensemble séquentiel d'Erlang (la partie du langage qui se produit dans un thread, en particulier la manipulation de données) est (principalement) purement fonctionnel; ainsi un thread peut envoyer un message à un autre thread contenant des données et aucun des deux threads ne doit se soucier des données modifiées par l'autre thread pendant qu'il l'utilise.

Certaines langues mélangent les deux modèles en offrant un stockage local par thread, avec ou sans système de type pour distinguer l'emplacement de stockage local par thread des emplacements globaux. Le stockage local par thread est généralement une fonctionnalité pratique qui permet à un nom de variable de désigner différents emplacements de stockage dans différents threads.

Quelques suivis (difficiles) qui peuvent être intéressants pour comprendre ce que sont les fils:

  • Quel est le minimum qu'un noyau doit faire pour prendre en charge plusieurs threads?
  • Dans un environnement multiprocesseur, que faut-il pour migrer un thread d'un processeur à un autre?
  • Que faudrait-il pour implémenter le multithreading coopératif ( coroutines ) dans votre langage de programmation préféré sans prise en charge par le système d'exploitation et sans utiliser sa prise en charge intégrée le cas échéant? (Attention, la plupart des langages de programmation n'ont pas les primitives nécessaires pour implémenter des coroutines dans un seul thread.)
  • À quoi pourrait ressembler un langage de programmation s'il avait une concurrence mais pas de concept (explicite) de threads? (Premier exemple: le pi-calcul .)
Gilles 'SO- arrête d'être méchant'
la source
C'est la chose la plus intéressante que j'ai lue depuis des mois.
JSON
2

Ça dépend. Si vous considérez les threads tels que définis par exemple par POSIX (et offerts par les systèmes Unix) ou par Windows (pas familier avec la dernière, vous devrez demander spécifiquement), alors cela donne votre réponse (essentiellement comme l'explique la réponse @WanderingLogic). Linux a sa propre idée des threads, en utilisant l' clone(2)appel système non standard . Il offre un contrôle assez fin de ce que les parents et les enfants partagent. Cela va jusqu'à avoir fork(2)et vfork(2)essentiellement des wrappers autour de l'interne clone(2), en l'appelant avec des drapeaux spécifiques, c'est-à-dire que vous pouvez créer des "threads" qui ne partagent presque rien avec le parent. Consultez sa page de manuel pour plus de détails, ils sont disponibles en ligne par exemple ici . Oui, Linux propose des threads de style POSIX, mais bien plus encore.

vonbrand
la source
0

Partage des discussions:

  • Espace d'adressage
  • Tas
  • Données statiques
  • Segments de code
  • Descripteurs de fichiers
  • Variables globales
  • Processus enfants
  • Alarmes en attente
  • Signaux et gestionnaires de signaux
  • Information comptable

Les discussions ont leur propre:

  • Compteur de programme
  • Registres
  • Empiler
  • Etat
ericcurtin
la source