web-dev-qa-db-fra.com

Threads vs Processus sous Linux

J'ai récemment entendu quelques personnes dire que sous Linux, il est presque toujours préférable d'utiliser des processus plutôt que des threads, car Linux est très efficace dans la gestion des processus et qu'il existe de nombreux problèmes (tels que le verrouillage) associés aux threads. Cependant, je suis méfiant, car il semble que les threads pourraient apporter un gain de performances assez important dans certaines situations.

Ma question est donc la suivante: est-ce que je devrais utiliser des processus ou des threads face à une situation que les threads et les processus pourraient très bien gérer? Par exemple, si j'écrivais un serveur Web, devrais-je utiliser des processus ou des threads (ou une combinaison des deux)?

237
user17918

Linux utilise un modèle de threading 1-1, avec (pour le noyau) aucune distinction entre processus et threads - tout est simplement une tâche pouvant être exécutée. *

Sous Linux, l’appel système clone clone une tâche, avec un niveau de partage configurable, parmi lesquels:

  • CLONE_FILES: partage la même table de descripteur de fichier (au lieu de créer une copie)
  • CLONE_PARENT: ne configurez pas de relation parent-enfant entre la nouvelle tâche et l'ancienne (sinon, getppid() de l'enfant = getpid() du parent)
  • CLONE_VM: partage le même espace mémoire (au lieu de créer une copie VACHE )

fork() appelle clone(le moins partage) et pthread_create() appelle clone(most plus). **

forking coûte un peu plus cher que pthread_createing en raison de la copie de tables et de la création de mappages COW pour la mémoire, mais les développeurs du noyau Linux ont essayé (et réussi) de minimiser ces coûts.

Basculer entre les tâches, si elles partagent le même espace mémoire et plusieurs tables, sera un peu moins cher que si elles ne sont pas partagées, car les données peuvent déjà être chargées dans le cache. Cependant, le changement de tâche est encore très rapide même si rien n'est partagé - c'est une autre chose que les développeurs du noyau Linux essaient de s'assurer (et réussissent à assurer).

En fait, si vous utilisez un système multiprocesseur, le partage non peut en réalité être bénéfique pour la performance: si chaque tâche est exécutée sur un processeur différent, la synchronisation de la mémoire partagée coûte cher.


* Simplifié. CLONE_THREAD provoque le partage des signaux (ce qui nécessite CLONE_SIGHAND, qui partage la table du gestionnaire de signaux).

** simplifié. Il existe à la fois SYS_fork et SYS_clone syscalls, mais dans le noyau, les sys_fork et sys_clone sont tous deux des enveloppes très minces autour de la même fonction do_fork, qui est elle-même est une fine enveloppe autour de copy_process. Oui, les termes process, thread et task sont utilisés de manière interchangeable dans le noyau Linux ...

307
ephemient

Linux (et même Unix) vous offre une troisième option.

Option 1 - processus

Créez un exécutable autonome qui gère une partie (ou toutes les parties) de votre application et appelez-le séparément pour chaque processus, par ex. le programme exécute des copies de lui-même pour déléguer des tâches.

Option 2 - discussions

Créer un exécutable autonome qui démarre avec un seul thread et créer des threads supplémentaires pour effectuer certaines tâches

Option 3 - fourche

Uniquement disponible sous Linux/Unix, c'est un peu différent. Un processus forké est vraiment son propre processus avec son propre espace d'adressage - il n'y a rien que l'enfant puisse faire (normalement) pour affecter l'espace d'adressage de son parent ou de ses frères et sœurs (contrairement à un thread) - vous bénéficiez ainsi d'une robustesse accrue.

Toutefois, les pages de mémoire ne sont pas copiées, elles sont copiées en écriture. Par conséquent, vous utilisez généralement moins de mémoire que vous ne le pensiez.

Considérons un programme de serveur Web composé de deux étapes:

  1. Lire les données de configuration et d'exécution
  2. Servir les demandes de page

Si vous utilisiez des threads, l'étape 1 serait effectuée une fois et l'étape 2 dans plusieurs threads. Si vous utilisez des processus "traditionnels", les étapes 1 et 2 doivent être répétées pour chaque processus, ainsi que la mémoire pour stocker la configuration et les données d'exécution dupliquées. Si vous avez utilisé fork (), vous pouvez exécuter l'étape 1 une fois, puis fork (), en laissant les données d'exécution et la configuration en mémoire, non modifiées, ni copiées.

Donc, il y a vraiment trois choix.

57
MarkR

Cela dépend de nombreux facteurs. Les processus sont plus lourds que les threads et ont un coût de démarrage et d'arrêt plus élevé. La communication interprocessus (IPC) est également plus dure et plus lente que la communication inter-thread.

À l'inverse, les processus sont plus sûrs et sécurisés que les threads, car chaque processus s'exécute dans son propre espace d'adressage virtuel. Si un processus se bloque ou a un dépassement de tampon, il n’affecte aucun autre processus, tandis que si un thread se bloque, tous les autres threads du processus sont supprimés, et si un thread a un dépassement de tampon, il s’ouvre. un trou de sécurité dans tous les threads.

Ainsi, si les modules de votre application peuvent fonctionner de manière essentiellement autonome avec peu de communication, vous devriez probablement utiliser des processus si vous pouvez vous permettre les coûts de démarrage et d'arrêt. Les performances de IPC seront minimes et vous serez un peu plus en sécurité contre les bugs et les failles de sécurité. Si vous avez besoin de chaque performance que vous pouvez obtenir ou si vous avez beaucoup de données partagées (telles que des structures de données complexes), optez pour les threads.

50
Adam Rosenfield

D'autres ont discuté des considérations.

Peut-être la différence importante est-elle que, dans Windows, les processus sont lourds et coûteux comparés aux threads, et que dans Linux, la différence est beaucoup plus petite, l'équation est donc équilibrée à un point différent.

10
dmckee

Il était une fois Il y avait Unix et dans ce bon vieil Unix, il y avait beaucoup de frais généraux pour les processus, alors certaines personnes intelligentes ont été de créer des threads, qui partageraient le même espace d'adressage avec le processus parent et ils n'avaient besoin que d'un contexte réduit commutateur, ce qui rendrait le changement de contexte plus efficace.

Dans un Linux contemporain (2.6.x), il n'y a pas beaucoup de différence de performances entre le changement de contexte d'un processus et celui d'un thread (seul le fichier MMU est additionnel pour le thread). Il y a un problème avec l'espace d'adressage partagé, ce qui signifie qu'un pointeur défectueux dans un thread peut corrompre la mémoire du processus parent ou d'un autre thread dans le même espace d'adressage.

Un processus est protégé par la MMU, donc un pointeur défectueux ne provoquera qu'un signal 11 et aucune corruption.

En général, j'utiliserais des processus (peu de surcharge de contexte dans Linux, mais une protection de la mémoire due à MMU), mais je me demandais si j'avais besoin d'une classe de planificateur temps réel, qui est une tasse de thé différente.

Pourquoi pensez-vous que les threads ont un tel gain de performances sous Linux? Avez-vous des données pour cela, ou est-ce juste un mythe?

8
robert.berger

Dans quelle mesure vos tâches sont-elles couplées étroitement?

S'ils peuvent vivre indépendamment les uns des autres, utilisez des processus. S'ils s'appuient l'un sur l'autre, utilisez des threads. De cette manière, vous pouvez arrêter et relancer un processus incorrect sans interférer avec le fonctionnement des autres tâches.

5
Robert

Pour compliquer davantage les choses, il existe une chose telle que stockage local au thread et la mémoire partagée Unix.

Le stockage local des threads permet à chaque thread d'avoir une instance distincte d'objets globaux. La seule fois que je l'utilisais était lors de la construction d'un environnement d'émulation sous Linux/Windows, pour le code d'application exécuté dans un RTOS. Dans le RTOS, chaque tâche était un processus avec son propre espace d'adressage, dans l'environnement d'émulation, chaque tâche était un fil (avec un espace d'adressage partagé). En utilisant TLS pour des choses comme des singletons, nous avons pu avoir une instance distincte pour chaque thread, comme dans l'environnement 'réel' RTOS.

La mémoire partagée peut (à l’évidence) vous apporter (évidemment) les avantages en termes de performances de permettre à plusieurs processus d’accéder à la même mémoire, mais au coût/risque de devoir synchroniser correctement les processus. Une façon de procéder consiste à créer une structure de données en mémoire partagée avec un processus, puis à lui envoyer un descripteur via une communication interprocessus classique (un canal nommé, par exemple).

4
KeyserSoze

La décision entre thread/processus dépend un peu de l'utilisation que vous comptez en faire. L'un des avantages d'un processus est qu'il possède un PID et qu'il peut être supprimé sans mettre également fin au parent.

Pour un exemple concret de serveur Web, Apache 1.3 ne supportait que plusieurs processus, mais dans la version 2.0, ils ajoutaient ne abstraction afin que vous puissiez basculer entre les deux. Commentairessembleà convenez que les processus sont plus robustes mais que les threads peuvent donner de meilleures performances (sauf pour les fenêtres où les performances des processus sont nulles et vous veux seulement utiliser des discussions).

3
hlovdal

Je suis d'accord avec ce que vous avez entendu. Lorsque nous comparons notre cluster (xhpl et autres), nous obtenons toujours de meilleures performances avec les processus sur les threads. </anecdote>

3
eduffy

Dans mes travaux récents sur LINUX, il est important de connaître les bibliothèques. Si vous utilisez des threads, assurez-vous que les bibliothèques que vous pouvez utiliser sur les threads sont thread-safe. Cela m'a brûlé plusieurs fois. Notamment, libxml2 n'est pas thread-safe prêt à l'emploi. Il peut être compilé avec thread-safe mais ce n’est pas ce que vous obtenez avec aptitude install.

3
aal8

Dans la plupart des cas, je préférerais les processus aux threads. Les threads peuvent être utiles lorsque vous avez une tâche relativement petite (temps système supplémentaire >> pris par chaque unité de tâche divisée) et qu'il est nécessaire de partager la mémoire entre elles. Pensez à un large éventail. De plus (offtopic), notez que si l'utilisation de votre processeur est proche de 100%, le multithreading ou le traitement ne procurera aucun avantage. (en fait ça va empirer)

2
neal aise

Je pense que tout le monde a fait un excellent travail en répondant à votre question. J'ajoute simplement plus d'informations sur les threads et les processus sous Linux pour clarifier et résumer certaines des réponses précédentes dans le contexte du noyau. Donc, ma réponse concerne le code spécifique au noyau sous Linux. Selon la documentation du noyau Linux, il n'y a pas de distinction claire entre thread et processus, sauf que thread utilise un espace d'adressage virtuel partagé contrairement à process. Notez également que le noyau Linux utilise le terme "tâche" pour faire référence au processus et au thread en général.

"Il n'y a pas de structures internes implémentant des processus ou des threads, il existe plutôt une structure struct task_struct décrivant une unité de planification abstraite appelée tâche"

En outre, selon Linus Torvalds, vous ne devez PAS du tout penser au processus par rapport au thread, car il est trop limitatif et que la seule différence réside dans le COE ou le contexte d'exécution en termes de "séparer l'espace d'adressage du parent" ou l'espace d'adressage partagé. En fait, il utilise un exemple de serveur Web pour faire valoir son point ici (qui recommande fortement de lire).

Crédit complet à documentation du noyau Linux

1
grepit

Threads -> Threads partage un espace mémoire, c'est une abstraction du processeur, il est léger. Processus -> Les processus ont leur propre espace mémoire, c'est une abstraction d'un ordinateur. Pour paralléliser une tâche, vous devez résumer un processeur. Cependant, l’utilisation d’un processus par rapport à un thread présente des avantages, c’est la sécurité et la stabilité, tandis qu’un thread utilise moins de mémoire que de processus et offre une latence moindre. Un exemple en termes de Web serait chrome et firefox. Dans le cas de Chrome, chaque onglet est un nouveau processus. Par conséquent, l'utilisation de la mémoire de chrome est supérieure à celle de firefox, tandis que la sécurité et la stabilité fournies sont supérieures à celles de firefox. La sécurité fournie ici par chrome est meilleure, puisque chaque onglet est un nouveau processus, un onglet différent ne peut pas surveiller l'espace mémoire d'un processus donné.

1