Fils et signaux POSIX

J’ai essayé de comprendre les complexités de l’interaction entre les threads POSIX et les signaux POSIX. En particulier, je suis intéressé par:

  • Quelle est la meilleure façon de contrôler à quel thread un signal est transmis (en supposant qu’il ne soit pas fatal en premier lieu)?
  • Quelle est la meilleure façon de dire à un autre thread (qui pourrait effectivement être occupé) que le signal est arrivé? (Je sais déjà que c’est une mauvaise idée d’utiliser des variables de condition pthread d’un gestionnaire de signal.)
  • Comment puis-je gérer en toute sécurité les informations indiquant qu’un signal est apparu sur d’autres threads? Est-ce que cela doit se produire dans le gestionnaire de signaux? (Je ne veux pas en général tuer les autres threads; j’ai besoin d’une approche beaucoup plus subtile.)

Pour plus d’informations sur les raisons pour lesquelles je veux cela, je cherche à convertir le package TclX pour prendre en charge les threads ou à le diviser et à créer au moins quelques parties utiles pour les threads. Les signaux font partie de ces parties qui présentent un intérêt particulier.

  • Quelle est la meilleure façon de contrôler à quel thread un signal est transmis?

Comme @ zoli2k l’a indiqué, nommer explicitement un seul thread pour gérer tous les signaux que vous voulez gérer (ou un ensemble de threads ayant chacun des responsabilités de signal spécifiques) est une bonne technique.

  • Quelle est la meilleure façon de dire à un autre thread (qui pourrait effectivement être occupé) que le signal est arrivé? […]
  • Comment puis-je gérer en toute sécurité les informations indiquant qu’un signal est apparu sur d’autres threads? Est-ce que cela doit se produire dans le gestionnaire de signaux?

Je ne dirai pas “mieux”, mais voici ma recommandation:

Bloquer tous les signaux souhaités dans main , de sorte que tous les threads héritent de ce masque de signal. Ensuite, façonnez le thread de réception du signal spécial en tant que boucle d’événement pilotée par le signal, en envoyant les signaux nouvellement arrivés comme une autre communication intra-thread .

La manière la plus simple de le faire est d’avoir le thread qui accepte les signaux dans une boucle en utilisant sigwaitinfo ou sigtimedwait . Le thread convertit alors les signaux d’une manière ou d’une autre, diffusant peut-être un pthread_cond_t , réveillant d’autres threads avec plus d’E / S, en mettant en queue une commande dans une queue adaptée aux threads spécifique à l’application, peu importe.

Alternativement, le thread spécial pourrait permettre à des signaux d’être délivrés à un gestionnaire de signal, démasquant la livraison uniquement lorsqu’il est prêt à traiter les signaux. (Toutefois, la transmission des signaux via les gestionnaires a tendance à être plus sujette aux erreurs que l’acceptation des signaux via la famille sigwait .) Dans ce cas, le gestionnaire de signaux du récepteur effectue une action simple et asynchrone: définir sig_atomic_t flags sigaddset(&signals_i_have_seen_recently, latest_sig) , write () un octet dans un self-pipe non bloquant, etc. Puis, de retour dans sa boucle principale masquée, le thread communique la réception du signal aux autres threads comme ci-dessus.

(UPDATED @caf souligne à juste titre que les approches sigwait sont supérieures.)

Selon la norme POSIX, tous les threads doivent apparaître avec le même PID sur le système et en utilisant pthread_sigmask() vous pouvez définir le masque de blocage du signal pour chaque thread.

Comme il est permis de définir un seul gestionnaire de signal par PID, je préfère gérer tous les signaux d’un thread et envoyer pthread_cancel() si un thread en cours d’exécution doit être annulé. C’est la méthode préférée contre pthread_kill() car elle permet de définir des fonctions de nettoyage pour les threads.

Sur certains systèmes plus anciens, en raison de l’absence de prise en charge du kernel, les threads en cours d’exécution peuvent avoir un PID différent de celui du thread parent. Voir FAQ pour la gestion des signaux avec linuxThreads sous Linux 2.4 .

IMHO, les signaux Unix V et les threads Posix ne se mélangent pas bien. Unix V est 1970. POSIX est 1980;)

Il existe des points d’annulation et si vous autorisez les signaux et les pthreads dans une application, vous finirez par écrire des boucles autour de chaque appel, ce qui peut surprendre de manière inattendue.

Donc, ce que j’ai fait dans les (rares) cas où je devais programmer le multithread sous Linux ou QNX était de masquer tous les signaux pour tous les threads (sauf un).

Lorsqu’un signal Unix V arrive, le processus bascule la stack (ce qui était autant de simultanéité dans Unix V que dans un processus).

Comme le suggèrent les autres articles ici, il serait peut-être possible maintenant de dire au système quel thread posix doit être victime de ce changement de stack.

Une fois que vous avez réussi à faire fonctionner votre thread de gestionnaire de signaux, la question rest de savoir comment transformer les informations de signal en quelque chose de civilisé, que d’autres threads peuvent utiliser. Une infrastructure pour les communications inter-thread est requirejse. Un modèle, utile, est le modèle d’acteur, où chacun de vos threads est une cible pour un mécanisme de messagerie en cours de processus.

Donc, au lieu d’annuler d’autres threads ou de les tuer (ou d’autres choses bizarres), vous devriez essayer de placer le signal Signal dans le contexte de votre gestionnaire de signaux, puis utiliser vos mécanismes de communication pour envoyer des messages sémantiquement utiles à ces acteurs. qui ont besoin des informations relatives au signal.

Où je suis si loin:

  • Les signaux arrivent dans différentes classes principales, dont certaines devraient simplement tuer le processus de toute façon (SIGILL) et d’autres n’ont jamais besoin de faire quoi que ce soit (SIGIO; plus facile de faire des E / S asynchrones). Ces deux classes n’ont pas besoin d’action.
  • Certains signaux n’ont pas besoin d’être traités immédiatement; les goûts de SIGWINCH peuvent être mis en queue jusqu’à ce qu’il soit pratique (comme un événement de X11).
  • Les plus difficiles sont celles où vous voulez y répondre en interrompant ce que vous faites, mais sans aller jusqu’à effacer un fil. En particulier, SIGINT en mode interactif doit restr réactif.

Je dois encore sortinger par rapport à sigaction , pselect , sigwait , sigaltstack , et tout un tas d’autres bits et morceaux d’API POSIX (et non-POSIX).