2011-01-12 3 views
2

J'ai écrit une application Linux dans laquelle le processus principal de 'consommateur' forge un tas de processus 'lecteur' (~ 16) qui lisent les données du disque et les transmettent au 'consommateur' pour l'affichage. Les données sont passées sur un socket qui a été créé avant la fourche en utilisant socketpair.Si vous utilisez la mémoire partagée, y a-t-il encore des avantages pour les processus sur le threading?

Je l'origine écrit avec cette limite de processus pour 3 raisons:

  1. Le processus de consommation a des contraintes en temps réel, donc je voulais éviter les allocations de mémoire dans le consommateur. Les lecteurs sont libres d'allouer de la mémoire comme ils le souhaitent, ou même d'être écrits dans une autre langue (par exemple avec la récupération de place), et ceci n'interrompt pas le consommateur, qui a la priorité FIFO. En outre, l'accès au disque ou d'autres E/S dans le processus de lecture n'interrompra pas le consommateur. Je me suis dit qu'avec des fils, je ne pouvais pas obtenir de telles garanties. L'utilisation de processus m'empêchera, le programmeur, de faire des choses stupides comme l'utilisation de variables globales et l'effondrement de la mémoire d'autres processus. Je pensais que le fait d'abandonner un tas de travailleurs serait le meilleur moyen d'utiliser plusieurs architectures CPU, et je me suis dit que l'utilisation de processus au lieu de threads serait généralement plus sûre.

Tous les lecteurs ne sont pas toujours actifs, mais ceux qui sont actifs envoient constamment de grandes quantités de données. Dernièrement, je pensais que pour optimiser cela en évitant les copies de mémoire associées à l'écriture et à la lecture de la socket, il serait bon de simplement lire les données directement dans un tampon de mémoire partagée (shm_open/mmap). Alors seulement un index dans cette mémoire partagée serait passé au-dessus de la douille, et le consommateur lirait directement de lui avant de le marquer comme disponible encore.

De toute façon, l'un des plus grands avantages des processus par rapport aux threads est de évitez d'écraser l'espace mémoire d'un autre thread. Pensez-vous que le passage à la mémoire partagée détruirait tous les avantages que j'ai dans cette architecture? Y a-t-il encore un avantage à utiliser des processus dans ce contexte, ou devrais-je simplement passer mon application à utiliser des threads?

Répondre

3

Votre hypothèse selon laquelle vous ne pouvez pas répondre à vos contraintes temps réel avec des threads est erronée. Les IO ou l'allocation de mémoire dans les threads de lecture ne peuvent pas bloquer le thread consommateur tant que le thread consommateur n'utilise pas malloc lui-même (ce qui peut bien sûr conduire à des conflits de verrouillage). Je recommande de lire ce que POSIX a à dire sur le sujet si vous n'êtes pas sûr. Comme pour les autres raisons d'utiliser des processus au lieu de threads (sécurité, possibilité d'écrire les lecteurs dans une langue différente, etc.), ceux-ci sont parfaitement légitimes. Tant que votre processus consommateur traite le tampon de mémoire partagée comme des données externes potentiellement dangereuses, je ne pense pas que vous perdiez une grande sécurité en passant des tubes à la mémoire partagée.

+0

Vraiment? D'une certaine façon, j'avais l'impression que si 'malloc' ressentait le besoin d'augmenter la taille du tas en utilisant' sbrk', cela pourrait bloquer tout le processus. De même, si un thread effectue des opérations de mémoire qui créent une erreur de page, il peut bloquer tous les threads dans le processus. Peut-être que c'est faux? – Steve

+0

C'est faux. Il ne fait que bloquer le fil. Strictement parlant, il s'agit d'un détail d'implémentation (sauf peut-être sur les systèmes conformes à l'option POSIX realtime?), Mais aucune implémentation sensée ne permettrait à aucune de ces opérations de bloquer d'autres threads. Certainement sur Linux, les BSD, et tout autre Unix grand public c'est seulement le fil qui se bloque. –

+0

D'accord, merci pour l'info. – Steve

-1

La principale raison que j'ai rencontrée dans mon expérience pour remplacer les processus par les threads était l'efficacité. Si vos processus utilisent beaucoup de code ou de mémoire non partagée qui pourraient être partagés en multithreading, vous pourriez gagner beaucoup de performance sur des processeurs hautement threadés comme les processeurs SUN Sparc ayant 64 threads ou plus par CPU. Dans ce cas, le cache CPU, en particulier pour le code, sera beaucoup plus efficace avec un processus multithread (le cache est petit sur Sparc).

Si vous constatez que votre logiciel ne s'exécute pas plus rapidement lors de l'exécution d'un nouveau matériel avec plus de threads de processeur, vous devez envisager le multithreading.Sinon, vos arguments pour l'éviter me semblent bons.

Je n'ai pas encore rencontré ce problème sur les processeurs Intel, mais cela pourrait arriver dans le futur quand ils ajouteraient plus de cœurs par CPU.

+0

Tant que votre archtecture utilise un cache mappé physiquement (comme le font SPARC et x86/x86-64), le même segment de code mappé dans plusieurs processus aura le même advantange de cache. – caf

0

Oui, exactement pour la raison que vous avez dite. Il est préférable d'avoir chaque mémoire de processus protégée et de partager uniquement ce qui est vraiment nécessaire pour partager. Ainsi, chaque consommateur peut allouer et utiliser ses ressources sans se soucier du verrouillage. En ce qui concerne votre communication d'index entre votre tâche, il convient de noter que vous pourriez alors utiliser une zone dans votre mémoire partagée pour cela et utiliser mutex pour les accès, car il est probablement moins lourd que la communication socket. La communication des descripteurs de fichiers (sockets, pipes, fichiers, etc.) implique toujours le noyau, la mémoire partagée avec des verrous de mutex ou des sémaphores uniquement lorsqu'il y a un conflit.

Un point à prendre en compte lors de la programmation avec de la mémoire partagée dans un environnement multiprocesseur est d'éviter les fausses dépendances sur les variables. Cela se produit lorsque deux objets indépendants partagent la même ligne de cache. Quand on est modifié, il "salit" aussi l'autre, ce qui signifie que si un autre processeur accède à l'autre objet, il déclenche une synchronisation de cache entre les CPU. Cela peut entraîner une mauvaise mise à l'échelle. En alignant les objets sur la taille de la ligne de cache (64 octets en général, mais peuvent différer de l'architecture à l'architecture), on peut facilement éviter cela.

Questions connexes