#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).
#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.
#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.
#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.
#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 }
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.
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
'').
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 }