-
Notifications
You must be signed in to change notification settings - Fork 4
Appels système
#Syscall
##Définition
Les appels système sont le seul moyen de communication entre le kernel space et l'user space. Il s'agit de fonctions bien spécifiques que l'on peut appeler et qui accordent le temps de leur exécution l'accès aux privilèges kernel. Il faut noter tout de même que les mécanismes utilisés pour exécuter ces appels sont lent, et que leur utilisation est contrainte par le nombre limité de paramètres que l'on peut passer à ces fonction. Cela étant dit, cette contrainte est souvent contournée en passant en paramètre un pointeur vers une structure comprenant autant de paramètres que nécessaire.
##Les appels système dans TacOS
###Fonctionnement
C'est du coté du kernel que les appels systèmes sont gérés. Pour chaque fonction que l'on veut rendre disponible sous forme d'appel système, on associe un identifiant dans une table du kernel. En plus de cela, le kernel dispose d'une fonction syscall_entry
qui est en fait un handler d'interruption. Cette fonction a pour but de récupérer les valeurs de eax
, ebx
, ecx
et edx
, puis d'appeler la fonction d'appel système dont l'identifiant est eax
en lui passant ebx
, ecx
et edx
en paramètres.
Du coté utilisateur, il suffit donc de déclencher l'interruption logiciel correspondant à syscall_entry
en ayant préalablement mis les bonnes valeurs dans eax
, ebx
, ecx
et edx
.
###Utilisation d'un appel système
Comme on vient de le voir, pour exécuter un appel système il faut paramétrer les registres et déclencher une interruption logicielle. Cependant il est plus simple d'utiliser la fonction
void syscall(uint32_t func, uint32_t param1, uint32_t param2, uint32_t param3);
définie dans syscall.h
En outre, il est conseillé d'ajouter une couche d'abstraction au dessus de cet appel pour permettre par exemple une vérification des types des paramètres.
Par exemple, l'appel système sys_write est réalisé de la manière suivante:
ssize_t write(int fd, const void *buf, size_t count) {
syscall(SYS_WRITE, fd, (uint32_t) buf, (uint32_t)(&count));
return count;
}
###Création d'un appel système
La création d'un nouvel appel système implique deux étapes :
- Création de la fonction
Par convention, les fonctions correspondant à des appels système s'appellent sys_*. De plus, pour simplifier la définition de telles fonctions, il convient d'utiliser les macro suivantes :
SYSCALL_HANDLER0(name)
SYSCALL_HANDLER1(name, param)
SYSCALL_HANDLER2(name, param1, param2)
SYSCALL_HANDLER3(name, param1, param2, param3)
Par exemple, l'appel système sys_hlt est défini de la manière suivante:
SYSCALL_HANDLER0(sys_hlt)
{
asm("hlt");
}
- Enregistrement de l'appel auprès du noyau
Il faut à présent préciser au noyau que cette fonction est un handler d'appel système. Pour cela il suffit d'appeler la fonction suivante (depuis le kernel bien entendu, le mieux étant pour le moment dans le cmain de kernel.c):
int syscall_set_handler(uint32_t syscall_id, syscall_handler_t handler);
D'autre part, on trouve dans syscall.h la définition de tous les identifiants d'appel système déja créés. Par défaut, ce sont les identifiant définis dans ce fichier qui servent de référence, et il est donc conseillé de les utiliser partout plutôt que de mettre directement les valeurs.
###Améliorations possibles
Comme expliqué plus haut, le mécanisme d'interruption utilisé pour réaliser les appels système est relativement lent. Il existe une alternative sur architecture x86: SYSENTER/SYSEXIT. Ce sont deux instructions qui permettent de réaliser la même opération mais de manière plus efficace. Il pourrait être intéressant de l'implémenter, tout en gardant si possible le mécanisme d'interruption dans le cas où le processeur ne soit pas compatible.