Entrées-sorties

Introduction

Rappel sur les apports d’UNIX

  • Toute entrée / sortie se fait par le biais d’un « fichier » (du point de vue du programmeur).
  • Les ressources d’E/S sont en général matérialisées dans le système de fichiers (répertoire /dev pour les périphériques ou devices).

Homogénéité:

  • des concepts
  • des appels systèmes (read, write, open, close, lseek)
  • de la gestion des droits d’accès

Notion de fichier

Descripteur de fichier

Un descripteur de fichier est un entier qui identifie, au sein d’un processus, un fichier ouvert par ce processus.

/* open retourne un descripteur de fichier... */
int open (const char *path, int oflag, [int mode]);
/* ... utilisé ensuite par les fonctions d'E/S */
int read (int fildes, void *buf, int nbyte);
int write(int fildes, void *buf, int nbyte);
int lseek(int fildes, int offset, int whence);
int close(int fildes);

Informations liées à un DF

Le système fait correspondre à chaque DF une structure de donnée contenant les informations sur le fichier ouvert :

  • type de ressource d’E/S (stockage, périphérique…)
  • mode d’ouverture (lecture, ajout, écriture)
  • adresse et taille du tampon, le cas échéant
  • etc…

Illustration

_images/es_filedescriptors.png

Remarques

La structure de données contenant les informations sur le fichier ouvert

  • est créée uniquement pour la durée d’ouverture du fichier
    • les données persistantes sont stockées ailleurs (dépendant du type de ressource considéré)
  • est propre à chaque processus
    • un même fichier peut être ouvert en lecture par un processus, et en écriture par une autre

Remarque (suites)

La structure de données contenant les informations sur le fichier ouvert

  • n’est accessible qu’au système d’exploitation
    • consultées / utilisées par les processus par le biais d’appels systèmes

Types de ressources d’E/S

  • Fichiers « classiques »
  • Périphériques
    • Flux (character)
    • Adressables (block)
    • Spéciaux
  • Tubes
  • Sockets

Fichier « classique »

Gérés par le système de fichiers.

char buf[10];
int fd = open("fich1.dat", O_RDONLY);
read(fd, buf, 10);
close(fd);
fd = open("fich2.dat", O_WRONLY);
write(fd, buf, 10);
close(fd);

Périphériques Flux

  • Également appelés périphériques character
  • L’ordre de lecture / écriture est imposé (séquentiel)
int souris = open("/dev/input/mice", O_RDONLY);
int carte_son = open("/dev/dsp", O_WRONLY);

Périphériques Adressables

  • Également appelés périphériques block
  • On peut choisir l’ordre dans lequel on lit / écrit
  • Exemples : disque dur, clés USB
int dd = open("/dev/sda1", O_RDWR);
lseek(dd, 10, SEEK_SET);
read(dd, buf, 1);

NB : dans cet exemple, on accède directement au périphérique (sans passer par le système de fichiers)

Périphériques spéciaux

  • /dev/zero : périphérique de taille non bornée ne contenant que des zéros (“\x0”, pas le chiffre “0”)
  • /dev/random : périphérique de taille non bornée contenant des valeurs aléatoires (et non reproductibles)
  • /dev/null : périphérique dans lequel on peut toujours écrire (les données écrites sont perdues)
  • /dev/full : périphérique toujours plein (toute tentative d’écriture se solde par une erreur)

Tube – Pipe

ls -l | grep pchampin
  • Ressource virtuelle créée par le système, composée
    • d’un fichier ouvert en écriture
    • d’un fichier ouvert en lecture
  • Une mémoire tampon permet de limiter le risque de blocage des processus utilisant
    • NB: les échanges ont bien lieu via la mémoire, sans stockage → rapide

Illustration

_images/es_pipe.png

Remarques

Le tube peut être

  • soit matérialisé dans le système de fichier (mknod)

    NB: simplement pour le nommer, toujours pas de stockage

  • soit transmis directement par un parent à son/ses enfants

→ pour communiquer entre un père et ses enfants

→ pour communiquer entre enfants d’un même père

Communication père-fils

int p[2];
pipe(p);
int pid = fork();
if (pid > 0)       write(p[1], data, data_len);
else if (pid == 0) read(p[0], data, data_len);

Socket

  • Permet de communiquer à travers un réseau
  • Homogène à un fichier…
    • appels système read, write
  • … mais possédant aussi des appels spécifiques
    • recv, send, recvfrom, sendto
  • Cf. module de Programmation réseau (semestre 4)

Descripteurs de fichiers standards

Principe

  • Par convention, les trois premiers descripteurs de fichiers (0, 1 et 2) ont une sémantique particulière : entrée, sortie et erreur standard
  • Intéressant pour les programmes orientés « ligne de commande »
  • Permet de combiner différents programmes grâce aux redirections

Redirection des E/S standard

prog1 <fich1 >fich2

ou

prog1 | prog2 | prog3

Redirection par programme

Appel système dup2: duplique un descripteur de fichier

fd = obtenir_descripteur(); // open, pipe...
dup2(fd, 1);
close(fd);
execl("prog1", "prog1");

NB: on peut en principe rediriger n’importe quel descripteur de fichier, mais dans ce cas la réutilisabilité est limitée.

Alternatives et optimisation

Entrées / sorties asynchrones

  • Les appels systèmes read et write sont dits bloquants : le processus passe dans l’état bloqué jusqu’à l’aboutissement de l’opération.
  • Il existe une contrepartie non-bloquante ou asynchrone de ces appels systèmes : aio_read et aio_write.
    • Ces appels systèmes retournent immédiatement après leur appel, sans bloquer le processus appelant ;
    • ils offrent plusieurs méthodes pour « avertir » le processus que l’opération s’est terminée.

L’appel système mmap

  • Aligne une partie d’un fichier sur une zone mémoire,
    • et assure la synchronisation entre les deux grace au mécanisme de pagination.
  • Utilisable avec tout descripteur de fichier addressable :
    • fichier de stockage « classique »
    • périphérique addressables
    • d’autres (que nous verrons plus tard)