Page suivante Page précédente Table des matières

7. Manipulation des fichiers, opérations de bas niveau

7.1 Ouverture, fermeture, lecture, écriture

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);

Ouverture d'un fichier nommé pathname. Les flags peuvent prendre l'une des valeurs suivantes : O_RDONLY (lecture seulement), O_WRONLY (écriture seulement), O_RDWR (lecture et écriture). Cette valeur peut être combinée éventuellement (par un ``ou logique'') avec des options: O_CREAT (création du fichier si il n'existe pas déjà), O_TRUNC (si le fichier existe il sera tronqué), O_APPEND (chaque écriture se fera à la fin du fichier), etc.

En cas de création d'un nouveau fichier, le mode sert à préciser les droits d'accès. Lorsqu'un nouveau fichier est créé, mode est combiné avec le umask du processus pour former les droits d'accès du fichier. Les permissions effectives sont alors (mode & ~umask).

Le paramètre mode doit être présent quand les flags contiennent O_CREAT.

L'entier retourné par open() est un descripteur de fichier (-1 en cas d'erreur), qui sert à référencer le fichier par la suite.

Les descripteurs 0, 1 et 2 correspondent aux fichiers standards stdin, stdout et stderr qui sont normalement déjà ouverts (0=entrée, 1=sortie, 2=erreurs).

#include <unistd.h>
#include <sys/types.h>

int close(int fd);
int read(int fd, char *buf, size_t count);
size_t write(int fd, const char *buf, size_t count);

close() ferme le fichier indiqué par le descripteur fd. Retourne 0 en cas de succès, -1 en cas d'échec. read() demande à lire au plus count octets sur fd, à placer dans le tampon buf. Retourne le nombre d'octets qui ont été effectivement lus, qui peut être inférieur à la limite donnée pour cause de non-disponibilité (-1 en cas d'erreur, 0 en fin de fichier). write() tente d'écrire sur le fichier les count premiers octets du tampon buf. Retourne le nombre d'octets qui ont été effectivement écrits, -1 en cas d'erreur.

Exemple :


 
  1     /* copie.c */

  2     /*
  3        Entrées-sorties de bas niveau
  4        Usages:
  5        1: copie              (entrée-standard -> sortie standard)  
  6        2: copie fichier      (fichier -> sortie standard)
  7        3: copie source dest  (source -> dest)
  8     */

  9     #include <stdlib.h>
 10     #include <stdio.h>
 11     #include <sys/types.h>
 12     #include <sys/stat.h>
 13     #include <fcntl.h>
 14     #include <unistd.h>

 15     #define ENTREE_STANDARD 0
 16     #define SORTIE_STANDARD 1
 17     #define TAILLE 4096

 18     void erreur_fatale(char s[])
 19     {
 20             fprintf(stderr, "Erreur fatale %s\n", s);
 21             exit(EXIT_FAILURE);
 22     }

 23     void transfert(int entree, int sortie)
 24     {
 25             int nb_lus, nb_ecrits;
 26             char tampon[TAILLE];

 27             if (entree == -1)
 28                     erreur_fatale("Fichier d'entrée introuvable");
 29             if (sortie == -1)
 30                     erreur_fatale
 31                         ("Ne peut pas ouvrir le fichier de sortie");

 32             for (;;) {
 33                     nb_lus = read(entree, tampon, TAILLE);
 34                     if (nb_lus <= 0)
 35                             break;
 36                     nb_ecrits = write(sortie, tampon, nb_lus);
 37                     if (nb_ecrits != nb_lus)
 38                             erreur_fatale("Probleme d'ecriture");
 39             };
 40     }

 41     int main(int argc, char *argv[])
 42     {
 43             int entree, sortie;

 44             switch (argc) {
 45             case 1:
 46                     transfert(ENTREE_STANDARD, SORTIE_STANDARD);
 47                     break;
 48             case 2:
 49                     entree = open(argv[1], O_RDONLY);
 50                     transfert(entree, SORTIE_STANDARD);
 51                     close(entree);
 52                     break;
 53             case 3:
 54                     entree = open(argv[1], O_RDONLY);
 55                     sortie =
 56                         open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0666);
 57                     transfert(entree, sortie);
 58                     close(entree);
 59                     close(sortie);
 60                     break;
 61             default:
 62                     erreur_fatale("Usage: copie [src [dest]]");
 63             };
 64             exit(EXIT_SUCCESS);
 65     }
 

Problème. Montrez que la taille du tampon influe sur les performances des opérations d'entrée-sortie. Pour cela, modifiez le programme précédent pour qu'il accepte 3 paramètres : les noms des fichiers source et destination, et la taille du tampon (ce tampon sera alloué dynamiquement).

7.2 Suppression

  
#include <stdio.h>
int remove(const char *pathname);

Cette fonction supprime le fichier pathname, et retourne 0 en cas de succès (-1 sinon).

Exercice: écrire un substitut pour la commande rm.

7.3 Positionnement

#include <unistd.h>
#include <sys/types.h>

off_t lseek(int fildes, off_t offset, int whence);

lseek() repositionne le pointeur de lecture. Similaire à fseek(). Pour connaître la position courante faire un appel à stat().

Exercice. Écrire un programme pour manipuler un fichier relatif d'enregistrements de taille fixe.

7.4 Verrouillage

#include <sys/file.h>
int flock(int fd, int operation)

Lorsque operation est LOCK_EX, il y a verrouillage du fichier désigné par le descripteur fd. Le fichier est déverrouillé par l'option LOCK_UN.

Problème. Ecrire une fonction mutex() qui permettra de délimiter une section critique dans un programme C. Exemple d'utilisation :

#include "mutex.h"
...
mutex("/tmp/foobar",MUTEX_BEGIN);
...
mutex("/tmp/foobar",MUTEX_END);
Le premier paramètre indique le nom du fichier utilisé comme verrou. Le second précise si il s'agit de verrouiller ou déverrouiller. Faut-il prévoir des options MUTEX_CREATE, MUTEX_DELETE ? Qu'arrive-t'il si un programme se termine en ``oubliant'' de fermer des sections critiques ?

Fournir le fichier d'interface mutex.h, l'implémentation mutex.c, et des programmes illustrant l'utilisation de cette fonction.

7.5 Informations sur les fichiers/répertoires/...

#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name, struct stat *buf);
int fstat(int filedes, struct stat *buf);

Ces fonctions permettent de connaître diverses informations sur un fichier désigné par un chemin d'accès (stat()) ou par un descripteur (fstat()).

Exemple :


 
  1     /* taille.c */
  2     /*
  3             indique la taille et la nature 
  4             des "fichiers" cités en paramètres 
  5     */

  6     #include <stdlib.h>
  7     #include <stdio.h>
  8     #include <sys/stat.h>
  9     #include <errno.h>
 10     #include <string.h>

 11     int main(int argc, char *argv[])
 12     {
 13             int k;
 14             struct stat s;

 15             if (argc < 2) {
 16                     fprintf(stderr, "Usage: %s fichier ... \n", argv[0]);
 17                     exit(EXIT_FAILURE);
 18             };
 19             for (k = 1; k < argc; k++) {
 20                     printf("%20s:\t", argv[k]);
 21                     if (stat(argv[k], &s) == 0) {
 22                             printf("%8ld ", s.st_size);
 23                             if (S_ISREG(s.st_mode))
 24                                     printf("(fichier)");
 25                             else if (S_ISLNK(s.st_mode))
 26                                     printf("(lien symbolique)");
 27                             else if (S_ISDIR(s.st_mode))
 28                                     printf("(repertoire)");
 29                             else if (S_ISCHR(s.st_mode))
 30                                     printf
 31                                         ("(peripherique mode caracteres)");
 32                             else if (S_ISBLK(s.st_mode))
 33                                     printf("(peripherique mode bloc)");
 34                             else if (S_ISFIFO(s.st_mode))
 35                                     printf("(fifo)");
 36                             else if (S_ISSOCK(s.st_mode))
 37                                     printf("(socket)");
 38                     } else {
 39                             switch (errno) {
 40                             case ENOENT:
 41                                     printf("le fichier n'existe pas");
 42                                     break;
 43                             default:
 44                                     printf("erreur %s", strerror(errno));
 45                                     break;
 46                             };
 47                     };
 48                     printf("\n");
 49             };
 50             exit(EXIT_SUCCESS);
 51     }
 

7.6 Parcours de répertoires

Le parcours d'un répertoire, pour obtenir la liste des fichiers et répertoires qu'il contient, se fait grâce aux fonctions:

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
int closedir(DIR *dir);
void rewinddir(DIR *dir);
void seekdir(DIR *dir, off_t offset);
off_t telldir(DIR *dir);

Voir la documentation pour des exemples.

Exercice : écrire une version simplifiée de la commande ls.

Exercice : écrire une commande qui fasse apparaître la structure d'une arborescence. Exemple d'affichage :

C++
| CompiSep
| Fichiers
Systeme
| Semaphores
| Pipes
| Poly
  | SVGD
  | Essais
| Fifos
Conseil: écrire une fonction à deux paramètres: le chemin d'accès du répertoire et le niveau de récursion.

7.7 Duplication de descripteurs

int dup(int oldfd);
int dup2(int oldfd, int newfd);

Ces deux fonctions créent une copie du descripteur oldfd. dup() utilise le plus petit numéro de descripteur libre. dup2() réutilise le descripteur newfd, en fermant éventuellement le fichier qui lui était antérieurement associé.

La valeur retournée est celle du descripteur, ou -1 en cas d'erreur.

Exemple


 
  1     /* redirection.c */

  2     /*
  3       Le fichier cité en paramètre est passé à travers
  4       la commande wc.
  5     */

  6     #include <sys/types.h>
  7     #include <fcntl.h>
  8     #include <stdio.h>
  9     #include <stdlib.h>
 10     #include <unistd.h>
 11     #include <errno.h>

 12     #define COMMANDE "wc"

 13     int main(int argc, char *argv[])
 14     {
 15             int fd;

 16             if (argc != 2) {
 17                     fprintf(stderr, "Usage: %s fichier\n", argv[0]);
 18                     exit(EXIT_FAILURE);
 19             };

 20             fd = open(argv[1], O_RDONLY);
 21             if (fd == -1) {
 22                     perror("open");
 23                     exit(EXIT_FAILURE);
 24             };

 25             /* transfert du descripteur dans celui de l'entrée standard */
 26             if (dup2(fd, 0) < 0) {
 27                     perror("dup2");
 28                     exit(EXIT_FAILURE);
 29             };
 30             close(fd);

 31             system(COMMANDE);
 32             if (errno != 0) {
 33                     perror("system");
 34                     exit(EXIT_FAILURE);
 35             };
 36             exit(EXIT_SUCCESS);
 37     }
 

Exercice : que se produit-il si on essaie de rediriger la sortie standard d'une commande à la manière de l'exemple précédent ? (essayer avec ``ls'', ``ls -l'').

7.8 mmap() : fichiers "mappés" en mémoire

Un fichier "mappé en mémoire" apparaît comme un tableau d'octets, ce qui permet de le parcourir en tous sens plus commodément qu'avec des seek(), read() et write().

C'est technique beaucoup plus économique que de copier le fichier dans une zone allouée en mémoire : c'est le système de mémoire virtuelle qui s'occupe de lire et écrire physiquement les pages du fichier au moment où on tente d'y accéder, et gère tous les tampons.

#include <unistd.h>
#include <sys/mman.h>

void  *  mmap(void  *start,  size_t length, 
              int prot , int flags, 
              int fd, off_t offset);
int munmap(void *start, size_t length);

La fonction mmap() "mappe" en mémoire un morceau (de longueur length, en partant du offset-ième octet) du fichier désigné par le descripteur fd , et retourne un pointeur sur la zone de mémoire correspondante.

On peut définir quelques options (protection en lecture seule, partage, etc) grâce à prot et flags. Voir pages de manuel. munmap() "libère" la mémoire.


 
  1     /*
  2       inverse.c

  3       Affichage des lignes d'un fichier en partant de la fin
  4       Exemple d'utilisation de mmap

  5       M. Billaud, Octobre 2002
  6     */

  7     #include <unistd.h>
  8     #include <stdio.h>
  9     #include <stdlib.h>
 10     #include <sys/types.h>
 11     #include <sys/stat.h>
 12     #include <sys/mman.h>
 13     #include <fcntl.h>

 14     /* affichage à l'envers du contenu d'un tampon de texte */

 15     void afficher(char tampon[], int taille)
 16     {
 17             char *c;
 18             int nb;                 /* longueur ligne courante, y compris '\n'  */

 19             nb = 1;
 20             for (c = tampon + taille - 1; c >= tampon; c--) {
 21                     if (*c == '\n') {
 22                             write(1, c + 1, nb);
 23                             nb = 0;
 24                     }
 25                     nb++;
 26             }
 27             write(1, c + 1, nb);
 28     }

 29     int main(int argc, char *argv[])
 30     {
 31             int fd;
 32             struct stat s;
 33             char *t;

 34             if (argc != 2) {
 35                     fprintf(stderr, "Usage: %s fichier\n", argv[0]);
 36                     exit(EXIT_FAILURE);
 37             }
 38             if (stat(argv[1], &s) != 0) {
 39                     perror("Contrôle du type");
 40                     exit(EXIT_FAILURE);
 41             }
 42             if (!S_ISREG(s.st_mode)) {
 43                     fprintf(stderr, "%s n'est pas un fichier\n", argv[1]);
 44                     exit(EXIT_FAILURE);
 45             }
 46             fd = open(argv[1], O_RDONLY);
 47             if (fd < 0) {
 48                     perror("Ouverture");
 49                     exit(EXIT_FAILURE);
 50             }

 51             t = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 52             if (t == MAP_FAILED) {
 53                     perror("Mise en mémoire");
 54                     exit(EXIT_FAILURE);
 55             }

 56             afficher(t, s.st_size);

 57             if (munmap(t, s.st_size) < 0) {
 58                     perror("Détachement");
 59                     exit(EXIT_FAILURE);
 60             }
 61             close(fd);
 62     }
 


Page suivante Page précédente Table des matières