GNU/Linux >> Tutoriels Linux >  >> Linux

Quand un signal est-il traité et pourquoi certaines informations se bloquent-elles ?

termA ne se fige pas. Il affiche simplement le rappel dans la zone de saisie. Appuyez simplement sur le Enter touche pour continuer la saisie.


Avant d'expliquer votre problème, un peu de contexte sur la façon dont read la commande fonctionne. Il lit les données d'entrée de stdin jusqu'au EOF est rencontré. Il est prudent de dire l'appel à read La commande n'est pas bloquante lorsqu'il s'agit de lire des fichiers sur disque. Mais quand stdin est connecté au terminal, la commande se bloquera jusqu'à ce que l'utilisateur tape quelque chose.

Comment fonctionnent les gestionnaires de signaux ?

Une explication simple sur le fonctionnement du traitement du signal. Voir l'extrait ci-dessous dans C qui agit juste sur SIGINT ( alias CTRL+C )

#include <stdio.h>
#include <signal.h>

/* signal handler definition */
void signal_handler(int signum){
  printf("Hello World!\n");
}

int main(){
  //Handle SIGINT with a signal handler
  signal(SIGINT, signal_handler);
  //loop forever!
  while(1);
}

Il enregistrera le gestionnaire de signal puis entrera dans la boucle infinie. Lorsque nous atteignons Ctrl-C , nous pouvons tous convenir que le gestionnaire de signal signal_handler() doit s'exécuter et "Hello World!" imprime à l'écran, mais le programme était dans une boucle infinie. Pour imprimer "Hello World!" il doit avoir été le cas qu'il a rompu la boucle pour exécuter le gestionnaire de signal, n'est-ce pas ? Il devrait donc sortir de la boucle ainsi que du programme. Voyons :

gcc -Wall -o sighdl.o signal.c
./sighdl.o 
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!

Comme l'indique la sortie, chaque fois que nous avons émis Ctrl-C , "Hello World!" s'imprime, mais le programme revient à la boucle infinie. Ce n'est qu'après avoir émis un SIGQUIT signaler avec Ctrl-\ le programme a-t-il réellement quitté. En fonction de votre ulimit paramètres, il viderait un noyau ou imprimerait le numéro de signal reçu.

Bien que l'interprétation selon laquelle la boucle se terminerait est raisonnable, elle ne prend pas en compte la principale raison de la gestion du signal, c'est-à-dire la gestion des événements asynchrones. Cela signifie que le gestionnaire de signal agit en dehors du flux standard du contrôle du programme ; en fait, l'ensemble du programme est enregistré dans un contexte, et un nouveau contexte est créé juste pour que le gestionnaire de signal s'exécute. Une fois que le gestionnaire de signal a terminé ses actions, le contexte est rétabli et le flux d'exécution normal commence (c'est-à-dire le while(1) ).

Pour répondre à vos questions,

La conclusion a vérifié que lorsque bash exécute une commande externe au premier plan, il ne gère aucun signal reçu jusqu'à ce que le processus de premier plan se termine

L'élément clé à noter ici est le externe partie commande. Dans le premier cas, où sleep est un processus externe mais dans le second cas, read est un intégré du shell lui-même. Donc la propagation du signal vers ces deux diffère dans ces deux cas

type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep

1.Ouvrez un terminal nommé termA et exécutez le fichier créé callback.sh avec /bin/bash callback.sh pour la première fois, les informations s'affichent instantanément.

Oui, ce comportement est attendu. Parce qu'à ce moment, seule la fonction est définie et le gestionnaire d'interruptions est enregistré dans la fonction myCallback et le signal n'est pas encore reçu dans le script. Au fur et à mesure de la séquence d'exécution, le message du read l'invite est lancée pour la première fois.

2.Ouvrez un nouveau terminal, nommé termB et exécutez pkill -USR1 -f callback.sh la première fois, les informations s'affichent instantanément dans termA

Oui, tandis que le read la commande attend une chaîne suivie de Entrée appui sur la touche qui signale le EOF , il reçoit un signal SIGUSR1 à partir de l'autre terminal, le contexte d'exécution en cours est enregistré et le contrôle est commuté sur le gestionnaire de signal qui imprime la chaîne avec la date actuelle.

Dès que le gestionnaire a fini de s'exécuter, le contexte reprend au while boucle dans laquelle le read La commande attend toujours une chaîne d'entrée. Jusqu'au read commande est réussie, tous les pièges de signal suivants imprimeront simplement la chaîne à l'intérieur du gestionnaire de signal.

Continuez dans termB, exécutez pkill -USR1 -f callback.sh la deuxième fois.

Comme expliqué précédemment, le read la commande n'est pas terminée pour une fois dans votre boucle while, seulement si elle réussit à lire une chaîne, la prochaine itération de la boucle commencerait et un nouveau message d'invite serait lancé.

Source de l'image :L'interface de programmation Linux par Michael KerrisK


La conclusion a vérifié que lorsque bash exécute une commande externe au premier plan, il ne gère aucun signal reçu jusqu'à ce que le processus de premier plan se termine.

bash fait gérer les signaux à C / niveau d'implémentation, mais n'exécutera pas les gestionnaires définis avec trap jusqu'à ce que le processus de premier plan soit terminé. C'est requis par la norme :

When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.

Notez que le standard ne fait aucune différence entre les commandes intégrées et les commandes externes ; dans le cas d'un "utilitaire" comme read , qui n'est pas exécuté dans un processus séparé, il n'est pas évident de savoir si un signal se produit dans son "contexte" ou dans celui du shell principal, et si un gestionnaire défini avec trap doit être exécuté avant ou après le read renvoie.

problème 1 :callback.sh contient une boucle while infinie, comment expliquer qu'il ne gère aucun signal reçu jusqu'à ce que le processus de premier plan se termine., dans ce cas, le processus de premier plan ne se termine jamais.

C'est faux. bash n'exécute pas le while boucle dans un processus séparé. Puisqu'il n'y a pas de processus de premier plan bash est en attente, il peut exécuter n'importe quel gestionnaire défini avec trap immédiatement. Si read était une commande externe, ça et non while serait le processus de premier plan.

issue2 :Non please input something for foo: shown dans termeA ;

allez à termB, exécutez pkill -USR1 -f callback.sh la troisième fois.

Les informations suivantes s'affichent à nouveau dans termA :callback function called at Mon Nov 19 09:07:24 HKT 2018

Toujours pas de please input something for foo: affiché dans termA.Pourquoi l'information please input something for foo: geler?

Il ne gèle pas. Appuyez simplement sur Entrée et il s'affichera à nouveau.

C'est simplement que

a) bash va redémarrer le read intégré en place lorsqu'il est interrompu par un signal - il ne reviendra pas et repassera par le while boucle

b) il ne réaffichera pas l'invite définie avec -p dans ce cas.

Notez que bash n'affichera même plus l'invite dans le cas où un SIGINT du clavier a été géré, même s'il supprime la chaîne lue jusqu'à présent par l'utilisateur :

$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"

$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'

Cela imprimera you entered 'foobar' si le signal a été envoyé depuis une autre fenêtre avec kill -INT <pid> au lieu d'utiliser Ctrl-C depuis la borne.

Tout le contenu de cette dernière partie (comment read est interrompu, etc) est très bash spécifique. Dans d'autres shells comme ksh ou dash , et même en bash lorsqu'il est exécuté en mode POSIX (bash --posix ), tout signal traité interrompra en fait le read intégré. Dans l'exemple ci-dessus, le shell imprimera you entered '' et sortir après le premier ^C, et si le read est appelée depuis une boucle, la boucle sera redémarrée.


Linux
  1. Quand et pourquoi devrais-je utiliser Apt-get Update ?

  2. Que fait un programme lorsqu'il envoie un signal Sigkill ?

  3. Pourquoi est-il recommandé de créer un groupe et un utilisateur pour certaines applications ?

  4. Quand setsid() est-il utile ou pourquoi devons-nous regrouper les processus sous Linux ?

  5. Pourquoi stdout a-t-il besoin d'un vidage explicite lorsqu'il est redirigé vers un fichier ?

Qu'est-ce qu'un serveur Linux et pourquoi votre entreprise en a-t-elle besoin ?

Pourquoi OpenStack signale-t-il le type d'hyperviseur comme QEMU alors que libvirt_type est KVM ?

Pourquoi certains Emoji N&B et d'autres sont-ils trop gros ?

Pourquoi clang génère-t-il du texte inintelligible lorsqu'il est redirigé ?

Quand le système envoie-t-il un SIGTERM à un processus ?

Pourquoi Debian et Ubuntu sont-ils par défaut au niveau d'exécution 2 ?