Programmation Orientée Système Cours 9: La Gestion des Fichiers et des Répertoires Francesco Belardinelli Laboratoire IBISC Diapos basées sur Introduction to UNIX par Mme Cantaut 6 février 2017
Cours 9 : La Gestion des Fichiers Overture, fermeture, lecture et écriture des fichiers Manipulation des fichiers Manipulation des répertoires Overture, fermeture, lecture et écriture des répertoires
Le Filesystem et le langage C en C on peut lire et écrire des données dans des fichiers les accès aux fichiers se font par l intermédiaire d une mémoire-tampon (buffer) ça permet de réduire le nombre d accès aux périphériques (disque, etc.) avant de lire ou d écrire dans un fichier, on notifie son accès par la commande open() la fonction open() prend comme argument le nom du fichier et initialise un flot de données (utilisé pour l écriture ou la lecture) après le traitement des données, on annule la liaison entre le fichier et le flot grâce à la fonction close()
Ouverture d un Fichier La function primitive open() permet à un processus de réaliser une ouverture de fichier #include <fcntl.h> int open(const char *nom, int flags); int open(const char *nom, int flags, mode_t mode); nom est la référence du fichier à ouvrir flags demande la réalisation d opérations particulières au cours de l ouverture et/ou paramètre le comportement des opérations de lecture/écriture dans le fichier ouvert il s agit d une disjonction ( ) de constantes définies dans fcntl.h cette disjonction comporte : exactement une des trois constantes précisant le mode d ouverture O_RDONLY : lecture seule O_WRONLY : écriture seule O_RDWR : lecture et écriture des constantes signalant les opérations complémentaires O_TRUNC : fichier tronqué à l ouverture O_APPEND : positionnement de l offset à la fin du fichier O_CREAT : création du fichier s il n existe pas. Il faut associer des droits O_EXCL : si O_CREAT est positionné et le fichier existe, open renvoie une erreur O_NONBLOCK : empêche le processus appelant d être bloqué lors de l ouverture. O_SYNC : les écritures dans le fichier bloquent le processus appelant jusqu à terminaison En cas de succés, open() renvoie le descripteur du fichier En cas d échec, la valeur renvoyée est -1
Ouverture d un Fichier exemple : le programme o1.c ouvre le fichier in1 pour la lecture, et imprime la valeur du descripteur de fichier si on n a pas créé le fichier in1, alors il va imprimer -1 si in1 existe, alors on va imprimer 3, le descripteur de fichier qu on a obtenu #include <stdio.h> #include <fcntl.h> int main() { int fd; fd = open("in1", O_RDONLY); printf("%d\n", fd); return 0; } pourquoi 3? pour chaque processus, trois flux d E/S sont ouverts par défaut : stdin, stdout et stderr ces trois prennent les descripteurs de fichiers 0, 1 et 2
Ouverture d un Fichier la fonction perror() peut être utilisée pour renvoyer un message d erreur comprensible à l utilisateur exemple : le programme o2.c essaie d ouvrir le fichier out1 pour l écriture. #include <stdio.h> #include <stdlib.h> #include <fcntl.h> int main() { int fd; fd = open("out1", O_WRONLY); if (fd < 0) { perror("out1"); exit(1); } else printf("%d\n", fd); return 0; } il échoue si out1 n existe pas pour ouvrir un nouveau fichier pour l écriture, on doit utiliser (O_WRONLY O_CREAT O_TRUNC) comme argument de flags l argument mode ne doit être utilisée que si on crée un nouveau fichier il spécifie les autorisations du nouveau fichier 0644 est la valeur la plus typique (rw-r--r--)
Créations d un Fichier dans C on a à disposition la fonction int create(const char *nom, mode_t mode); pour créer un fichier de nom nom l appel à fonction create(nom, mode); correspond à l appel open(nom, O_WRONLY O_CREAT O_TRUNC, mode);
Fermeture d un Fichier La fonction primitive close() permet à un processus de fermer un descripteur de fichier #include <unistd.h> int close(int fd); où fd est le descripteur retourné par la fonction open() correspondante l OS peut réutiliser le descripteur de fichier exemple : le programme c1.c ouvre le fichier in1 deux fois, après il ferme in1 une fois, enfin il essaye de fermer le même fichier une deuxième fois.
Lecture d un Fichier la fonction read() permet à un processus de lire le contenu d un fichier #include <unistd.h> int read(int fd, char *buf, int size) son appel correspond à la demande de lecture d au plus size caractères dans le fichier de descripteur fd les caractères lus sont stockés dans l espace mémoire d adresse buf ce pointeur doit donc correspondre à une zone de taille au moins égale à size read() renvoie un entier correspondant au nombre de bits effectivement lus
Algorithme de fonctionnement 1. read() renvoie -1 en cas d erreur de paramètre 2. s il n y a pas de verrou exclusif impératif sur le fichier 2.1 si la fin du fichier n est pas atteinte, elle lit des caractères à partir de l offset courant, jusqu à ce que la fin du fichier soit atteinte ou que size octets aient été lus. 2.2 elle renvoie le nombre de caractères lus et la valeur de l offset est augmentée de ce nombre. 2.3 si la fin de fichier est atteinte, elle renvoie 0. 3. s il y a un verrou exclusif impératif sur le fichier : 3.1 si le mode de lecture est bloquant (O_NONBLOCK non positionné), le processus est bloqué jusqu à ce qu il n y ait plus de verrou ou que l appel soit interrompu. 3.2 si le mode de lecture est non bloquant, aucun caractère n est lu et la value retournée est -1.
Lecture d un Fichier exemple : le programme r1.c lit le fichier in1 et affiche sur la sortie standard (écran) son contenu le buffer c doit pointer sur une adresse de mémoire valide ; ceci est réalisé par calloc on peut également déclarer c comme un tableau statique de 100 caractères : char c[100]; c est terminé par \0 pour assurer que printf() comprenne si read() renvoie 0, alors la fin du fichier a été atteinte si read() renvoie moins d octets qu on avait demandé, alors on a atteint la fin du fichier
Ecriture d un Fichier la fonction write() permet à un processus d écrire dans un fichier #include <unistd.h> int write(int fd, char *buf, int size); son appel correspond à la demande d écriture, dans le fichier de descripteur fd, de size caractères stockés à l adresse buf dans l espace d adressage du processus. le programme w1.c ouvre le fichier out3 pour l écriture et écrit la chaîne Hello world!
Algorithme de fonctionnement 1. write() renvoie -1 en cas d erreur de paramètre 2. s il n y a pas de verrou impératif, exclusif ou partagé, sur le fichier, l écriture est réalisée. Le nombre de caractères écrits est renvoyé : une valeur inférieure à size signale une erreur. 3. s il y a un verrou impératif, exclusif ou partagé, sur le fichier : 3.1 si aucun des indicateurs O_NONBLOCK et O_NDELAY n est positionné, le processus est bloqué jusqu à ce qu il n y ait plus de verrou de ce type ou que l appel soit interrompu. 3.2 si l un des indicateurs précédents est positionné, le retour est immédiat sans écriture avec la valeur -1.
Les Sorties Standards trois sorties standard peuvent être utilisées sans qu il soit nécessaire de les ouvrir ou de les fermer : stdin (standard input) : unité d entrée (par défaut, le clavier ; valeur = 0) stdout (standard output) : unité de sortie (par défaut, l écran ; valeur = 1) ; stderr (standard error) : unité d affichage des messages d erreur (par défaut, l écran ; valeur = 2). on peut lire sur l entrée standard, en utilisant read(0,...), et écrire sur la sortie standard en utilisant write(1,...) exemple : on peut écrire un programme simpcat qui copie l entrée standard sur la sortie standard #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main() { char c; while (read(0, &c, 1) == 1) write(1, &c, 1); return 0; }
Exemples d E/S sur Fichiers le programme tfile_ex.c ouvre le fichier tfile deux fois, il écrit une fois et lit une fois par deux descripteurs de fichiers différents #include<string.h> #include<unistd.h> #include<stdio.h> #include<fcntl.h> #define string "tfile" int main(void){ int fd[2]; char buf1[12] = "just a test"; char buf2[12]; fd[0] = open(string,o_rdwr); printf("overture de %s avec id %d\n", string, fd[0]); fd[1] = open(string,o_rdwr); printf("overture de %s avec id %d\n", string, fd[1]); printf("écriture de %s sur le ficher avec id %d\n", buf1, fd[0]); write(fd[0],buf1,strlen(buf1)); printf("lecture du ficher avec id %d sur buf2\n", fd[1]); printf("écriture de buf2 sur la sortie standard\n"); write(1, buf2, read(fd[1],buf2,12)); printf("\n"); close(fd[0]); close(fd[1]); return 0; } Exemple : programme copierfichier.c pour copier un fichier dans un autre.
Table du Système Le schéma suivant résume l organisation des tables mises en jeu par le mécanisme d entrée-sorties d un système Unix :
Table du Système Tables des descripteurs : 1 par processus, descripteur = indice. Table des fichiers ouverts : nb descripteurs correspondant, mode d ouverture, offset, pointeur sur le i-noeud en mémoire. Table des inodes en mémoire : nb total d ouvertures, id disque logique, état de l inode. Table des verrous.
Manipulation des Fichiers de nombreuses caractéristiques des fichiers sont regroupées dans la structure stat #include <sys/types.h> #include <sys/stat.h> struct stat{ dev_t st_dev; /* ID du disque logique */ ino_t st_ino; /* Numéro i-noeud sur le disque */ mode_t st_mode; /* Type du fichier et droits des utilisateurs */ nlink_t st_nlink; /* Nb liens physiques */ uid_t st_uid; /* UID propriétaire */ gid_t st_gid; /* GID propriétaire */ off_t st_size; /* Taille totale en octets */ time_t st_atime; /* Heure dernier accès */ time_t st_mtime; /* Heure dernière modification */ time_t st_ctime; /* Heure dernier changement état */ };
Manipulation des Fichiers Le champ st_mode définit le type du fichier (généralement sur 4 bits) et les droits d accès au fichier (généralement sur 12 bits). S_IFMT S_IFREG, S_IFBLK Type du fichier S_IFCHR,S_IFDIR S_IFLNK,S_IFFIFO S_IFSOCK,S_IFDOOR S_ISUID S_ISUID S_ISVTX S_ISRWXU S_IRUSR S_IWUSR S_IXUSR S_ISRWXG S_IRGRP S_IWGRP S_IXGRP S_ISRWXO S_IROTH S_IWOTH S_IXOTH Les droits rwxr-x--x correspondent à la disjonction S_ISRWXU S_IRGRP S_IXGRP S_IXOTH
Manipulation des Fichiers st_mode & S_IFMT Type du fichier Fonction de Test S_IFREG Fichier régulier S_ISREG() S_IFBLK Fichier spécial bloc S_ISBLK() S_IFCHR Fichier spécial caractère S_ISCHR() S_IFDIR Répertoire S_ISDIR() S_IFLNK Lien symbolique S_ISLNK() IF_FIFO Tube nommé S_ISFIFO() S_IFSOCK Socket S_ISSOCK() IF_DOOR Door S_ISDOOR()
Manipulation des Fichiers stat est un appel système qui est utilisé pour déterminer des informations concernant un fichier en fonction de son chemin d accès #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *path, struct stat *buf); où const char *path est le chemin du fichier qui est interrogé struct stat *buf est la structure stat où les données sur le fichier seront stockées aucun droit particulier sur le fichier n est nécessaire valeur de retour : 0 si succès, -1 si échec Example 1 Programme ex_stat.c pour afficher les infos relatives à un fichier. Programme compare_inodes.c pour comparer deux i-noeuds. Programme test_droits.c pour tester les droits d un fichier.
Manipulation des Répertoires La bibliothéque dirent.h contient la définition du type DIR La consultation d un répertoire suppose l acquisition d un pointeur sur un objet de ce type qui désigne dès lors ce répertoire La bibliothéque dirent.h contient aussi la définition d une structure dirent correspondant à une entrée dans un répertoire struct dirent{ ino_t d_ino; /* numéro sérial de fichier */ off_t d_off; /* distance des fichiers */ unsigned char d_type; /* type de fichier */ char d_name[256]; /* nom du fichier */ }
Entrées/Sorties sur les Répertoires Les fonctions de consultation sont : #include <sys/types.h> #include <dirent.h> /* ouverture d un répertoire. */ DIR *opendir(const char *nom); /* lecture d un répertoire. */ struct dirent *readdir(dir *dir); /* rembobinage d un répertoire. */ void rewinddir(dir *dir); /* fermeture d un répertoire. */ int closedir(dir *dir);
Affichage d un Chemin de Répertoire Pour obtenir le répertoire de travail courant sous UNIX, on utilise la commande pwd L appel système derrière la commande pwd est l appel getcwd() #include<unistd.h> char *getcwd(char * buf, /* chemin d accès renvoyé */ size_t bufsize /* sizeof buf */ ); getcwd() renvoie un pointeur vers buf en cas de succès et NULL en cas d échec bufsize est la taille maximale du chemin
Affichage d un Chemin de Répertoire exemple : programme getcwd_ex.c pour imprimer à l écran le répertoire de travail courant #include<stdio.h> #include<stdlib.h> #include<unistd.h> int main(){ long max; char *buf; max = pathconf("/",_pc_path_max); buf =(char*)malloc(max); getcwd(buf,max); printf("%s\n",buf); return 0; }
Ouverture d un Répertoire L en-tête <dirent.h> déclare des fonctions d ouverture, de lecture, de rembobinage et de fermeture de répertoires Pour afficher les fichiers dans un répertoire, on doit d abord l ouvrir en utilisant la fonction opendir() : DIR *opendir(const char *path); opendir() renvoie un pointeur sur DIR DIR est une structure de données qui représente un répertoire une valeur de retour NULL indique une erreur path doit être le chemin à un répertoire existant
Lecture d un Répertoire Pour parcourir un répertoire ouvert on utilise la fonction readdir() : struct dirent *readdir(dir *PDIR); où PDIR est le pointeur obtenu à partir d un précédent appel à opendir() readdir() renvoie un pointeur sur une structure dirent dont le membre d_name contient le nom du fichier courant chaque appel successif à readdir() avance au fichier suivant dans le répertoire readdir() renvoie NULL soit dans le cas d une erreur ou une fois qu on a traversé tous les fichiers dans le répertoire pour distinguer ces deux conditions, vérifier errno après chaque appel readdir() comme readdir() change errno seulement si une erreur s est produite, on doit la réinitialisé explicitement avant chaque appel readdir()
Fermeture d un Répertoire Finalement, on utilise closedir() pour fermer le répertoire une fois qu on a terminé : int closedir(dir *PDIR); où PDIR est le pointeur obtenu à partir d un précédent appel opendir()
Entrées/Sorties sur les Répertoires Exemple : le programme opendir_ex.c énumère le contenu du répertoire de travail courant : #include <errno.h> #include <dirent.h> #include <stdio.h> #include <stdlib.h> int main(){ DIR *pdir; struct dirent *pent; pdir = opendir("."); /*"." refers to the current dir*/ if (!pdir){ printf("opendir() failure; terminating.\n"); exit(1); } errno = 0; while ((pent = readdir(pdir))){ printf("%s\n", pent->d_name); } if (errno){ printf ("readdir() failure; terminating.\n"); exit(1); } closedir(pdir); return 0; }
Rembobinage d un Répertoire La fonction rewinddir() réinitialise un flux répertoire à la première entrée : void rewinddir(dir *PDIR); rewinddir() s assure également que le répertoire reflète fidèlement les changements dans le répertoire (suppression de fichiers, renommage, etc) depuis le dernier appel opendir() ou rewinddir() exemple : programme rewinddir_ex.c pour afficher le premier élément du répertoire courant, rembobiner, et afficher à nouveau le premier élément
Création d un Répertoire D autres opérations relatives aux répertoires, telles que la création, la suppression et la modification, sont disponibles la fonction mkdir() crée un nouveau répertoire : int mkdir (const char *dirname); int mkdir (const char *dirname, mode_t perm); où dirname contient le nom du nouveau répertoire un nom de répertoire non valide donne un échec perm indique les permissions du répertoire
Suppression d un Répertoire La fonction rmdir() supprime un répertoire existant : int rmdir(const char *dirname); rmdir() échoue si le répertoire n est pas vide pour supprimer un répertoire qui contient des fichiers, on doit supprimer ces fichiers d abord et ensuite appeler rmdir() exemple : #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(int argc,char *argv[]){ int md,rd; md = mkdir(argv[1],0777); if(md == 0) printf("%s directory is created\n",argv[1]); else printf("%s directory is not created\n",argv[1]); rd = rmdir(argv[2]); if(rd == 0) printf("%s directory is removed\n",argv[2]); else printf("%s directory is not removed\n",argv[2]); return 0; }
Changement de Répertoire La fonction chdir() change le répertoire de travail courant du processus : int chdir(const char *dirname); Si dirname n est pas un nom de chemin d accès valide ou si le processus ne dispose pas des autorisations nécessaires, chdir() échoue. Exemple : programme minils.c
Conclusions : La Gestion des Fichiers et des Répertoires Overture, fermeture, lecture et écriture des fichiers Manipulation des fichiers Manipulation des répertoires Overture, fermeture, lecture et écriture des répertoires