Comment fonctionnent les signaux sous Unix? Je suis passé par WR Stevens mais j’ai été incapable de comprendre. Aidez-moi, s’il vous plaît.
L’explication ci-dessous n’est pas exacte et plusieurs aspects de son fonctionnement diffèrent d’un système à l’autre (et peut-être même du même système d’exploitation sur un matériel différent pour certaines parties), mais je pense qu’il est généralement suffisant pour satisfaire votre curiosité. Utilise les. La plupart des gens commencent à utiliser des signaux dans la programmation sans même avoir ce niveau de compréhension, mais avant de les utiliser, je voulais les comprendre.
Le kernel du système d’exploitation dispose d’une structure de données appelée bloc de contrôle de processus pour chaque processus en cours d’exécution qui contient des données sur ce processus. Cela peut être recherché par l’identifiant de processus (PID) et comprend un tableau des actions de signal et des signaux en attente.
Lorsqu’un signal est envoyé à un processus, le kernel du système d’exploitation recherche le bloc de contrôle de processus de ce processus et examine la table d’action du signal pour localiser l’action pour le signal particulier envoyé. Si la valeur de l’action du signal est SIG_IGN
le kernel oublie le nouveau signal. Si la valeur de l’action du signal est SIG_DFL
le kernel recherche l’action de traitement du signal par défaut pour ce signal dans une autre table et réalise cette action. Si les valeurs sont quelque chose d’autre, cela est supposé être une adresse de fonction dans le processus auquel le signal est envoyé et qui devrait être appelé. Les valeurs pour SIG_IGN
et SIG_DFL
sont des nombres SIG_DFL
en pointeurs de fonctions dont les valeurs ne sont pas des adresses valides dans l’espace adresse d’un processus (telles que 0 et 1, qui sont toutes deux à la page 0, jamais associées à un processus).
Si une fonction de traitement du signal a été enregistrée par le processus (la valeur de l’action de signal n’était ni SIG_IGN ni SIG_DFL), une entrée dans la table de signal en attente est faite pour ce signal et ce processus est marqué quelque chose, comme des données à rendre disponibles pour un appel à read
, à attendre un signal ou plusieurs autres choses).
Maintenant, la prochaine fois que le processus sera exécuté, le kernel du système d’exploitation appenda d’abord des données à la stack et modifiera le pointeur d’instruction de ce processus afin qu’il ressemble presque au processus lui-même. Ce n’est pas tout à fait correct et s’écarte assez de ce qui se passe réellement pour en parler plus en détail.
La fonction de gestionnaire de signaux peut faire tout ce qu’elle fait (cela fait partie du processus pour lequel elle a été appelée, elle a donc été écrite avec des connaissances sur ce que ce programme devrait faire avec ce signal). Lorsque le gestionnaire de signaux retourne, le code normal du processus recommence à s’exécuter. (encore une fois, pas précis, mais plus à ce sujet ensuite)
Ok, ce qui précède devrait vous donner une bonne idée de la manière dont les signaux sont transmis à un processus. Je pense que cette bonne version de cette idée est nécessaire avant de pouvoir saisir l’idée complète, qui inclut des choses plus compliquées.
Très souvent, le kernel du système d’exploitation doit savoir quand un gestionnaire de signaux retourne. Cela est dû au fait que les gestionnaires de signaux prennent un argument (qui peut nécessiter un espace de stack), vous pouvez bloquer le même signal deux fois pendant l’exécution du gestionnaire de signaux et / ou faire redémarrer les appels système après la transmission du signal. Pour accomplir ceci un peu plus que les changements de pointeur de stack et d’instruction.
Ce qui doit arriver, c’est que le kernel ait besoin de faire en sorte que le processus lui dise qu’il a fini d’exécuter la fonction de gestionnaire de signaux. Cela peut être fait en mappant une section de RAM dans l’espace d’adressage du processus qui contient du code pour effectuer cet appel système et l’adresse du retour pour la fonction de gestionnaire de signaux (la valeur supérieure de la stack lorsque cette fonction est en cours d’exécution). ce code. Je pense que c’est comme ça que ça se passe sous Linux (au moins les versions les plus récentes). Une autre façon d’y parvenir (je ne sais pas si cela est fait, mais cela pourrait être) serait de faire en sorte que l’adresse de retour de la fonction de gestionnaire de signal soit une adresse invalide (telle que NULL) qui provoquerait une interruption sur la plupart des systèmes. , ce qui donnerait à nouveau le contrôle du kernel du système d’exploitation. Ce n’est pas grave, mais le kernel doit reprendre le contrôle pour réparer la stack et savoir que le gestionnaire de signal est terminé.
que le kernel Linux mappe une page dans le processus pour cela, mais que l’appel système réel pour enregistrer les gestionnaires de signaux (quels appels sigaction) prend un paramètre paramètre sa_restore, qui doit être utilisé comme adresse de retour du signal gestionnaire, et le kernel s’assure juste qu’il est mis là. Le code à cette adresse émet l’appel système ( sigreturn
) et le kernel sait que le gestionnaire de signal est terminé.
Je suppose surtout que vous savez comment les signaux sont générés en premier lieu. Le système d’exploitation peut les générer pour le compte d’un processus à cause de quelque chose, comme une temporisation expirant, un processus enfant mourant, accéder à la mémoire à laquelle il ne devrait pas accéder ou émettre une instruction qu’il ne doit pas ou celui qui est privilégié), ou beaucoup d’autres choses. Le temporisateur est fonctionnellement un peu différent des autres car il peut se produire lorsque le processus n’est pas en cours d’exécution, et ressemble donc davantage aux signaux envoyés avec l’appel du système de mise à kill
. Pour les signaux non liés à la temporisation envoyés au nom du processus en cours, ils sont générés lorsqu’une interruption se produit car le processus en cours fait quelque chose de mal. Cette interruption donne le contrôle du kernel (tout comme un appel système) et le kernel génère le signal à transmettre au processus en cours.
Considérez la fonction de signal comme des interruptions, implémentées par le système d’exploitation (plutôt que dans le matériel).
Alors que votre programme traverse joyeusement son locus d’exécution rooté dans main (), ces interruptions peuvent se produire, entraîner l’envoi du programme sur un vecteur (gestionnaire), y exécuter le code, puis retourner à l’emplacement où il a été interrompu.
Ces interruptions (signaux) peuvent provenir de diverses sources, par exemple des erreurs matérielles telles que l’access à des adresses incorrectes ou mal alignées, la mort d’un processus enfant, des signaux générés par l’utilisateur à l’aide de la commande kill
ou d’autres processus utilisant l’appel système kill
. La façon dont vous consumz des signaux consiste à désigner des gestionnaires qui sont dissortingbués par le système d’exploitation lorsque les signaux se produisent. Notez que certains de ces signaux ne peuvent pas être manipulés, ce qui entraîne simplement la mort du processus.
Mais ceux qui peuvent être manipulés peuvent être très utiles. Vous pouvez les utiliser pour la communication inter-processus, c’est-à-dire qu’un processus envoie un signal à un autre processus, qui le gère, et dans le gestionnaire, il fait quelque chose d’utile. De nombreux démons font des choses utiles comme relire le fichier de configuration si vous leur envoyez le bon signal.
Certains problèmes qui ne sont pas traités dans toutes les instructions ci-dessus sont multi-core, s’exécutant dans l’espace kernel tout en recevant un signal, dormant dans l’espace kernel tout en recevant un signal, redémarrage de l’appel système et latence du gestionnaire de signal.
Voici quelques points à considérer:
Réponses:
siginterrupt(2)
. Vous pouvez décider si vous souhaitez que les appels système puissent être redémarrés pour un certain signal lorsque vous enregistrez le signal en utilisant sigaction(2)
avec le drapeau SA_RESTART
. Si un appel système est émis et est interrompu par un signal et n’est pas redémarré automatiquement, vous obtenez une valeur de retour EINTR
(interrompue) et vous devez gérer cette valeur. Vous pouvez également consulter l’appel système restart_syscall(2)
pour plus de détails. Quelques notes expliquant pourquoi tout cela est si complexe:
kill(2)
et plus). Alors, quelle est la latence de la gestion du signal?
Le signal n’est qu’une interruption dans l’exécution du processus. Un processus peut se signaler ou provoquer la transmission d’un signal à un autre processus. Peut-être qu’un parent peut envoyer un signal à son enfant pour le terminer, etc.
Cochez le lien suivant pour comprendre.
https://unix.stackexchange.com/questions/80044/how-signals-work-internally
http://www.linuxjournal.com/article/3985
http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show