les entrées-sorties

les entrées-sorties introduction il y a 2 types d'entrée-sortie: les opérations bas niveau. un fichier y est repéré par un entier positif de 0 à (getdtablesize(2) ou mieux) getrlimit(rlimit_nofile). exemple: on demande à lire n octets. les opérations haut niveau, d'interface plus simple. mais passant par un buffer intermédiaire! un fichier y est repéré par un file *, structure dont l'intérieur importe peu. exemple: on demande à lire une ligne. librairies bas niveau le schéma de principe est: un processus a une table de fichiers ouverts qui pointe vers une table des fichiers ouverts qui pointe vers la table des v-nodes, interface avec le disque physique. plusieurs processus peuvent donc référencer le même fichier; et même un processus peut référencer le même fichier plusieurs fois avec des positions différentes. on peut aussi sous plusieurs descripteurs de fichiers référencer le même fichier avec la même position (2 fd -> 1 entrée de la table des fichiers ouverts). un descripteur de fichier est simplement un entier positif. appel : open(2) c'est l'appel de base pour ouvrir un fichier: #include <fcntl.h> int open(const char *path, int oflag, mode_t mode); les principaux flags sont: o_rdonly, o_wronly, o_rdwr : mode d'ouverture o_ndelay : a un sens pour les fifos et les lignes asynchrones o_noctty : empêche de devenir terminal de contrôle du processus o_sync : les écritures sont forcées sur disque o_append : ouvrir en mode append o_creat : si le fichier n'existe pas, le créer: son propriétaire sera l'effective-uid du processus son groupe sera le groupe du répertoire (bsd et répertoire en set-gid) ou l'effective-gid du processus ses droits seront mode and complement(umask) o_trunc : tronquer le fichier s'il existe déjà o_excl : si l'on a demandé la création et que le fichier existait, alors erreur. les modes usuels sont donc: open("file",o_rdonly); open("file",o_wronly | o_creat | o_trunc, 0666); open("file",o_wronly | o_creat | o_append, 0666); appel : creat(2) n'a plus de sens réel car c'est un déguisement de: open(path, o_wronly | o_creat | o_trunc, mode); appels: access(2) permet de tester simplement si on aura le droit de lire, d'écrire, d'exécuter: int access(char *path, r_ok); int access(char *path, w_ok); int access(char *path, x_ok); appel : lseek(2) pour se positionner dans le fichier: off_t lseek(int fd, off_t offset, int whence) avec comme troisième champ : seek_set, seek_cur ou seek_end. l'appel perl est toujours seek. appel : read(2) et write(2) lire et écrire: int read(int fd, char *buf, int nbyte); int write(int fd, char *buf, int nbyte); le résultat de ces fonctions est le nombre d'octets lus ou écrits, -1 en cas d'erreur. appel : dup(2) et dup2(2) int dup(int fd); int dup2(int fd1, int fd2); duplique une entrée dans la process control block. i.e. on obtient 2 pointeurs vers la même position dans le même fichier: dup prend le premier descripteur libre dup2 ferme fd2 s'il est ouvert et le prend application : on ouvre un fichier, on le réouvre sous un numéro déterminé pour le donner à un fils (cas des shells). appel : fcntl(2) permet des opérations spéciales: dupliquer avec une contrainte sur la borne inférieure faut-il ou non fermer le fichier lors d'un exec(2)? positionner ou vérifier les flags d'ouverture du fichier (fndelay, fappend, fasync). gérer les verrous gérer le process-group appel : ioctl(2) permet de gérer tout ce qui peut dépendre du type de périphérique associé. exemple: rembobiner une bande, définir le caractère type control-c. appel: mknod(2) pour créer les descripteurs de périphériques. il y a aussi la commande mknod(8). exemple: mknod /dev/null c 3 2 nb : c'est très lié à l'intérieur du noyau! librairies haut niveau un descripteur de fichier est ici une structure file *. extern struct _iobuf { int _cnt; unsigned char *_ptr; unsigned char *_base; int _bufsiz; short _flag; char _file; /* should be short */ } _iob[]; #define file struct _iobuf i.e. on accède aux données du fichier via un buffer intermédiaire. appel fopen(3) #include <stdio.h> file *fopen(char *filename, char *type); le type est r, w, a, r+, w+ ou a+. cette fonction rend un pointeur null en cas d'erreur. appel fclose(3) vider les buffers, fermer le fichier: int fclose(file *f); appels fseek(3), ftell(3) int fseek(file *f,long offset,ptrname /* 0, 1 ou 2 */); int ftell(file *f); ftell est nécessaire car fseek rend -1 ou 0... appels [f]getc(3) et [f]putc(3) lire et écrire par caractère: int getc(file *f); # may be a macro int fgetc(file *f); # function int putc(char c, file *f); int fputc(char c, file *f); appels fgets(3) et fputs(3) char *fgets(char *s, int n, file *f); char *fputs(char *s, file *f); lire une ligne (ou le nombre de caractères), écrire une ligne. nb : il existe aussi gets(char *line), implicitement sur l'entrée standard (stdin), mais qui ne teste pas les débordements (donc très déconseillé!). nb : il existe aussi puts, implicitement sur la sortie standard. mais puts ajoute un passage à la ligne donc puts(line) et puts(line,stdout) ne sont pas synonymes! appels fprintf(3), fscanf(3) similaire à printf() et scanf() avec en premier argument le fichier: fprintf(stderr,"error message %d\n",errno); /* very bad example: use perror() */ appel fflush(3) cet appel int fflush(file *f); force l'écriture de ce qui peut être dans les buffers file *. appels setbuf(3), setbuffer(3) ces fonctions permettent de contrôler la bufferisation ou non, la taille des buffers. nb : par défaut sur stdout, les sorties sont bufferisés; et elles ne le sont pas sur stderr. correspondance haut et bas niveau int fileno(file *f); file *fdopen(int fd,char *type); assurent la correspondance entre haut et bas niveau. lecture des répertoires un répertoire est lu à travers des primitives spéciales: /* system v */ #include <dirent.h> dir *opendir(char *dirname); struct dirent *readdir(dir *dirp); # d_namlen && strncmp(name,directp->d_name,strlen(name)) == 0) { /* found */ } en perl, c'est très simple: #!/usr/local/bin/perl dir = opendir("/my/directory"); while ($file = readdir(dir)) { ... } # or @files = readdir(dir); closedir(dir); cas particulier des terminaux les caractères passent du programme (shell ou utilisateur) vers le couple clavier/écran via une gestion de terminal et un driver. le gestionnaire de ligne a les rôles suivants: echo des caractères tapés assemblage en lignes, avec gestion des effacements (control-h, control-w, control-u, control-r), du flot (control-s, control-q) gestion des signaux (control-c, control-z, etc.) fin de fichier (control-d) conversion de caractères : tabulation, retour-chariot; gestion de l'affichage (délai après certains caractères) gestion physique: parité, nombre de stop, raccrochage, etc. tous ces paramètres sont contrôlés par la commande stty: en berkeley stty all > /dev/ttyp0 en system v stty -a < /dev/ttyp0 programmation berkeley la structure de référence est: struct sgttyb mode; /* char sg_ispeed; char sg_ospeed; char sg_erase; char sg_kill; short sg_flags; */ if (ioctl(fd,tiocgetp,&mode) < 0) { /* erreur */ } mode.sg_flags |= raw; mode.sg_flags &= ~(echo | crmod | tandem | anyp); if (ioctl(fd,tiocsetn,&mode) < 0) { /* erreur */ } programmation system v la structure de référence est ici: struct termios param; /* tcflag_t c_iflag; /* input modes */ tcflag_t c_oflag; /* output modes */ tcflag_t c_cflag; /* control modes */ tcflag_t c_lflag; /* local modes */ cc_t c_cc[nccs]; /* control chars */ */ if (ioctl(fd,tiocsetn,¶m) == -1) { /* erreur */ } param.c_iflag &= ~icrnl; param.c_iflag &= ~inlcr; param.c_lflag &= ~echo; param.c_lflag &= ~icanon; param.c_lflag &= ~isig; param.c_lflag &= ~iexten; param.c_cc[vmin] = 1; param.c_cc[vtime] = 0; if (ioctl(fd,tcsanow,¶m) == -1) { /* erreur */ } les paramètres de temps et de minimum de caractères font référence aux rafales de caractères, typiquement utilisés par les éditeurs de texte pour reconnaître les flèches et autres clés de fonctions. cf infra. les gestions d'écran en berkeley, les terminaux sont décrits par un grand fichier texte /etc/termcap. en system v, il y a un fichier /usr/lib/terminfo/x/xyz par terminal. il existe des conversions entre les 2 formats, mais la sémantique sous-jacente diffère par endroits. essentiellement, on trouve dans ces descriptions: taille de l'écran séquence à envoyer pour effacer l'écran, la fin de ligne, insérer ou détruire une ligne ou des lignes, passer en écran alterné, initialiser le terminal, etc. séquence de caractères émises par les flèches et autres clés de fonction, etc. l'accès berkeley est via la librairie -ltermcap: char term[256], bp[1024], filled[500], *area = filled; strcpy(term,(char *)getenv("term")); if (tgetent(bp,term) != 1) { /* erreur */ } co = tgetnum("co"); up = tgetstr("ku",&area); en system v, il faut y préférer curs_terminfo(3). cas particulier: taille de l'écran en berkeley, elle est gérée au niveau du terminal directement, à l'aide du signal sigwinch: stty rows 34 cols 80 struct winsize winsize; if (ioctl(1, tiocgwinsz, &winsize) == 0 && winsize.ws_col > 0 && winsize.ws_row > 0) { en system v, ce sont les variables d'environnement lines et columns qui doivent être mises à jour en cas de changement de taille.

les entrées-sorties  Précédent 666  Précédent 665  Précédent 664  Précédent 663  Précédent 662  Précédent 661  Précédent 660  Précédent 659  Précédent 658  Précédent 657  Précédent 656  Précédent 655  Précédent 654  Précédent 653  Précédent 652  Précédent 651  Précédent 650  Précédent 649  Précédent 648  Précédent 647  Précédent 646  Précédent 645  Précédent 644  Précédent 643  Précédent 642  Précédent 641  Précédent 640  Précédent 639  Précédent 638  Précédent 637  Suivant 668  Suivant 669  Suivant 670  Suivant 671  Suivant 672  Suivant 673  Suivant 674  Suivant 675  Suivant 676  Suivant 677  Suivant 678  Suivant 679  Suivant 680  Suivant 681  Suivant 682  Suivant 683  Suivant 684  Suivant 685  Suivant 686  Suivant 687  Suivant 688  Suivant 689  Suivant 690  Suivant 691  Suivant 692  Suivant 693  Suivant 694  Suivant 695  Suivant 696  Suivant 697