Traitement des fichiers ordinaires
File I/O : Fichiers ordinaires
Propriétés :
- File I/O propose une interface au niveau du noyau Linux pour la gestion de fichiers ordinaires et spéciaux.
- Pour les fichiers ordinaires, le noyau implémente un cache des données par blocs de 4KiB usuellement.
- Le nombre de fichiers qu’un processus peut manipuler est limité à 1024 par
défaut. Celui-ci peut cependant être augmenté à l’aide de la commande
ulimit -n
Opérations
Linux propose une large palette de services pour traiter avec les fichiers ordinaires
Opération | syscall |
---|---|
Ouverture d’un fichier | open |
Création d’un fichier | creat |
Lecture du contenu d’un fichier | read |
Écriture de données dans un fichier | write |
Positionnement dans le fichier | lseek |
Troncation d’un fichier | ftruncate |
Mise en mémoire d’un fichier | mmap |
Lecture/écriture et positionnement combinés | pread/pwrite |
Lecture des métadonnées (status) d’un fichier | fstat |
Synchronisation des données avec le disque | fsync |
Fermeture d’un fichier | close |
Ouverture d’un fichier
L’ouverture d’un fichier est obtenue à l’aide de l’appel système open()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open (const char* pathname, int flags);
Exemple
int fd = open ("/home/myusername/myfile", O_RDONLY);
if (fd == -1)
/* error*/
Comportement
La fonction open()
associe le fichier spécifié par l’argument pathname
au
descripteur fd
, lequel est retourné en cas de succès.
Arguments
- L’argument
pathname
spécifie le chemin et le nom du fichier à ouvrir. Le chemin peut être absolu ou relatif. - L’argument
flags
spécifie le mode d’accès au fichier, soit:O_RDONLY
: en mode lectureO_WRONLY
: en mode écritureO_RDWR
: en mode lecture et écriture
- En mode écriture, l’utilisateur peut spécifier si les nouvelles données doivent être
ajoutées au contenu existant ou si celles-ci doivent le remplacer
O_APPEND
: ajouter les données au contenu existantO_TRUNC
: remplacer les données du fichier
- Par exemple
int fd = open ("/home/myusername/myfile", O_WRONLY | O_APPEND);
Création d’un fichier ordinaire
La création d’un fichier ordinaire est obtenue à l’aide de l’appel système
creat()
ou la fonction open()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat (const char* pathname, mode_t mode);
int open (const char* pathname, int flags, mode_t mode);
Exemple
int fd = creat ("/home/myusername/myfile", 0664);
int fd = open ("/home/myusername/myfile", O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd == -1)
/* error*/
Comportement
- Ces deux appels sont équivalents. Ils permettent de créer un nouveau fichier. Si le fichier est déjà existant, le contenu de celui-ci sera simplement effacé.
- Il faut noter que si le fichier existe déjà, le mode du nouveau fichier ne sera pas adapté au mode spécifié lors de l’appel de la fonction.
Arguments
- L’argument
pathname
spécifie le chemin et le nom du fichier à créer. Le chemin peut être absolu ou relatif. - L’argument
flags
spécifie le mode d’accès au fichier, en principe:O_WRONLY | O_CREAT | O_TRUNC
- L’argument
mode
spécifie les droits d’accès au fichier. Ce mode peut être passé sous forme octale ou sous forme symbolique en utilisant les constantes suivantes:
Permission | Owner | Group | Other |
---|---|---|---|
all | S_IRWXU | S_IRWXG | S_IRWXO |
read | S_IRUSR | S_IRGRP | S_IROTH |
write | S_IWUSR | S_IWGRP | S_IWOTH |
execute | S_IXUSR | S_IXGRP | S_IXOTH |
Il est important de noter que les droits du fichier est déterminé avec un et
logique entre le mode spécifié lors de la création du fichier et le complément à un (inverse)
du masque de création de fichiers attribué à l’utilisateur (valeur umask). Ce
dernier peut être obtenu avec la commande umask
Lecture d’un fichier
La lecture d’un fichier est obtenue à l’aide de l’appel système read()
#include <unistd.h>
ssize_t read (int fd, void *buf, size_t len);
Exemple
char buf[100];
size_t len = sizeof(buf);
ssize_t nr = read (fd, buf, len);
if (nr == -1)
/* error*/
Comportement
- La fonction
read()
permet de lire au maximumlen
octets du fichier et de les stocker dansbuf
. En cas d’erreur, la fonction retourne-1
. La position du fichier est avancée du nombre d’octets lus, permettant ainsi de lire les octets restants.
Valeur de retour
- Si la valeur de retour est égale à
len
tout est en ordre - Si la valeur de retour est égale à
0
, cela indique la fin du fichier (EOF) et qu’il ne reste plus rien à lire - Si la valeur de retour est inférieure à
len
, mais supérieure à0
, le nombre d’octets lus est stocké dansbuf
. Plusieurs causes peuvent être à l’origine de cette situation. Pour en connaître la raison, il suffit d’exécuter une lecture supplémentaire. - Si la valeur de retour est égale à
-1
, cela indique qu’une erreur est survenue. Dans ce cas, sierrno
est mis àEINTR
, cela indique qu’un signal a été levé et qu’il faut réeffectuer une lecture, sinon une erreur sévère est survenue et il faut, par conséquent, stopper la lecture du fichier.
Lecture complète d’un fichier
char buf[100];
while (1) {
ssize_t nr = read(fd, buf, sizeof(buf));
if (nr == 0) break; // --> all data have been read
if (nr == -1) {
if (errno == EINTR) continue; // --> continue reading
char estr[100] = {
[0] = 0,
};
strerror_r(errno, estr, sizeof(estr) - 1);
fprintf(stderr, "ERROR: %s\n", estr);
break; // --> error: stop reading
}
/* process read data */
}
Écriture d’un fichier
L’écriture de données dans un fichier s’effectue à l’aide de l’appel système
write()
#include <unistd.h>
ssize_t write (int fd, const void* buf, size_t len);
Exemple
const char* buf = "data to be written";
size_t len = strlen(buf);
ssize_t count = write (fd, buf, len);
if (count == -1)
/* error, check errno */
else if (count != len)
/* severe error, but errno not set */
Comportement
- La fonction
write()
permet de stocker dans le fichier leslen
octets contenus dansbuf
. En cas d’erreur, la fonction retourne-1
. La position du fichier est avancée du nombre d’octets écrits, permettant ainsi d’ajouter les octets supplémentaires. - Si la valeur retournée
count
est différente delen
, cela signifie qu’une erreur sévère est survenue.
Synchronisation des données avec le disque
La synchronisation des données d’un fichier ordinaire contenues dans la
mémoire cache s’effectue de façon asynchrone. Linux propose plusieurs
services pour forcer cette synchronisation. Cette dernière peut s’effectuer
avec l’appel système fsync()
.
#include <unistd.h>
int fsync (int fd);
Exemple
int ret = fsync(fd);
if (ret == -1)
/* error, check errno */
Comportement
- La fonction
fsync()
demande au noyau de copier toutes les données restantes (contenue du fichier et métadonnées associées au fichier) sur le disque.
Positionnement dans un fichier
Usuellement, l’accès à un fichier s’effectue de manière linéaire. Cependant,
certaines applications requièrent de pouvoir se déplacer librement dans le
fichier pour accéder aux données souhaitées. Ce positionnement s’effectue
avec l’appel système lseek()
#include <unistd.h>
off_t lseek (int fd, off_t offset, int whence);
Exemple
off_t ret = lseek (fd, 1150, SEEK_SET);
if (ret == -1)
/* error, check errno */
Comportement
- La fonction
lseek()
déplace deoffset
octets la position du pointeur d’accès aux données dans le fichier. Ce déplacement s’effectue en relation avec l’originewhence
(début, position courante ou fin). La nouvelle position est retournée. En case d’erreur, on obtient-1
.
Arguments
- L’argument
offset
spécifie le décalage du pointeur par rapport à une origine. Ce décalage peut être positif ou négatif. - L’argument
whence
spécifie l’origine du pointeur pour le calcul de la nouvelle position. Celui-ci peut prendre les valeurs suivantes:SEEK_SET
: le pointeur d’origine est placé au début du fichierSEEK_CUR
: le pointeur d’origine est placé à la position courante/actuelleSEEK_END
: le pointeur d’origine est placé à la fin du fichier
Exemples
- pour obtenir la position courante
off_t pos = lseek(fd, 0, SEEK_CUR);
- pour placer le pointeur au début du fichier
off_t pos = lseek(fd, 0, SEEK_SET);
- pour placer le pointeur à la fin du fichier
off_t pos = lseek(fd, 0, SEEK_END);
Troncation d’un fichier
La troncation d’un fichier est obtenue à l’aide de l’appel système
ftruncate()
#include <sys/types.h>
#include <unistd.h>
int ftruncate (int fd, off_t length);
Exemple
int ret = ftruncate (fd, 1150);
if (ret == -1)
/* error, check errno */
Comportement
- La fonction
ftruncate()
tronque le fichier àlength
octets. - Si le fichier était plus long, les données supplémentaires sont perdues.
- Si le fichier était plus court, il est étendu, et la portion supplémentaire est remplie d’octets nuls.
Lecture/écriture et positionnement combinés
Linux offre des services de lecture et d’écriture avec positionnement, en
combinant les fonctions read
et write
avec lseek
. Ces opérations
s’effectuent avec les appels système pread
et pwrite
#define _XOPEN_SOURCE 500
#include <unistd.h>
ssize_t pwrite (int fd, const void* buf, size_t count, off_t pos);
ssize_t pread (int fd, void* buf, size_t count, off_t pos);
Exemple
ssize_t count = pwrite (fd, buf, len, 0);
if (count == -1)
/* error, check errno */
ssize_t nr = pread (fd, buf, len, 0);
if (nr == -1)
/* error, check errno */
- La première fonction
pwrite()
permet de stocker dans au début du fichier (à la position0
) leslen
octets contenus dansbuf
. - La deuxième fonction
pread()
permet de lirelen
premiers octets du fichier (de la position0
) et de les stocker dansbuf
.
Lecture des métadonnées (statut) d’un fichier
Linux offre divers services pour obtenir les métadonnées d’un fichier. Ces
opérations peuvent s’effectuer avec les appels système stat
, fstat
et lstat
#include <unistd.h>
int stat (const char* path, struct stat* buf);
int fstat (int fd, struct stat* buf);
int lstat (const char* path, struct stat* buf);
Exemple
struct stat status;
int ret = fstat (fd, &status);
if (ret == -1)
/* error, check errno */
Comportement
- L’appel de la fonction
fstat
retourne les informations liées au fichier dans la structurestat
. En cas d’erreur, la fonction retourne-1
. - La fonction
stat
retourne les mêmes informations que la fonctionfstat
, mais utilise le nom du fichier et non pas le descripteur. - Si le fichier est un lien symbolique, la fonction
lstat
retourne les métadonnées du fichier symbolique et non pas celles du fichier cible
Arguments
La structure struct stat
fournit les informations suivantes:
struct stat {
dev_t st_dev; // ID of device containing file
ino_t st_ino; // inode number
mode_t st_mode; // protection
nlink_t st_nlink; // number of hard links
uid_t st_uid; // user ID of owner
gid_t st_gid; // group ID of owner
dev_t st_rdev; // device ID (if special file)
off_t st_size; // total size, in bytes
blksize_t st_blksize; // blocksize for file system I/O
blkcnt_t st_blocks; // number of 512B blocks allocated
time_t st_atime; // time of last access
time_t st_mtime; // time of last modification
time_t st_ctime; // time of last status change
};
Commande Linux
Il est possible d’obtenir ces informations en utilisant la commande
stat <filename>
Fermeture d’un fichier ordinaire
La fermeture d’un fichier ordinaire s’effectue à l’aide de l’appel système
close()
int close (int fd);
Exemple
int ret = close (fd);
if (ret == -1)
/* error, check errno */
Comportement
- La fonction
close()
permet de fermer le fichier et de libérer les ressources liées à son ouverture et de le dissocier du processus. - Il est important de noter que la fermeture du fichier ne garantit pas que toutes les données contenues dans la mémoire cache du noyau seront copiées sur le disque. Ce processus est totalement asynchrone sous Linux.
Standard I/O : Fichiers ordinaires
Hormis l’accès direct aux fonctions du noyau Linux, C/C++ proposent d’autres
services pour le traitement des fichiers. Ces services sont fournis par les
bibliothèques standard (C: stdio.h
/ C++: cstdio
ou iostream
).
Ces bibliothèques implémentent des caches supplémentaires au niveau des processus dans l’espace utilisateurs (user space), évitant ainsi de nombreux appels système (moins de changements de contexte entre espaces utilisateur (processus) et le noyau Linux).
Standard I/O : Fonctions principales
La bibliothèque standard C <stdio.h>
propose toute une série de méthodes
pour l’accès aux fichiers au format ASCII ou binaire.
/* functions to open, close, rename and remove files */
FILE* fopen (const char* path, const char* mode);
int fclose (FILE* stream);
int rename (const char* oldpath, const char* newpath);
int remove (const char* path);
/* functions to read/write binary files */
size_t fread (void* buf, size_t size, size_t nr, FILE* stream);
size_t fwrite (void* buf, size_t size, size_t nr, FILE* stream);
/* functions to read/write text files */
int fgetc (FILE* stream);
int ungetc(int c, FILE* stream);
char* fgets (char* str, int size, FILE* stream);
int fputc (int c, FILE* stream);
int fputs (const char* str, FILE* stream);
/* auxiliary functions */
int fseek (FILE* stream, long offset, int whence);
int fflush (FILE* stream);
int ferror (FILE* stream);
int feof (FILE* stream);
Utilisation
L’utilisation des bibliothèques Standard I/O est recommandée lors de manipulation de fichiers avec de petites quantités de données à la fois (ASCII ou binaire) ou accès aux données orienté caractère ou ligne.
Elles offrent:
- Diminution des appels système
- Amélioration des performances (accès aux entrées/sorties par blocs)