Adloun

Mémoire, fichiers et entrées-sorties

Cours complet · informatique (MP2I/MPI), chapitre 8 · MP2I et MPI

Travailler ce chapitre sur Adloun Exercices corrigés de ce chapitre

8.1 Ce que le système fait pour vous

Le programme donne à cette partie un objectif mesuré : « donner un premier aperçu des liens qu'assurent les systèmes d'exploitation … entre les programmes et les ressources offertes par les machines qui les exécutent ». Et il écarte aussitôt l'essentiel de ce qu'un cours de système contiendrait : « le fonctionnement du matériel, l'architecture des ordinateurs, la conception des systèmes, la gestion des interfaces, les protocoles de communication, la virtualisation … sont hors programme ».

Restent trois choses, et elles suffisent à comprendre pourquoi un programme plante : comment il occupe la mémoire, comment il lit et écrit des fichiers, et — au chapitre chap:concurrence — comment plusieurs fils s'y bousculent. Le système Linux sert de support, « le plus propice pour introduire les éléments de ce programme ».

8.2 La mémoire d'un processus

8.2.1 Pile et tas

Définition 8.1Deux zones, deux régimes

Un programme en cours d'exécution dispose de plusieurs zones de mémoire. Deux nous intéressent :

  • la pile (stack) : les blocs d'activation des appels de fonction, empilés et dépilés automatiquement. Rapide, de taille bornée, gérée sans que vous n'y pensiez.
  • le tas (heap) : la mémoire demandée explicitement par malloc, rendue par free. Vaste, à durée de vie choisie, entièrement à votre charge en C.
Définition 8.2Portée syntaxique et durée de vie

Le programme demande de distinguer ces deux notions, qu'on confond volontiers.

  • La portée est syntaxique : c'est la région du texte du programme où un nom désigne cette variable. En C, les accolades la délimitent.
  • La durée de vie est temporelle : c'est l'intervalle pendant lequel la case mémoire existe.

Elles ne coïncident pas. Un objet du tas survit à la fonction qui l'a créé — c'est tout son intérêt.

Nature de la variableOù elle vitDurée de vie
globalezone des globalestout le programme
locale, paramètrepilel'appel de la fonction
allouée par `malloc`tasjusqu'au `free`
AttentionRendre l'adresse d'une variable locale

int* faute(void) {
    int x = 42;
    return &x;        /* x MEURT ici : l'adresse rendue ne désigne plus rien */
}

Le bloc d'activation est dépilé au retour ; la case existe encore physiquement, et un prochain appel la réutilisera. Le programme lira donc une valeur qui change toute seule. gcc -Wall avertit : function returns address of local variable. C'est un avertissement qu'on ne discute pas.

8.2.2 La protection mémoire

Définition 8.3Violation de segment

Le système attribue à chaque processus des plages d'adresses. Toute lecture ou écriture hors de ces plages est interceptée par le matériel, et le processus est tué : c'est la segmentation violation, que le programme cite explicitement.


int* p = NULL;
*p = 1;              /* Segmentation fault */
ImportantLe plantage est le bon cas, pas le mauvais
Accès fautifDétecté ?Conséquence
Hors du processusoui, par le matérielmort immédiate, message clair
Hors tableau, dans le processusnoncorruption silencieuse

Le second cas est infiniment plus dangereux : t[7] sur un tableau de cases tombe presque toujours dans la mémoire du processus, et écrase une autre variable. La violation de segment, elle, vous arrête net et vous dit où.

Bonne pratique (Les outils qui voient ce que le compilateur ne voit pas)


gcc -fsanitize=address -g prog.c -o prog     # instrumente les accès mémoire
valgrind ./prog                              # détecte fuites et lectures fautives

Ces outils ne sont pas exigibles, mais ils transforment un « ça plante parfois » en un message nommant le fichier et la ligne. Le programme demande d'ailleurs une « expérience » pratique du système, pas seulement une connaissance théorique.

8.3 Les fichiers

8.3.1 L'interface : un flux d'octets

Définition 8.4Ce qu'un fichier offre au programme

Un fichier se présente au programme comme une suite d'octets munie d'une taille et d'un accès séquentiel : on l'ouvre, on lit ou on écrit à la position courante — qui avance — et on le ferme.


FILE* f = fopen("donnees.txt", "r");     /* "r" lecture, "w" écriture */
if (f == NULL) { /* le fichier n'existe pas, ou les droits manquent */ }
int x;
fscanf(f, "%d", &x);
fclose(f);

let f = open_in "donnees.txt" in
let ligne = input_line f in      (* lève End_of_file en fin de fichier *)
close_in f

Le programme borne l'exigence : « les seules notions exigibles sont celles permettant à un programme de gérer l'ouverture, la fermeture et l'accès à un ou plusieurs fichiers ».

AttentionToujours vérifier l'ouverture, toujours fermer

fopen rend NULL quand il échoue, et rien ne vous oblige à le regarder : le fscanf suivant déréférencera NULL. Et un fichier non fermé garde ses écritures en tampon : le programme se termine, le fichier est incomplet. Ces deux fautes n'apparaissent qu'à l'exécution, souvent seulement quand le disque est plein ou le fichier absent.

8.3.2 L'implémentation : blocs et nœuds d'index

Définition 8.5Le nœud d'index

Sur le disque, un fichier est un ensemble de blocs de taille fixe, pas nécessairement contigus. Un nœud d'index — l'inode — porte les métadonnées (taille, droits, dates) et la liste des blocs. Point capital : l'inode ne contient pas le nom du fichier.

Définition 8.6Liens physiques et liens symboliques

Le programme demande de présenter « le partage de blocs (avec liens physiques ou symboliques) et l'organisation hiérarchique de l'espace de nommage ».

Lien physiqueLien symbolique
Ce qu'il estun second nom du même inodeun fichier contenant un chemin
Si la cible est renomméerien ne changele lien pointe dans le vide
Peut viser un répertoirenonoui
Peut franchir un disquenonoui
Commande`ln cible nom``ln -s cible nom`

Un fichier n'est effacé que lorsque son dernier nom disparaît : rm retire un nom, pas un contenu.

Définition 8.7Droits et attributs

Chaque inode porte des droits pour trois catégories — propriétaire, groupe, autres — et trois opérations : lecture , écriture , exécution .


-rw-r--r--   rapport.txt     le propriétaire lit et écrit, les autres lisent
drwxr-xr-x   travaux/        un répertoire ; x signifie « on peut le traverser »

Sur un répertoire, ne veut pas dire « exécutable » mais « traversable » : sans lui, on ne peut pas accéder à ce qu'il contient, même en connaissant les noms.

8.3.3 L'espace de nommage

Définition 8.8Une seule arborescence

Contrairement aux lettres de lecteur, un système de type Unix n'a qu'un arbre, de racine /. Un support amovible y est monté en un point : son contenu apparaît alors sous ce répertoire. Le programme attend « une expérience du montage d'un support de fichiers amovible, de la gestion des droits d'accès … de la création et du déplacement des parties de l'arborescence et de la gestion des liens ».

8.4 Flux standard, redirections, tubes

Définition 8.9Les trois flux

Tout processus démarre avec trois flux ouverts :

FluxCOCamlRôle
entrée standard`stdin``stdin`ce que le programme lit
sortie standard`stdout``stdout`les résultats
sortie d'erreur`stderr``stderr`les messages d'erreur
ImportantPourquoi deux sorties, et non une

Parce qu'elles sont redirigées séparément. Un programme dont les résultats vont dans un fichier doit pouvoir encore vous parler à l'écran :


./analyse < donnees.txt > resultats.txt      # les erreurs restent à l'écran
./analyse < donnees.txt > resultats.txt 2> journal.txt   # séparées

Écrire ses messages d'erreur sur la sortie standard les mélange aux résultats et rend le fichier inexploitable. C'est une faute de conception fréquente, et elle ne se voit qu'au moment de la redirection.

Définition 8.10Le tube

Un tube (pipe) branche la sortie standard d'un programme sur l'entrée standard d'un autre : les deux s'exécutent en même temps, les données circulant au fil de l'eau.


./generer | ./filtrer | ./compter

Le programme demande explicitement que « le professeur expose également ses étudiants à la réalisation d'enchaînements de programmes via des tubes ».

Bonne pratique (Écrire des programmes qui se chaînent)

Un programme qui lit stdin et écrit stdout se combine avec tous les autres ; un programme qui ouvre lui-même &quot;entree.txt&quot; ne se combine avec rien. C'est le même principe que l'abstraction du chapitre chap:abstraction : dépendre d'un contrat — ici, un flux — et non d'une réalisation particulière.

8.5 Ce qu'il faut retenir

ImportantMémoire et fichiers, en cinq points
  • Pile : automatique, bornée, durée de vie = l'appel. Tas : explicite, vaste, durée de vie choisie.
  • Portée est syntaxique, durée de vie est temporelle : ne jamais rendre l'adresse d'une locale.
  • La violation de segment protège ; l'accès hors tableau resté dans le processus, lui, corrompt en silence.
  • Un fichier est un inode — droits, taille, blocs — et des noms qui le désignent. rm retire un nom.
  • Trois flux, redirigeables séparément : les erreurs sur stderr, toujours. Un tube fait circuler les données entre deux programmes qui tournent ensemble.

Continuer sur Adloun : animation, QCM, fiches, exercices