Maison  >  Article  >  Tutoriel système  >  Les appels système sous Linux ne sont pas des entrées légales dans le noyau

Les appels système sous Linux ne sont pas des entrées légales dans le noyau

王林
王林avant
2024-03-19 10:34:02846parcourir

Sous Linux, les appels système sont le seul moyen pour l'espace utilisateur d'accéder au noyau. Ils constituent la seule entrée légale au noyau. En fait, d'autres méthodes telles que les fichiers de périphérique et /proc sont finalement exécutées via des appels système.

Normalement, les applications sont programmées via des sockets de programmation d'application (API) plutôt que directement via des appels système, et ces sockets de programmation n'ont pas réellement besoin de correspondre aux appels système fournis par le noyau. Une API définit un ensemble de sockets de programmation utilisés par les applications. Ils peuvent être implémentés sous la forme d'un seul appel système ou en appelant plusieurs appels système. Il n'y a aucun problème même si aucun appel système n'est utilisé. En fait, les API peuvent être implémentées sur différents systèmes d'exploitation, fournissant exactement les mêmes sockets aux applications, mais leur implémentation sur ces systèmes peut être très différente.

Dans le monde Unix, les sockets de programmation d'applications les plus populaires sont basés sur la norme POSIX et Linux est compatible POSIX.

Du point de vue des programmeurs, ils n'ont besoin de s'occuper que de l'API, et le noyau ne s'occupe que des appels système ; la façon dont les fonctions de bibliothèque et les applications utilisent les appels système ne concerne pas le noyau.

Les appels système (souvent appelés appels système sous Linux) sont généralement appelés via des fonctions. Ils nécessitent généralement la définition d'un ou plusieurs paramètres (entrées) et peuvent provoquer certains effets secondaires. Cet effet secondaire est représenté par une longue valeur de retour indiquant le succès (valeur 0) ou l'erreur (valeur négative). Lorsqu'une erreur se produit lors d'un appel système, le code d'erreur est écrit dans la variable globale errno. En appelant la fonction perror(), cette variable peut être traduite en une chaîne d'erreur que l'utilisateur peut comprendre.

Il y a deux particularités dans l'implémentation des appels système : 1) Il y a un qualificatif asmlinkage dans la déclaration de fonction, qui est utilisé pour notifier au compilateur d'extraire uniquement les paramètres de la fonction de la pile. 2) L'appel système getXXX() est défini comme sys_getXXX() dans le noyau. Il s'agit de la convention de dénomination que tous les appels système sous Linux doivent suivre.

Numéro d'appel système : sous Linux, chaque appel système se voit attribuer un numéro d'appel système, et l'appel système peut être associé à ce numéro unique. Lorsqu'un processus de l'espace utilisateur exécute un appel système, le numéro d'appel système est utilisé pour indiquer quel appel système doit être exécuté ; le processus ne mentionne pas le nom de l'appel système ; Une fois le numéro d'appel système attribué, il ne peut plus être modifié (sinon l'application compilée plantera). Si un appel système est supprimé, le numéro d'appel système qu'il occupe ne peut pas être recyclé. Linux a un appel système "inutilisé" sys_ni_syscall(), qui non seulement renvoie -ENOSYS mais n'effectue aucun autre travail. Ce numéro d'erreur est spécifiquement conçu pour les appels système non valides. Cela semble rare, mais si un appel système est supprimé, cette fonction se charge de « combler le vide ».

Le noyau enregistre une liste de tous les appels système enregistrés dans la table des appels système et la stocke dans sys_call_table. Il est lié à l’architecture et généralement défini dans Entry.s. Ce tableau attribue un numéro d'appel système unique à chaque appel système valide.

Il est difficile pour les programmes de l'espace utilisateur d'exécuter directement le code du noyau. Ils ne peuvent pas appeler directement des fonctions dans l'espace du noyau, car le noyau réside dans un espace d'adressage protégé. L'application doit informer le système sous une forme ou une autre, indiquant au noyau qu'il doit exécuter un appel système, et le système passe à l'état du noyau. appel du noyau Linux , afin que le noyau puisse exécuter l'appel système au nom de l'application. Ces mécanismes de notification au noyau sont implémentés via des interruptions logicielles. Les interruptions logicielles sur les systèmes x86 sont formées par l'instruction int$0x80. Cette instruction déclenchera une exception, obligeant le système à passer en mode noyau et à exécuter le gestionnaire d'exceptions n° 128. Ce programme est le gestionnaire d'appels système et son nom est system_call(). Il est étroitement lié à l'architecture matérielle et est généralement. dans l'entrée Compilé en langage assembleur dans le fichier .s.

Tous les appels système sont piégés dans le noyau sous la même forme que le système Linux drapeau rouge, donc le simple piégeage dans l'espace du noyau ne suffit pas. Par conséquent, le numéro d’appel système doit être transmis au noyau. Sur x86, ce transfert s'effectue en plaçant le numéro d'appel dans le registre eax avant de déclencher le softirq. De cette façon, une fois le gestionnaire d'appels système exécuté, les données peuvent être obtenues à partir d'eax. Le system_call() mentionné ci-dessus vérifie la validité du numéro d'appel système donné en le comparant avec NR_syscalls. S'il est inférieur ou égal à NR_syscalls, la fonction renvoie -ENOSYS. Sinon, l'appel système correspondant est exécuté : call*sys_call_table(,%eax,4);

Étant donné que les entrées de la table des appels système sont stockées en type 32 bits (4 octets), le noyau doit diviser le numéro d'appel système donné par 4, puis utiliser le résultat pour interroger la position du périphérique dans la table. . Comme le montre la figure 1 :

Il a déjà été mentionné que non seulement le numéro d'appel du système, mais également la saisie de certains paramètres externes sont requis. Le moyen le plus simple consiste à stocker ce paramètre dans un registre, tout comme en passant le numéro d'appel système. Sur les systèmes x86, ebx, ecx, edx, esi et edi stockent les 5 premiers paramètres dans l'ordre. Dans le cas peu probable où six paramètres ou plus seraient requis, un registre distinct devrait être utilisé pour stocker les pointeurs pointant vers les adresses de l'espace utilisateur de tous ces paramètres. Les valeurs de retour vers l'espace utilisateur sont également transmises via des registres. Sur les systèmes x86, il est stocké dans le registre eax.

Les appels système doivent vérifier soigneusement si tous leurs paramètres sont légaux et valides. Les appels système sont exécutés dans l'espace du noyau. Si les utilisateurs sont autorisés à transmettre des entrées illégales au noyau, la sécurité et la stabilité du système seront mises à rude épreuve. Le test le plus important consiste à détecter si le pointeur de surveillance fourni par l'utilisateur est valide. Avant que le noyau ne reçoive un pointeur de surveillance de l'espace utilisateur, le noyau doit s'assurer :

.

1) La zone mémoire vidéo pointée par l'aiguille de la montre appartient à l'espace utilisateur

2) La zone mémoire pointée par l'aiguille du compteur se trouve dans l'espace d'adressage du processus

3) S'il s'agit d'une lecture, la mémoire lue doit être marquée comme lisible. En cas d'écriture, la mémoire doit être marquée comme inscriptible.

Le noyau propose deux manières d'effectuer la détection nécessaire et de copier les données entre l'espace noyau et l'espace utilisateur. L'une de ces deux méthodes doit être appelée.

内核调用用户态函数_内核调用call_linux 内核调用

copy_to_user() : écriture des données dans l'espace utilisateur, nécessitant 3 paramètres. Le premier paramètre est l'adresse mémoire de destination dans l'espace de processus. La seconde est l'adresse source dans l'espace noyau

.Le troisième est la largeur des données (nombre d'octets) qui doivent être copiées.

copy_from_user() : La lecture des données depuis l'espace utilisateur nécessite 3 paramètres. Le premier paramètre est l'adresse mémoire de destination dans l'espace de processus. Le second est la source dans l'espace noyau

Adresse. Le troisième est la largeur des données (nombre d'octets) qui doivent être copiées.

Remarque : ces deux éléments peuvent provoquer une obstruction. Ces situations se produisent lorsque les pages contenant des données utilisateur sont transférées sur le disque dur plutôt que dans la mémoire mathématique. À ce moment-là, le noyau Linux appelle et le processus se mettra en veille jusqu'à ce que le gestionnaire de défauts de page remplace la page du disque dur vers la mémoire chimique.

Le noyau est dans le contexte du processus lors de l'exécution d'un appel système, et le pointeur actuel pointe vers la tâche en cours, qui est le processus qui a provoqué l'appel système. Dans le contexte d'un processus, le noyau peut dormir (par exemple, en bloquant un appel système ou en appelant explicitement planning()) mais peut être occupé. Lorsque l'appel système revient, le contrôle reste dans system_call(), qui est en fin de compte responsable du basculement vers l'espace utilisateur et de permettre au processus utilisateur de poursuivre son exécution.

Il est très simple d'ajouter un temps d'appel système à Linux. Comment concevoir et implémenter un appel système est le dilemme. La première étape de l'implémentation d'un appel système consiste à décider de son objectif. Cet objectif doit être clair et unique. N'essayez pas d'écrire un appel système polyvalent. ioctl est un matériel pédagogique back-end. Les paramètres, valeurs de retour et codes d'erreur du nouvel appel système sont très importants. Une fois qu'un appel système est compilé, l'enregistrer comme appel système à venir est une tâche fastidieuse, qui suit généralement les étapes suivantes :

1) Ajoutez une entrée à la fin de la table des appels système (généralement située dans Entry.s). En comptant à partir de 0, la position d'une entrée système dans le tableau est son numéro d'appel système. Comme le premier

10 appels système sont attribués au numéro d'appel système 9

2) Pour toute architecture, le numéro d'appel système doit être défini dans include/asm/unistd.h

3) Les appels système doivent être compilés dans l'image du noyau (ne peuvent pas être compilés en modules). Cela doit juste être placé dans un fichier associé sous kernel/.

Généralement, les appels système sont pris en charge par la bibliothèque C. Les programmes utilisateur peuvent utiliser des appels système (ou utiliser des fonctions de bibliothèque, qui sont ensuite réellement appelées) en incluant des fichiers d'en-tête standard et en établissant une liaison avec la bibliothèque C. Heureusement, Linux lui-même fournit un ensemble de macros permettant un accès direct aux appels système. Il définira le registre et appellera l'instruction int$0x80. Cette macro s'appelle _syscalln(), où n va de 0 à 6. Elle représente le nombre de paramètres qui doivent être transmis à l'appel système. En effet, la macro doit savoir exactement combien d'arguments sont insérés dans les registres et dans quel ordre. Prenons l'exemple de l'appel système ouvert :

L'appel système open() est défini comme suit :

longopen(constchar*nom de fichier,intflags,intmode)

La façon d'appeler directement la macro de cet appel système est :

#defineNR_open5

_syscall3(long,open,constchar*,filename,int,flags,int,mode)

De cette façon, l'application peut utiliser directement open(). Appelez simplement l'appel système open() et placez directement la macro dans l'application. Pour chaque macro, il y a 2+2*n paramètres. La signification de chaque paramètre est simple et claire, et ne sera pas expliquée en détail ici.

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer