Linux/Unix : Les entrées / sorties

27 octobre 2013 rdorigny 0 commentaires

Dans le monde Unix, les entrées/sorties sont traitées sous forme de fichiers, on dit que tout est fichiers sous Unix.

La gestion des fichiers distingue les primitives (sorte de fonctions de bas niveau, comparable à des appels système) qui traitent des descripteurs et des fonctions de plus haut-niveau qui manipulent des flux.




1) Les flux standards

Chaque processus dispose d'une table de descripteur qui correspond aux fichiers qu'il utilise.
Par défaut:
  • 0 : stdin pour les flux d'entrée standard comme celui du clavier, ouvert en lecture seule, le processus peut recevoir des données par son intermédiaire,
  • 1 : stdout pour les flux de sortie standard comme celui du clavier, ouvert en écriture seule, pour l’affichage,
  • 2 : stderr pour les flux d'erreurs,
  • 3+ : les fichiers ouverts par le processus.

  • En C, on distingue deux grands types de commandes:
  • primitives : réalisent les opérations de bas niveau et permettent de travailler directement en zone système,
  • fonctions : sont des opérations de niveau haut, elles utilisent les primitives pour faire ce qu'il y a réaliser. Les fonctions sont réputées plus rapides, elles sont globalement dans la bibliothèque .
  • 2) Les opérations de base (primitives)

    2.1) Ouverture/fermeture d'un fichier

    Pour ouvrir un fichier, on dispose de la fonction open.
    #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *chemin,int typeOpen, mode t droits_Fic);


    La fonction retourne le descripteur de fichier avec pour référence le pointeur chemin, si erreur la fonction retourne -1 et errno. Le paramètre type Open définit le type d'ouverture désiré :
  • O_RDONLY : ouvert en lecture seule,
  • O_WRONLY : ouvert en seule,
  • O_RDWR : ouvert en lecture écriture,
  • O_APPEND : écriture en fin de fichier,
  • O_TRUNC : écrase le fichier précédent,
  • O_CREAT : création du fichier s'il n'existe pas.

  • Par exemple: open("/tmp/myfic",O_WRONLY|O_CREAT|O_TRUNC,0777);

    Pour fermer un fichier on utilise, la fonction close :
    #include <unistd.h> int close(int desc);


    A noter que l'appel système exit() clôture tous les fichiers ouvert par le processus.

    2.2) Lecture/Ecriture dans un fichier

    Les fonctions read() et write() sont utilisées pour lire ou écrire dans un fichier.
    #include <unistd.h> //buffer pointe sur la zone mémoire et nb précise le nombre d'octets à lire int read(int desc,void *buffer,unsigned nb); int write(int desc,void *buffer,unsigned nb);


    2.2) Déplacement dans un fichier

    #include <sys/types.h> #include <unistd.h> off_t lseek(int desc,off_t depl,int vers); //depl représente le déplacement qui peut-être négatif ou nul par rapport à une origine donnée // par le paramètre vers.


    Le fichier unistd.h contient trois constantes macro-définies qui sont les seules valeurs :
  • SEEK_SET : la position courante sera depl,
  • SEEK_CUR : déplacement depuis la position courante,
  • SEEK_END : déplacement depuis la fin du fichier.

  • La primitive retournera la position courante ou -1 en cas d'erreur.

    3) les entrées/sorties standards

    La bibliothèque stdio.h définie les entrées/sorties standard, c'est une couche au dessus des primitives POSIX précédentes.

    3.1)ouverture/fermeture d'un fichier

    #include <sys/types.h> #include <stdio.h> FILE *fopen(const char *ref,const char mode); int fclose(FILE *ptr);

    fopen provoque l'ouverture du fichier pointé par ref et renvoie un pointeur sur l'objet FILE associé au fichier. En cas, d'échec la fonction renvoie NULL.
    Le paramètre mode précise le mode d'ouverture du fichier. Les options principales sont:
  • r : lecture,
  • w : écriture,
  • a : écriture en fin de fichier ou création.
  • 3.2)Lecture dans un fichier

    #include <sys/types.h> #include <stdio.h> //Pour lire un caractère FILE fgetc(FILE *ptr); //Pour lire une chaîne de caractère d'au plus taille-1 char *fgets(char *buf, int taille, FILE *ptr); //fread() lit nombre objets de taille caractères à l'adresse buf, et renvoit le nombre d'octets lus int fread(void *buf,size_t taille, size_t nombre, FILE *ptr); //Pour une lecture formatée int fscanf(FILE *ptr, const char *format,...);

    3.3)Ecriture dans un fichier

    #include <stdio.h> //Pour écrire un caractère unsigned char int fputc(int c,FILE *ptr); //Pour écrire une chaîne de caractère d'au plus taille-1 char *fputs(char *buf, FILE *ptr); //fwrite() écrit nombre objets de taille caractères à l'adresse buf, et renvoit le nombre d'octets écrits int fread(void *buf,size_t taille, size_t nombre, FILE *ptr); //Ecriture formatée int fprintf(FILE *ptr, const char *format,...);

    Il existe d'autres fonctions comme fseek() pour se déplacer dans le fichier et feof() pour tester la fin du fichier.

    4)Les verrous

    4.1)Les verrous externes

    Il s'agit d'un fichier dont la présence provoque le verrouillage de la ressource à protéger. Pour le créer,on utilise la primitive open avec O_CREAT pour la création de fichiers et O_EXCL pour activer le mode exclusif.

    Si le fichier verrou n'existe pas, la primitive créera le fichier retournera son descripteur, et si le fichier existe la primitive retourne -1 avec errno=EEXIST.

    Pour retirer l'exclusion, on retire le fichier verrou par la primitive unlink().

    Second cas d'utilisation: Par le mécanisme du verrou, il est possible de limiter une commande à une seule exécution. Voici un exemple de code:
    #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/errno.h> extern int eerno; int main(void) { int desc; if ((desc=open("cad",O_CREAT|O_EXCL,0))==-1 && errno==EEXIST){ printf("Le process est déjà lancé!!"); exit(1); } else { close(desc); //Action du processus printf("Début attente 30s..."); sleep(30); printf("Fin du process!!"); unlink("cad"); exit(0); } }

    Seconde cas d'utilisation: Par le mécanisme du verrou, il est aussi possible de limiter l'accès d'un fichier à une seule instance. Voici un exemple de code:
    #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/errno.h> extern int errno; int main(void) { int desc; if ((desc=open("cad",O_CREAT|O_EXCL,0))==-1 && errno==EEXIST) printf("Impossible d'accéder au fichier, il est en section critique!!n"); else { printf("Début de section critique!!n"); //C'est ici que l'on doit agir... sleep(30); printf("Fin de section critique!!n"); unlink("cad"); } exit(0); }

    Ce mécanisme est fort utile, mais attention, il est coûteux en accès disque et CPU. En outre, il y a une possibilité d'interbloquage (deadlock).

    4.2)Les verrous externes

    Il s'agit d'un mécanisme de la norme POSIX. Le système gère une table des verrous dont chaque itération est au format de la structure flock, qui peut être consultée ou modifiée avec la primitive fcntl().

    La structure flock:
  • l_type: F_RDLCK partagée en lecture,F_WRLCK exclusif en lecture,F_UNLCK absence de verrou,
  • l_whence: 0:début de fichier, 1:position courante, 2:fin de fichier,
  • l_start: position d début whence à verrouiller,
  • l_len: longueur de la zone à verrouiller (si null, tout le fichier est verrouillé),
  • l_pid: PID du processus propriétaire.

  • Attention, par défaut les verrous internes sont justes consultatifs, ils n’empêchent pas réllement les accès! Certaines implémentations autorisent le verrouillage exclusif à l'aide du set-gid bit en complément du verrouillage du verrouillage interne.

    Voici un exemple de code:
    #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/errno.h> extern int errno; int main(void) { int fichier; struct flock verrou; fichier=open("tmp.txt",O_WRONLY|O_CREAT|O_TRUNC,0777); verrou.l_type=F_WRLCK; verrou.l_whence=SEEK_SET; verrou.l_start=0; verrou.l_len=0; printf("Je verouille...n"); fcntl(fichier,F_SETLKW,&verrou); printf("OK verouillé...n"); sleep(5); verrou.l_type=F_UNLCK; printf("Je déverouille...n"); fcntl(fichier,F_SETLKW,&verrou); sleep(5); exit(0); }

    Conclusion

    Nous avons vu les quelques fonctions d'entrées/sorties dédiées au C Système Unix/Linux. Mais aussi la notion de verrou qui sera complété dans le monde des threads par d'autres mécanismes plus performants.







    Pseudonyme (obligatoire) :
    Adresse mail (obligatoire) :
    Site web :

    Pour valider votre commentaire, écrivez le texte affiché sur l'image :



    © 2017 www.doritique.fr par Robert DORIGNY