Qu'entend-on par «un appel système» sinon l'implémentation dans le langage de programmation?

14

Je voudrais comprendre le terme «appel système». Je sais que les appels système sont utilisés pour obtenir les services du noyau à partir d'une application en espace utilisateur.

La partie sur laquelle j'ai besoin d'éclaircissements est la différence entre un "appel système" et une "implémentation C de l'appel système".

Voici une citation qui m'embrouille:

Sur les systèmes de type Unix, cette API fait généralement partie d'une implémentation de la bibliothèque C (libc), telle que glibc, qui fournit des fonctions d'encapsulation pour les appels système, souvent nommées de la même manière que les appels système qu'elles appellent.

Quels sont les «appels système qu'ils appellent»? Où est leur source? Puis-je les inclure directement dans mon code?

Est-ce que "l'appel système" dans un sens générique n'est qu'une interface définie par POSIX mais pour voir réellement l'implémentation, on pourrait examiner la source C et voir comment va réellement la communication entre l'espace utilisateur et le noyau?

Note d'information: j'essaie de comprendre si, à la fin, chaque fonction c finit par interagir avec les appareils de /dev.

TheMeaningfulEngineer
la source

Réponses:

21

Les appels système en soi sont un concept. Ils représentent des actions que les processus peuvent demander au noyau d'effectuer.

Ces appels système sont implémentés dans le noyau du système de type UNIX. Cette implémentation (écrite en C et en asm pour les petites pièces) exécute en fait l'action dans le système.

Ensuite, les processus utilisent une interface pour demander au système l'exécution des appels système. Cette interface est spécifiée par POSIX. Il s'agit d'un ensemble de fonctions de la bibliothèque standard C. Ce sont en fait des wrappers, ils peuvent effectuer des vérifications puis appeler une fonction spécifique au système dans le noyau qui lui dit de faire les actions requises par l'appel système. Et l'astuce est que les fonctions qui sont l'interface sont nommées de la même façon que les appels système eux-mêmes et sont souvent appelées directement "les appels système".

Vous pouvez appeler la fonction dans le noyau qui effectue l'appel système directement via le mécanisme spécifique au système. Le problème est que cela rend votre code absolument pas portable.

Ainsi, un appel système est:

  • un concept, une séquence d'actions effectuée par le noyau pour offrir un service à un processus utilisateur
  • la fonction de la bibliothèque standard C que vous devez utiliser dans votre code pour obtenir ce service à partir du noyau.
lgeorget
la source
1
Avez-vous un exemple de la "fonction wrapper" et un appel système réel? (chemins de fichiers sous Linux ou liens vers des sources)
TheMeaningfulEngineer
3
Par exemple, il s'agit de l'implémentation de l' getpidappel système dans le noyau Linux: lxr.free-electrons.com/source/kernel/timer.c?v=2.6.35#L1337 . Et c'est la fonction wrapper dans la bibliothèque standard GNU C glibc-2.19: fossies.org/dox/glibc-2.19/… .
lgeorget
@Igeorget: vos liens ne fonctionnent plus. Lien mis à jour pour l'implémentation du noyau: github.com/torvalds/linux/blob/… . Je ne pouvais pas trouver ce que fait la glibc ces jours-ci, ce code est impossible à naviguer.
rchard2scout
6

Un appel système est un moyen de demander à votre système d'exploitation (noyau) d'effectuer une opération au nom de votre programme, ce que le programme ne peut pas faire par lui-même (ou est simplement gênant). La raison de ne pas pouvoir effectuer certaines opérations est normalement que permettre à un programme aléatoire de les faire pourrait compromettre l'intégrité du système, comme faire des E / S (directement sur la RAM, écraser quoi que ce soit).

POSIX définit une interface pour les programmes, certaines fonctions que votre programme peut appeler. Certains d'entre eux se traduisent plus ou moins directement par des appels système, d'autres nécessitent plus de détails. C'est le runtime de votre langage, par exemple la bibliothèque C, qui est responsable de l'offre de l'interface POSIX, de la mise en package des arguments et de la remise des résultats à la main de l'appelant.

Les systèmes Unixy offrent des interfaces POSIX plus ou moins directement comme appels système. En règle générale, il existe un moyen d'appeler directement les appels système, recherchez syscall(2)les détails sur la façon d'utiliser cette fonction sous Linux.

vonbrand
la source
1
Vous touchez un point important que les autres réponses viennent juste effleurer. Toute fonction qu'un programmeur raisonnablement capable pourrait écrire pour lui - même ( par exemple strlen, strcpy, sqrtet qsort) peut être et est probablement dans l' espace utilisateur, chargé à partir d' une bibliothèque. (Surtout libc; les fonctions mathématiques comme sqrtet les fonctions trigonométriques et hyperboliques sont probablement dans libm, la bibliothèque de mathématiques.)… (Suite)
Scott
1
(suite) ... Mais il n'y a aucun moyen d' un utilisateur peut écrire son propre fork, killou la openfonction, car ceux - ci ont besoin d' accéder au système d'exploitation espace mémoire du noyau (par exemple, la table de processus) ou des instructions privilégiées (par exemple, E / S). Par conséquent, le code qui exécute ces fonctions doit être dans le noyau du système d'exploitation; par conséquent, les fonctions système ou les appels système.
Scott
5

Bien sûr, faisons le combien de directions pouvons-nous-regarder-cet-éléphant? chose.

L'appel système réel est, dans votre programme construit, l'instruction machine qui déclenche l'escalade de privilèges en mode noyau, et dans le noyau lui-même, c'est le code que l'instruction appelle. Le code libc (et chaque exécution de langage) configure les registres de la machine et les paramètres de stockage où le code du noyau s'attend à les trouver, ce qui peut être des endroits très étranges en raison des contraintes imposées à cette instruction machine.

Une fois dans le code du système d'exploitation lui-même, il y a un peu de déroulement de l'image miroir du truc spécifique à la machine que le runtime de l'espace utilisateur a fait, puis un appel de sous-programme parfaitement ordinaire.
Si vous voulez voir exactement comment cela fonctionne dans un système d'exploitation complet, tirez la source du noyau ( git clone https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/) et faites par exemple git grep -i system\ call. Tirez sur la source glibc et faites de même.

jthill
la source
C'est vrai, mais fouiller dans Linux ou glibc est un peu lourd ...
vonbrand
3

Sous Linux au moins, le mécanisme d'appel système fonctionne sous la plupart des architectures en plaçant des données spécifiquement formatées (généralement une sorte de structure c) dans certains registres ou adresses de mémoire prédéfinies.

Le problème vient cependant de forcer le CPU à effectuer le basculement dans l'espace du noyau afin qu'il puisse exécuter le code du noyau privilégié pour répondre à l'appel. Cela se fait en forçant un défaut quelconque (un défaut étant une division par 0, un débordement indéfini ou un défaut de segmentation, etc.), ce qui oblige le noyau à prendre en charge l'exécution pour gérer le défaut.

Normalement, le noyau gère les erreurs en tuant le processus à l'origine ou en exécutant un gestionnaire fourni par l'utilisateur. Cependant, dans le cas d'un appel système, il vérifiera plutôt les registres et les emplacements de mémoire prédéfinis et s'ils contiennent une demande d'appel système, il exécutera cela en utilisant les données fournies par le processus utilisateur dans la structure en mémoire. Cela doit généralement être fait avec un assemblage spécialement conçu à la main et pour faciliter l'utilisation de l'appel système pour l'utilisateur, la bibliothèque C du système doit l'encapsuler en fonction. Pour une interface de niveau inférieur, veuillez consulter http://man7.org/linux/man-pages/man2/syscall.2.html pour obtenir des informations sur le fonctionnement des appels système et sur la façon d'appeler sans encapsuleur C.

Cela donne une simplification excessive, ce n'est pas vrai dans toutes les architectures (mips a une instruction syscall spéciale) et ne fonctionne pas nécessairement de la même manière sur tous les systèmes d'exploitation. Si vous avez des commentaires ou des questions, n'hésitez pas à les poser.

Modifié: Remarque, concernant votre commentaire sur les choses dans / dev / c'est en fait une interface de niveau supérieur au noyau, pas une interface inférieure. Ces appareils utilisent en fait (environ) 4 appels système en dessous. Leur écrire est identique à un appel système d'écriture, lire un appel système de lecture, les ouvrir / fermer équivaut aux appels système ouverts et fermés et exécuter un ioctl provoque un appel système spécial d'ioctl qui en soi est une interface pour accéder à l'un des nombreux ioctl du système appels (appels spéciaux, généralement spécifiques à un appareil avec une utilisation trop étroite pour écrire un appel système complet pour eux).

Vality
la source
1

Chaque appel système a un entier associé. Ce nombre entier est fonction de la valeur de retour de l'appel système, du nombre d'arguments de l'appel système et du type des arguments. Ce numéro d'appel système n'est rien d'autre qu'un décalage dans le vecteur d'appel système global, ce vecteur qui n'est accessible qu'en mode privilégié contient un pointeur vers les gestionnaires appropriés. Le processus lors de l'appel d'un appel système, une interruption logicielle (interruption d'interruption) serait générée, donc un gestionnaire d'interruption serait exécuté qui déterminerait quel appel système devrait être appelé. Ensuite, le noyau copiera les arguments de l'appel système passé par l'utilisateur qui se trouve sur la pile dans les registres du processeur, et une fois le service demandé terminé, les données seront recopiées dans la pile à partir des registres du processeur. C'est l'une des raisons pour lesquelles les appels système sont limités,

Naresh
la source
Chaque appel (opération) est identifié en interne par un numéro, vrai. Mais le nombre dépend de l' opération , pas de la valeur de retour ni du nombre d'arguments. Une explication de la façon dont cela fonctionnait à partir de Userland sur x86 est ici
vonbrand