Introduzione ai sistemi operativi con UNIX
Prec 3. Alcune system call di UNIX per l'I/O Succ

3.3 open

Infine la system call open(2). Il suo prototipo è:
int
     open(const char *path, int flags, ...);
path
percorso del file da aprire
flags
opzioni
...
argomento mode di tipo mode_t richiesto per l'opzione O_CREAT
In FreeBSD mode_t è un tipo intero senza segno a 16 bit (unsigned short). Il file header /usr/include/fcntl.h definisce tutte le costanti flag che si usano con open(2) ed è necessario includerlo per usare queste costanti:
#define O_RDONLY        0x0000          /* open for reading only */
#define O_WRONLY        0x0001          /* open for writing only */
#define O_RDWR          0x0002          /* open for reading and writing */
#define O_NONBLOCK      0x0004          /* do not block on open */
#define O_APPEND        0x0008          /* append on each write */
#define O_CREAT         0x0200          /* create if nonexistent */
#define O_TRUNC         0x0400          /* truncate to zero length */
#define O_EXCL          0x0800          /* error if create and file exists */
#define O_SHLOCK        0x0010          /* atomically obtain a shared lock */
#define O_EXLOCK        0x0020          /* atomically obtain an exclusive lock */
#define O_DIRECT        0x00010000      /* eliminate or reduce cache effects */
#define O_FSYNC         0x0080          /* synchronous writes */
#define O_NOFOLLOW      0x0100          /* do not follow symlinks */
Se tutto va bene, open(2) ritorna il descrittore del file, ossia un intero positivo o nullo (non negativo), altrimenti ritorna -1 e setta errno. Tra i vari errori possibili notiamo quello di codice 24 (EMFILE) "Too many open files". Il sistema limita il numero di descrittori di file che uno stesso processo può tenere aperti contemporaneamente. Questo numero rappresenta il numero di righe della tabella di descrittori del processo. Se scrivete un programma che per errore continua ad aprire file senza mai chiuderli raggiungerete facilmente questo limite, nonostante possa essere praticamente più che sufficiente per programmi corretti (sul mio sistema ad esempio il limite imposto dal kernel è 2743, come ritornato dalla system call getdtablesize(2), ma la costante NOFILE definita nella libreria C in <sys/param.h> definisce un valore molto inferiore, 64). Una buona pratica di codifica di programmi che devono girare in sistemi multiprogrammati è di liberare tutte le risorse che si utilizzano quanto prima possibile, ossia non appena non servono più. Per i file quindi chiudeteli non appena non servono più utilizzando la system call close(2). Quando un programma C termina tutti i file aperti vengono chiusi automaticamente dalla exit(3) implicita o esplicita.

In FreeBSD il massimo numero di file che un processo può tenere aperti è una variabile di stato del kernel che si legge e scrive da shell tramite il comando sysctl(8):

$ sysctl kern.maxfilesperproc
kern.maxfilesperproc: 2743
# sysctl kern.maxfilesperproc=1000
kern.maxfilesperproc: 2743 -> 1000
le descrizioni delle variabili si trovano in sysctl(3).

Il sistema operativo mantiene aggiornato per ogni file un puntatore che marca la posizione corrente, e la open lo pone inizialmente all'inizio del file.

I flag possono essere combinati con l'operatore di OR logico bit a bit, che in C si indica con |. Se attivato il flag O_CREAT indica che il file deve essere creato se non esiste. Se O_CREAT non è specificata e si tenta di aprire un file inesistente si verifica l'errore ENOENT. Ad esempio:

int d=open(path, O_CREAT | O_TRUNC | O_WRONLY, mode);
chiede di creare il file il cui nome si trova nella stringa path se non esiste (O_CREAT). Notare che se il file non esiste verrà creato coi permessi specificati dal terzo parametro. Esistono costanti simboliche per specificare i permessi e sono descritte in chmod(2). Parleremo meglio dei permessi di UNIX in seguito. Tenete anche presente che il valore di umask(2) viene invertito tramite NOT bit a bit e poi posto in AND bit a bit con la maschera di permessi specificata, per ottenere la maschera realmente utilizzata. Se invece il file esiste, si chiede di troncarlo a dimensione zero, in modo da riscriverlo completamente (O_TRUNC), senza che i suoi permessi correnti vengano variati. In entrambi i casi il file viene aperto in modalità di sola scrittura.

Un'altro uso tipico della open(2) è il seguente:

fd=open(name, O_RDONLY, 0);
oppure semplicemente:
fd=open(name, O_RDONLY);
l'effetto è di aprire in sola lettura un file esistente. Come detto prima si verifica errore 2 (ENOENT) "No such file or directory" se il file non esiste.

Continuando l'esempio del comando cat(1) ecco una estensione di mycat che accetta un qualsiasi numero di file da visualizzare come parametri sulla riga di comando, proprio come il vero cat(1). Da notare l'utilizzo di close(2) non appena abbiamo finito di leggere tutto il file. Non dimenticate di chiamare la close(2) in questi casi!

/* mycat.c :) */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

/* return status to pass to the  environment */
int status=EXIT_SUCCESS;

void cat(int d)
{ char buf[BUFSIZ];
  int n;

  while ((n=read(d, buf, BUFSIZ)) > 0)
    write(1, buf, n);

  if (n) status=EXIT_FAILURE;
}

int main(int argc, char *argv[])
{ int d;

  if (argc==1) /* no arguments, use stdin */
    cat(0);
  else
    while (--argc > 0)
      if (!strcmp(*++argv, "-"))
        cat(0);
      else if ((d=open(*argv, O_RDONLY))==-1)
      { perror(*argv);
        status=EXIT_FAILURE;
      }
      else
      { cat(d);
        close(d);
      }

  exit(status);
}

Proprio come cat(1), mycat se il nome del file non è specificato, oppure se questo è un trattino o meno (`-'), legge da stdin.


Prec Indice Succ
write Livello superiore Elenco delle principali system call