8
votes

Filets / signaux POSIX: moyen portable de déterminer à quel fil un signal a été livré?

J'ai un serveur multithreadé (à l'aide des threads POSIX), avec un thread pour chaque connexion persistante. Dans l'un des threads, l'autre extrémité de la connexion se ferme, ce qui entraîne une sigpipe étant livrée. Y a-t-il un (de préférence portable) pour déterminer quel thread (et donc quelle connexion) cela est arrivé pour que je puisse avoir mon gestionnaire de signal, soit le nettoyage de thread / connexion fonctionnant lui-même ou définissez un drapeau de manière à ce que le fil principal et de travailleur voit respectivement que ils ont besoin de le faire plus tard?

Edit: Je me demande si je pouvais peut-être utiliser et errno, stockez-le dans un tableau global et l'associer à l'identifiant du serveur pour le thread, puis recherchez et errno dans le gestionnaire de signal. L'errno spécifique du fil serait-elle visible pour le gestionnaire de signal? Est ma compréhension de la façon dont le threadsafe errno fonctionne même dans le ballaccark?


10 commentaires

Si un signal a été remis à un thread particulier, ce thread gérera le signal. C'est ce qui fournit un signal à un moyen de filetage spécifique. Je ne comprends pas ce qu'il y a à vérifier?


Parce que cela ne me dit pas quel fil spécifique c'était.


Je ne comprends pas, le fil est le fil qui manipule le signal. Quelles informations sur le thread actuel devez-vous savoir?


Pourquoi vous souciez-vous du fil et non du socket lui-même?


Pour un, je dois pouvoir dire au fil principal qu'il peut libérer ses données concernant le thread de manipulation de la connexion. Pour ce faire, le fil principal doit savoir quelle connexion particulière (et ainsi l'indice dans son éventail d'informations de connexion / thread) est terminée. Pour ce faire, le gestionnaire de signal doit savoir s'il a été appelé à partir d'un "filetage de connexion 28" ou "filetage de raccordement 91" afin qu'il puisse dire au fil principal "Faites votre nettoyage pour # 28" ou "Faites votre nettoyage pour # 91. " J'essaie de comprendre comment obtenir ces informations de l'intérieur du gestionnaire de signal afin que cela puisse être envoyé.


J'ai trouvé des preuves anecdotiques que pthread_self est sans danger à utiliser dans un gestionnaire de signal sur de nombreux systèmes, je cherche toujours une référence définitive.


Peut-être. Mais il y a toujours le problème de savoir si le Sigpipe sera effectivement envoyé au fil associé à la connexion qui a été fermé, car ma compréhension est que le noyau l'envoie à n'importe quel fil qui ne l'a pas bloqué. Si je dirigeai tous les signaux sur un seul thread, d'autre part, il semble qu'il n'y ait aucune association entre le signal et le fil de prise / gestionnaire particulier qui lui entraîne.


Peut-être devriez-vous clarifier votre titre de question. Pour le moment, il est dit que vous souhaitez déterminer à quel fil un signal a été livré, mais on dirait que vous ne pensez pas que la réponse à cette question vous aide à résoudre votre problème.


@BestSsss a raison - vous ne devriez pas gérer les connexions par thread ici, mais par socket.


Peut-être que je n'ai pas assez clair (c'est connu pour arriver). Il existe une correspondance individuelle entre le filetage ouvrier et la prise de connexion. Si je sais quel fil a provoqué l'élevage de la sigpipe, je sais que la prise de quelle prise était allumée.


4 Réponses :


12
votes

Je ne pense pas non plus, non. Une meilleure solution pour les serveurs multithreads consiste à supprimer le signal Sigpipe (en appelant signal (Sigpipe, sig_ign) dans le cadre de la routine de démarrage du programme), puis ayez le contrat thread La valeur d'erreur (-1 / epipe) renvoyée par envoi () à la place.

Les signaux et la multithreading ne se mélangent pas bien.


7 commentaires

J'avais envisagé cette possibilité; Le problème est que, alors je ne détecterai pas la déconnexion tant que je dois envoyer des données sur cette connexion - ce qui pourrait ne jamais être jamais.


Cela pourrait facilement être attrapé en sélectionnant sur cette FD pour erreur uniquement


Mmm, tu as raison. J'utilise Epoll et j'ai oublié d'Epollhup.


Je pense que vous constaterez que le "ne détecte pas la déconnexion tant que je dois envoyer la prochaine fois que la connexion" problème est un problème distinct, et vous devrez y faire face si vous utilisez des signaux ou non. TCP par défaut ne détecte pas qu'une connexion inactive a été coupée pendant une longue période (si jamais), en fonction des paramètres des options de conserve TCP de l'hôte. Sur certains OS (E.G. Linux), vous pouvez définir les options Keepalive sur une base de socket (via setsockopt (FD, SOL_TCP, TCP_KEELIDLE), tandis que sur les autres (Windows), c'est un seul réglage global.


... bien sûr sous n'importe quel système d'exploitation, vous pouvez éviter le problème en envoyant manuellement un peu de données factices de temps en temps sur toute connexion TCP autrement inactif, juste pour exercer la connexion TCP. Si la paite distante ne reconnaît pas les données factices dans une courte période (plusieurs minutes), vous obtiendrez une erreur alors.


C'est à droite - Tous les serveurs de réseau , pas seulement des multithreads, devraient ignorer Sigpipe et traiter avec EPIpe . @Steelydan: sigpipe ne sera plus livré rapidement que eppe - le signal n'est généré que à la même heure qu'un épipe retourner autrement arriver.


Très bien merci. Donc, si je viens de masquer Sigpipe, je devrais être bon; Je pense que c'est l'itinéraire que je vais prendre. J'apprécie l'aide!



0
votes

Mon approche serait d'avoir le gestionnaire de signal pour Sigpipe, il suffit d'écrire son identifiant de thread à une variable globale et d'utiliser un autre mécanisme pour signaler ceci sur le thread, qui devrait le gérer.


6 commentaires

Eh bien, cela soulève toujours la même question fondamentale de la manière portable et sûre de la sécurité du signal que je dois savoir quel fil particulier cela s'est produit, puisqu'à je dois comprendre ce que l'ID de thread est.


C'est assez facile: par exemple. Soulevez un événement via tout mécanisme d'événement que votre système prend en charge (cela inclut l'envoi de quelque chose à un tuyau) et un fil d'accès à ces événements (qui inclut la sélection de ce tuyau), lisez la variable globale (verrouillage protégée) si nécessaire et travaillez sur ce


Je pense qu'un problème avec l'approche globale-variable est ce qui se passe si deux signaux sont soulevés en succession rapide - le deuxième signal pourrait écraser la valeur écrite par le premier signal avant le premier gestionnaire exécuté. Vous auriez besoin d'une foie FIFO protégée par la serrure au moins ... mais vous rencontrez dans le deuxième problème, ce qui est que vous n'êtes pas autorisé à verrouiller un mutex de l'intérieur d'un gestionnaire de signal.


J'ai bien peur que je ne sois pas sûr de comprendre ce que vous suggérez. Dis-vous que le gestionnaire de signal envoie quelque chose au fil de gestion de l'événement, puis en regardant (disons) quel tube particulier il a été reçu sur le thread de gestion d'événements sait à quel fil la notification d'événement est venue? J'avais envisagé une architecture essentiellement similaire pour cela, mais j'en ai chiée, car il faudrait trois descripteurs de fichiers par connexion (un pour la prise plus de deux pour l'une ou l'autre extrémité du tuyau), ce qui serait contre la limite plutôt plus rapide que Je voudrais. (Dirigé à Eugen Rieck)


@Steelydan en fonction de votre définition de "portable", il pourrait ou non être beaucoup plus simple. Le plus portable étant le tuyau, mais vous vous rendez compte que vous n'avez pas besoin d'être connecté à cela tout le temps - il suffit de vous connecter après avoir reçu le signal!


Je suppose que cela fonctionnerait avec des pipes nommées: le fil principal garde une extrémité ouverte tout le temps et les sondages à ce sujet, puis le thread du travailleur n'ouvre que l'autre extrémité quand elle doit. Bien que gardant des programmes extérieurs malveillants (ou mal pensés) (ou d'autres utilisateurs) d'y accéder devient important dans ce cas. Je ne pensais que des tuyaux (2) des tuyaux jusqu'à présent.



0
votes

Qu'envoyez d'utiliser une variable globale (filetage déclarée-local) pour stocker l'ID de thread, initiez-le sur la création de thread et la désigner dans le gestionnaire de signal du fil.

Pour les détails de GCC à ce sujet, voir ici: http://gcc.gnu.org /onlinedocs/gcc/thread_002dlocal.html ou plus général ici: http: // fr. wikipedia.org/wiki/thread-local_storage


2 commentaires

Les fonctions de stockage du fil-local ne sont pas sécurisées, comme je le comprends.


@Steely Dan: Je ne parlais pas de données spécifiques à thread en utilisant des fonctions pour y accéder. S'il vous plaît voir la référence que je viens d'ajouter à mon message, car ce que je faisais appelé à la place.



2
votes

Vous pouvez utiliser Pthread_Self en toute sécurité dans un gestionnaire de signal. C'est async-Signal-Safe .

Cette réponse est pour la postérité. Je ne recommande pas cette approche techniquement. Les autres réponses et commentaires suggérant une approche différente, seraient mieux à mettre en œuvre.

Au moment où cette question a été posée, la sécurité de pthread_self n'était pas nécessairement évidente. Le POSIX: 2008 Corrigendum Technique 1, daté de 2013 (deux ans après que cette question a été posée), clarifie que pthread_self est explicitement async-signal-coffre-fort, comme indiqué dans le Linux Signal (7) Man .

En conséquence, vous avez mentionné dans les commentaires que vous n'étiez pas sûr si le noyau "envoie [un Sigpipe] à n'importe quel fil qui ne l'a pas bloqué" . C'est une bonne préoccupation - est-ce un signal dirigé vers le processus ou un signal dirigé sur le thread? - et, heureusement, mal placé. Un Sigpipe généré par le noyau est un signal "généré de manière synchrone" en réponse à certains appel d'E / S et envoyé au fil d'appel. Ceci a été explicitement clarifié dans le corrigendum 2 (2004) à POSIX: 2001 pour dire qu'un sigpipe en réponse à un écrire est " envoyé au fil " (emphase).


0 commentaires