Gestion des Entrée/Sortie
dans un système d'exploitation
Un exemple : UNIX-GNU/LINUX
Structure d'un système d'exploitation
Matériel
Gestion des processus Gestion mémoire
Gestion des Entrée/Sortie Gestion de fichiers
Interface Utilisateur Applications
Pilotes des périphériques
E/S abstraites Disque Disquette
Imprimante Carte Graphique
USB
La gestion des E/S
Pilotes
● En général un pilote par type de périphérique
● Disque
● Imprimante
● Carte graphique
● Scanner
● USB : pilote générique
● Certains pilotes peuvent utiliser d'autres pilotes
● Disque
● Imprimante
E/S abstraites
● Les pilotes fournissent une interface d'accès uniforme à tous les périphériques qui sont vus comme un flot de caractères ou octets
● Chaque périphérique est adressé de manière logique par un nom abstrait :
● /dev/* dans UNIX
● C :, D :, LPT1 :, … dans Windows
● La couche E/S abstraite utilise cette interface et et ces noms pour fournir des fonctions de base d'E/S
Fonctions de l'interface
● 5 fonctions :
● ouvrir : fournit un descripteur utilisé dans la suite des opérations
● fermer : invalide le descripteur
● lire : transfère en mémoire une suite de caractères depuis le périphérique
● écrire : transfère depuis la mémoire une suite de caractères sur le périphérique
● se-déplacer : lorsque le périphérique le permet, se déplacer dans le flot
IMPORTANT
Les primitives lire et écrire manipulent un flot d'octets elles n'effectuent donc aucune transformation du format interne vers le format externe des données
Si le contenu du fichier lu est binaire le résultat sera également binaire
En écriture il en est de même l'écriture d'un nombre entier ou réel n'affichera pas la valeur du nombre sur la sortie standard par exemple
Ouverture
● Vérification des droits
● Mise en place des structures de données permettant de gérer les opérations futures : mise à jour des tables système, tampon d'E/S ...
● Les tampons d'E/S sont des zones de mémoire non accessibles au programmeur permettant de synchroniser les opérations logiques des opérations physiques
● Un ensemble de commandes est envoyé au périphérique, par le pilote pour le mettre dans
Ouverture
● Du point de vue programmation la fonction retourne au programme appelant
● un identifiant permettant d'adresser le périphérique : le descripteur
ou
● un code d'erreur
● Syntaxe :
descripteur ← ouvrir ( nom_logique, mode)
● mode : lecture, écriture, lecture/écriture
Programme
Système
descripteur ← ouvrir ( périphérique, mode) Table des descripteurs
Pilote
Lecture
● Si des tampons sont associés au périphérique et que ceux ci sont vides un ensemble de caractères est transféré depuis le périphérique dans un tampon
● Le nombre de caractères dépend des caractéristiques physiques du périphérique
● Transfert un ensemble de caractères vers une partie la mémoire à une adresse accessible au programme
● Syntaxe :
résultat ← lire ( descripteur, adresse, nombre)
● résultat : nombre de caractères lus ou un code d'erreur
Programme
Système résultat ← lire(descripteur, adresse, nombre)
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique
1 2
pointeur
Programme
Système résultat ← lire(descripteur, adresse, nombre)
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique 1
pointeur
Écriture
● Transfert un ensemble de caractères depuis une partie la mémoire à une adresse accessible au programme dans un tampon ou sur le périphérique
● Si des tampons sont associés au périphérique et que ceux ci sont pleins leurs contenus sont écrits sur le périphérique
● Syntaxe :
résultat ← écrire ( descripteur, adresse,nombre)
● résultat : nombre de caractères écrits ou un code d'erreur
Programme
Système résultat ← écrire(descripteur, adresse, nombre)
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique 1
2 pointeur
Programme
Système résultat ← écrire(descripteur, adresse, nombre)
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique pointeur
Fermeture
● Vidage éventuels des tampons associés au périphérique : écriture physique, sur le périphérique de leurs contenus
● Suppression des structures associées au périphérique
● Invalidation du descripteur : les opérations ultérieures seront rejetées
● Résultat : un éventuel code d'erreur pour un descripteur invalide
● Syntaxe :
code_retour ← fermer ( descripteur, mode)
● Certains périphériques duplex peuvent être fermés dans un seul sens (socket réseau )
Programme
Système résultat ← fermer(descripteur, mode)
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique
se-déplacer
● Lorsque le périphérique le périphérique ou le pilote le permet cette fonction déplace le pointeur dans le flot associé au descripteur
● Cette fonction déplace le pointeur sur la position actuelle dans le flot à une position déterminée passée en paramètre
● Syntaxe :
se-deplacer ( descripteur, déplacement, origine)
● Déplacement : nombre d'octets
● Origine : position de départ
● actuelle, début fichier, absolue
Programme
Système
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique
pointeur Caractères lus
AVANT
Programme
Système résultat ← se-déplacer(descripteur, actuelle, 100)
Table des descripteurs
Tampon 1 Tampon 2 Tampon 3
Pilote Tampons associés
Aux périphérique
pointeur Caractères lus
APRÈS
Exemple UNIX/LINUX
Caractéristiques
● Le système d'E/S est appelé E/S de bas niveau
● Utilisable pour lire :
● les périphériques,
● les fichiers,
● les fichiers répertoires,
● Les tubes, nommés ou non,
● Les sockets réseau
● Tous les périphériques sont accessibles au travers d'un pseudo système de fichiers se trouvant dans le répertoire /dev
Le pseudo système de fichier /dev
● Suivant les versions il est statique ou dyna- mique
● Statique :
● L'arborescence du système de fichier contient tous les périphériques accessibles par le système
● Une commande MAKEDEV majeur mineur permet de créer une nouvelle entrée correspondant à l'ajout d'un nouveau pilote au système
● Dynamique :
Nommage des périphériques
● Disques :
● un nom pour le disque en totalité
● un nom pour chaque partition du disque
● /dev/sda : le premier disque
● /dev/sda1, /dev/sda2, /dev/sda3 pour les trois partitions de ce disque
● Terminaux : /dev/tty*
● En lecture le clavier, en écriture l'écran
● Lecteur de CD/DVD : /dev/cdrom
● Graveur de CD/DVD : /dev/sr0
Les primitives d'E/S
● Elles sont au nombre de 5 :
● open : ouvrir un fichier ou un périphérique
● close : fermer un fichier
● read : lire un fichier
● write : écrire un fichier
● lseek : déplacer le pointeur de fichier
● Les fichiers sont accessibles au travers d'un descripteur, un entier, retourné par la primitive open
● L'invalidation d'un descripteur par close interdit
open
● Syntaxe :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags) int open(const char *pathname, int flags, mode_t mode)
int creat(const char *pathname, mode_t mode)
open
● La première forme permet d'ouvrir un fichier existant
● La seconde de créer et ouvrir un fichier
● La troisième de créer un fichier
● La page de manuel obtenue par : man 2 open
documente les différentes options
● Les tubes anonymes (pipe) ne peuvent être ouverts, ils sont ouverts lors de leur création
open
● pathname : nom du fichier à ouvrir
● flags indique le type d'opérations ultérieures sur le fichier
● O_RDONLY : lecture
● O_WRONLY : écriture
● O_RDWR : lecture/écriture
● O_CREAT : création du fichier
● O_TRUNC : le contenu d'un fichier existant est détruit ( lecture ou lecture/écriture )
● O_APPEND : mode ajout
● Plusieurs flags peuvent être combinés par un ou logique ( voir exemple )
open
● Mode, obligatoire, si O_CREAT ou creat, donne les droits du fichier lors d'une création
● Valeur de mode :
● S_IRWXU 0700, S_IRUSR 0400, S_IWUSR 0200
● S_IXUSR 0100, S_IRWXG 0070, S_IRGRP 0040
● S_IWGRP 0020, S_IXGRP 0010, S_IRWXO 0007
● S_IROTH 0004, S_IWOTH 0002, S_IXOTH 0001
● creat permet de créer un fichier
Descripteur hérités du shell
● Le shell ouvre le terminal 3 fois pour créer les fichiers de communication processus ↔ utilisateur
● une en lecture : descripteur 0, l'entrée standard
● une en écriture tamponné sur une ligne (\
n) la sortie standard, descripteur 1
● une en écriture non tamponné la sortie d'erreur standard, descripteur 2
Exemples
● Ouvrir le premier disque en lecture :
int diska ;
diska = open ( ʺ/dev/sda , O_RDONLY) ;ʺ if ( diska == -1 ) {
fprintf(stderr, ....
return -1 }
/* Le disque est accessible en utilisant le descripteur diska */
● Ouverture du fichier /tmp/fich.txt existant en lecture/écriture
int f ;
f = open( /tmp/fich.txt , O_RDWR) ;ʺ ʺ if ( f == -1 ) {
fprintf(stderr, ….
return -1 : }
● Ouverture du fichier fich.txt du répertoire courant en lecture/écriture, créé avec les droits rwx r-- ---
int f ;
f = open( fich.txt ,ʺ ʺ
O_RDWR|O_CREAT, S_IRWXU|S_IRGRP) ; if ( f == -1 ) {
fprintf(stderr, ….
return -1 : }
Lecture : read
● Syntaxe :
#include <unistd.h>
size_t read(int fd, void *buf, size_t count)
fd : descripteur
buf : est un pointeur sur une zone de count octets au moins recevant le résultat de la lecture
count est le nombre maximum de caractères ou octets à lire
● Résultat retourné : le nombre d'octets lus
read
● Cette primitive lit au maximum count octets si c'est possible
● Le résultat retourné est <= count
● -1 indique une erreur
● 0 fin de fichier
● >0 et <= count des caractères ont été effectivement lus
● Il est possible que l'on ne puisse lire la totalité des caractères nécessaires en une seule fois.
Dans ce cas l'opération doit être recommencée
Exemple : lecture de 64 octets
char tampon[128] ; int fich , n;
n = read (fich , (void *) tampon, 64 ) ; if ( n > 0 && n < 64 ) {
/* Les 64 caractères n'ont pas été lus
Il faut essayer de relire les caractères manquants
*/
Exemple : lecture de 64 entiers
int t[128] ; int fich , n;
n = read (fich , (void *) t, 64 * sizeof (int)) ;
if ( n > 0 && n < 64 * sizeof (int) ) {
/* Les 64 entiers n'ont pas été lus
Il faut essayer de relire les entiers manquants
Ecriture write
● Syntaxe :
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count)
fd : descripteur
buf : est un pointeur sur une zone de count octets contenant les caractères à écrire
count est le nombre maximum de caractères à écrire
● Résultat retourné : le nombre d'octets écrits ou -1 en cas d'erreur
Déplacement du pointeur sur le caractère courant
● Syntaxe :
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence)
● offset : déplacement
● whence : base du déplacement SEEK_SET : début du fichier
Une exemple complet : ouverture d'un fichier et écriture de son contenu sur la
sortie standard
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#define NB 512 /* taille d'un secteur de disque */
int main () {
char *erreur_ouverture = "Erreur d'ouverture du fichier fichier.txt\n";
char tampon[NB];
int fich, nlus, necrit;
fich = open("/tmp/fichier.txt",O_RDONLY);
if (fich == -1) {
write(2, erreur_ouverture, strlen(erreur_ouverture));
return -1;
}
/* Lecture nb premiers caractères du fichier dans tampon */
nlus = read (fich , tampon, NB );
while ( nlus != 0 ) { /* tant que non fin de fichier */
if ( nlus == -1 ) {
/* Une erreur s'est produite durant la lecture */
/* écriture du message système correspondant à l'erreur */
perror("Erreur de lecture de fichier.txt");
close (fich);
return -1;
} /* nlus == -1 */
/*
Il n'y a pas eu d'erreur de lecture
Le nombre de caractères lus est > 0 et <= NB
Écriture des caractères lus sur la sortie standard */
necrit = write ( 1, tampon, nlus);
if ( necrit != nlus ) { fprintf(stderr,
"Le nombre de caractères écrits : %d est différent du nombre de caractères lus %d\n", necrit, nlus);
/*
Ce n'est pas une erreur
il faudrait écrire les caractères restants */
close (fich);
return 0;
}
nlus = read (fich , tampon, NB );
}
/* Fermeture du fichier */
close (fich);
return 0;
}
Ecriture d'une fonction de lecture d'un
descripteur jusqu'à obtention du nombre de
caractères nécessaires ou fin de fichier
/*
int readn ( int fd ,char *ptr, int a_lire )
Lit le descripteur fd jusqu'à avoir lu a_lire caractères S'arrête en cas d'erreur ou fin de fichier
register int fd ; descripteur à lire
register char * ptr; ou ranger les caractères lus register int a_lire; nbre de caractères à lire/
Retourne : le nombre de caracteres lus -1 en cas d'erreur
0 = fin de fichier
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int readn ( int fd ,char *ptr , int a_lire ){
int restant ; int nlus;
restant = a_lire;
while ( restant > 0 ) {
nlus = read ( fd , ptr , restant );
if ( nlus < 0 ) {
perror("readn erreur de lecture");
return nlus;
}
if ( nlus == 0 )
break; /* EOF */
/* Lecture des caractères restants */
restant -= nlus;
ptr += nlus;
}
/* On retourne le nombre de caractères lus */
return ( a_lire - restant );
}