7.
ENTREES SORTIES DE BAS NIVEAU
7.1 CYCLE DE VIE D'UN FICHIER
Définitions
Rappelons les définitions et les opérations de base sur les fichiers.
• Le fichier doit être créé, préalablement à toute opération. C'est l'opération de
création.
• Lors de son utilisation, il est nécessaire d'initialiser certaines données internes.
C'est l'opération d'ouverture.
• Il est ensuite possible de lire ou d'écrire des données. On définit ainsi les
opérations de lecture et d'écriture.
• Après utilisation, le fichier doit être refermé. Il pourra bien évidemment être
réutilisé. C'est l'opération de fermeture.
• La suppression du fichier réalise l'opération de destruction.
Appels systèmes de gestion des fichiers
Les appels système associés aux opérations décrites précédemment sont :
creat création du fichier et gestion de ses droits d'accès
open ouverture du fichier (lecture, écriture, mis à jour)
write écriture
read lecture
lseek positionnement du pointeur
close fermeture d'un fichier
link création d'un lien (nom synonyme)
unlink suppression d'un lien
chmod modification des droits d'accès
stat informations sur l'état d'un fichier
access contrôle des droits d'accès.
Les appels système creat et open utilisent le nom externe du fichier (chemin d'accès ou
pathname). Tous les autres utilisent son nom interne (descripteur logique du fichier).
La destruction d'un fichier n'est effective que lorsque tous les chemins d'accès le
désignant dans le système de fichiers sont détruits.
Le cycle de vie d'un fichier et les appels système correspondants sont résumés dans le
schéma suivant :
Cycle de vie d'un fichier
7.2 OPERATIONS DE BAS NIVEAU SUR LES FICHIERS
7.2.1 Opérations de bas niveau et fonctions de la bibliothèque C
Les appels système
Les appels systèmes de gestion des fichiers sont des primitives d'entrées/sorties de bas
niveau qui permettent de réaliser les opérations de base sur les fichiers.
La bibliothèque C
Les fonctions de la bibliothèque C sont des interfaces de haut niveau des appels
systèmes. Elles permettent de réaliser des opérations d'entrée/sortie beaucoup plus
complexes.
7.2.2 Attributs des fichiers
Dans l'environnement UNIX, les attributs des fichiers sont les suivants :
• un propriétaire (le créateur en général) dont l'identité est modifiable par la
commande SHELL chown. Il faut évidemment être le propriétaire ou l'administrateur
pour pouvoir exécuter cette commande,
• un groupe (c'est-à-dire un ensemble d'utilisateurs) modifiable par la commande
SHELL chgrp,
• des droits d'accès.
Ces droits d'accès sont constitués de trois champs : un pour le propriétaire, un pour le
groupe et un pour le reste des utilisateurs dont chacun précise les accès autorisés en
lecture, en écriture ou en exécution. Un fichier avec les droits rwxr-x--x peut être lu, écrit
et exécuté par le propriétaire, lu et exécuté par le groupe et seulement exécuté par les
autres.
Neuf drapeaux binaires permettent de définir les protections, les droits d'accès, le type
d'utilisation des fichiers.
Si le mode est autorisé, le drapeau est positionné à 1, 0 sinon. Ainsi, le tribits rw- s'écrit
110 en système binaire, 6 en système décimal.
Par exemple, les droits rwxrwxrwx s'écrivent également 777, les droits
r--rw---x s'écrivent 461.
7.2.3 Test des droits d'accès
Synopsis
#include <unistd.h>
int access (char *chemin, int type_access);
Description
Le paramètre est un disjonction logique (opérateur sur les bits |) des constantes :
R_OK accessible en lecture,
W_OK accessible en écriture,
X_OK accessible en exécution,
F_OK test d'existence du fichier.
7.3 MODIFICATION DES ATTRIBUTS
7.3.1 Modification des droits d'accès
Synopsis
#include <sys/stat.h>
int chmod(const char* ref, mode_t mode_acces);
int fchmod(int fd, mode_t mode_acces);
Description
Le fichier référencé, par son descripteur ou par son nom reçoit les attributs définis par la
variable mode_acces. Le propriétaire effectif du fichier doit être également le
propriétaire du fichier ou le super utilisateur.
Exemple et exercice
Ecrire un programme qui mette en évidence la différence des droits d'accès entre le
propriétaire réel et le propriétaire effectif et qui modifie le propriétaire et le groupe d'un
fichier. On affichera également les droits d'accès du fichier en lecture et en écriture,
après un contrôle de son existence.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
char *chemin="essai";
void acces_autorise(void)
{if(access(chemin,W_OK))
printf("accès en écriture sur %s interdit au propriétaire réel\n", chemin);
else
printf("accès en écriture sur %s autorisé au propriétaire réel\n",chemin);
int main(void)
{mode_t mode;
if(access(chemin,F_OK)) {printf("fichier %s inexistant\n",chemin); exit(0);}
system("ls -l essai");
acces_autorise();
if(chown(chemin,200,100)==-1) perror("chown");
acces_autorise();
system("ls -l essai");
// résultat
-rwxr-xr-x 1 root other 21100 Mai 03 14:03 essai
-rwxr-xr-x 1 user1 dba 21100 Mai 03 14:03 essai
7.3.2 Modification du propriétaire
Synopsis
#include <sys/stat.h>
int chown(const char* ref, uid_t uid, gid_t gid);
int fchown(int fd, uid_t uid, gid_t gid);
Description
• Modification du propriétaire ou du groupe du fichier.
• Si l'un des bit suid ou guid était positionné, il est supprimé.
• Sur certaines implémentations (BSD), seul l'administrateur peut utiliser cet appel
système.
7.4 CREATION D'UN INODE ASSOCIE A UN FICHIER SPECIAL
L'appel système à utiliser est variable selon le type du fichier créé.
Fichier ordinaire Fichier spécial Tube Lien symbolique
open mknod pipe symlink
creat mkfifo
Synopsis
#include <sys/stat.h>
int mknod(const char *chemin, mode_t droits, dev_t majeur/mineur);
7.5 OPERATIONS SUR LES FICHIERS
7.5.1 Descripteur de fichier
Lors de la création ou de l'ouverture d'un fichier est associé un nombre entier appelé
descripteur de fichier. C'est l'équivalent de l'étiquette logique d'un fichier en FORTRAN
puisque l'utilisateur accède ensuite au fichier par utilisation de son descripteur dans les
appels systèmes.
7.5.2 Ouverture
L'opération d'ouverture retourne le descripteur du fichier après contrôle des droits
d'accès.
Synopsis
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(char *chemin , int mode_ouverture,... mode_t droits);
Description
chemin est le nom externe du fichier, de type quelconque. Pour les répertoire, seule
l'ouverture en lecture est possible et il est recommandé d'utiliser l'appel opendir.
Un appel open peut être bloquant dans les situations suivantes :
• ouverture d'un tube nommé en écriture en l'absence de lecteur ou en lecture en
absence de rédacteur,
• ouverture d'un terminal non prêt.
L'argument mode_ouverture permet de réaliser des opérations d'E/S particulières lors
de l'ouverture et/ou de paramétrer le comportement des opérations de lecture/écriture
ultérieures. Il est construit par disjonction bit à bit de constantes macro définies dans le
fichier <fcntl.h>.
• Cette disjonction comporte
exactement une des trois constantes
O_RDONLY si le fichier chargé est accessible en lecture seulement,
O_WRONLY si le fichier chargé est accessible en écriture seulement,
O_RDWR si le fichier chargé est accessible en écriture et en lecture.
des constantes dont la fonction est de permettre l'exécution d'une opération
particulière ou de modifier le comportement de l'opération. Les constantes essentielles
retenues par la normes POSIX sont :
O_TRUNC si le fichier existe, sa taille devient nulle.
O_CREAT création d'un fichier ordinaire d'une taille vide s'il n'existe pas. Un
paramètre supplémentaire est alors nécessaire pour définir ses droits ultérieurs.
O_EXCL si le drapeau O_CREAT est positionné et que le fichier existe, retourne une
erreur (-1)
O_NOCTTY le terminal courant ne sera pas le terminal de contrôle du processus,
O_APPEND écriture en mode ajout à la fin du fichier,
O_NONBLOCK modifie l'opération d'ouverture bloquante en renvoyant un message
d'erreur avec la variable errno contenant la constante EAGAIN sauf dans le cas d'un
tube nommé.
Sous SYSTEME V, deux drapeaux complémentaires sont définis :
O_SYNC les opérations d'écriture en mode bloc sont synchrones (vidage du cache),
O_NDELAY les opérations normalement bloquantes ne le sont plus et renvoient un
message d'erreur.
7.5.3 Création : appel creat
La création d'un fichier peut être réalisée avec l'appel système creat.
Synopsis
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char *chemin, mode_t droits_acces);
Description
chemin nom du fichier,
droits_acces droits d'accès du fichier.
7.5.4 Lecture et écriture
Les opérations de lecture et d'écriture sont réalisées, après l'ouverture du fichier, par les
appels système read et write.
Synopsis
size_t read(int fd, void* buf, size_t nbyte);
size_t write(int fd, void* buf, size_t nbyte);
avec
fd le descripteur de fichier,
buf l'adresse du premier octet en mémoire de la zone où sera exécutée
l'opération de lecture ou d'écriture,
nbyte le nombre de bytes à transférer.
Exemple : les variables entières nlus et necrits représentent le nombre d'octets
effectivement lus ou écrits lors de l'exécution des appels système read et write.
Cas particuliers
nlus = necrits = 0 fin du fichier rencontrée.
nlus = necrits = -1 erreur en lecture ou en écriture.
Algorithme de lecture
Début
retour -1 si erreur de paramètre d'appel
s'il n'existe pas de verrou exclusif impératif en lecture
alors
si la fin du fichier n'est pas atteinte, alors lecture depuis la position courante de
nbyte caractères au plus (fin de fichier éventuellement atteinte) et retour du nombre
d'octets effectivement lus sinon retour 0
sinon
si le mode de lecture est bloquant (drapeaux O_NOBLOCK et O_NDELAY non
positionnés), alors blocage du processus jusqu'à la suppression du verrou
sinon aucun caractère n'est lu et errno vaut EAGAIN
Fin
Algorithme d'écriture
retour -1 si erreur de paramètre d'appel
s'il n'existe pas de verrou, exclusif, impératif ou partagé en écriture
alors
écriture (asynchrone par défaut, synchrone avec le drapeau O_SYNC) depuis la
position courante ou depuis la fin du fichier (drapeau O_APPEND) de nbyte caractères
au plus et retour du nombre d'octets effectivement écrits.
sinon
si aucun des drapeaux O_NOBLOCK et O_NDELAY n'est positionné, le processus
est bloqué jusqu'à la suppression du verrou
sinon aucun caractère n'est écrit et errno vaut EAGAIN
Fin
Exemple 1
#define BUFSIZE 512
#include <stdio.h>
int main(void)
{/* écriture sur la sortie standard du caractère saisi sur l'entrée standard */
char buf[BUFSIZE];
int n;
while (( n = read(0,buf,BUFSIZE)) >0)
write(1,buf,n);
On peut remarquer dans cet exemple que l'exécution préalable de l'appel système open
n'est pas obligatoire. Dans ce cas, il y a ouverture avec les paramètres par défaut.
Exemple 2
#define BUFSIZE 1024
#define PMODE 644
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main (int argc, char *argv[])
/* écriture simplifiée de la commande UNIX de copie d'un fichier cp */
{int f1,f2,n;
char buf[BUFSIZE];
/* prototype */
void erreur( char *, char *);
int open(char *, int, mode_t);
if(argc != 3) erreur ("Usage : cp depuis vers", (char *) NULL);
if((f1 = open(argv[1], O_RDONLY, 444)) == -1)
erreur("cp : ouverture impossible %s",argv[1]);
if((f2 = creat(argv[2],PMODE)) == -1) erreur("cp : création impossible %s",argv[2]);
while ((n = read(f1, buf, BUFSIZE)) > 0)
{if (n==-1) exit(-1);
if (write(f2, buf, n) != n) erreur("cp : erreur en écriture", (char *) NULL);
exit(1);
return(1);
void erreur(char *s1, char *s2)
{fprintf(stderr,s1,s2);
printf("\n");
exit(-1);
Exemple 3
/* Utilisation des appels système read, write pour écrire une fonction getchar bufférisee
*/
#define BUFSIZE 512
#define CMASK 0377
struct _iobuf
{char *_ptr; /* pointeur sur l'octet courant */
int _rcnt; /* nombre d'octets en lecture dans le tampon */
int _wcnt; /* nombre d'octets en écriture dans le tampon */
char *_base; /* adresse de base du début du tampon */
char _flag; /* mode d'ouverture du fichier */
char _file; /* numéro du fichier */
char _cbuff; /* tampon d'un caractère */
char _pad; /* tampon pour le nombre pair de bytes */
#define putc(c,p) \
(--(p)->_wcnt>=0?((int)(*(p)->_ptr++=(c))) : _flsbf((c),p))
#define putchar(c) putc(c,stdout)
int main(void)
{int c;
while ((c = getchar()) != EOF) putchar(c);
exit(0);
getchar()
{static char buf [BUFSIZE ];
static char *bufp = buf;
static int n = 0;
if (n == 0)
{n = read(0,buf,BUFSIZE);
bufp = buf;
return((--n >=0) ? *bufp++ & CMASK : EOF);
7.5.5 Fermeture
L'appel système close ferme les fichiers ouverts.
Synopsis
#include <unistd.h>
int close(int fd);
Exemple
L'appel système close supprime la connexion entre un fichier ouvert et son descripteur
assurant ainsi la fermeture des fichiers. Bien entendu, le fichier sera réutilisable par la
suite.
7.6 DEPLACEMENT DANS UN FICHIER
Les fichiers ordinaires sont vus comme des fichiers séquentiels par le système
d'exploitation UNIX avec un pointeur sur l'octet courant. Toutefois, l'accès aléatoire aux
différents octets est possible avec l'appel système lseek.
Synopsis
off_t int lseek(int fd, off_t offset , int origine);
Exemple
La variable offset modifie le pointeur sur l'octet courant du fichier dont le descripteur
est fd; la nouvelle position (en octets) est calculée de la façon suivante :
si origine = 0, le déplacement est absolu et croissant par rapport l'origine du
fichier,
si origine = 1, le déplacement est relatif et croissant par rapport la position
courante,
si origine = 2, le déplacement est absolu et croissant par rapport la fin du fichier
(mode ajout).
La norme POSIX a remplacé les valeurs 0, 1, 2 par les constantes SEEK_SET, SEEK_CUR,
SEEK_END.
Exemple : lseek(fd,0L,2);
7.7 APPELS DE COMMANDES DE L'INTERPRETE
On peut, depuis un programme écrit en C, appeler des commandes de l'interprète par
utilisation de l'appel système system.
Synopsis
#include <stdlib.h>
int system(const char *programme);
Exemple : le programme en cours est suspendu. La commande spécifiée par la chaîne
de caractères *programme est exécutée si elle existe (code de retour différent de zéro).
Dans le cas contraire, un code de retour nul est retourné.
Exemples
a) Voici un programme qui permet l'affichage de la date dans l'environnement MS/DOS.
#include <stdio.h>
int main(void)
{system("date");
exit(0);
b) ce programme permet d'exécuter plusieurs commandes (sans argument).
#include <stdio.h>
int main(argc,argv)
int argc;
char *argv[];
{int i;
for(i= 0; i <argc; i++ ) system(*++argv);
exit(0);
// résultat
utilisation : multi date time