web-dev-qa-db-fra.com

Comment l'appel système sous Linux est-il implémenté?

Lorsque j'invoque un appel système en mode utilisateur, comment l'appel a-t-il été traité dans le système d'exploitation?

Invoque-t-il un exécutable binaire ou une bibliothèque standard?

Si oui, de quel genre de chose a-t-il besoin pour terminer l'appel?

41
MainID

Jetez un œil à this .

À partir de la version 2.5, le noyau Linux a introduit un nouveau mécanisme d'entrée d'appel système sur les processeurs Pentium II +. En raison de problèmes de performances sur les processeurs Pentium IV avec la méthode d'interruption logicielle existante, un mécanisme alternatif d'entrée d'appel système a été implémenté à l'aide des instructions SYSENTER/SYSEXIT disponibles sur les processeurs Pentium II +. Cet article explore ce nouveau mécanisme. La discussion est limitée à l'architecture x86 et toutes les listes de code source sont basées sur le noyau Linux 2.6.15.6.

  1. Que sont les appels système?

    Les appels système fournissent aux processus utilisateur un moyen de demander des services au noyau. Quel genre de services? Services qui sont gérés par le système d'exploitation comme le stockage, la mémoire, le réseau, la gestion des processus, etc. Par exemple, si un processus utilisateur veut lire un fichier, il devra effectuer des appels système "ouvrir" et "lire". En général, les appels système ne sont pas appelés directement par les processus. La bibliothèque C fournit une interface pour tous les appels système.

  2. Que se passe-t-il lors d'un appel système?

    Un extrait de code du noyau est exécuté à la demande d'un processus utilisateur. Ce code s'exécute dans l'anneau 0 (avec le niveau de privilège actuel -CPL- 0), qui est le plus haut niveau de privilège dans l'architecture x86. Tous les processus utilisateur s'exécutent dans l'anneau 3 (CPL 3).

    Donc, pour implémenter le mécanisme d'appel système, nous avons besoin

    1) un moyen d'appeler le code de la sonnerie 0 à partir de la sonnerie 3.

    2) du code noyau pour traiter la demande.

  3. Bonne vieille façon de procéder

    Il y a quelque temps encore, Linux implémentait des appels système sur toutes les plates-formes x86 à l'aide d'interruptions logicielles. Pour exécuter un appel système, le processus utilisateur copiera le numéro d'appel système souhaité dans% eax et exécutera "int 0x80". Cela générera une interruption 0x80 et une routine de service d'interruption sera appelée. Pour l'interruption 0x80, cette routine est une routine de "traitement de tous les appels système". Cette routine s'exécutera dans l'anneau 0. Cette routine, telle que définie dans le fichier /usr/src/linux/Arch/i386/kernel/entry.S, sauvegardera l'état actuel et appellera le gestionnaire d'appels système approprié en fonction de la valeur en% eax.

  4. Nouvelle façon brillante de le faire

    Il a été découvert que cette méthode d'interruption logicielle était beaucoup plus lente sur les processeurs Pentium IV. Pour résoudre ce problème, Linus a implémenté un mécanisme d'appel système alternatif pour tirer parti des instructions SYSENTER/SYSEXIT fournies par tous les processeurs Pentium II +. Avant d'aller plus loin avec cette nouvelle façon de faire, familiarisons-nous avec ces instructions.

38
GregD

Cela dépend de ce que vous entendez par appel système. Voulez-vous dire un appel de bibliothèque C (via la glibc) ou un appel système réel? Les appels de bibliothèque C finissent toujours par utiliser des appels système.

L'ancienne façon de faire des appels système était via une interruption logicielle, c'est-à-dire l'instruction int. Windows avait int 0x2e alors que Linux avait int 0x80. Le système d'exploitation configure un gestionnaire d'interruption pour 0x2e ou 0x80 dans la table de descripteur d'interruption (IDT). Ce gestionnaire exécute ensuite l'appel système. Il copie les arguments du mode utilisateur au mode noyau (ceci est contrôlé par une convention spécifique au système d'exploitation). Sous Linux, les arguments sont passés en utilisant ebx, ecx, edx, esi et edi. Sous Windows, les arguments sont copiés à partir de la pile. Le gestionnaire effectue alors une sorte de recherche (pour trouver l'adresse de la fonction) et exécute l'appel système. Une fois l'appel système terminé, l'instruction iret revient en mode utilisateur.

La nouvelle méthode est sysenter et sysexit. Ces deux instructions font essentiellement tout le travail de registre pour vous. Le système d'exploitation définit les instructions via les registres spécifiques au modèle (MSR). Après cela, c'est pratiquement la même chose que d'utiliser int.

11
wj32

Il passe par la glibc, qui émet une interruption 0x80 après avoir rempli les registres avec des paramètres. Le gestionnaire d'interruption du noyau recherche alors l'appel syscall dans la table syscall et appelle la fonction sys _ * () appropriée.

Vraiment simplifié, mais ce qui se passe, c'est qu'une interruption se produit lorsque vous essayez d'accéder à une adresse mémoire réservée. L'interruption fait passer le contexte en mode noyau et exécute le code noyau (appel système réel) pour le compte de l'utilisateur. Une fois l'appel terminé, le contrôle est retourné au code utilisateur.

3
tvanfosson

int X dans Assembly se traduit par un numéro d'appel système n.
L'appel système Ex read peut recevoir un numéro 4.
Au démarrage du système, le système d'exploitation construit une table de pointeurs appelée table de descripteur d'interruption (IDT) qui contient une liste d'adresses pour les appels système avec le privilège nécessaire pour les exécuter.
Le niveau de privilège actuel (CPL) est sauvegardé dans l'un des bits du registre CS (techniquement 2 bits sur x86).
Voici les étapes suivies d'une instruction int:
• Récupère le nième descripteur de l'IDT, où n est l'argument de int.
• Vérifiez que CPL dans% cs est <= DPL, où DPL est le niveau de privilège dans le descripteur.
• Si ce n'est pas le cas, l'utilisateur n'a pas eu suffisamment de privilèges pour l'exécuter et entraînera l'exécution d'une instruction int 13 (erreur de protection générale), (l'utilisateur n'avait pas assez de privilèges)
• Si oui alors le code utilisateur a suffisamment de privilèges pour faire cet appel système, le contexte d'exécution courant est sauvegardé (registres etc), car nous passons maintenant en mode noyau.
Les informations incluent des registres, des drapeaux parce que lorsque l'appel système est terminé, nous voulons continuer l'exécution là où nous sommes partis. • Les paramètres de l'appel système sont enregistrés sur la pile du noyau, car les appels système sont exécutés en mode noyau.

VSYSCALL (APPEL SYSTÈME RAPIDE)
Chaque fois qu'un appel système est exécuté par l'utilisateur, le système d'exploitation enregistre l'état actuel de la machine (c'est-à-dire le registre, le pointeur de pile, etc.) et passe en mode noyau pour l'exécution. Pour certains appels système, il n'est pas nécessaire de sauvegarder tout le registre. L'appel système Ex gettime of day lit l'heure actuelle et l'appel système revient. Ainsi, certains appels système sont implémentés via ce que l'on appelle vsyscalls. Ici, lorsqu'un appel système est effectué, il est exécuté dans l'espace utilisateur lui-même sans jamais basculer vers le noyau. Le temps est donc gagné.
Voir ici pour plus de détails sur vsyscall http://www.trilithium.com/johan/2005/08/linux-gate/
et ici Tout le monde peut comprendre comment fonctionne gettimeofday?

1
Deepthought

Un appel système est composé d'une instruction d'interruption spéciale, d'un numéro d'appel système et d'arguments.

  1. L'instruction spéciale trap est utilisée pour passer du mode utilisateur au mode noyau qui a des privilèges illimités.
  2. Le numéro et les arguments de l'appel système sont transmis par registre.
0
Chris Tsui