Cours n°2 :
Appels systèmes
et entrée / sortie

Réseau et Prog. Bas Niveau
Victor Poupet

Système d'exploitation

Appels systèmes

Les appels systèmes ressemblent à des procédures (appels de fonction), avec toutefois des différences importantes :

Entrée / Sortie

int open(char *path, int flags[, int mode]);
int close(int fd);
int read(int fd, char *buf, int size);
int write(int fd, char *buf, int size);
off_t lseek(int fd, off_t offset, int whence);

Il existe 5 appels systèmes principaux pour manipuler des fichiers.

man 2 open

OPEN(2)                BSD System Calls Manual                OPEN(2)

NAME
  open -- open or create a file for reading or writing
SYNOPSIS
  #include <fcntl.h>
  int open(const char *path, int oflag, ...);
DESCRIPTION
  The file name specified by path is opened for reading and/or writing, as specified by the argument oflag; the file descriptor is returned to the calling process.

  The oflag argument may indicate that the file is to be created if it does not exist (by specifying the O_CREAT flag).  In this case, open requires a third argument mode_t mode; the file is created with mode mode as described in chmod(2) and modified by the process' umask value (see umask(2)).

Open

int open(char *path, int flags[, int mode]);

#include <fcntl.h>

main() {
  int fd;
  fd = open("test.txt", O_WRONLY | O_TRUNC);
  printf("%d\n", fd);
}

L'appel open sert à demander l'accès à un fichier

Open (flags)

O_RDONLY open for reading only
O_WRONLY open for writing only
O_RDWR open for reading and writing
O_NONBLOCK do not block on open or for data to become available
O_APPEND append on each write
O_CREAT create file if it does not exist
O_TRUNC truncate size to 0
O_EXCL error if O_CREAT and the file exists
...

Close

int close(int fd);

#include <fcntl.h>
#include <unistd.h>
main() {
  int fd1, fd2;
  fd1 = open("test.txt", O_RDONLY);
  if (fd1 < 0) {
    exit(1);
  }
  fd2 = open("test.txt", O_RDONLY);
  if (fd2 < 0) {
    exit(1);
  }
  if (close(fd1) < 0) {
    exit(1);
  }
}

L'appel close permet de libérer un descripteur de fichier

Read

int read(int fd, char *buf, int size);

int main() {
  char *b;
  int fd, sz;
  b = malloc(sizeof(char) * 11);
  fd = open("test.txt", O_RDONLY);
  if (fd < 0) {
    exit(1);
  }
  do {
    sz = read(fd, b, 10);
    b[sz] = '\0';
    printf("lu: %s\n", b);
  } while (sz == 10);
  close(fd);
}

L'appel read sert à lire des octets dans un fichier

Write

int write(int fd, char *buf, int size);

int main() {
  int fd, sz;
  char *txt;

  fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
  if (fd < 0) {
    exit(1);
  }

  txt = "Bonjour\n";
  sz = write(fd, txt, 8);

  close(fd);
}

L'appel write permet d'écrire des octets dans un fichier

Pointeur de fichier

int main() {
  char *c;
  int fd, sz;
  c = malloc(sizeof(char) * 10);
  fd = open("test.txt", O_RDWR | O_APPEND);
  if (fd < 0) {
    exit(1);
  }

  while(1) {
    sz = read(fd, c, 10);
    write(fd, c, sz);
    if (sz < 10) break;
  }
  close(fd);
}

Les fichiers ouverts sont tous associés à un pointeur de fichier qui indique un emplacement dans le fichier

Ouvertures multiples

int main() {
  int fd1, fd2;

  fd1 = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC);
  fd2 = open("test.txt", O_WRONLY);

  write(fd1, "un ", 3);
  write(fd2, "deux ", 5);
  write(fd1, "trois ", 6);
}

⟶ deutroisint main() {
  int fd1, fd2;

  fd1 = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC | O_APPEND);
  fd2 = open("test.txt", O_WRONLY | O_APPEND);

  write(fd1, "un ", 3);
  write(fd2, "deux ", 5);
  write(fd1, "trois ", 6);
}

⟶ un deux trois

On peut ouvrir plusieurs fois un même fichier (même en écriture)

stdin, stdout, stderr

main() {
  char c;
  while (read(0, &c, 1) == 1) {
    write(1, &c, 1);
  }
}

Les trois premiers descripteurs de fichiers sont automatiquement attribués à chaque processus :

On peut directement utiliser ces descripteurs de fichiers sans avoir à utiliser open.

lseek

int main() {
  char c[10];
  int fd, sz;
  fd = open("test.txt", O_RDWR);
  if (fd < 0) exit(1);

  int rp = 0;
  int wp = lseek(fd, 0, SEEK_END);
  do {
    lseek(fd, rp, SEEK_SET);
    sz = read(fd, c, 10);
    rp = lseek(fd, 0, SEEK_CUR);

    lseek(fd, wp, SEEK_SET);
    write(fd, c, sz);
    wp = lseek(fd, 0, SEEK_CUR);
  } while (sz == 10);
  close(fd);
}

lseek permet de déplacer manuellement le pointeur de fichier

Il est possible de déplacer le curseur au-delà de la fin du fichier

stdio.h

int main() {
  FILE *fpin, *fpout;
  int i=0;

  fpin = fopen("test.txt", "r");
  fpout = fopen("res.txt", "w");
  char s[80];

  while(fgets(s, 80, fpin)) {
    fprintf(fpout, "ligne %d: %s", i, s);
    i++;
  }

  fclose(fpin);
  fclose(fpout);
}

En général, on n'appelle pas directement les appels systèmes mais on utilise des fonctions de la librairie C qui les appellent indirectement :


Ces fonctions manipulent les fichiers par l'intermédiaire de pointeurs de fichiers (file pointers) de type FILE* (structure contenant un descripteur de fichier, et des informations supplémentaires)