Introduction aux appels système

18 Jul 2022 18 Jul 2022 2914 vues ESSADDOUKI Mostafa 11 min de lecture

Introduction aux appels système

Définition : Appel système

Les appels système fournissent une interface aux services mis à disposition par un système d'exploitation. Un appel système est une méthode permettant à un programme informatique de demander un service au noyau du système d'exploitation sur lequel il s'exécute.

Ces appels sont généralement disponibles sous forme de fonctions écrites en C et C++, bien que certaines tâches de bas niveau (par exemple, les tâches où le matériel doit être accessible directement) puissent être écrites à l'aide d'instructions en langage assembleur.

Fréquence d'utilisation

Les systèmes exécutent des milliers d'appels système par seconde. Cependant, la plupart des programmeurs ne voient jamais ce niveau de détail.

API et appels système

En règle générale, les développeurs d'applications conçoivent des programmes selon une interface de programmation d'application (API). L'API spécifie un ensemble de fonctions disponibles pour un programmeur d'application, y compris les paramètres transmis à chaque fonction et les valeurs de retour auxquelles le programmeur peut s'attendre.

Les API les plus courantes
APISystèmesBibliothèque
API WindowsSystèmes WindowsWindows API
API POSIXUNIX, Linux, macOSlibc (pour C)
API JavaMachine virtuelle Java (JVM)Bibliothèques Java

Un programmeur accède à une API via une bibliothèque de code fournie par le système d'exploitation. Dans le cas d'UNIX et de Linux pour les programmes écrits en langage C, la bibliothèque s'appelle libc.

Relation entre API et appels système

Dans les coulisses, les fonctions qui composent une API invoquent généralement les appels système réels au nom du programmeur d'application. Par exemple :

  • La fonction Windows CreateProcess() (utilisée pour créer un nouveau processus) invoque en fait l'appel système NTCreateProcess() dans le noyau Windows.
  • La fonction POSIX fork() invoque l'appel système correspondant dans le noyau Linux/UNIX.
Programme applicatif
       │
       ▼
    [ API ]  ← Interface de programmation (ex: CreateProcess, fork)
       │
       ▼
    [ Bibliothèque ]  ← Libc, bibliothèques systèmes
       │
       ▼
    [ Interface d'appel système ]  ← Gère les numéros d'appels
       │
       ▼
    [ Noyau ]  ← Exécute l'appel système réel
        

Figure : Hiérarchie entre application, API et noyau

L'environnement d'exécution (RTE)

Définition : RTE (Run-Time Environment)

L'environnement d'exécution (RTE) est la suite complète de logiciels nécessaire pour exécuter des applications écrites dans un langage de programmation donné, y compris ses compilateurs ou interpréteurs ainsi que d'autres logiciels, tels que des bibliothèques et des chargeurs (loaders).

Le RTE fournit une interface d'appel système qui sert de lien vers les appels système mis à disposition par le système d'exploitation. L'interface d'appel système intercepte les appels de fonction dans l'API et invoque les appels système nécessaires dans le système d'exploitation.

Numéros d'appels système

Typiquement, un numéro est associé à chaque appel système, et l'interface d'appel système maintient une table indexée selon ces numéros. L'interface d'appel système invoque ensuite l'appel système prévu dans le noyau du système d'exploitation et renvoie l'état de l'appel système.

   
Exemple de numéros d'appels système (Linux x86-64) Assembleur
; Les appels système sont identifiés par des numéros
; /usr/include/asm/unistd_64.h

#define __NR_read 0      ; Numéro pour read
#define __NR_write 1     ; Numéro pour write
#define __NR_open 2      ; Numéro pour open
#define __NR_close 3     ; Numéro pour close
#define __NR_fork 57     ; Numéro pour fork
#define __NR_execve 59   ; Numéro pour execve
#define __NR_exit 60     ; Numéro pour exit
#define __NR_getpid 39   ; Numéro pour getpid
Abstraction pour le programmeur

L'appelant n'a besoin de rien savoir de la façon dont l'appel système est implémenté ou de ce qu'il fait pendant l'exécution. Au lieu de cela, l'appelant n'a qu'à obéir à l'API et à comprendre ce que le système d'exploitation fera à la suite de l'exécution de cet appel système.

Passage des paramètres aux appels système

Les appels système se produisent de différentes manières, selon l'ordinateur utilisé. Souvent, plus d'informations sont nécessaires que simplement l'identité de l'appel système souhaité.

Méthodes de passage de paramètres

Figure : Trois méthodes générales pour passer des paramètres au système d'exploitation

Les trois méthodes principales

Méthode 1

Passage par registres

L'approche la plus simple consiste à passer les paramètres dans des registres du processeur.

Avantage : Rapide et simple.

Inconvénient : Limité par le nombre de registres disponibles.

Méthode 2

Passage par bloc mémoire

Les paramètres sont stockés dans un bloc ou une table en mémoire, et l'adresse du bloc est passée dans un registre.

Avantage : Pas de limitation sur le nombre de paramètres.

Inconvénient : Nécessite un accès mémoire supplémentaire.

Méthode 3

Passage par pile

Les paramètres sont placés ou poussés sur une pile par le programme et retirés de la pile par le système d'exploitation.

Avantage : Flexible et sans limitation de taille.

Inconvénient : Gestion de pile plus complexe.

Cas particulier de Linux

Approche de Linux

Linux utilise une combinaison de ces approches :

  • S'il y a cinq paramètres ou moins, des registres sont utilisés.
  • S'il y a plus de cinq paramètres, la méthode des blocs est utilisée.

Certains systèmes d'exploitation préfèrent la méthode du bloc ou de la pile car ces approches ne limitent pas le nombre ou la longueur des paramètres transmis.

Exemple concret sous Linux x86-64

   
Passage de paramètres pour write (3 paramètres) C/Assembleur
// En C, l'appel système write
ssize_t write(int fd, const void *buf, size_t count);
; En assembleur x86-64, les paramètres sont passés dans des registres
; rax = numéro de l'appel système (1 pour write)
; rdi = premier paramètre (fd - descripteur de fichier)
; rsi = deuxième paramètre (buf - pointeur vers le tampon)
; rdx = troisième paramètre (count - nombre d'octets)

mov rax, 1      ; numéro de l'appel système write
mov rdi, 1      ; fd = 1 (stdout)
mov rsi, msg    ; buf = adresse du message
mov rdx, 14     ; count = 14 octets
syscall         ; déclenche l'appel système

Processus complet d'un appel système

  1. Programme applicatif appelle une fonction de l'API (ex: read()).
  2. La bibliothèque (libc) prépare les paramètres et place le numéro de l'appel système.
  3. L'interface d'appel système exécute une instruction spéciale (syscall ou int 0x80).
  4. Le processeur passe en mode noyau.
  5. Le gestionnaire d'appels système dans le noyau examine le numéro et aiguille vers la fonction correspondante.
  6. La fonction noyau exécute le service demandé.
  7. Le résultat est retourné au programme utilisateur via les registres.
  8. Le processeur repasse en mode utilisateur.
  9. L'application reçoit la valeur de retour de l'appel.

Les six catégories d'appels système

Les appels système peuvent être regroupés en six catégories principales :

CatégorieDescriptionExemples
Contrôle de processusGestion des processus (création, terminaison, attente)fork(), exit(), wait(), exec()
Gestion de fichiersManipulation de fichiers et répertoiresopen(), read(), write(), close()
Gestion de périphériquesContrôle des périphériques matérielsioctl(), read(), write() sur /dev/*
Maintenance des informationsObtention d'informations systèmetime(), getpid(), uname()
CommunicationsCommunication entre processuspipe(), shmget(), socket()
ProtectionGestion des permissions et sécuritéchmod(), chown(), umask()
 Exercice pratique

Traçage d'appels système

 Niveau : Intermédiaire

Sous Linux, on peut utiliser la commande strace pour tracer les appels système effectués par un programme.

Considérons le programme C suivant :

#include <stdio.h>
#include <unistd.h>

int main() {
    printf("Bonjour, monde!\n");
    
    int pid = getpid();
    printf("Mon PID est %d\n", pid);
    
    FILE* f = fopen("test.txt", "w");
    if (f) {
        fprintf(f, "Test d'écriture\n");
        fclose(f);
    }
    
    return 0;
}

Questions :

  1. Quels appels système seront probablement invoqués par ce programme ?
  2. Pour chaque appel identifié, à quelle catégorie appartient-il ?
  3. Comment les paramètres sont-ils transmis pour l'appel write() ?
  4. Quel est le rôle de la bibliothèque libc dans ce programme ?
Points clés à retenir
  • Les appels système sont l'interface entre les programmes utilisateur et le noyau.
  • Les programmeurs utilisent généralement des API (Windows, POSIX, Java) plutôt que des appels système directs.
  • La bibliothèque libc fait le lien entre les fonctions API et les appels système sous Linux/Unix.
  • Chaque appel système possède un numéro unique utilisé par le noyau pour l'identifier.
  • Les paramètres peuvent être passés par registres, bloc mémoire ou pile.
  • Linux utilise les registres pour ≤ 5 paramètres, la méthode des blocs pour plus de paramètres.
  • Les appels système se divisent en six catégories : contrôle de processus, gestion de fichiers, gestion de périphériques, maintenance des informations, communications et protection.
  • L'environnement d'exécution (RTE) fournit l'infrastructure nécessaire pour exécuter les programmes.
  • Le programmeur n'a pas besoin de connaître l'implémentation des appels système, seulement l'API.

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.