12
votes

Sécurité du fil en C

Imaginez que j'écris une bibliothèque dans C. En outre, imaginez cette bibliothèque à utiliser à partir d'un environnement multi-threadé. Comment puis-je le faire fil-coffre-fort? plus spécifique: comment assurer que certaines fonctions sont exécutées uniquement par un fil à la fois?

en face de Java ou C # par exemple, c n'a pas de moyen de traiter des threads / serrures / etc., ni la bibliothèque standard C. Je sais que les systèmes d'exploitation des systèmes de support, mais en utilisant leur API limiterait beaucoup la compatibilité de ma bibliothèque. Quelles possibilités ai-je, pour garder ma bibliothèque aussi compatible / portable que possible? (Par exemple, s'appuyant sur OpenMP ou sur des threads POSIX pour le maintenir compatible avec au moins tous les systèmes d'exploitation de type UNIX?)


5 commentaires

Vous aurez besoin d'une bibliothèque. Pthreads est le choix habituel * Nix.


Il y a beaucoup de façons d'assurer la sécurité du fil sans utiliser d'API de filetage. Quels systèmes d'exploitation et les architectures de la CPU envisagez-vous de soutenir?


J'aimerais prendre en charge UNIX comme des systèmes similaires (en particulier Linux et Mac OS X) et Windows. Je n'ai pas besoin de moyens sophistiqués pour faire face aux fils. Les serrures simples et la possibilité pour les filets à la file d'attente pour les serrures suffisent


J'ai oublié de mentionner les CPU: Intel et AMD CPU traditionnel. Cependant, je me demande pourquoi votre réponse dépend de cette information ... (Je ne suis pas capable d'écrire assembleur.)


Il existe des solutions pour créer un fil de bibliothèque sûr, n'utiliser aucun des mécanismes de verrouillage. L'une des tâches est de s'assurer qu'il n'y a pas de variables statisées dans l'appel de la fonction et aucune variables globales mondiales ou statiques.


9 Réponses :


5
votes

Vous devrez utiliser la bibliothèque de threading de votre système d'exploitation. Sur POSIX, qui sera généralement pthreads et vous voulez pthread_mutex_lock .

Windows a sa propre bibliothèque de threading et vous voudrez regarder soit Sections critiques ou Createmutex . Les sections critiques sont plus optimisées mais sont limitées à un processus unique et vous ne pouvez pas les utiliser dans waitformultiplateObjects .


1 commentaires

THX pour la clarification et les pointeurs à un matériau initial sur le sujet



20
votes

Vous pouvez créer des emballages avec #Ifdef. C'est vraiment le meilleur que vous puissiez faire. (Ou vous pouvez utiliser une bibliothèque tierce pour le faire).

Je vais montrer comment je l'ai fait comme exemple pour Windows et Linux. C'est en C ++ et non C mais encore une fois c'est juste un exemple: xxx


5 commentaires

Quoi de neuf aujourd'hui avec tout le monde me descendit sans raison apparente? : /


ils le font pour un certain nombre de raisons, probablement ils ne sont pas déposés ou sont en colère contre leur patron - ne pas s'inquiéter, je vais "fixer" que les dommages sont mieux que je peux, car ils ne vous ont donné aucune justification critique pour cela


thx pour votre réponse. De vos connaissances, il faut-il écrire beaucoup de code connexe pour prendre en charge Windows et Posix?


@Dave: Non, pas vraiment. Que + ~ 200 lignes pour gérer la création de fil et la destruction est tout ce que j'ai.


+1 De mon côté aussi ... Un peu simpliste mais c'est ce que l'OP a besoin.



1
votes

Si votre objectif est d'être compatible avec des systèmes d'exploitation de type UNIX, j'utiliserais un filetage POSIX.

Cela étant dit, si vous souhaitez également prendre en charge Windows, vous devez disposer de deux chemins de code pour cela - Pthreads sur des threads UNIX et Windows sous Windows. Il est assez facile de simplement faire votre propre "bibliothèque de thread" pour les envelopper.

Il y a plusieurs personnes qui font cela (comme Opthreads ), mais la plupart d'entre eux que j'ai utilisé sont c ++, pas c.


2 commentaires

THX pour fournir le lien! Avez-vous plus de documents, qui enseignent, comment écrire des emballages minimaux autour des API de threading existantes?


J'aurais attrapé Opthreads - c'est C ++, mais il gère bien les appels. Il serait facile de regarder cela et de faire une version C (car c'est assez petit, mais gère très bien les choses et est très robuste.)



0
votes

Utiliser les threads Posix vous semble une bonne idée (mais je ne suis pas d'expert). En particulier, Posix a de bonnes primitives pour assurer l'exclusion mutuelle.

Si vous avez dû créer une bibliothèque sans dépendances, vous devrez mettre en œuvre vous-même les algorithmes d'exclusion mutuelle, qui est une mauvaise idée.


2 commentaires

Impossible de mettre en œuvre des mécanismes de verrouillage très simples par moi-même que je suis capable de mapper 1: 1 sur Posix / Winapi en utilisant #define?


Oui, je suppose que cela fonctionnerait. Il pourrait y avoir de légères différences de fonctionnalité, mais que cela ne serait probablement pas si difficile à trier.



2
votes

Vous devez également éviter les variables statiques et globales pouvant être modifiées en évitant le code de synchronisation sur tout le module


0 commentaires

3
votes

C'est une idée fausse que la bibliothèque Pthreads ne fonctionne pas sur Windows. Consultez Sourceforge.net . Je recommanderais Pthreads parce que c'est une plate-forme croisée et ses mutiles sont bien plus rapides que par exemple. Les Mutiles intégrées de Windows.


0 commentaires

4
votes

Vous avez deux options principales:

1) Vous spécifiez quel environnement multi-threadé votre bibliothèque est votre bibliothèque et utilisez les fonctions de synchronisation de cet environnement.

2) Vous devez spécifier que votre bibliothèque est pas thread-coffre-fort. Si votre appelant veut l'utiliser dans un environnement multi-threadé, il est limité de faire la sécurité de la sécurité, en utilisant une synchronisation externe si nécessaire pour sérialiser tous les appels vers votre bibliothèque. Si votre bibliothèque utilise des poignées et n'a pas besoin d'un état global, cela peut par exemple dire que s'ils ont une poignée, ils utilisent uniquement dans un seul thread, ils n'ont pas besoin de synchronisation sur cette poignée, car elle est automatiquement sérialisée.

Évidemment, vous pouvez adopter une approche multi-pack de (1) et utiliser des constantes de compilation pour prendre en charge tous les environnements que vous connaissez.

Vous pouvez également utiliser une architecture de rappel, une dépendance du temps de liaison ou des macros, pour laisser votre appelant vous dire comment synchroniser. C'est une sorte de mélange de (1) et (2).

Mais il n'y a pas d'environnement multi-threadé standard, il est donc presque impossible d'écrire du code autonome qui est le fil-coffre-fort partout, à moins qu'il ne soit complètement apatride (c'est-à-dire que les fonctions sont toutes libres de côté) . Même vous devez interpréter «l'effet secondaire» libéralement, car bien sûr, la norme C ne définit pas les fonctions de la bibliothèque. C'est un peu comme demander comment écrire de code C pouvant exécuter dans un gestionnaire d'interruption matérielle. "Qu'est-ce qu'une interruption?", Vous pourriez très bien demander: "Et quelles sont les choses que je pourrais faire en C ne sont pas valables dans une?". Les seules réponses sont spécifiques au système d'exploitation.


1 commentaires

+1. Numéro 2) n'est pas en réalité aussi mauvais que cela sonne - si vous avez des fonctions telles que Dosomethobuffer (char * tampon, int option); , vous pouvez dire "c'est le fil-sûr aussi longtemps que vous ne pouvez pas 'T Appelez-le sur le même tampon de deux fils différents en même temps ».



3
votes

Écrivez votre propre verrouillage.

Depuis que vous ciblez des PC, vous avez affaire à l'architecture X86 qui fournit de manière native tout le support multi-threading dont vous avez besoin. Accédez à votre code et identifiez toutes les fonctions qui ont des ressources partagées. Donnez à chaque ressource partagée un compteur 32 bits. Ensuite, l'utilisation des opérations verrouillées implémentées par les CPU de garder une trace du nombre de threads utilisez chaque ressource partagée et apportez un thread qui souhaite utiliser une ressource partagée attendue jusqu'à ce que la ressource soit libérée.

Voici un très bon blog post sur les opérations imbriquées: Instructions enclenchées de C / C ++

L'auteur se concentre principalement sur l'utilisation des emballages verrouillés Win32, mais à peu près, chaque système d'exploitation a ses propres emballages pour les opérations verrouillées et vous pouvez toujours écrire l'assemblage (chacune de ces opérations n'est qu'une instruction).


6 commentaires

Thx pour le lien intéressant. Cependant, si je vous comprends correctement, je devrais écrire l'assemblage pour contourner les dépendances de l'API du système d'exploitation. Mais cela ne présenterait-il pas aux dépendances du modèle de la CPU? De votre expérience, quel serait moins de travail: fournir un soutien à des API de filetage multiple ou à la maintenance des blocs ASM spécifiques à la CPU dans ma bibliothèque?


Puisque vous ne parlez que de courir sur PCS, vous ne parlez à peu près à une architecture de la CPU X86. Sauf si vous avez l'intention de soutenir les processeurs Pre-Pentium 4, je dirais que c'est beaucoup moins de travail pour écrire l'assemblage X86 une fois que d'écrire des implémentations de 3+ C ++.


Thx, je ne savais pas qu'il y a une instruction commune définie entre les processeurs


La partie où cela tombe est à "... et faites n'importe quel fil qui veut utiliser une ressource partagée attendue jusqu'à ce que la ressource soit libérée." Pour ce faire, vous devez soit invoquer le système d'exploitation / Filetage-Bibliothèque de la fonction de sommeil spécifique, ou occupé-attente - ce qui est affreux.


Je ne suis pas vraiment d'accord avec toi ici. Selon les types d'opérations que la bibliothèque va s'exécuter avec les ressources partagées, il peut être parfaitement logique de faire des attentes occupées (ou comme ils sont plus communément appelés Spirlocks). Si les opérations sont suffisamment petites que les ressources ne seront bloquées que pendant une très courte période de temps, les spirlocks sont préférables en raison des conséquences de la performance de l'écriture de la pile du fil à la mémoire principale lorsque le fil entre dans l'état d'attente et la rechargez-la pour évaluer le Verrouiller et potentiellement commencer l'exécution. Tout dépend de ce que vous faites.


Les spinlocks ne vont pas être géniaux si cela est jamais exécuté sur une machine à cœur unique, car lorsque vous êtes malchanceux suffisamment pour qu'un thread soit préempté tout en maintenant une serrure (et oui, cela sera minimisé si les sections critiques sont petites Assez, mais tôt ou tard, il arrivera), le fil "en attente" tournera ensuite pour tout le reste de son délai avant que le fil "verrouillé" ait une chance de courir à nouveau. Je suis tout à fait d'accord sur le fait que tout dépend de ce que vous faites, je ne pense tout simplement pas que les spirlocks de l'espace d'utilisateur sont une bonne solution à usage général.



0
votes

"Imagine j'écris une bibliothèque en C. En outre, imaginez cette bibliothèque à utiliser à partir d'un environnement multi-threadé. Comment puis-je le faire fil-coffre-fort? Plus spécifique: Comment puis-je assurer que certaines fonctions sont exécutés uniquement par un fil à la fois? "

Vous ne pouvez pas - écrire une fonction de refuge ou de meilleure ré-entrante. À moins que vous souhaitiez écrire des serrures à l'échelle du système - une très mauvaise idée.

" en face de Java ou C # par exemple, c n'a pas de moyen de traiter des threads / serrures / etc. " "

Ceci est une blague - non? Bien avant de développer Java et C #, les serrures ont été inventées et largement utilisées comme objets de synchronisation ...

"Je sais que les systèmes d'exploitation supporte les threads, mais en utilisant leur API limiterait beaucoup la compatibilité de ma bibliothèque."

La chose est que de telles bibliothèques existent déjà - F.e. wxwidgets, qui offrent le portable wxthread ... (mais ceci est C ++)

Quoi qu'il en soit, il y a 2 "saveurs" principales de C: l'ANSI C et le GNU C -> Deux mondes différents ... Choisissez-en un ou l'autre.


1 commentaires

C'est une réponse assez médiocre. Vous n'avez pas répondu à la question, mais plutôt échapper à des défenses sans signification de C. «Écrivez simplement un meilleur code», «Les serrures à l'échelle du système sont mauvaises de toute façon», etc.