Couche de gestion de fichiers
Architecture 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
Fonctionnalités de la couche de gestion de fichiers
● Structure :
● Linéaire
● Arborescente
● Nom des fichiers :
● Longueur
● Alphabet utilisable
● Méthode d'accès :
● Séquentiel
● Séquentiel indexé
● Direct
Fonctionnalités
● Allocation de l'espace :
● Statique
● Dynamique
● Contenu : binaire, caractères (ASCII, ISO-8859- 1, UTF8)
● Opérations :
● Création : allouer de l'espace
● Détruire : libérer l'espace, les données ne sont généralement pas détruites elles deviennent simplement inaccessibles
Fonctionnalités du SGF
● Propriétaire
● Droits simples
● Lire
● Écrire
● Détruire
● Liste d'accès et de contrôle (LAC/ACL) : spécifie quelles opérations et qui peut les réaliser
● Conversion entre format de représentation des données interne (binaire) ↔ externe (Code)
Format interne ↔ Format externe
● Pas de différence pour les données caractères
● Différents pour les entiers, les réels, les données structurées
● Exemples :
● Entiers : 12
● ASCII : 2 octets 0X31 0X32
● Binaire sur 4 octets 0X0000000C
● Réels : +0.15
● ASCII 5 octets 0X2B 0X30 0X2E 0X31 0X35
Interface d'accès
Système de gestion fichier
Structure dans un système « simple »
● Avantages :
● Interface d'accès simple à réaliser
● Efficace si bien réalisé
● Inconvénients :
● Un seul système de fichiers utilisable
● Difficile d'échanger des données avec un autre système
Interface d'accès
Système de fichiers virtuel (SFV ou VFS)
SGF 1
SGF
2 SGF
3
SGF
4 SGF
5
SGF 6
Structure moderne ou ouverte
● Interface d'accès uniforme quel que soit le
système de fichiers utilisé : système de fichiers virtuel
● Le système de fichiers virtuel sert d'interface avec les différents types de SGF utilisables
● SGFn : système de fichiers utilisables
● ext2, ext3, ext4
● JFS
● FAT, VFAT, NTFS ...
Système de fichier virtuel
● Présenter un modèle de fichier commun capa- ble de représenter tous les systèmes de fichiers utilisables
● SGF générique dont les opérations sont tra- duites en opérations réalisées par le système de fichiers sous-jacent
● Problème important : comment réaliser une fonctionnalité du SFV alors que le SGF sous
jacent ne l'implémente pas (droits,propriétaires, ...)
Vue utilisateur
● Fichier
● Nom
● Taille souvent non connue à la création
● Taille doit pouvoir diminuer ou augmenter
● « déseffacer » un fichier
● Structure arborescente
Vue système
● Établir la correspondance entre le nom et l'emplacement des données sur le support
● Création :
● Trouver de l'espace disponible sur le support
● Quelle quantité de place allouer ?
● Lecture/Écriture : comment réaliser les opéra- tions efficacement ?
● Modification de la taille : éviter de morceler l'espace libre ou le fichier
● Effacement : effacement des données pour des questions de sécurité
Création d'un SGF
● Disque découpé en partitions
● Création du système de fichier dans une partition à l'aide d'un utilitaire :
● mkfs : make file system
● « formattage » de la partition
● Mise en place des structures de données permettant de réaliser les opérations :
● Création de la table des fichiers
● Liste des blocs libres
Espace disponible pour les
données
Méta-données
Interface d'accès aux systèmes de gestion
de fichiers dans GNU/Linux
Caractéristiques
● S'appuie sur la couche de gestion des E/S dite de « bas niveau »
● Couche dite de « Haut niveau »
● Implémentée dans de nombreuses fonctions de bibliothèques ( 100 ? )
● Représentation interne ↔ externe
● Les fichiers sont non structurés : suite d'octets
● Notion de ligne dans certaines fonctions (getline) : \n
● Descripteurs de fichier : FILE * défini dans stdio.h avec stdin, stdout, stderr.
Ouverture d'un fichier
#include <stdio.h>
Ouvrir un fichier :
FILE *fopen(const char *path, const char *mode)
Ouvre le fichier dont le nom est pointé par path, dans le mode défini par la chaîne de caractères pointées par mode.
Résultat retourné : les descripteur d'accès au fichier ou NULL en cas d'erreur
Ouverture à haut niveau d'un descripteur de bas niveau
FILE *fdopen(int fd, const char *mode)
Le descripteur fd doit être valide et ouvert dans un mode compatible avec celui spécifié par mode
Résultat retourné : le descripteur d'accès de haut niveau ou NULL en cas d'erreur
Cette fonction est utilisée pour accéder à un tube à haut-niveau par exemple
Ouverture d'un fichier en l'associant à un descripteur donné
FILE *freopen(const char *path, const char *mode, FILE *stream)
ouvre path dans mode et l'associe au descripteur stream
principalement utilisé pour rediriger un fichier standard d'E/S
freopen("toto.ascii", "w", stdout)
ferme la sortie standard, ouvre toto.ascii et l'associe à stdout
mode
● Mode indique le type d'opération sur le fichier r : lecture
w : écriture avec troncature ou création r+ : lecture/écriture
w+ : lecture/écriture avec troncature, création du fichier si nécessaire
a : mode ajout en écriture, avec création du fichier si nécessaire
a+ : lecture en début, écriture en fin et création du fichier si nécessaire.
Si création les droits sont 0666 & ~umask.
Exemple de toto.txt en lecture
#include <stdio.h>
int main() {
FILE* f;
/* Ouverture de toto.txt, du répertoire courant en lecture */
f = fopen("toto.txt","r");
if (f == NULL) {
fprintf(stderr, "Echec de l'ouverture du fichier toto.txt");
return -1;
}
/* Le fichier est ouvert et utilisable */
Ouverture de /tmp/toto.txt comme sortie standard = stdout
#include <stdio.h>
int main() { FILE* f;
printf("Redirection de la sortie standard dans /tmp/toto.txt\n");
f = freopen("/tmp/toto.txt","w", stdout);
if (f == NULL) {
fprintf(stderr, "Echec de l'ouverture du fichier toto.txt");
return -1;
}
printf("La sortie standard est redirigée dans /tmp/toto.txt\n");
return 0;
}
Exécution
$ pwd
/tmp/tmp
$ cc -Wall toto.c -o toto
$ ./toto
Redirection de la sortie standard dans /tmp/toto.txt
$ cat ../toto.txt
La sortie standard est redirigée dans /tmp/toto.txt
$
Fermeture du fichier
#include <stdio.h>
int fclose(FILE *fp);
Ferme le descripteur fp
Retourne 0 en cas de succès EOF en cas d'échec
Lecture/écriture non formatées
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
ptr : pointera les octets résultats de la lecture ou les octets à écrire
nmenb : le nombre d'objets à lire
size : la taille unitaire des objets (souvent le résultat de sizeof )
Lecture/écriture non formatées
● Résultat retourné : le nombre d'objets lus ou écrits
● < nmemb en cas d'erreur ou fin de fichier
● fread ne fait pas de différence entre une erreur et la fin de fichier lors du retour du résultat
● UTILISER feof pour tester la fin de fichier
Lecture de 50 octets
#include <stdio.h>
int main() {
FILE* f;
char ligne[50];
int n;
f = fopen("/tmp/toto.txt","r");
if (f == NULL) { fprintf(stderr,
"Echec de l'ouverture du fichier toto.txt");
return -1;
}
/* Lecture de 50 caractères ou octets */
n = fread(ligne, sizeof(char), 50, f);
if (n != 50) { /* message */
Lecture de 50 entiers
#include <stdio.h>
int main() {
FILE* f;
int t[50];
int n;
f = fopen("/tmp/toto.txt","r");
if (f == NULL) { fprintf(stderr,
"Echec de l'ouverture du fichier toto.txt");
return -1;
}
/* Lecture de 50 entiers */
n = fread(t, sizeof (int), 50, f);
if (n != 50) { /* message */
}
/* 50 entiers ont été lus */
Un exemple complet : affichage du contenu
d'un fichier
#include <stdio.h>
int main(int argc, char **argv) {
int nlu, necrit;
FILE *desc;
char tampon[50];
if (argc != 2){
fprintf(stderr,
"%s : il faut un parametre : un nom de fichier\n", argv[0]);
return -1;
}
/* Ouverture du fichier */
desc = fopen(argv[1], "r" );
if (desc == NULL) {
perror("Impossible d'ouvrir le fichier");
return -2;
}
/* Lecture du fichier */
nlu = fread(tampon, sizeof(char), 50, desc);
while (nlu != 0) {
necrit = fwrite(tampon, sizeof(char), nlu, stdout);
if (necrit != nlu)
fprintf(stderr, "%s : moins de caracteres ecrits que lus %d\n", argv[0], necrit);
nlu = fread(tampon, 1, 50, desc);
}
fclose(desc) ; return 0;
}
Test de la fin de fichier
#include <stdio.h>
int feof(FILE *stream);
Retourne :
0 si la fin de fichier n'a pas été atteinte,
≠ 0 (= vrai) dans le cas contraire.
E/S formatées
● Ces fonctions permettent de réaliser un
passage du format externe au format interne lors d'une lecture
● Du format interne au format externe lors d'une écriture
● Le type de données est indiqué par une chaîne de caractères, le format, qui est passée en
paramètre aux fonctions
● Ce format est important car il est utilisé par les fonctions pour déterminer le nombre
d'éléments à lire ou écrire
Exemples
printf("Résultats\nI :%d\nF :%f\n", i, f);
● La chaîne de format comporte deux
spécificateurs de format %d et %f printf dépilera donc deux paramètres de la pile
● Les deux seront de type int
printf("Résultats\nI :%d\nF :%c\n", i, f);
● Types int et char
printf("Résultats\nI :%d\nF :%f\nc:%c", i, f) ;
● 3 spécificateurs mais 2 variables seulement le caractère affiché est indéfini
Ecriture formatées : printf
#include <stdio.h>
● Ecrit sur la sortie standard
int printf(const char *format, ...);
● Ecrire dans un fichier
int fprintf(FILE *f, const char *format, ...);
● Ecrire en mémoire
int sprintf(char *s, const char *format, ...);
int snprintf(char *s, size_t size, const char *format, ...);
Format
● chaîne de caractères comportant des
caractères quelconque et des spécificateurs de format introduit par le caractère %
● Suivi éventuellement de : - cadre à gauche
+ affiche le signe + si positif n : nombre maxi de caractères .
m : nombre mini de caractères l : données de type long
Format
● Suivi par un des caractères suivants : d, o, x, u, f, e, g, c, s.
● Seul % et un des caractères d, o, x, u, f, e, g, c, s sont obligatoires
Format : conversion et type de données
%d int décimal signée
%ld long int décimal signé
%u unsigned int décimal non signée
%lu unsigned long int décimal non signé
%o unsigned int octal non signé
%lo unsigned long int octal non signé
Format : conversion et type de données
%x unsigned int hexadécimal non signé
%lx unsigned long int hexadécimal non signé
%f double décimale virgule fixe
%lf long double décimale virgule fixe
Format : conversion et type de données
%e double décimal notation exponentielle
%le long double décimal notation exponentielle
%g double décimal, représentation la plus courte parmi %f et %e
%lg long double décimal, représentation la plus courte parmi %lf et %le
%c unsigned char caractère
%s char* chaîne de caractères
Exemples
printf("|%d|\n",1234); |1234|
printf("|%d|\n",-1234); |-1234|
printf("|%+d|\n",1234); |+1234|
printf("|%+d|\n",-1234); |-1234|
printf("|% d|\n",1234); | 1234|
printf("|% d|\n",-1234); |-1234|
printf("|%x|\n",0x56ab); |56ab|
printf("|%X|\n",0x56ab); |56AB|
printf("|%o|\n",1234); |2322|
printf("|%10d|\n",1234); | 1234|
printf("|%10.6d|\n",1234); | 001234|
printf("|%.6d|\n",1234); |001234|
printf("|%f|\n",1.234567890123456789e5); |123456.789012|
printf("|%.4f|\n",1.234567890123456789e5); |123456.7890|
printf("|%.20f|\n",1.234567890123456789e5);
|123456.78901234567456413060|
printf("|%20.4f|\n",1.234567890123456789e5); | 123456.7890|
printf("|%e|\n",1.234567890123456789e5); |1.234568e+05|
printf("|%.4e|\n",1.234567890123456789e5); |1.2346e+05|
printf("|%.20e|\n",1.234567890123456789e5);
|1.23456789012345674564e+05|
printf("|%20.4e|\n",1.234567890123456789e5); | 1.2346e+05|
printf("|%.4g|\n",1.234567890123456789e-5); |1.235e-05|
printf("|%.4g|\n",1.234567890123456789e5); |1.235e+05|
Entrées formattées
● Syntaxe :
● #include <stdio.h>
● int scanf (const char * format, ...);
● int fscanf (FILE * descripteur, const char *format, ...);
● int sscanf (const char * str, const char * format, ...);
●
scanf = fscanf(stdin,
format de *scanf
● %d int décimal signé
● %hd short int décimal signé
● %ld long int décimal signé
● %u unsigned int décimal non signé
● %hu unsigned short int décimal non signé
● %lu unsigned long int décimal non signé
● %o int octal
● %ho short int octal
● %lo long int octal
● %x int hexadécimal
● %hx short int hexadécimal
● %lx long int hexadécimal
● %f float flottante virgule fixe
● %lf doubleflottante virgule fixe
● %e , %le idem f mais notation exponentielle
● %g, %lg idem f ou e
● %c char caractère
● %s char * chaîne de caractères
Lecture de caractères ou ligne
● #include <stdio.h>
● int fgetc(FILE *stream);
● char *fgets(char *s, int size, FILE *f);
● int getc(FILE *stream);
● int getchar(void);
● char *gets(char *s); DANGEUREUX
● int ungetc(int c, FILE *stream);
● s pour string signifie ligne : ensemble de caractères terminés par un \n
#include <stdio.h>
Lire une line terminée par \n
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
Lire une line terminée par delim
ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
Résultat : nombre de caractère lus séparateur inclus
Écriture de caractères ou ligne
● #include <stdio.h>
● int fputc(int c, FILE *stream);
● int fputs(const char *s, FILE *stream);
● int putc(int c, FILE *stream);
● int putchar(int c);
● int puts(const char *s);
● Mêmes remarques que pour les lectures