Guillaume Chanel
Remerciements à Jean-Luc Falcone
Cours système d'exploitation by Guillaume Chanel, Jean-Luc Falcone and University of Geneva is licensed under CC BY-NC-SA 4.0
Les appels systèmes sont des fonctions permettant aux programmes d'utiliser les fonctionnalités/services du noyau.
syscall
La fonction syscall
déclenche une interruption qui change le mode du CPU (privilèges plus élevés) et passe la main au noyau pour effectuer une opération spécifique:
long syscall(long number, ...)
La fonction prend en paramètre un nombre spécifiant l'appel système, et une liste variable d'arguments dépendant de l'appel système choisi.
syscall
: exemple
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>
int main() {
pid_t pid;
pid = syscall(SYS_getpid);
syscall(SYS_kill, pid, SIGHUP);
}
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char *argv[]) {
pid_t pid;
pid = getpid();
kill( pid, SIGHUP);
}
Les appels système sont souvent utilisés au travers de librairies de plus haut niveau
// Appel systeme Posix
int open(const char *pathname, int flags, mode_t mode);
// Fonction ANSI-C
FILE *fopen(const char *path, const char *mode);
Les types utilisés peuvent être opaques
Les types opaques sont des structures de données (i.e. typedef) qui ne sont pas définies dans l’interface (i.e. fichier header). Cela permet:
On aimerait représenter des personnes par la structure suivante:
struct person {
char *name;
int age;
int isMarried;
int hasChildren;
int canDrive;
int speaksEnglish;
};
typedef struct person person_t;
int match( const person_t *p, int isMarried, int hasChildren, int canDrive,
int speaksEnglish ) {
if( isMarried && ! p->isMarried ) {
return 0;
}
if( hasChildren && ! p->hasChildren ) {
return 0;
}
//...
if( speaksEnglish && ! p->speaksEnglish ) {
return 0;
}
return 1;
}
person_t alice = { "Alice", 24, 0, 1, 0, 1 };
person_t bob = { "Bob", 37, 1, 0, 1, 1, };
person_t dudes[] = {alice,bob};
int i;
for( i=0; i < 2; i++ ) {
person_t p = dudes[i];
if( match( &p, 0, 1, 0, 1 ) ) {
printf( "%s is selected \n", p.name );
}
}
Cette solution présente de nombreux inconvénients:
On pourrait utiliser les bits d'un entier pour contenir la même information:
0001 | Est marrié |
0010 | A des enfants |
0100 | Permis de conduire |
1000 | Parle anglais |
On peut les combiner (OR - symbole "|") pour gérer tous les cas possibles:
0101 | Est marrié et peut conduire |
0011 | Marrié avec des enfants |
… | … |
struct person {
char *name;
int age;
int properties;
};
typedef struct person person_t;
int match( const person_t *p, int properties ) {
return ( properties & (p->properties) ) == properties;
}
person_t alice = { "Alice", 24, HAS_CHILDREN | SPEAKS_ENGLISH };
person_t bob = { "Bob", 37, IS_MARRIED | CAN_DRIVE | SPEAKS_ENGLISH };
person_t dudes[] = {alice,bob};
int i;
for( i=0; i < 2; i++ ) {
person_t p = dudes[i];
if( match( &p, CAN_DRIVE | SPEAKS_ENGLISH ) ) {
printf( "%s is suitable \n", p.name );
}
}
Problème: La plupart des langages de programmation n'autorisent qu'une valeur de retour par fonction. Or on veut souvent retourner:
errno.h
)errno
pour passer un code d'erreur.errno.h
.Exemples de codes standard POSIX (man errno
)
E2BIG | Argument list too long |
---|---|
EACCES | Permission denied |
EADDRINUSE | Address already in use |
ENOSPC | No space left on device |
ENOENT | No such file or directory |
ETIMEDOUT | Connection timed out |
EBUSY | Device or resource busy |
… | … |
short
, int
, long
)NULL
/* Cherche des entrees dans une base de donnees
Retourne:
- soit le nombre de resultats trouves
- soit -1 en cas d'erreurs
Garni le tableau results avec les resultats.
*/
int lookup( DataBase *db, query_t query, int *results );
int results[MAX_RESULTS];
int num = lookup( myDB, q, results );
if( num == -1 ) {
// Gerer l'erreur
} else {
int i;
for( i = 0; i < num; i++ ) {
// Gerer result[i]
}
}
errno
indique le type d'erreur (#define
associés)
if( num < 0 ) {
switch(errno) {
case ECONNREFUSED:
//Gerer un refus de connection;
break;
case EPERM:
//Gerer une operation non autorisée;
break;
case ...
}
}
strerror
)La fonction strerror
retourne un message d'erreur (chaîne de caractère)
if( num < 0 ) {
printf(stderr, "An error has occured: %s\n", strerror(errno));
}
perror
)La fonction perror
permet d'afficher un message d'erreur automatiquement lié a errno sur la sortie d'erreur standard:
//On suppose que le fichier passé en premier paramètre du program est inexistant
int main(int argc, char* argv[]) {
char* unFichierInexistant = argv[1];
if (open(unFichierInexistant, O_RDONLY) < 0) {
perror(unFichierInexistant);
return -1;
}
}
Le résultat du programme pourrait donc être:
$ ./mon-programme /un/fichier/inexistant
/un/fichier/inexistant: No such file or directory
errno
est une variable globale