Maison >Tutoriel système >Linux >Une brève analyse de la gestion du signal asynchrone Linux
Le système Linux est un système d'exploitation qui prend en charge l'exécution simultanée de tâches multiples. Il peut exécuter plusieurs processus en même temps, améliorant ainsi l'utilisation et l'efficacité du système. Cependant, si l'échange de données et la collaboration sont requis entre ces processus, certaines méthodes de communication inter-processus (IPC) doivent être utilisées, telles que les files d'attente de messages, la mémoire partagée, les sémaphores, etc. Parmi eux, signal est une méthode IPC relativement simple et flexible, qui permet à un processus d'envoyer un court message à un autre processus pour l'informer qu'un événement ou une exception s'est produit. Il existe deux types de signaux dans les systèmes Linux, à savoir les signaux synchrones et les signaux asynchrones. Cet article analysera brièvement la méthode de gestion des signaux asynchrones Linux, y compris la signification, la génération, l'envoi, la réception, le traitement et l'ignorance des signaux asynchrones.
Quand j'ai appris la programmation Linux pour la première fois, j'ai toujours pensé que la gestion du signal asynchrone était une chose très magique. Les programmes utilisateur peuvent utiliser des appels système tels que singal pour enregistrer une fonction de traitement du signal (fonction de gestion) pour un certain signal.
Le code binaire du programme a un certain flux d'exécution dans la mémoire. Pourquoi le programme est-il « interrompu » après avoir reçu un signal asynchrone, puis passe à la fonction handle pour s'exécuter ? Comment le noyau peut-il avoir la capacité de faire faire un tel saut au programme ? Il est impossible de modifier temporairement le code exécutable du programme, n'est-ce pas ?
Après avoir acquis quelques connaissances sur le noyau, j'ai réalisé qu'après qu'un processus reçoit un signal, il n'est pas immédiatement "interrompu". Au lieu de cela, il enregistre d'abord la réception d'un certain signal dans la structure de contrôle du processus (task_struct), puis attend que le process Lorsqu'il est sur le point de revenir du mode noyau au mode utilisateur, le processus est "interrompu" et la fonction handle est appelée.
Quand le processus utilisateur reviendra-t-il du mode noyau au mode utilisateur ? Généralement, il existe trois situations principales : l'appel système (le processus utilisateur entre activement dans le noyau), l'interruption (le processus utilisateur entre passivement dans le noyau) et l'exécution planifiée (le processus utilisateur passe de l'attente d'exécution à l'exécution).
Il faut un certain temps au processus pour revenir de l'état noyau à l'état utilisateur après avoir reçu le signal. Mais ce temps est généralement très court, au moins une interruption d'horloge amènera le processus utilisateur dans le noyau à une fréquence relativement élevée (par exemple, une fois toutes les millisecondes) (bien sûr, uniquement pour le processus en cours d'exécution).
Lorsque le processus est sur le point de revenir du mode noyau au mode utilisateur, s'il y a un signal qui doit être traité, la fonction handle correspondante sera appelée (bien sûr, le handle peut ne pas être enregistré et le noyau le fera gérer le signal par défaut). Notez que le processus est toujours en mode noyau. Comment le noyau appelle-t-il la fonction handle en mode utilisateur ?
Puis-je l'appeler directement ? Bien sûr que non. Le code du noyau s'exécute sous un niveau de privilège CPU élevé. Si la fonction handle est appelée directement, la fonction handle sera également exécutée sous le même privilège CPU. Ensuite, l'utilisateur pourra faire ce qu'il veut dans la fonction handle.
Par conséquent, le handle appelant doit d’abord revenir en mode utilisateur. Mais après le retour en mode utilisateur, le déroulement du programme n'est plus contrôlé par le noyau. Est-il possible que le noyau modifie réellement temporairement le code exécutable du processus utilisateur ?
L'approche actuelle du noyau est assez intelligente. Une fois qu'un processus utilisateur entre dans le noyau, il laissera une adresse de retour sur la pile de noyau correspondante afin que le processus puisse revenir. La façon dont le noyau appelle la fonction handle consiste à modifier temporairement l'adresse de retour sur la pile, puis à revenir selon le processus d'origine de retour en mode utilisateur. En conséquence, ce retour va à la fonction handle. (Bien sûr, ce n’est pas seulement l’adresse de retour qui doit être modifiée, mais toute la pile d’appels.)
Bien que l'adresse de retour ait été temporairement modifiée, le processus utilisateur finira par revenir à l'adresse de retour d'origine. Alors, où doivent être enregistrées l’adresse de retour d’origine et sa pile d’appels ? L'espace de pile du noyau du processus est limité et il doit également gérer les appels système qui peuvent survenir dans la fonction handle, il est donc irréaliste que le noyau mette ces informations sur la pile du noyau, et elles ne peuvent être poussées que sur la pile utilisateur.
Lorsque la fonction handle est exécutée, le processus d'exécution revient au noyau. De même, en raison des différents niveaux de privilèges du processeur, vous ne pouvez pas simplement utiliser l'instruction RET pour revenir de la fonction handle au noyau. Un appel système doit être exécuté.
Une fois le handle exécuté, pourquoi devons-nous revenir au noyau, puis revenir du noyau à l'adresse de retour d'origine ? Ce serait très pratique si vous retourniez directement à l'adresse de retour d'origine. Et ce n'est pas difficile à faire. L'adresse de retour d'origine et sa pile d'appels ont été poussées sur la pile utilisateur. Le noyau n'a besoin que d'une petite manipulation sur la pile d'appels de la fonction handle.
1. Revenir à l'adresse de retour d'origine ne signifie pas simplement revenir à cette adresse. Vous devez restaurer l'intégralité de la scène (principalement les registres, etc.). Bien sûr, le noyau peut également appuyer sur du code sur la pile utilisateur pour effectuer ces opérations ;
2. Il peut maintenant y avoir plus d'un signal à traiter. Il est préférable de laisser le processus utilisateur revenir au noyau et continuer à traiter d'autres signaux ;
Pour revenir au noyau, le noyau pousse d'abord une adresse de retour sur la pile utilisateur avant de revenir à la fonction handle, afin qu'il puisse revenir à l'adresse spécifiée lors du retour du handle. Cette adresse spécifiée se trouve en fait également sur la pile utilisateur du processus. Le noyau place plusieurs instructions sur cette adresse (place le code exécutable sur la pile) pour permettre au processus d'appeler un appel système appelé sigreturn.
La pile utilisateur avant de revenir à la fonction handle est à peu près la suivante :
Données d'origine -> Instruction pour appeler sigreturn (que son adresse soit a) -> Adresse de retour d'origine et sa pile d'appels -> Adresse de retour (la valeur est a) -> Variable de pile de handle
Le noyau place l'instruction sigreturn sur la pile d'appels de la fonction handle, ce qui est la pratique sous Linux 2.4. Chaque fois que la fonction handle de l'utilisateur est appelée, tant d'instructions doivent être copiées dans la pile utilisateur, ce qui n'est pas bon.
Linux 2.6 possède une page appelée page vsyscall, qui contient des instructions préparées par le noyau pour les programmes utilisateur, notamment l'appel de l'instruction sigreturn. Cette page vsyscall est mappée à la fin de l'espace d'adressage virtuel de chaque processus, est partagée par tous les processus utilisateur et est en lecture seule pour les processus utilisateur. De cette façon, il n'est pas nécessaire d'insérer l'instruction sigreturn dans la pile d'appels de la fonction handle. Définissez simplement l'adresse de retour de la fonction handle sur le code correspondant dans la page vsyscall.
Afin d'appeler automatiquement sigreturn pour revenir au noyau après l'exécution du handle, le noyau fait beaucoup de choses. Alors pouvons-nous accepter que les utilisateurs appellent eux-mêmes sigreturn ?
Bien sûr, cela est possible. Juste pour faire du mécanisme de traitement du signal un mécanisme complet, le noyau ne l'a pas fait. Sinon, si l'utilisateur oublie d'appeler sigreturn dans la fonction handle, le processus peut planter inexplicablement. Et il est difficile pour le compilateur de trouver de telles erreurs.
Une fois que le processus a appelé l'appel système sigreturn et est rentré dans le noyau, l'adresse de retour d'origine et sa pile d'appels appuyée sur la pile utilisateur sont obtenues. Finalement, le noyau modifiera la pile afin que le processus revienne à l'adresse de retour d'origine lorsqu'il reviendra dans l'espace utilisateur.
Cet article analyse brièvement la méthode de gestion des signaux asynchrones Linux, y compris la signification, la génération, l'envoi, la réception, le traitement et l'ignorance des signaux asynchrones. En comprenant et maîtrisant ces connaissances, nous pouvons maîtriser les connaissances de base du traitement du signal Linux, améliorant ainsi la stabilité et l'efficacité du système. Bien entendu, la gestion des signaux asynchrones Linux possède de nombreuses autres fonctionnalités et utilisations, qui nécessitent un apprentissage et une recherche continus. J'espère que cet article pourra vous apporter de l'inspiration et de l'aide.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!