GNU/Linux >> Tutoriels Linux >  >> Linux

Introduction aux threads Linux – Partie I

Un thread d'exécution est souvent considéré comme la plus petite unité de traitement sur laquelle travaille un planificateur.

Un processus peut avoir plusieurs threads d'exécution qui sont exécutés de manière asynchrone.

Cette exécution asynchrone permet à chaque thread de gérer indépendamment un travail ou un service particulier. Par conséquent, plusieurs threads s'exécutant dans un processus gèrent leurs services, ce qui constitue globalement la capacité complète du processus.

Dans cet article, nous aborderons les principes fondamentaux des threads et développerons la compréhension de base requise pour apprendre la pratique aspects des threads Linux.

Linux Threads Series :partie 1 (cet article), partie 2, partie 3.

Pourquoi les threads sont-ils obligatoires ?

Maintenant, on pourrait se demander pourquoi avons-nous besoin de plusieurs threads dans un processus ?? Pourquoi un processus avec un seul thread principal (par défaut) ne peut-il pas être utilisé dans toutes les situations.

Eh bien, pour répondre à cela, prenons un exemple :

Supposons qu'il existe un processus recevant des entrées en temps réel et correspondant à chaque entrée, il doit produire une certaine sortie. Maintenant, si le processus n'est pas multi-thread, c'est-à-dire si le processus n'implique pas plusieurs threads, alors l'ensemble du traitement dans le processus devient synchrone. Cela signifie que le processus prend une entrée, la traite et produit une sortie.

La limitation dans la conception ci-dessus est que le processus ne peut pas accepter une entrée tant qu'il n'a pas terminé de traiter la précédente et dans le cas où le traitement d'une entrée prend plus de temps que prévu, l'acceptation d'autres entrées est suspendue.

Pour tenir compte de l'impact de la limitation ci-dessus, si nous mappons l'exemple générique ci-dessus avec un processus de serveur de socket qui peut accepter une connexion d'entrée, traitez-les et fournissez la sortie au client de socket. Maintenant, si lors du traitement d'une entrée si le processus du serveur prend plus de temps que prévu et qu'entre-temps une autre entrée (demande de connexion) arrive sur le serveur de socket, le processus du serveur ne pourra pas accepter la nouvelle connexion d'entrée car elle est déjà bloquée dans traitement de l'ancienne connexion d'entrée. Cela peut entraîner un délai d'expiration de la connexion au niveau du client socket, ce qui n'est pas du tout souhaité.

Cela montre que le modèle d'exécution synchrone ne peut pas être appliqué partout, d'où l'exigence d'un modèle d'exécution asynchrone ressenti qui est implémenté à l'aide de threads.

Différence entre les threads et les processus

Voici quelques-unes des principales différences entre le thread et les processus :

  • Les processus ne partagent pas leur espace d'adressage tandis que les threads s'exécutant sous le même processus partagent l'espace d'adressage.
  • D'après ce qui précède, il est clair que les processus s'exécutent indépendamment les uns des autres et que la synchronisation entre les processus est prise en charge par le noyau uniquement, tandis que la synchronisation des threads doit être prise en charge par le processus sous lequel les threads s'exécutent
  • La commutation de contexte entre les threads est rapide par rapport à la commutation de contexte entre les processus
  • L'interaction entre deux processus n'est réalisée que par la communication inter-processus standard, tandis que les threads s'exécutant sous le même processus peuvent communiquer facilement car ils partagent la plupart des ressources telles que la mémoire, le segment de texte, etc.

Threads utilisateur vs threads noyau

Les threads peuvent exister dans l'espace utilisateur ainsi que dans l'espace noyau.

Un espace utilisateur les threads sont créés, contrôlés et détruits à l'aide des bibliothèques de threads de l'espace utilisateur. Ces threads ne sont pas connus du noyau et, par conséquent, le noyau n'est nulle part impliqué dans leur traitement. Ces threads suivent le multitâche coopératif dans lequel un thread libère le processeur de son propre chef, c'est-à-dire que le planificateur ne peut pas préempter le thread. L'avantage des threads de l'espace utilisateur est que la commutation entre deux threads n'implique pas beaucoup de surcharge et est généralement très rapide alors que du côté négatif puisque ces threads suivent le multitâche coopératif, donc si un thread est bloqué, tout le processus est bloqué.

Un espace noyau thread est créé, contrôlé et détruit par le noyau. Pour chaque thread existant dans l'espace utilisateur, il existe un thread noyau correspondant. Étant donné que ces threads sont gérés par le noyau, ils suivent donc le multitâche préemptif dans lequel le planificateur peut préempter un thread en cours d'exécution avec un thread de priorité plus élevée qui est prêt à être exécuté. Le principal avantage des threads du noyau est que même si l'un des threads est bloqué, l'ensemble du processus n'est pas bloqué car les threads du noyau suivent une planification préemptive, tandis que du côté négatif, le changement de contexte n'est pas très rapide par rapport aux threads de l'espace utilisateur.

Si nous parlons de Linux, les threads du noyau sont optimisés à tel point qu'ils sont considérés comme meilleurs que les threads de l'espace utilisateur et principalement utilisés dans tous les scénarios, sauf lorsque l'exigence principale est celle du multitâche coopératif.

Problème avec les fils

Il y a quelques problèmes majeurs qui surviennent lors de l'utilisation des threads :

  • De nombreux systèmes d'exploitation n'implémentent pas les threads en tant que processus, mais les considèrent comme faisant partie du processus parent. Dans ce cas, que se passerait-il si un thread appelait fork() ou pire encore, que se passerait-il si un thread exécutait un nouveau binaire ? ? Ces scénarios peuvent avoir des conséquences dangereuses, par exemple dans le problème ultérieur, tout le processus parent pourrait être remplacé par l'espace d'adressage du binaire nouvellement exécuté. Ce n'est pas du tout souhaité. Linux, qui est conforme à POSIX, s'assure que l'appel d'un fork() duplique uniquement le thread qui a appelé la fonction fork() tandis qu'un exécutable de l'un des threads arrêterait tous les threads du processus parent.
  • Un autre problème qui peut survenir est celui des problèmes de simultanéité. Étant donné que les threads partagent tous les segments (à l'exception du segment de pile) et peuvent être préemptés à tout moment par le planificateur, toute variable globale ou structure de données pouvant être laissée dans un état incohérent par la préemption d'un thread pourrait causer de graves problèmes lors de la prochaine haute priorité thread exécute la même fonction et utilise les mêmes variables ou structures de données.

Pour le problème 1 mentionné ci-dessus, tout ce que nous pouvons dire, c'est qu'il s'agit d'un problème de conception et que la conception des applications doit être réalisée de manière à minimiser les problèmes de ce type.

Pour le problème 2 mentionné ci-dessus, en utilisant des mécanismes de verrouillage, le programmeur peut verrouiller un morceau de code à l'intérieur d'une fonction de sorte que même si un changement de contexte se produit (lorsque la variable globale de la fonction et les structures de données étaient dans un état incohérent), le thread suivant ne peut pas non plus exécutez le même code jusqu'à ce que le bloc de code verrouillé à l'intérieur de la fonction soit déverrouillé par le thread précédent (ou le thread qui l'a acquis).


Linux
  1. Fondamentaux des signaux Linux - Partie I

  2. Introduction aux principes fondamentaux du routage IP Linux (Partie 1)

  3. Créer un processus Linux ?

  4. Créer un démon sous Linux

  5. Linux :transformer en service

Exemples de commandes Linux curl – Partie 2

Comment tuer un processus sous Linux

Commande Pstree sous Linux

Commande Kill sous Linux

Une introduction au navigateur Vivaldi sous Linux

Surveillance des processus sous Linux