%% fileadv.tex
%%
-%% Copyright (C) 2000-2015 Simone Piccardi. Permission is granted to
+%% Copyright (C) 2000-2019 Simone Piccardi. Permission is granted to
%% copy, distribute and/or modify this document under the terms of the GNU Free
%% Documentation License, Version 1.1 or any later version published by the
%% Free Software Foundation; with the Invariant Sections being "Un preambolo",
%% license is included in the section entitled "GNU Free Documentation
%% License".
%%
-
\chapter{La gestione avanzata dei file}
\label{cha:file_advanced}
+
In questo capitolo affronteremo le tematiche relative alla gestione avanzata
dei file. Inizieremo con la trattazione delle problematiche del \textit{file
locking} e poi prenderemo in esame le varie funzionalità avanzate che
contemporaneamente sullo stesso file non è possibile determinare la sequenza
in cui essi opereranno.
-Questo causa la possibilità di una \itindex{race~condition} \textit{race
- condition}; in generale le situazioni più comuni sono due: l'interazione fra
-un processo che scrive e altri che leggono, in cui questi ultimi possono
-leggere informazioni scritte solo in maniera parziale o incompleta; o quella
-in cui diversi processi scrivono, mescolando in maniera imprevedibile il loro
-output sul file.
+Questo causa la possibilità di una \textit{race condition}; in generale le
+situazioni più comuni sono due: l'interazione fra un processo che scrive e
+altri che leggono, in cui questi ultimi possono leggere informazioni scritte
+solo in maniera parziale o incompleta; o quella in cui diversi processi
+scrivono, mescolando in maniera imprevedibile il loro output sul file.
In tutti questi casi il \textit{file locking} è la tecnica che permette di
-evitare le \itindex{race~condition} \textit{race condition}, attraverso una
-serie di funzioni che permettono di bloccare l'accesso al file da parte di
-altri processi, così da evitare le sovrapposizioni, e garantire la atomicità
-delle operazioni di lettura o scrittura.
+evitare le \textit{race condition}, attraverso una serie di funzioni che
+permettono di bloccare l'accesso al file da parte di altri processi, così da
+evitare le sovrapposizioni, e garantire la atomicità delle operazioni di
+lettura o scrittura.
\subsection{L'\textit{advisory locking}}
La prima modalità di \textit{file locking} che è stata implementata nei
sistemi unix-like è quella che viene usualmente chiamata \textit{advisory
locking},\footnote{Stevens in \cite{APUE} fa riferimento a questo argomento
- come al \textit{record locking}, dizione utilizzata anche dal manuale delle
+ come al \textit{record locking}, dizione utilizzata anche dal manuale della
\acr{glibc}; nelle pagine di manuale si parla di \textit{discrectionary file
lock} per \func{fcntl} e di \textit{advisory locking} per \func{flock},
mentre questo nome viene usato da Stevens per riferirsi al \textit{file
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{LOCK\_SH} & Richiede uno \textit{shared lock} sul file.\\
- \const{LOCK\_EX} & Richiede un \textit{esclusive lock} sul file.\\
- \const{LOCK\_UN} & Rilascia il \textit{file lock}.\\
- \const{LOCK\_NB} & Impedisce che la funzione si blocchi nella
- richiesta di un \textit{file lock}.\\
+ \constd{LOCK\_SH} & Richiede uno \textit{shared lock} sul file.\\
+ \constd{LOCK\_EX} & Richiede un \textit{esclusive lock} sul file.\\
+ \constd{LOCK\_UN} & Rilascia il \textit{file lock}.\\
+ \constd{LOCK\_NB} & Impedisce che la funzione si blocchi nella
+ richiesta di un \textit{file lock}.\\
\hline
\end{tabular}
\caption{Valori dell'argomento \param{operation} di \func{flock}.}
fondamentale da capire è che un \textit{file lock}, qualunque sia
l'interfaccia che si usa, anche se richiesto attraverso un file descriptor,
agisce sempre su di un file; perciò le informazioni relative agli eventuali
-\textit{file lock} sono mantenute dal kernel a livello di \itindex{inode}
-\textit{inode}, dato che questo è l'unico riferimento in comune che possono
-avere due processi diversi che aprono lo stesso file.
+\textit{file lock} sono mantenute dal kernel a livello di \textit{inode}, dato
+che questo è l'unico riferimento in comune che possono avere due processi
+diversi che aprono lo stesso file.
In particolare, come accennato in fig.~\ref{fig:file_flock_struct}, i
-\textit{file lock} sono mantenuti in una \itindex{linked~list} \textit{linked
- list} di strutture \kstruct{file\_lock}. La lista è referenziata
-dall'indirizzo di partenza mantenuto dal campo \var{i\_flock} della struttura
-\kstruct{inode} (per le definizioni esatte si faccia riferimento al file
-\file{include/linux/fs.h} nei sorgenti del kernel). Un bit del campo
-\var{fl\_flags} di specifica se si tratta di un lock in semantica BSD
-(\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}) o un \textit{file lease}
-(\const{FL\_LEASE}, vedi sez.~\ref{sec:file_asyncronous_lease}).
+\textit{file lock} sono mantenuti in una \textit{linked list} di strutture
+\kstructd{file\_lock}. La lista è referenziata dall'indirizzo di partenza
+mantenuto dal campo \var{i\_flock} della struttura \kstruct{inode} (per le
+definizioni esatte si faccia riferimento al file \file{include/linux/fs.h} nei
+sorgenti del kernel). Un bit del campo \var{fl\_flags} di specifica se si
+tratta di un lock in semantica BSD (\constd{FL\_FLOCK}) o POSIX
+(\constd{FL\_POSIX}) o un \textit{file lease} (\constd{FL\_LEASE}, vedi
+sez.~\ref{sec:file_asyncronous_lease}).
\begin{figure}[!htb]
\centering
ulteriori istanze di un \textit{file lock} quanto piuttosto degli ulteriori
riferimenti allo stesso. Questo viene realizzato dal kernel secondo lo schema
di fig.~\ref{fig:file_flock_struct}, associando ad ogni nuovo \textit{file
- lock} un puntatore alla voce nella \itindex{file~table} \textit{file table}
-da cui si è richiesto il blocco, che così ne identifica il titolare. Il
-puntatore è mantenuto nel campo \var{fl\_file} di \kstruct{file\_lock}, e
-viene utilizzato solo per i \textit{file lock} creati con la semantica BSD.
+ lock} un puntatore alla voce nella \textit{file table} da cui si è richiesto
+il blocco, che così ne identifica il titolare. Il puntatore è mantenuto nel
+campo \var{fl\_file} di \kstruct{file\_lock}, e viene utilizzato solo per i
+\textit{file lock} creati con la semantica BSD.
Questa struttura prevede che, quando si richiede la rimozione di un
\textit{file lock}, il kernel acconsenta solo se la richiesta proviene da un
-file descriptor che fa riferimento ad una voce nella \itindex{file~table}
-\textit{file table} corrispondente a quella registrata nel blocco. Allora se
-ricordiamo quanto visto in sez.~\ref{sec:file_dup} e
-sez.~\ref{sec:file_shared_access}, e cioè che i file descriptor duplicati e
-quelli ereditati in un processo figlio puntano sempre alla stessa voce nella
-\itindex{file~table} \textit{file table}, si può capire immediatamente quali
-sono le conseguenze nei confronti delle funzioni \func{dup} e \func{fork}.
+file descriptor che fa riferimento ad una voce nella \textit{file table}
+corrispondente a quella registrata nel blocco. Allora se ricordiamo quanto
+visto in sez.~\ref{sec:file_dup} e sez.~\ref{sec:file_shared_access}, e cioè
+che i file descriptor duplicati e quelli ereditati in un processo figlio
+puntano sempre alla stessa voce nella \textit{file table}, si può capire
+immediatamente quali sono le conseguenze nei confronti delle funzioni
+\func{dup} e \func{fork}.
Sarà così possibile rimuovere un \textit{file lock} attraverso uno qualunque
-dei file descriptor che fanno riferimento alla stessa voce nella
-\itindex{file~table} \textit{file table}, anche se questo è diverso da quello
-con cui lo si è creato,\footnote{attenzione, questo non vale se il file
- descriptor fa riferimento allo stesso file, ma attraverso una voce diversa
- della \itindex{file~table} \textit{file table}, come accade tutte le volte
- che si apre più volte lo stesso file.} o se si esegue la rimozione in un
-processo figlio. Inoltre una volta tolto un \textit{file lock} su un file, la
-rimozione avrà effetto su tutti i file descriptor che condividono la stessa
-voce nella \itindex{file~table} \textit{file table}, e quindi, nel caso di
-file descriptor ereditati attraverso una \func{fork}, anche per processi
-diversi.
+dei file descriptor che fanno riferimento alla stessa voce nella \textit{file
+ table}, anche se questo è diverso da quello con cui lo si è
+creato,\footnote{attenzione, questo non vale se il file descriptor fa
+ riferimento allo stesso file, ma attraverso una voce diversa della
+ \textit{file table}, come accade tutte le volte che si apre più volte lo
+ stesso file.} o se si esegue la rimozione in un processo figlio. Inoltre una
+volta tolto un \textit{file lock} su un file, la rimozione avrà effetto su
+tutti i file descriptor che condividono la stessa voce nella \textit{file
+ table}, e quindi, nel caso di file descriptor ereditati attraverso una
+\func{fork}, anche per processi diversi.
Infine, per evitare che la terminazione imprevista di un processo lasci attivi
dei \textit{file lock}, quando un file viene chiuso il kernel provvede anche a
rimuovere tutti i blocchi ad esso associati. Anche in questo caso occorre
tenere presente cosa succede quando si hanno file descriptor duplicati; in tal
caso infatti il file non verrà effettivamente chiuso (ed il blocco rimosso)
-fintanto che non viene rilasciata la relativa voce nella \itindex{file~table}
-\textit{file table}; e questo avverrà solo quando tutti i file descriptor che
-fanno riferimento alla stessa voce sono stati chiusi. Quindi, nel caso ci
-siano duplicati o processi figli che mantengono ancora aperto un file
-descriptor, il \textit{file lock} non viene rilasciato.
+fintanto che non viene rilasciata la relativa voce nella \textit{file table};
+e questo avverrà solo quando tutti i file descriptor che fanno riferimento
+alla stessa voce sono stati chiusi. Quindi, nel caso ci siano duplicati o
+processi figli che mantengono ancora aperto un file descriptor, il
+\textit{file lock} non viene rilasciato.
\subsection{Il \textit{file locking} POSIX}
\item[\errcode{EDEADLK}] si è richiesto un \textit{lock} su una regione
bloccata da un altro processo che è a sua volta in attesa dello sblocco
di un \textit{lock} mantenuto dal processo corrente; si avrebbe pertanto
- un \itindex{deadlock} \textit{deadlock}. Non è garantito che il sistema
- riconosca sempre questa situazione.
+ un \textit{deadlock}. Non è garantito che il sistema riconosca sempre
+ questa situazione.
\item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima
di poter acquisire un \textit{file lock}.
\item[\errcode{ENOLCK}] il sistema non ha le risorse per il blocco: ci
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{F\_RDLCK} & Richiede un blocco condiviso (\textit{read lock}).\\
- \const{F\_WRLCK} & Richiede un blocco esclusivo (\textit{write lock}).\\
- \const{F\_UNLCK} & Richiede l'eliminazione di un \textit{file lock}.\\
+ \constd{F\_RDLCK} & Richiede un blocco condiviso (\textit{read lock}).\\
+ \constd{F\_WRLCK} & Richiede un blocco esclusivo (\textit{write lock}).\\
+ \constd{F\_UNLCK} & Richiede l'eliminazione di un \textit{file lock}.\\
\hline
\end{tabular}
\caption{Valori possibili per il campo \var{l\_type} di \struct{flock}.}
specifica l'azione da compiere; i valori utilizzabili relativi al \textit{file
locking} sono tre:
\begin{basedescript}{\desclabelwidth{2.0cm}}
-\item[\const{F\_GETLK}] verifica se il \textit{file lock} specificato dalla
+\item[\constd{F\_GETLK}] verifica se il \textit{file lock} specificato dalla
struttura puntata da \param{lock} può essere acquisito: in caso negativo
sovrascrive la struttura \param{flock} con i valori relativi al blocco già
esistente che ne blocca l'acquisizione, altrimenti si limita a impostarne il
campo \var{l\_type} con il valore \const{F\_UNLCK}.
-\item[\const{F\_SETLK}] se il campo \var{l\_type} della struttura puntata da
+\item[\constd{F\_SETLK}] se il campo \var{l\_type} della struttura puntata da
\param{lock} è \const{F\_RDLCK} o \const{F\_WRLCK} richiede il
corrispondente \textit{file lock}, se è \const{F\_UNLCK} lo rilascia; nel
caso la richiesta non possa essere soddisfatta a causa di un blocco
preesistente la funzione ritorna immediatamente con un errore di
\errcode{EACCES} o di \errcode{EAGAIN}.
-\item[\const{F\_SETLKW}] è identica a \const{F\_SETLK}, ma se la richiesta di
+\item[\constd{F\_SETLKW}] è identica a \const{F\_SETLK}, ma se la richiesta di
non può essere soddisfatta per la presenza di un altro blocco, mette il
processo in stato di attesa fintanto che il blocco precedente non viene
rilasciato; se l'attesa viene interrotta da un segnale la funzione ritorna
\begin{figure}[!htb]
\centering \includegraphics[width=9cm]{img/file_lock_dead}
- \caption{Schema di una situazione di \itindex{deadlock} \textit{deadlock}.}
+ \caption{Schema di una situazione di \textit{deadlock}.}
\label{fig:file_flock_dead}
\end{figure}
del processo 2; il processo 1 si bloccherà fintanto che il processo 2 non
rilasci il blocco. Ma cosa accade se il processo 2 nel frattempo tenta a sua
volta di ottenere un lock sulla regione A? Questa è una tipica situazione che
-porta ad un \itindex{deadlock} \textit{deadlock}, dato che a quel punto anche
-il processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro processo.
-Per questo motivo il kernel si incarica di rilevare situazioni di questo tipo,
-ed impedirle restituendo un errore di \errcode{EDEADLK} alla funzione che
-cerca di acquisire un blocco che porterebbe ad un \itindex{deadlock}
-\textit{deadlock}.
+porta ad un \textit{deadlock}, dato che a quel punto anche il processo 2 si
+bloccherebbe, e niente potrebbe sbloccare l'altro processo. Per questo motivo
+il kernel si incarica di rilevare situazioni di questo tipo, ed impedirle
+restituendo un errore di \errcode{EDEADLK} alla funzione che cerca di
+acquisire un blocco che porterebbe ad un \textit{deadlock}.
Per capire meglio il funzionamento del \textit{file locking} in semantica
POSIX (che differisce alquanto rispetto da quello di BSD, visto
kernel. Lo schema delle strutture utilizzate è riportato in
fig.~\ref{fig:file_posix_lock}; come si vede esso è molto simile all'analogo
di fig.~\ref{fig:file_flock_struct}. In questo caso nella figura si sono
-evidenziati solo i campi di \kstruct{file\_lock} significativi per la
+evidenziati solo i campi di \kstructd{file\_lock} significativi per la
semantica POSIX, in particolare adesso ciascuna struttura contiene, oltre al
\ids{PID} del processo in \var{fl\_pid}, la sezione di file che viene bloccata
grazie ai campi \var{fl\_start} e \var{fl\_end}. La struttura è comunque la
stessa, solo che in questo caso nel campo \var{fl\_flags} è impostato il bit
\const{FL\_POSIX} ed il campo \var{fl\_file} non viene usato. Il blocco è
-sempre associato \itindex{inode} all'\textit{inode}, solo che in questo caso
-la titolarità non viene identificata con il riferimento ad una voce nella
-\itindex{file~table} \textit{file table}, ma con il valore del \ids{PID} del
-processo.
+sempre associato all'\textit{inode}, solo che in questo caso la titolarità non
+viene identificata con il riferimento ad una voce nella \textit{file table},
+ma con il valore del \ids{PID} del processo.
\begin{figure}[!htb]
\centering \includegraphics[width=12cm]{img/file_posix_lock}
\end{figure}
Quando si richiede un \textit{file lock} il kernel effettua una scansione di
-tutti i blocchi presenti sul file\footnote{scandisce cioè la
- \itindex{linked~list} \textit{linked list} delle strutture
- \kstruct{file\_lock}, scartando automaticamente quelle per cui
- \var{fl\_flags} non è \const{FL\_POSIX}, così che le due interfacce restano
- ben separate.} per verificare se la regione richiesta non si sovrappone ad
-una già bloccata, in caso affermativo decide in base al tipo di blocco, in
-caso negativo il nuovo blocco viene comunque acquisito ed aggiunto alla lista.
+tutti i blocchi presenti sul file\footnote{scandisce cioè la \textit{linked
+ list} delle strutture \kstruct{file\_lock}, scartando automaticamente
+ quelle per cui \var{fl\_flags} non è \const{FL\_POSIX}, così che le due
+ interfacce restano ben separate.} per verificare se la regione richiesta
+non si sovrappone ad una già bloccata, in caso affermativo decide in base al
+tipo di blocco, in caso negativo il nuovo blocco viene comunque acquisito ed
+aggiunto alla lista.
Nel caso di rimozione invece questa viene effettuata controllando che il
\ids{PID} del processo richiedente corrisponda a quello contenuto nel blocco.
Il comportamento seguito in questo caso è che la funzione ha successo ed
esegue l'operazione richiesta sulla regione indicata; è compito del kernel
preoccuparsi di accorpare o dividere le voci nella lista dei \textit{file
- lock} per far si che le regioni bloccate da essa risultanti siano coerenti
+ lock} per far sì che le regioni bloccate da essa risultanti siano coerenti
con quanto necessario a soddisfare l'operazione richiesta.
\begin{figure}[!htbp]
\item[\errcode{EINVAL}] si è usato un valore non valido per \param{cmd}.
\end{errlist}
ed inoltre \errcode{EDEADLK} e \errcode{ENOLCK} con lo stesso significato
- che hanno con \funcd{fcntl}.
+ che hanno con \func{fcntl}.
}
\end{funcproto}
consentiti sono i seguenti:
\begin{basedescript}{\desclabelwidth{2.0cm}}
-\item[\const{F\_LOCK}] Richiede un \textit{lock} esclusivo sul file, e blocca
+\item[\constd{F\_LOCK}] Richiede un \textit{lock} esclusivo sul file, e blocca
il processo chiamante se, anche parzialmente, la sezione indicata si
sovrappone ad una che è già stata bloccata da un altro processo; in caso di
sovrapposizione con un altro blocco già ottenuto le sezioni vengono unite.
-\item[\const{F\_TLOCK}] Richiede un \textit{exclusive lock}, in maniera
+\item[\constd{F\_TLOCK}] Richiede un \textit{exclusive lock}, in maniera
identica a \const{F\_LOCK}, ma in caso di indisponibilità non blocca il
processo restituendo un errore di \errval{EAGAIN}.
-\item[\const{F\_ULOCK}] Rilascia il blocco sulla sezione indicata, questo può
+\item[\constd{F\_ULOCK}] Rilascia il blocco sulla sezione indicata, questo può
anche causare la suddivisione di una sezione bloccata in precedenza nelle
due parti eccedenti nel caso si sia indicato un intervallo più limitato.
-\item[\const{F\_TEST}] Controlla la presenza di un blocco sulla sezione di
+\item[\constd{F\_TEST}] Controlla la presenza di un blocco sulla sezione di
file indicata, \func{lockf} ritorna $0$ se la sezione è libera o bloccata
dal processo stesso, o $-1$ se è bloccata da un altro processo, nel qual
caso \var{errno} assume il valore \errval{EAGAIN} (ma su alcuni sistemi può
\func{exec}. Per questo stesso motivo la funzione non è equivalente a
\func{flock} e può essere usata senza interferenze insieme a quest'ultima.
+
+\subsection{Gli \textit{open file descriptor locks}}
+\label{sec:open_file_descriptor_locks}
+
+Come illustrato in dettaglio nella precedente sez.~\ref{sec:file_posix_lock},
+la chiusura di un file su cui sono presenti dei \textit{file lock} comporta
+l'immediato rilascio degli stessi, anche se questi sono stati acquisiti da un
+processo diverso.
+
+da finire.
+
% TODO trattare i POSIX file-private lock introdotti con il 3.15,
% vedi http://lwn.net/Articles/586904/ correlato:
% http://www.samba.org/samba/news/articles/low_point/tale_two_stds_os2.html
opportune verifiche nei processi, questo verrebbe comunque rispettato.
Per poter utilizzare il \textit{mandatory locking} è stato introdotto un
-utilizzo particolare del bit \itindex{sgid~bit} \acr{sgid} dei permessi dei
-file. Se si ricorda quanto esposto in sez.~\ref{sec:file_special_perm}), esso
-viene di norma utilizzato per cambiare il \ids{GID} effettivo con cui viene
-eseguito un programma, ed è pertanto sempre associato alla presenza del
-permesso di esecuzione per il gruppo. Impostando questo bit su un file senza
-permesso di esecuzione in un sistema che supporta il \textit{mandatory
- locking}, fa sì che quest'ultimo venga attivato per il file in questione. In
-questo modo una combinazione dei permessi originariamente non contemplata, in
-quanto senza significato, diventa l'indicazione della presenza o meno del
-\textit{mandatory locking}.\footnote{un lettore attento potrebbe ricordare
- quanto detto in sez.~\ref{sec:file_perm_management} e cioè che il bit
- \acr{sgid} viene cancellato (come misura di sicurezza) quando di scrive su
- un file, questo non vale quando esso viene utilizzato per attivare il
- \textit{mandatory locking}.}
+utilizzo particolare del bit \acr{sgid} dei permessi dei file. Se si ricorda
+quanto esposto in sez.~\ref{sec:file_special_perm}), esso viene di norma
+utilizzato per cambiare il \ids{GID} effettivo con cui viene eseguito un
+programma, ed è pertanto sempre associato alla presenza del permesso di
+esecuzione per il gruppo. Impostando questo bit su un file senza permesso di
+esecuzione in un sistema che supporta il \textit{mandatory locking}, fa sì che
+quest'ultimo venga attivato per il file in questione. In questo modo una
+combinazione dei permessi originariamente non contemplata, in quanto senza
+significato, diventa l'indicazione della presenza o meno del \textit{mandatory
+ locking}.\footnote{un lettore attento potrebbe ricordare quanto detto in
+ sez.~\ref{sec:file_perm_management} e cioè che il bit \acr{sgid} viene
+ cancellato (come misura di sicurezza) quando di scrive su un file, questo
+ non vale quando esso viene utilizzato per attivare il \textit{mandatory
+ locking}.}
L'uso del \textit{mandatory locking} presenta vari aspetti delicati, dato che
neanche l'amministratore può passare sopra ad un \textit{file lock}; pertanto
un processo che blocchi un file cruciale può renderlo completamente
inaccessibile, rendendo completamente inutilizzabile il sistema\footnote{il
- problema si potrebbe risolvere rimuovendo il bit \itindex{sgid~bit}
- \acr{sgid}, ma non è detto che sia così facile fare questa operazione con un
- sistema bloccato.} inoltre con il \textit{mandatory locking} si può
-bloccare completamente un server NFS richiedendo una lettura su un file su cui
-è attivo un blocco. Per questo motivo l'abilitazione del \textit{mandatory
- locking} è di norma disabilitata, e deve essere attivata filesystem per
-filesystem in fase di montaggio, specificando l'apposita opzione di
-\func{mount} riportata in sez.~\ref{sec:filesystem_mounting}, o con l'opzione
-\code{-o mand} per il comando omonimo.
+ problema si potrebbe risolvere rimuovendo il bit \acr{sgid}, ma non è detto
+ che sia così facile fare questa operazione con un sistema bloccato.}
+inoltre con il \textit{mandatory locking} si può bloccare completamente un
+server NFS richiedendo una lettura su un file su cui è attivo un blocco. Per
+questo motivo l'abilitazione del \textit{mandatory locking} è di norma
+disabilitata, e deve essere attivata filesystem per filesystem in fase di
+montaggio, specificando l'apposita opzione di \func{mount} riportata in
+sez.~\ref{sec:filesystem_mounting}, o con l'opzione \code{-o mand} per il
+comando omonimo.
Si tenga presente inoltre che il \textit{mandatory locking} funziona solo
sull'interfaccia POSIX di \func{fcntl}. Questo ha due conseguenze: che non si
attuale delle cose è sconsigliabile fare affidamento sul \textit{mandatory
locking}.
+% TODO il supporto è stato reso opzionale nel 4.5, verrà eliminato nel futuro
+% (vedi http://lwn.net/Articles/667210/)
+
\itindend{file~locking}
\itindend{mandatory~locking}
\label{sec:file_noblocking}
Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra
-\textit{fast} e \textit{slow} \textit{system call},\index{system~call~lente}
-che in certi casi le funzioni di I/O eseguite su un file descritor possono
-bloccarsi indefinitamente. Questo non avviene mai per i file normali, per i
-quali le funzioni di lettura e scrittura ritornano sempre subito, ma può
-avvenire per alcuni \index{file!di~dispositivo} file di dispositivo, come ad
-esempio una seriale o un terminale, o con l'uso di file descriptor collegati a
-meccanismi di intercomunicazione come le \textit{pipe} (vedi
-sez.~\ref{sec:ipc_unix}) ed i socket (vedi sez.~\ref{sec:sock_socket_def}). In
-casi come questi ad esempio una operazione di lettura potrebbe bloccarsi se
-non ci sono dati disponibili sul descrittore su cui la si sta effettuando.
+\textit{fast} e \textit{slow} \textit{system call}, che in certi casi le
+funzioni di I/O eseguite su un file descriptor possono bloccarsi
+indefinitamente. Questo non avviene mai per i file normali, per i quali le
+funzioni di lettura e scrittura ritornano sempre subito, ma può avvenire per
+alcuni file di dispositivo, come ad esempio una seriale o un terminale, o con
+l'uso di file descriptor collegati a meccanismi di intercomunicazione come le
+\textit{pipe} (vedi sez.~\ref{sec:ipc_unix}) ed i socket (vedi
+sez.~\ref{sec:sock_socket_def}). In casi come questi ad esempio una operazione
+di lettura potrebbe bloccarsi se non ci sono dati disponibili sul descrittore
+su cui la si sta effettuando.
Questo comportamento è alla radice di una delle problematiche più comuni che
ci si trova ad affrontare nella gestione delle operazioni di I/O: la necessità
quella bloccata, mentre nel peggiore dei casi, quando la conclusione
dell'operazione bloccata dipende da quanto si otterrebbe dal file descriptor
``\textsl{disponibile}'', si potrebbe addirittura arrivare ad un
-\itindex{deadlock} \textit{deadlock}.
+\textit{deadlock}.
+
+\itindbeg{polling}
Abbiamo già accennato in sez.~\ref{sec:file_open_close} che è possibile
prevenire questo tipo di comportamento delle funzioni di I/O aprendo un file
immediatamente, restituendo l'errore \errcode{EAGAIN}. L'utilizzo di questa
modalità di I/O permette di risolvere il problema controllando a turno i vari
file descriptor, in un ciclo in cui si ripete l'accesso fintanto che esso non
-viene garantito. Ovviamente questa tecnica, detta \itindex{polling}
-\textit{polling}, è estremamente inefficiente: si tiene costantemente
-impiegata la CPU solo per eseguire in continuazione delle \textit{system call}
-che nella gran parte dei casi falliranno.
+viene garantito. Ovviamente questa tecnica, detta \textit{polling}, è
+estremamente inefficiente: si tiene costantemente impiegata la CPU solo per
+eseguire in continuazione delle \textit{system call} che nella gran parte dei
+casi falliranno.
+
+\itindend{polling}
É appunto per superare questo problema è stato introdotto il concetto di
\textit{I/O multiplexing}, una nuova modalità per la gestione dell'I/O che
tutti i sistemi che supportano i socket, compreso le varianti di System V ed
inserita in POSIX.1-2001; il suo prototipo è:\footnote{l'header
\texttt{sys/select.h} è stato introdotto con POSIX.1-2001, è ed presente con
- le \acr{glibc} a partire dalla versione 2.0, in precedenza, con le
- \acr{libc4} e le \acr{libc5}, occorreva includere \texttt{sys/time.h},
+ la \acr{glibc} a partire dalla versione 2.0, in precedenza, con le
+ \acr{libc4} e \acr{libc5}, occorreva includere \texttt{sys/time.h},
\texttt{sys/types.h} e \texttt{unistd.h}.}
\begin{funcproto}{
Per specificare quali file descriptor si intende selezionare la funzione usa
un particolare oggetto, il \textit{file descriptor set}, identificato dal tipo
-\type{fd\_set}, che serve ad identificare un insieme di file descriptor, in
-maniera analoga a come un \itindex{signal~set} \textit{signal set} (vedi
-sez.~\ref{sec:sig_sigset}) identifica un insieme di segnali. Per la
-manipolazione di questi \textit{file descriptor set} si possono usare delle
-opportune macro di preprocessore:
+\typed{fd\_set}, che serve ad identificare un insieme di file descriptor, in
+maniera analoga a come un \textit{signal set} (vedi sez.~\ref{sec:sig_sigset})
+identifica un insieme di segnali. Per la manipolazione di questi \textit{file
+ descriptor set} si possono usare delle opportune macro di preprocessore:
{\centering
\vspace{3pt}
\begin{funcbox}{
\fhead{sys/select.h}
-\fdecl{void \macro{FD\_ZERO}(fd\_set *set)}
+\fdecl{void \macrod{FD\_ZERO}(fd\_set *set)}
\fdesc{Inizializza l'insieme (vuoto).}
-\fdecl{void \macro{FD\_SET}(int fd, fd\_set *set)}
+\fdecl{void \macrod{FD\_SET}(int fd, fd\_set *set)}
\fdesc{Inserisce il file descriptor \param{fd} nell'insieme.}
-\fdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)}
+\fdecl{void \macrod{FD\_CLR}(int fd, fd\_set *set)}
\fdesc{Rimuove il file descriptor \param{fd} dall'insieme.}
-\fdecl{int \macro{FD\_ISSET}(int fd, fd\_set *set)}
+\fdecl{int \macrod{FD\_ISSET}(int fd, fd\_set *set)}
\fdesc{Controlla se il file descriptor \param{fd} è nell'insieme.}
}
\end{funcbox}}
In genere un \textit{file descriptor set} può contenere fino ad un massimo di
-\const{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva
+\macrod{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva
al limite per il numero massimo di file aperti (ad esempio in Linux, fino alla
serie 2.0.x, c'era un limite di 256 file per processo), ma da quando, nelle
versioni più recenti del kernel, questo limite è stato rimosso, esso indica le
inizializzati con \macro{FD\_ZERO}; passare a \func{select} un valore non
inizializzato può dar luogo a comportamenti non prevedibili. Allo stesso modo
usare \macro{FD\_SET} o \macro{FD\_CLR} con un file descriptor il cui valore
-eccede \const{FD\_SETSIZE} può dare luogo ad un comportamento indefinito.
+eccede \macro{FD\_SETSIZE} può dare luogo ad un comportamento indefinito.
La funzione richiede di specificare tre insiemi distinti di file descriptor;
il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di
bloccante, quindi anche in caso di \textit{end-of-file}.} il secondo,
\param{writefds}, per verificare la possibilità di effettuare una scrittura ed
il terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni come i
-dati urgenti \itindex{out-of-band} su un socket, (vedi
-sez.~\ref{sec:TCP_urgent_data}).
+dati urgenti su un socket, (vedi sez.~\ref{sec:TCP_urgent_data}).
Dato che in genere non si tengono mai sotto controllo fino a
-\const{FD\_SETSIZE} file contemporaneamente, la funzione richiede di
+\macro{FD\_SETSIZE} file contemporaneamente, la funzione richiede di
specificare qual è il valore più alto fra i file descriptor indicati nei tre
insiemi precedenti. Questo viene fatto per efficienza, per evitare di passare
e far controllare al kernel una quantità di memoria superiore a quella
\val{NULL} la funzione attende indefinitamente. Si può specificare anche un
tempo nullo (cioè una struttura \struct{timeval} con i campi impostati a
zero), qualora si voglia semplicemente controllare lo stato corrente dei file
-descriptor, e così può essere utilizzata eseguire il \itindex{polling}
-\textit{polling} su un gruppo di file descriptor. Usare questo argomento con
-tutti i \textit{file descriptor set} vuoti è un modo portabile, disponibile
-anche su sistemi in cui non sono disponibili le funzioni avanzate di
-sez.~\ref{sec:sig_timer_adv}, per tenere un processo in stato di
-\textit{sleep} con precisioni inferiori al secondo.
+descriptor, e così può essere utilizzata eseguire il \textit{polling} su un
+gruppo di file descriptor. Usare questo argomento con tutti i \textit{file
+ descriptor set} vuoti è un modo portabile, disponibile anche su sistemi in
+cui non sono disponibili le funzioni avanzate di sez.~\ref{sec:sig_timer_adv},
+per tenere un processo in stato di \textit{sleep} con precisioni inferiori al
+secondo.
In caso di successo la funzione restituisce il numero di file descriptor
pronti, seguendo il comportamento previsto dallo standard
caso di errore.
Si tenga presente infine che su Linux, in caso di programmazione
-\textit{multithread} se un file descriptor viene chiuso in un altro
+\textit{multi-thread} se un file descriptor viene chiuso in un altro
\textit{thread} rispetto a quello in cui si sta usando \func{select}, questa
non subisce nessun effetto. In altre varianti di sistemi unix-like invece
\func{select} ritorna indicando che il file descriptor è pronto, con
assunto nessuno dei due comportamenti se si vogliono scrivere programmi
portabili.
-
\itindend{file~descriptor~set}
Una volta ritornata la funzione, si potrà controllare quali sono i file
ricalcolano \param{timeout} tutte le volte. In genere questa caratteristica è
disponibile nei sistemi che derivano da System V e non è disponibile per
quelli che derivano da BSD; lo standard POSIX.1-2001 non permette questo
-comportamento e per questo motivo le \acr{glibc} nascondono il comportamento
+comportamento e per questo motivo la \acr{glibc} nasconde il comportamento
passando alla \textit{system call} una copia dell'argomento \param{timeout}.
Uno dei problemi che si presentano con l'uso di \func{select} è che il suo
multiplexing}, introdotto solo con le ultime revisioni dello standard (POSIX
1003.1g-2000 e POSIX 1003.1-2001). La scelta è stata quella di seguire
l'interfaccia creata da BSD, ma prevede che tutte le funzioni ad esso relative
-vengano dichiarate nell'header \headfile{sys/select.h}, che sostituisce i
+vengano dichiarate nell'header \headfiled{sys/select.h}, che sostituisce i
precedenti, ed inoltre aggiunge a \func{select} una nuova funzione
\funcd{pselect},\footnote{il supporto per lo standard POSIX 1003.1-2001, ed
- l'header \headfile{sys/select.h}, compaiono in Linux a partire dalle
+ l'header \headfile{sys/select.h}, compaiono in Linux a partire dalla
\acr{glibc} 2.1. Le \acr{libc4} e \acr{libc5} non contengono questo header,
- le \acr{glibc} 2.0 contengono una definizione sbagliata di \func{psignal},
+ la \acr{glibc} 2.0 contiene una definizione sbagliata di \func{psignal},
senza l'argomento \param{sigmask}, la definizione corretta è presente dalle
\acr{glibc} 2.1-2.2.1 se si è definito \macro{\_GNU\_SOURCE} e nelle
\acr{glibc} 2.2.2-2.2.4 se si è definito \macro{\_XOPEN\_SOURCE} con valore
struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timespec_struct}) per
indicare con maggiore precisione il timeout e non ne aggiorna il valore in
caso di interruzione. In realtà anche in questo caso la \textit{system call}
-di Linux aggiorna il valore al tempo rimanente, ma la funzione fornita dalle
+di Linux aggiorna il valore al tempo rimanente, ma la funzione fornita dalla
\acr{glibc} modifica questo comportamento passando alla \textit{system call}
una variabile locale, in modo da mantenere l'aderenza allo standard POSIX che
richiede che il valore di \param{timeout} non sia modificato.
viene sostituita da quella così indicata immediatamente prima di eseguire
l'attesa, e viene poi ripristinata al ritorno della funzione. L'uso
di \param{sigmask} è stato introdotto allo scopo di prevenire possibili
-\textit{race condition} \itindex{race~condition} quando oltre alla presenza di
-dati sui file descriptor come nella \func{select} ordinaria, ci si deve porre
-in attesa anche dell'arrivo di un segnale.
+\textit{race condition} quando oltre alla presenza di dati sui file descriptor
+come nella \func{select} ordinaria, ci si deve porre in attesa anche
+dell'arrivo di un segnale.
Come abbiamo visto in sez.~\ref{sec:sig_example} la tecnica classica per
rilevare l'arrivo di un segnale è quella di utilizzare il gestore per
-impostare una \index{variabili!globali} variabile globale e controllare questa
-nel corpo principale del programma; abbiamo visto in quell'occasione come
-questo lasci spazio a possibili \itindex{race~condition} \textit{race
- condition}, per cui diventa essenziale utilizzare \func{sigprocmask} per
-disabilitare la ricezione del segnale prima di eseguire il controllo e
-riabilitarlo dopo l'esecuzione delle relative operazioni, onde evitare
-l'arrivo di un segnale immediatamente dopo il controllo, che andrebbe perso.
+impostare una variabile globale e controllare questa nel corpo principale del
+programma; abbiamo visto in quell'occasione come questo lasci spazio a
+possibili \textit{race condition}, per cui diventa essenziale utilizzare
+\func{sigprocmask} per disabilitare la ricezione del segnale prima di eseguire
+il controllo e riabilitarlo dopo l'esecuzione delle relative operazioni, onde
+evitare l'arrivo di un segnale immediatamente dopo il controllo, che andrebbe
+perso.
Nel nostro caso il problema si pone quando, oltre al segnale, si devono tenere
sotto controllo anche dei file descriptor con \func{select}, in questo caso si
e si potrebbero eseguire di conseguenza le operazioni relative al segnale e
alla gestione dati con un ciclo del tipo:
\includecodesnip{listati/select_race.c}
-qui però emerge una \itindex{race~condition} \textit{race condition}, perché
-se il segnale arriva prima della chiamata a \func{select}, questa non verrà
-interrotta, e la ricezione del segnale non sarà rilevata.
+qui però emerge una \textit{race condition}, perché se il segnale arriva prima
+della chiamata a \func{select}, questa non verrà interrotta, e la ricezione
+del segnale non sarà rilevata.
Per questo è stata introdotta \func{pselect} che attraverso l'argomento
\param{sigmask} permette di riabilitare la ricezione il segnale
contestualmente all'esecuzione della funzione,\footnote{in Linux però, fino al
kernel 2.6.16, non era presente la relativa \textit{system call}, e la
- funzione era implementata nelle \acr{glibc} attraverso \func{select} (vedi
- \texttt{man select\_tut}) per cui la possibilità di \itindex{race~condition}
- \textit{race condition} permaneva; in tale situazione si può ricorrere ad
- una soluzione alternativa, chiamata \itindex{self-pipe trick}
- \textit{self-pipe trick}, che consiste nell'aprire una \textit{pipe} (vedi
- sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in lettura della
- stessa; si può indicare l'arrivo di un segnale scrivendo sul capo in
- scrittura all'interno del gestore dello stesso; in questo modo anche se il
- segnale va perso prima della chiamata di \func{select} questa lo riconoscerà
- comunque dalla presenza di dati sulla \textit{pipe}.} ribloccandolo non
-appena essa ritorna, così che il precedente codice potrebbe essere riscritto
-nel seguente modo:
+ funzione era implementata nella \acr{glibc} attraverso \func{select} (vedi
+ \texttt{man select\_tut}) per cui la possibilità di \textit{race condition}
+ permaneva; in tale situazione si può ricorrere ad una soluzione alternativa,
+ chiamata \itindex{self-pipe~trick} \textit{self-pipe trick}, che consiste
+ nell'aprire una \textit{pipe} (vedi sez.~\ref{sec:ipc_pipes}) ed usare
+ \func{select} sul capo in lettura della stessa; si può indicare l'arrivo di
+ un segnale scrivendo sul capo in scrittura all'interno del gestore dello
+ stesso; in questo modo anche se il segnale va perso prima della chiamata di
+ \func{select} questa lo riconoscerà comunque dalla presenza di dati sulla
+ \textit{pipe}.} ribloccandolo non appena essa ritorna, così che il
+precedente codice potrebbe essere riscritto nel seguente modo:
\includecodesnip{listati/pselect_norace.c}
in questo caso utilizzando \var{oldmask} durante l'esecuzione di
\func{pselect} la ricezione del segnale sarà abilitata, ed in caso di
introdotta in Linux come \textit{system call} a partire dal kernel 2.1.23 ed
inserita nelle \acr{libc} 5.4.28, originariamente l'argomento \param{nfds}
era di tipo \ctyp{unsigned int}, la funzione è stata inserita nello standard
- POSIX.1-2001 in cui è stato introdotto il tipo nativo \type{nfds\_t}.} il
+ POSIX.1-2001 in cui è stato introdotto il tipo nativo \typed{nfds\_t}.} il
cui prototipo è:
\begin{funcproto}{
\textbf{Flag} & \textbf{Significato} \\
\hline
\hline
- \const{POLLIN} & È possibile la lettura.\\
- \const{POLLRDNORM}& Sono disponibili in lettura dati normali.\\
- \const{POLLRDBAND}& Sono disponibili in lettura dati prioritari.\\
- \const{POLLPRI} & È possibile la lettura di \itindex{out-of-band} dati
- urgenti.\\
+ \constd{POLLIN} & È possibile la lettura.\\
+ \constd{POLLRDNORM}& Sono disponibili in lettura dati normali.\\
+ \constd{POLLRDBAND}& Sono disponibili in lettura dati prioritari.\\
+ \constd{POLLPRI} & È possibile la lettura di dati urgenti.\\
\hline
- \const{POLLOUT} & È possibile la scrittura immediata.\\
- \const{POLLWRNORM}& È possibile la scrittura di dati normali.\\
- \const{POLLWRBAND}& È possibile la scrittura di dati prioritari.\\
+ \constd{POLLOUT} & È possibile la scrittura immediata.\\
+ \constd{POLLWRNORM}& È possibile la scrittura di dati normali.\\
+ \constd{POLLWRBAND}& È possibile la scrittura di dati prioritari.\\
\hline
- \const{POLLERR} & C'è una condizione di errore.\\
- \const{POLLHUP} & Si è verificato un hung-up.\\
- \const{POLLRDHUP} & Si è avuta una \textsl{half-close} su un
+ \constd{POLLERR} & C'è una condizione di errore.\\
+ \constd{POLLHUP} & Si è verificato un hung-up.\\
+ \constd{POLLRDHUP} & Si è avuta una \textsl{half-close} su un
socket.\footnotemark\\
- \const{POLLNVAL} & Il file descriptor non è aperto.\\
+ \constd{POLLNVAL} & Il file descriptor non è aperto.\\
\hline
- \const{POLLMSG} & Definito per compatibilità con SysV.\\
+ \constd{POLLMSG} & Definito per compatibilità con SysV.\\
\hline
\end{tabular}
\caption{Costanti per l'identificazione dei vari bit dei campi
\footnotetext{si tratta di una estensione specifica di Linux, disponibile a
partire dal kernel 2.6.17 definendo la marco \macro{\_GNU\_SOURCE}, che
consente di riconoscere la chiusura in scrittura dell'altro capo di un
- socket, situazione che si viene chiamata appunto \itindex{half-close}
- \textit{half-close} (\textsl{mezza chiusura}) su cui torneremo con maggiori
- dettagli in sez.~\ref{sec:TCP_shutdown}.}
+ socket, situazione che si viene chiamata appunto \textit{half-close}
+ (\textsl{mezza chiusura}) su cui torneremo con maggiori dettagli in
+ sez.~\ref{sec:TCP_shutdown}.}
Il valore \const{POLLMSG} non viene utilizzato ed è definito solo per
compatibilità con l'implementazione di System V che usa i cosiddetti
librerie standard del C visti in sez.~\ref{sec:file_stream}. Da essa derivano
i nomi di alcune costanti poiché per quegli \textit{stream} sono definite tre
classi di dati: \textsl{normali}, \textit{prioritari} ed \textit{urgenti}. In
-Linux la distinzione ha senso solo per i dati urgenti \itindex{out-of-band}
-dei socket (vedi sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come
-\func{poll} reagisce alle varie condizioni dei socket torneremo in
-sez.~\ref{sec:TCP_serv_poll}, dove vedremo anche un esempio del suo utilizzo.
+Linux la distinzione ha senso solo per i dati urgenti dei socket (vedi
+sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come \func{poll} reagisce
+alle varie condizioni dei socket torneremo in sez.~\ref{sec:TCP_serv_poll},
+dove vedremo anche un esempio del suo utilizzo.
Le costanti relative ai diversi tipi di dati normali e prioritari che fanno
riferimento alle implementazioni in stile System V sono \const{POLLRDNORM},
L'uso di \func{poll} consente di superare alcuni dei problemi illustrati in
precedenza per \func{select}; anzitutto, dato che in questo caso si usa un
vettore di strutture \struct{pollfd} di dimensione arbitraria, non esiste il
-limite introdotto dalle dimensioni massime di un \itindex{file~descriptor~set}
-\textit{file descriptor set} e la dimensione dei dati passati al kernel
-dipende solo dal numero dei file descriptor che si vogliono controllare, non
-dal loro valore. Infatti, anche se usando dei bit un \textit{file descriptor
- set} può essere più efficiente di un vettore di strutture \struct{pollfd},
-qualora si debba osservare un solo file descriptor con un valore molto alto ci
-si troverà ad utilizzare inutilmente un maggiore quantitativo di memoria.
-
-Inoltre con \func{select} lo stesso \itindex{file~descriptor~set} \textit{file
- descriptor set} è usato sia in ingresso che in uscita, e questo significa
-che tutte le volte che si vuole ripetere l'operazione occorre reinizializzarlo
-da capo. Questa operazione, che può essere molto onerosa se i file descriptor
-da tenere sotto osservazione sono molti, non è invece necessaria con
-\func{poll}.
+limite introdotto dalle dimensioni massime di un \textit{file descriptor set}
+e la dimensione dei dati passati al kernel dipende solo dal numero dei file
+descriptor che si vogliono controllare, non dal loro valore. Infatti, anche se
+usando dei bit un \textit{file descriptor set} può essere più efficiente di un
+vettore di strutture \struct{pollfd}, qualora si debba osservare un solo file
+descriptor con un valore molto alto ci si troverà ad utilizzare inutilmente un
+maggiore quantitativo di memoria.
+
+Inoltre con \func{select} lo stesso \textit{file descriptor set} è usato sia
+in ingresso che in uscita, e questo significa che tutte le volte che si vuole
+ripetere l'operazione occorre reinizializzarlo da capo. Questa operazione, che
+può essere molto onerosa se i file descriptor da tenere sotto osservazione
+sono molti, non è invece necessaria con \func{poll}.
Abbiamo visto in sez.~\ref{sec:file_select} come lo standard POSIX preveda una
variante di \func{select} che consente di gestire correttamente la ricezione
risultati illustrati in precedenza. Come nel caso di \func{pselect} la
\textit{system call} che implementa \func{ppoll} restituisce, se la funzione
viene interrotta da un segnale, il tempo mancante in \param{timeout}, e come
-per \func{pselect} la funzione di libreria fornita dalle \acr{glibc} maschera
+per \func{pselect} la funzione di libreria fornita dalla \acr{glibc} maschera
questo comportamento non modificando mai il valore di \param{timeout} anche se
in questo caso non esiste nessuno standard che richieda questo comportamento.
Infine anche per \func{poll} e \func{ppoll} valgono le considerazioni relative
-alla possibilità di avere delle notificazione spurie della disponibilita di
+alla possibilità di avere delle notificazione spurie della disponibilità di
accesso ai file descriptor illustrate per \func{select} in
sez.~\ref{sec:file_select}, che non staremo a ripetere qui.
anche questa funzione non è molto efficiente quando deve essere utilizzata con
un gran numero di file descriptor,\footnote{in casi del genere \func{select}
viene scartata a priori, perché può avvenire che il numero di file
- descriptor ecceda le dimensioni massime di un \itindex{file~descriptor~set}
- \textit{file descriptor set}.} in particolare nel caso in cui solo pochi di
-questi diventano attivi. Il problema in questo caso è che il tempo impiegato
-da \func{poll} a trasferire i dati da e verso il kernel è proporzionale al
-numero di file descriptor osservati, non a quelli che presentano attività.
+ descriptor ecceda le dimensioni massime di un \textit{file descriptor set}.}
+in particolare nel caso in cui solo pochi di questi diventano attivi. Il
+problema in questo caso è che il tempo impiegato da \func{poll} a trasferire i
+dati da e verso il kernel è proporzionale al numero di file descriptor
+osservati, non a quelli che presentano attività.
Quando ci sono decine di migliaia di file descriptor osservati e migliaia di
eventi al secondo (il caso classico è quello di un server web di un sito con
servizio è chiamata \textit{epoll},\footnote{l'interfaccia è stata creata da
Davide Libenzi, ed è stata introdotta per la prima volta nel kernel 2.5.44,
ma la sua forma definitiva è stata raggiunta nel kernel 2.5.66, il supporto
- è stato aggiunto nelle \acr{glibc} a partire dalla versione 2.3.2.} anche se
+ è stato aggiunto nella \acr{glibc} a partire dalla versione 2.3.2.} anche se
sono state in discussione altre interfacce con le quali effettuare lo stesso
tipo di operazioni; \textit{epoll} è in grado di operare sia in modalità
\textit{level triggered} che \textit{edge triggered}.
-La prima versione di \textit{epoll} prevedeva l'apertura di uno speciale file
-di dispositivo, \texttt{/dev/epoll}, per ottenere un file descriptor da
+La prima versione di \textit{epoll} prevedeva l'uso di uno speciale file di
+dispositivo, \texttt{/dev/epoll}, per ottenere un file descriptor da
utilizzare con le funzioni dell'interfaccia ma poi si è passati all'uso di
apposite \textit{system call}. Il primo passo per usare l'interfaccia di
\textit{epoll} è pertanto quello ottenere detto file descriptor chiamando una
positivo o non valido per \param{flags}.
\item[\errcode{EMFILE}] si è raggiunto il limite sul numero massimo di
istanze di \textit{epoll} per utente stabilito da
- \sysctlfile{fs/epoll/max\_user\_instances}.
+ \sysctlfiled{fs/epoll/max\_user\_instances}.
\item[\errcode{ENFILE}] si è raggiunto il massimo di file descriptor aperti
nel sistema.
\item[\errcode{ENOMEM}] non c'è sufficiente memoria nel kernel per creare
La seconda versione della funzione, \func{epoll\_create1} è stata introdotta
come estensione della precedente (è disponibile solo a partire dal kernel
2.6.27) per poter passare dei flag di controllo come maschera binaria in fase
-di creazione del file descriptor. Al momento l'unico valore legale
-per \param{flags} (a parte lo zero) è \const{EPOLL\_CLOEXEC}, che consente di
+di creazione del file descriptor. Al momento l'unico valore legale per
+\param{flags} (a parte lo zero) è \constd{EPOLL\_CLOEXEC}, che consente di
impostare in maniera atomica sul file descriptor il flag di
-\textit{close-on-exec} (si è trattato il significato di \const{O\_CLOEXEC} in
-sez.~\ref{sec:file_open_close}), senza che sia necessaria una successiva
+\textit{close-on-exec} (vedi sez.~\ref{sec:proc_exec} e
+sez.~\ref{sec:file_shared_access}) senza che sia necessaria una successiva
chiamata a \func{fcntl}.
Una volta ottenuto un file descriptor per \textit{epoll} il passo successivo è
l'operazione richiesta.
\item[\errcode{ENOSPC}] si è raggiunto il limite massimo di registrazioni
per utente di file descriptor da osservare imposto da
- \sysctlfile{fs/epoll/max\_user\_watches}.
+ \sysctlfiled{fs/epoll/max\_user\_watches}.
\item[\errcode{EPERM}] il file associato a \param{fd} non supporta l'uso di
\textit{epoll}.
\end{errlist}
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{EPOLL\_CTL\_ADD}& Aggiunge un nuovo file descriptor da osservare
- \param{fd} alla lista dei file descriptor
- controllati tramite \param{epfd}, in
- \param{event} devono essere specificate le
- modalità di osservazione.\\
- \const{EPOLL\_CTL\_MOD}& Modifica le modalità di osservazione del file
- descriptor \param{fd} secondo il contenuto di
- \param{event}.\\
- \const{EPOLL\_CTL\_DEL}& Rimuove il file descriptor \param{fd} dalla lista
- dei file controllati tramite \param{epfd}.\\
+ \constd{EPOLL\_CTL\_ADD}& Aggiunge un nuovo file descriptor da osservare
+ \param{fd} alla lista dei file descriptor
+ controllati tramite \param{epfd}, in
+ \param{event} devono essere specificate le
+ modalità di osservazione.\\
+ \constd{EPOLL\_CTL\_MOD}& Modifica le modalità di osservazione del file
+ descriptor \param{fd} secondo il contenuto di
+ \param{event}.\\
+ \constd{EPOLL\_CTL\_DEL}& Rimuove il file descriptor \param{fd} dalla lista
+ dei file controllati tramite \param{epfd}.\\
\hline
\end{tabular}
\caption{Valori dell'argomento \param{op} che consentono di scegliere quale
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{EPOLLIN} & Il file è pronto per le operazioni di lettura
+ \constd{EPOLLIN} & Il file è pronto per le operazioni di lettura
(analogo di \const{POLLIN}).\\
- \const{EPOLLOUT} & Il file è pronto per le operazioni di scrittura
+ \constd{EPOLLOUT} & Il file è pronto per le operazioni di scrittura
(analogo di \const{POLLOUT}).\\
- \const{EPOLLRDHUP} & L'altro capo di un socket di tipo
+ \constd{EPOLLRDHUP} & L'altro capo di un socket di tipo
\const{SOCK\_STREAM} (vedi sez.~\ref{sec:sock_type})
ha chiuso la connessione o il capo in scrittura
della stessa (vedi
sez.~\ref{sec:TCP_shutdown}).\footnotemark\\
- \const{EPOLLPRI} & Ci sono \itindex{out-of-band} dati urgenti
- disponibili in lettura (analogo di
- \const{POLLPRI}); questa condizione viene comunque
+ \constd{EPOLLPRI} & Ci sono dati urgenti disponibili in lettura (analogo
+ di \const{POLLPRI}); questa condizione viene comunque
riportata in uscita, e non è necessaria impostarla
in ingresso.\\
\hline
- \const{EPOLLERR} & Si è verificata una condizione di errore
+ \constd{EPOLLERR} & Si è verificata una condizione di errore
(analogo di \const{POLLERR}); questa condizione
viene comunque riportata in uscita, e non è
necessaria impostarla in ingresso.\\
- \const{EPOLLHUP} & Si è verificata una condizione di hung-up; questa
+ \constd{EPOLLHUP} & Si è verificata una condizione di hung-up; questa
condizione viene comunque riportata in uscita, e non
è necessaria impostarla in ingresso.\\
\hline
- \const{EPOLLET} & Imposta la notifica in modalità \textit{edge
+ \constd{EPOLLET} & Imposta la notifica in modalità \textit{edge
triggered} per il file descriptor associato.\\
- \const{EPOLLONESHOT}& Imposta la modalità \textit{one-shot} per il file
+ \constd{EPOLLONESHOT}& Imposta la modalità \textit{one-shot} per il file
descriptor associato (questa modalità è disponibile
solo a partire dal kernel 2.6.2).\\
- \const{EPOLLWAKEUP} & Attiva la prevenzione della sospensione del sistema
+ \constd{EPOLLWAKEUP} & Attiva la prevenzione della sospensione del sistema
se il file descriptor che si è marcato con esso
diventa pronto (aggiunto a partire dal kernel 3.5),
può essere impostato solo dall'amministratore (o da
ed è utile per riconoscere la chiusura di una connessione dall'altro capo di
un socket quando si lavora in modalità \textit{edge triggered}.}
-Il secondo campo, \var{data}, è una \direct{union} che serve a identificare il
+% TODO aggiunto con il kernel 4.5 EPOLLEXCLUSIVE, vedi
+% http://lwn.net/Articles/633422/#excl
+
+Il secondo campo, \var{data}, è una \dirct{union} che serve a identificare il
file descriptor a cui si intende fare riferimento, ed in astratto può
contenere un valore qualsiasi (specificabile in diverse forme) che ne permetta
una indicazione univoca. Il modo più comune di usarlo però è quello in cui si
{La funzione ritorna il numero di file descriptor pronti in caso di successo e
$-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori già
- visti con \funcd{epoll\_wait}.
+ visti con \func{epoll\_wait}.
}
\end{funcproto}
-La funzione è del tutto analoga \funcd{epoll\_wait}, soltanto che alla sua
+La funzione è del tutto analoga \func{epoll\_wait}, soltanto che alla sua
uscita viene ripristinata la maschera di segnali originale, sostituita durante
l'esecuzione da quella impostata con l'argomento \param{sigmask}; in sostanza
la chiamata a questa funzione è equivalente al seguente codice, eseguito però
dettaglio le caratteristiche dei socket; in particolare si potrà trovare un
programma che utilizza questa interfaccia in sez.~\ref{sec:TCP_serv_epoll}.
+% TODO: trattare epoll_wait2, introdotta con il kernel 5.11 (vedi
+% https://lwn.net/Articles/837816/)
+
\itindend{epoll}
Abbiamo visto in sez.~\ref{sec:file_select} come il meccanismo classico delle
notifiche di eventi tramite i segnali, presente da sempre nei sistemi
unix-like, porti a notevoli problemi nell'interazione con le funzioni per
-l'\textit{I/O multiplexing}, tanto che per evitare possibili
-\itindex{race~condition} \textit{race condition} sono state introdotte
-estensioni dello standard POSIX e funzioni apposite come \func{pselect},
-\func{ppoll} e \funcd{epoll\_pwait}.
+l'\textit{I/O multiplexing}, tanto che per evitare possibili \textit{race
+ condition} sono state introdotte estensioni dello standard POSIX e funzioni
+apposite come \func{pselect}, \func{ppoll} e \func{epoll\_pwait}.
Benché i segnali siano il meccanismo più usato per effettuare notifiche ai
processi, la loro interfaccia di programmazione, che comporta l'esecuzione di
asincrone in qualunque momento. Questo comporta la necessità di dover
gestire, quando si deve tener conto di entrambi i tipi di eventi, le
interruzioni delle funzioni di attesa sincrone, ed evitare possibili
-\itindex{race~condition} \textit{race conditions}. In sostanza se non ci
-fossero i segnali non ci sarebbe da preoccuparsi, fintanto che si effettuano
-operazioni all'interno di un processo, della non atomicità delle
-\index{system~call~lente} \textit{system call} lente che vengono interrotte e
-devono essere riavviate.
+\textit{race conditions}. In sostanza se non ci fossero i segnali non ci
+sarebbe da preoccuparsi, fintanto che si effettuano operazioni all'interno di
+un processo, della non atomicità delle \textit{system call} lente che vengono
+interrotte e devono essere riavviate.
Abbiamo visto però in sez.~\ref{sec:sig_real_time} che insieme ai segnali
\textit{real-time} sono state introdotte anche delle interfacce di gestione
ricezione leggendone la notifica tramite l'uso di uno speciale file
descriptor. Trattandosi di un file descriptor questo potrà essere tenuto sotto
osservazione con le ordinarie funzioni dell'\textit{I/O multiplexing} (vale a
-dire con le solite \func{select}, \func{poll} e \funcd{epoll\_wait}) allo
+dire con le solite \func{select}, \func{poll} e \func{epoll\_wait}) allo
stesso modo di quelli associati a file o socket, per cui alla fine si potrà
attendere in contemporanea sia l'arrivo del segnale che la disponibilità di
accesso ai dati relativi a questi ultimi.
La funzione di sistema che permette di abilitare la ricezione dei segnali
tramite file descriptor è \funcd{signalfd},\footnote{in realtà quella
- riportata è l'interfaccia alla funzione fornita dalle \acr{glibc}, esistono
+ riportata è l'interfaccia alla funzione fornita dalla \acr{glibc}, esistono
infatti due versioni diverse della \textit{system call}; una prima versione,
- \func{signalfd}, introdotta nel kernel 2.6.22 e disponibile con le
+ \func{signalfd}, introdotta nel kernel 2.6.22 e disponibile con la
\acr{glibc} 2.8 che non supporta l'argomento \texttt{flags}, ed una seconda
versione, \funcm{signalfd4}, introdotta con il kernel 2.6.27 e che è quella
- che viene sempre usata a partire dalle \acr{glibc} 2.9, che prende un
+ che viene sempre usata a partire dalla \acr{glibc} 2.9, che prende un
argomento aggiuntivo \code{size\_t sizemask} che indica la dimensione della
- maschera dei segnali, il cui valore viene impostato automaticamente dalle
+ maschera dei segnali, il cui valore viene impostato automaticamente dalla
\acr{glibc}.} il cui prototipo è:
\begin{funcproto}{
\item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
con \func{signalfd} o il valore di \param{flags} non è valido.
\item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli \itindex{inode} \textit{inode}
+ dispositivo per la gestione anonima degli \textit{inode}
associati al file descriptor.
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
descriptor di \func{signalfd}.
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{SFD\_NONBLOCK}& imposta sul file descriptor il flag di
+ \constd{SFD\_NONBLOCK}&imposta sul file descriptor il flag di
\const{O\_NONBLOCK} per renderlo non bloccante.\\
- \const{SFD\_CLOEXEC}& imposta il flag di \const{O\_CLOEXEC} per la
+ \constd{SFD\_CLOEXEC}& imposta il flag di \const{O\_CLOEXEC} per la
chiusura automatica del file descriptor nella
esecuzione di \func{exec}.\\
\hline
Come anticipato, essendo questo lo scopo principale della nuova interfaccia,
il file descriptor può essere tenuto sotto osservazione tramite le funzioni
dell'\textit{I/O multiplexing} (vale a dire con le solite \func{select},
-\func{poll} e \funcd{epoll\_wait}), e risulterà accessibile in lettura quando
+\func{poll} e \func{epoll\_wait}), e risulterà accessibile in lettura quando
uno o più dei segnali indicati tramite \param{mask} sarà pendente.
La funzione può essere chiamata più volte dallo stesso processo, consentendo
potranno essere ricevuti normalmente una volta che si rimuova il blocco
imposto con \func{sigprocmask}.
-Oltre che con le funzioni dell'\textit{I/O multiplexing} l'uso del file
-descriptor restituito da \func{signalfd} cerca di seguire la semantica di un
-sistema unix-like anche con altre \textit{system call}; in particolare esso
+Oltre a poter essere usato con le funzioni dell'\textit{I/O multiplexing}, il
+file descriptor restituito da \func{signalfd} cerca di seguire la semantica di
+un sistema unix-like anche con altre \textit{system call}; in particolare esso
resta aperto (come ogni altro file descriptor) attraverso una chiamata ad
\func{exec}, a meno che non lo si sia creato con il flag di
-\const{SFD\_CLOEXEC} o si sia successivamente impostato il
+\const{SFD\_CLOEXEC} o si sia successivamente impostato il
\textit{close-on-exec} con \func{fcntl}. Questo comportamento corrisponde
anche alla ordinaria semantica relativa ai segnali bloccati, che restano
pendenti attraverso una \func{exec}.
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{0.90\textwidth}
+ \begin{minipage}[c]{0.95\textwidth}
\includestruct{listati/signalfd_siginfo.h}
\end{minipage}
\normalsize
\label{fig:fiforeporter_code_init}
\end{figure}
-Il contenuto di \struct{signalfd\_siginfo} ricalca da vicino quella della
-analoga struttura \struct{siginfo\_t} (illustrata in
+Il contenuto di \struct{signalfd\_siginfo} ricalca da vicino quella
+dell'analoga struttura \struct{siginfo\_t} (illustrata in
fig.~\ref{fig:sig_siginfo_t}) usata dall'interfaccia ordinaria dei segnali, e
restituisce dati simili. Come per \struct{siginfo\_t} i campi che vengono
avvalorati dipendono dal tipo di segnale e ricalcano i valori che abbiamo già
\var{ssi\_ptr} e \var{ssi\_int} per segnali inviati con \func{sigqueue}.}
Come esempio di questa nuova interfaccia ed anche come esempio di applicazione
-della interfaccia di \itindex{epoll} \textit{epoll}, si è scritto un programma
-elementare che stampi sullo standard output sia quanto viene scritto da terzi
-su una \textit{named fifo}, che l'avvenuta ricezione di alcuni segnali. Il
-codice completo si trova al solito nei sorgenti allegati alla guida (nel file
+della interfaccia di \textit{epoll}, si è scritto un programma elementare che
+stampi sullo \textit{standard output} sia quanto viene scritto da terzi su una
+\textit{named fifo}, che l'avvenuta ricezione di alcuni segnali. Il codice
+completo si trova al solito nei sorgenti allegati alla guida (nel file
\texttt{FifoReporter.c}).
In fig.~\ref{fig:fiforeporter_code_init} si è riportata la parte iniziale del
programma in cui vengono effettuate le varie inizializzazioni necessarie per
-l'uso di \itindex{epoll} \textit{epoll} e \func{signalfd}, a partire
-(\texttt{\small 12-16}) dalla definizione delle varie variabili e strutture
-necessarie. Al solito si è tralasciata la parte dedicata alla decodifica delle
-opzioni che consentono ad esempio di cambiare il nome del file associato alla
-\textit{fifo}.
+l'uso di \textit{epoll} e \func{signalfd}, a partire (\texttt{\small 12-16})
+dalla definizione delle varie variabili e strutture necessarie. Al solito si è
+tralasciata la parte dedicata alla decodifica delle opzioni che consentono ad
+esempio di cambiare il nome del file associato alla \textit{fifo}.
Il primo passo (\texttt{\small 19-20}) è la creazione di un file descriptor
-\texttt{epfd} di \itindex{epoll} \textit{epoll} con \func{epoll\_create} che è
-quello che useremo per il controllo degli altri. É poi necessario
-disabilitare la ricezione dei segnali (nel caso \signal{SIGINT},
-\signal{SIGQUIT} e \signal{SIGTERM}) per i quali si vuole la notifica tramite
-file descriptor. Per questo prima li si inseriscono (\texttt{\small 22-25}) in
-una maschera di segnali \texttt{sigmask} che useremo con (\texttt{\small 26})
+\texttt{epfd} di \textit{epoll} con \func{epoll\_create} che è quello che
+useremo per il controllo degli altri. É poi necessario disabilitare la
+ricezione dei segnali (nel caso \signal{SIGINT}, \signal{SIGQUIT} e
+\signal{SIGTERM}) per i quali si vuole la notifica tramite file
+descriptor. Per questo prima li si inseriscono (\texttt{\small 22-25}) in una
+maschera di segnali \texttt{sigmask} che useremo con (\texttt{\small 26})
\func{sigprocmask} per disabilitarli. Con la stessa maschera si potrà per
passare all'uso (\texttt{\small 28-29}) di \func{signalfd} per abilitare la
notifica sul file descriptor \var{sigfd}. Questo poi (\texttt{\small 30-33})
controllati con \texttt{epfd}.
Occorrerà infine (\texttt{\small 35-38}) creare la \textit{named fifo} se
-questa non esiste ed aprirla per la lettura (\texttt{\small 39-40}); una
-volta fatto questo sarà necessario aggiungere il relativo file descriptor
-(\var{fifofd}) a quelli osservati da \itindex{epoll} \textit{epoll} in maniera
-del tutto analoga a quanto fatto con quello relativo alla notifica dei
-segnali.
+questa non esiste ed aprirla per la lettura (\texttt{\small 39-40}); una volta
+fatto questo sarà necessario aggiungere il relativo file descriptor
+(\var{fifofd}) a quelli osservati da \textit{epoll} in maniera del tutto
+analoga a quanto fatto con quello relativo alla notifica dei segnali.
\begin{figure}[!htb]
\footnotesize \centering
interfaccia è stata introdotta in forma considerata difettosa con il kernel
2.6.22, per cui è stata immediatamente tolta nel successivo 2.6.23 e
reintrodotta in una forma considerata adeguata nel kernel 2.6.25, il
- supporto nelle \acr{glibc} è stato introdotto a partire dalla versione
+ supporto nella \acr{glibc} è stato introdotto a partire dalla versione
2.8.6, la versione del kernel 2.6.22, presente solo su questo kernel, non è
supportata e non deve essere usata.} La prima funzione di sistema prevista,
quella che consente di creare un timer, è \funcd{timerfd\_create}, il cui
l'argomento \param{flag} non è valido, o è diverso da zero per kernel
precedenti il 2.6.27.
\item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli \itindex{inode} \textit{inode}
- associati al file descriptor.
+ dispositivo per la gestione anonima degli \textit{inode} associati al file
+ descriptor.
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
descriptor di \func{signalfd}.
\end{errlist}
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{TFD\_NONBLOCK}& imposta sul file descriptor il flag di
- \const{O\_NONBLOCK} per renderlo non bloccante.\\
- \const{TFD\_CLOEXEC}& imposta il flag di \const{O\_CLOEXEC} per la
- chiusura automatica del file descriptor nella
- esecuzione di \func{exec}.\\
+ \constd{TFD\_NONBLOCK}& imposta sul file descriptor il flag di
+ \const{O\_NONBLOCK} per renderlo non bloccante.\\
+ \constd{TFD\_CLOEXEC} & imposta il flag di \const{O\_CLOEXEC} per la
+ chiusura automatica del file descriptor nella
+ esecuzione di \func{exec}.\\
\hline
\end{tabular}
\caption{Valori dell'argomento \param{flags} per la funzione
riguarda l'argomento \param{flags} che serve sempre ad indicare se il tempo di
scadenza del timer è da considerarsi relativo o assoluto rispetto al valore
corrente dell'orologio associato al timer, ma che in questo caso ha come
-valori possibili rispettivamente soltanto $0$ e \const{TFD\_TIMER\_ABSTIME}
+valori possibili rispettivamente soltanto $0$ e \constd{TFD\_TIMER\_ABSTIME}
(l'analogo di \const{TIMER\_ABSTIME}).
L'ultima funzione di sistema prevista dalla nuova interfaccia è
descriptor risulta pronto sarà possibile leggere il numero di volte che il
timer è scaduto con una ordinaria \func{read}.
-La funzione legge il valore in un dato di tipo \type{uint64\_t}, e necessita
+La funzione legge il valore in un dato di tipo \typed{uint64\_t}, e necessita
pertanto che le si passi un buffer di almeno 8 byte, fallendo con
\errval{EINVAL} in caso contrario, in sostanza la lettura deve essere
effettuata con una istruzione del tipo:
\textsl{asincrona}, quelle cioè in cui un processo non deve bloccarsi in
attesa della disponibilità dell'accesso al file, ma può proseguire
nell'esecuzione utilizzando invece un meccanismo di notifica asincrono (di
-norma un segnale, ma esistono anche altre interfacce, come \itindex{inotify}
-\textit{inotify}), per essere avvisato della possibilità di eseguire le
-operazioni di I/O volute.
+norma un segnale, ma esistono anche altre interfacce, come \textit{inotify}),
+per essere avvisato della possibilità di eseguire le operazioni di I/O volute.
\subsection{Il \textit{Signal driven I/O}}
Per questo motivo Stevens, ed anche le pagine di manuale di Linux, chiamano
questa modalità ``\textit{Signal driven I/O}''. Si tratta di un'altra
-modalità di gestione dell'I/O, alternativa all'uso di \itindex{epoll}
+modalità di gestione dell'I/O, alternativa all'uso di
\textit{epoll},\footnote{anche se le prestazioni ottenute con questa tecnica
sono inferiori, il vantaggio è che questa modalità è utilizzabile anche con
kernel che non supportano \textit{epoll}, come quelli della serie 2.4,
all'origine di Unix i soli programmi che potevano avere una tale esigenza
erano i demoni, attenendosi a uno dei criteri base della progettazione, che
era di far fare al kernel solo le operazioni strettamente necessarie e
-lasciare tutto il resto a processi in user space, non era stata prevista
-nessuna funzionalità di notifica.
+lasciare tutto il resto a processi in \textit{user space}, non era stata
+prevista nessuna funzionalità di notifica.
Visto però il crescente interesse nei confronti di una funzionalità di questo
tipo, che è molto richiesta specialmente nello sviluppo dei programmi ad
filesystem, sono state successivamente introdotte delle estensioni che
permettessero la creazione di meccanismi di notifica più efficienti dell'unica
soluzione disponibile con l'interfaccia tradizionale, che è quella del
-\itindex{polling} \textit{polling}.
+\textit{polling}.
Queste nuove funzionalità sono delle estensioni specifiche, non
standardizzate, che sono disponibili soltanto su Linux (anche se altri kernel
\itindbeg{file~lease}
+% TODO: questa funzionalità potrebbe essere estesa vedi:
+% https://lwn.net/Articles/796000/
+
La prima di queste funzionalità è quella del cosiddetto \textit{file lease};
questo è un meccanismo che consente ad un processo, detto \textit{lease
holder}, di essere notificato quando un altro processo, chiamato a sua volta
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{F\_RDLCK} & Richiede un \textit{read lease}.\\
- \const{F\_WRLCK} & Richiede un \textit{write lease}.\\
- \const{F\_UNLCK} & Rilascia un \textit{file lease}.\\
+ \constd{F\_RDLCK} & Richiede un \textit{read lease}.\\
+ \constd{F\_WRLCK} & Richiede un \textit{write lease}.\\
+ \constd{F\_UNLCK} & Rilascia un \textit{file lease}.\\
\hline
\end{tabular}
\caption{Costanti per i tre possibili valori dell'argomento \param{arg} di
(\textit{pipe} e dispositivi sono quindi esclusi). Inoltre un processo non
privilegiato può ottenere un \textit{lease} soltanto per un file appartenente
ad un \ids{UID} corrispondente a quello del processo. Soltanto un processo con
-privilegi di amministratore (cioè con la \itindex{capabilities} capability
-\const{CAP\_LEASE}, vedi sez.~\ref{sec:proc_capabilities}) può acquisire
-\textit{lease} su qualunque file.
+privilegi di amministratore (cioè con la capacità \const{CAP\_LEASE}, vedi
+sez.~\ref{sec:proc_capabilities}) può acquisire \textit{lease} su qualunque
+file.
Se su un file è presente un \textit{lease} quando il \textit{lease breaker}
esegue una \func{truncate} o una \func{open} che confligge con
Se il \textit{lease holder} non provvede a rilasciare il \textit{lease} entro
il numero di secondi specificato dal parametro di sistema mantenuto in
-\sysctlfile{fs/lease-break-time} sarà il kernel stesso a rimuoverlo o
+\sysctlfiled{fs/lease-break-time} sarà il kernel stesso a rimuoverlo o
declassarlo automaticamente (questa è una misura di sicurezza per evitare che
un processo blocchi indefinitamente l'accesso ad un file acquisendo un
\textit{lease}). Una volta che un \textit{lease} è stato rilasciato o
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{DN\_ACCESS} & Un file è stato acceduto, con l'esecuzione di una fra
- \func{read}, \func{pread}, \func{readv}.\\
- \const{DN\_MODIFY} & Un file è stato modificato, con l'esecuzione di una
- fra \func{write}, \func{pwrite}, \func{writev},
- \func{truncate}, \func{ftruncate}.\\
- \const{DN\_CREATE} & È stato creato un file nella directory, con
- l'esecuzione di una fra \func{open}, \func{creat},
- \func{mknod}, \func{mkdir}, \func{link},
- \func{symlink}, \func{rename} (da un'altra
- directory).\\
- \const{DN\_DELETE} & È stato cancellato un file dalla directory con
- l'esecuzione di una fra \func{unlink}, \func{rename}
- (su un'altra directory), \func{rmdir}.\\
- \const{DN\_RENAME} & È stato rinominato un file all'interno della
- directory (con \func{rename}).\\
- \const{DN\_ATTRIB} & È stato modificato un attributo di un file con
- l'esecuzione di una fra \func{chown}, \func{chmod},
- \func{utime}.\\
- \const{DN\_MULTISHOT}& Richiede una notifica permanente di tutti gli
- eventi.\\
+ \constd{DN\_ACCESS} & Un file è stato acceduto, con l'esecuzione di una fra
+ \func{read}, \func{pread}, \func{readv}.\\
+ \constd{DN\_MODIFY} & Un file è stato modificato, con l'esecuzione di una
+ fra \func{write}, \func{pwrite}, \func{writev},
+ \func{truncate}, \func{ftruncate}.\\
+ \constd{DN\_CREATE} & È stato creato un file nella directory, con
+ l'esecuzione di una fra \func{open}, \func{creat},
+ \func{mknod}, \func{mkdir}, \func{link},
+ \func{symlink}, \func{rename} (da un'altra
+ directory).\\
+ \constd{DN\_DELETE} & È stato cancellato un file dalla directory con
+ l'esecuzione di una fra \func{unlink}, \func{rename}
+ (su un'altra directory), \func{rmdir}.\\
+ \constd{DN\_RENAME} & È stato rinominato un file all'interno della
+ directory (con \func{rename}).\\
+ \constd{DN\_ATTRIB} & È stato modificato un attributo di un file con
+ l'esecuzione di una fra \func{chown}, \func{chmod},
+ \func{utime}.\\
+ \constd{DN\_MULTISHOT}& Richiede una notifica permanente di tutti gli
+ eventi.\\
\hline
\end{tabular}
\caption{Le costanti che identificano le varie classi di eventi per i quali
questa è una interfaccia specifica di Linux (pertanto non deve essere usata se
si devono scrivere programmi portabili), ed è basata sull'uso di una coda di
notifica degli eventi associata ad un singolo file descriptor, il che permette
-di risolvere il principale problema di \itindex{dnotify} \textit{dnotify}. La
-coda viene creata attraverso la funzione di sistema \funcd{inotify\_init}, il
-cui prototipo è:
+di risolvere il principale problema di \textit{dnotify}. La coda viene creata
+attraverso la funzione di sistema \funcd{inotify\_init}, il cui prototipo è:
\begin{funcproto}{
\fhead{sys/inotify.h}
delle risorse di sistema è previsto che un utente possa utilizzare un numero
limitato di istanze di \textit{inotify}; il valore di default del limite è di
128, ma questo valore può essere cambiato con \func{sysctl} o usando il file
-\sysctlfile{fs/inotify/max\_user\_instances}.
+\sysctlfiled{fs/inotify/max\_user\_instances}.
Dato che questo file descriptor non è associato a nessun file o directory
reale, l'inconveniente di non poter smontare un filesystem i cui file sono
Inoltre trattandosi di un file descriptor a tutti gli effetti, esso potrà
essere utilizzato come argomento per le funzioni \func{select} e \func{poll} e
con l'interfaccia di \textit{epoll}, ed a partire dal kernel 2.6.25 è stato
-introdotto anche il supporto per il \itindex{signal~driven~I/O}
-\texttt{signal-driven I/O}. Siccome gli eventi vengono notificati come dati
-disponibili in lettura, dette funzioni ritorneranno tutte le volte che si avrà
-un evento di notifica.
+introdotto anche il supporto per il \texttt{signal-driven I/O}. Siccome gli
+eventi vengono notificati come dati disponibili in lettura, dette funzioni
+ritorneranno tutte le volte che si avrà un evento di notifica.
Così, invece di dover utilizzare i segnali, considerati una pessima scelta dal
punto di vista dell'interfaccia utente, si potrà gestire l'osservazione degli
directory che si vogliono tenere sotto osservazione,\footnote{anche in questo
caso c'è un limite massimo che di default è pari a 8192, ed anche questo
valore può essere cambiato con \func{sysctl} o usando il file
- \sysctlfile{fs/inotify/max\_user\_watches}.} e si utilizzerà sempre un solo
+ \sysctlfiled{fs/inotify/max\_user\_watches}.} e si utilizzerà sempre un solo
file descriptor.
Il tipo di evento che si vuole osservare deve essere specificato
\textbf{Valore} & & \textbf{Significato} \\
\hline
\hline
- \const{IN\_ACCESS} &$\bullet$& C'è stato accesso al file in
- lettura.\\
- \const{IN\_ATTRIB} &$\bullet$& Ci sono stati cambiamenti sui dati
- dell'\itindex{inode} \textit{inode}
- (o sugli attributi estesi, vedi
- sez.~\ref{sec:file_xattr}).\\
- \const{IN\_CLOSE\_WRITE} &$\bullet$& È stato chiuso un file aperto in
- scrittura.\\
- \const{IN\_CLOSE\_NOWRITE}&$\bullet$& È stato chiuso un file aperto in
- sola lettura.\\
- \const{IN\_CREATE} &$\bullet$& È stato creato un file o una
- directory in una directory sotto
- osservazione.\\
- \const{IN\_DELETE} &$\bullet$& È stato cancellato un file o una
- directory in una directory sotto
- osservazione.\\
- \const{IN\_DELETE\_SELF} & -- & È stato cancellato il file (o la
- directory) sotto osservazione.\\
- \const{IN\_MODIFY} &$\bullet$& È stato modificato il file.\\
- \const{IN\_MOVE\_SELF} & & È stato rinominato il file (o la
+ \constd{IN\_ACCESS} &$\bullet$& C'è stato accesso al file in
+ lettura.\\
+ \constd{IN\_ATTRIB} &$\bullet$& Ci sono stati cambiamenti sui dati
+ dell'\textit{inode}
+ (o sugli attributi estesi, vedi
+ sez.~\ref{sec:file_xattr}).\\
+ \constd{IN\_CLOSE\_WRITE} &$\bullet$& È stato chiuso un file aperto in
+ scrittura.\\
+ \constd{IN\_CLOSE\_NOWRITE}&$\bullet$& È stato chiuso un file aperto in
+ sola lettura.\\
+ \constd{IN\_CREATE} &$\bullet$& È stato creato un file o una
+ directory in una directory sotto
+ osservazione.\\
+ \constd{IN\_DELETE} &$\bullet$& È stato cancellato un file o una
+ directory in una directory sotto
+ osservazione.\\
+ \constd{IN\_DELETE\_SELF} & -- & È stato cancellato il file (o la
directory) sotto osservazione.\\
- \const{IN\_MOVED\_FROM} &$\bullet$& Un file è stato spostato fuori dalla
- directory sotto osservazione.\\
- \const{IN\_MOVED\_TO} &$\bullet$& Un file è stato spostato nella
- directory sotto osservazione.\\
- \const{IN\_OPEN} &$\bullet$& Un file è stato aperto.\\
+ \constd{IN\_MODIFY} &$\bullet$& È stato modificato il file.\\
+ \constd{IN\_MOVE\_SELF} & & È stato rinominato il file (o la
+ directory) sotto osservazione.\\
+ \constd{IN\_MOVED\_FROM} &$\bullet$& Un file è stato spostato fuori dalla
+ directory sotto osservazione.\\
+ \constd{IN\_MOVED\_TO} &$\bullet$& Un file è stato spostato nella
+ directory sotto osservazione.\\
+ \constd{IN\_OPEN} &$\bullet$& Un file è stato aperto.\\
\hline
- \const{IN\_CLOSE} & & Combinazione di
- \const{IN\_CLOSE\_WRITE} e
- \const{IN\_CLOSE\_NOWRITE}.\\
- \const{IN\_MOVE} & & Combinazione di
- \const{IN\_MOVED\_FROM} e
- \const{IN\_MOVED\_TO}.\\
- \const{IN\_ALL\_EVENTS} & & Combinazione di tutti i flag
- possibili.\\
+ \constd{IN\_CLOSE} & & Combinazione di
+ \const{IN\_CLOSE\_WRITE} e
+ \const{IN\_CLOSE\_NOWRITE}.\\
+ \constd{IN\_MOVE} & & Combinazione di
+ \const{IN\_MOVED\_FROM} e
+ \const{IN\_MOVED\_TO}.\\
+ \constd{IN\_ALL\_EVENTS} & & Combinazione di tutti i flag
+ possibili.\\
\hline
\end{tabular}
\caption{Le costanti che identificano i bit della maschera binaria
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{IN\_DONT\_FOLLOW}& Non dereferenzia \param{pathname} se questo è un
- link simbolico.\\
- \const{IN\_MASK\_ADD} & Aggiunge a quelli già impostati i flag indicati
- nell'argomento \param{mask}, invece di
- sovrascriverli.\\
- \const{IN\_ONESHOT} & Esegue l'osservazione su \param{pathname} per una
- sola volta, rimuovendolo poi dalla \textit{watch
- list}.\\
- \const{IN\_ONLYDIR} & Se \param{pathname} è una directory riporta
- soltanto gli eventi ad essa relativi e non
- quelli per i file che contiene.\\
+ \constd{IN\_DONT\_FOLLOW}& Non dereferenzia \param{pathname} se questo è un
+ link simbolico.\\
+ \constd{IN\_MASK\_ADD} & Aggiunge a quelli già impostati i flag indicati
+ nell'argomento \param{mask}, invece di
+ sovrascriverli.\\
+ \constd{IN\_ONESHOT} & Esegue l'osservazione su \param{pathname} per
+ una sola volta, rimuovendolo poi dalla
+ \textit{watch list}.\\
+ \constd{IN\_ONLYDIR} & Se \param{pathname} è una directory riporta
+ soltanto gli eventi ad essa relativi e non
+ quelli per i file che contiene.\\
\hline
\end{tabular}
\caption{Le costanti che identificano i bit della maschera binaria
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{IN\_IGNORED} & L'osservatore è stato rimosso, sia in maniera
- esplicita con l'uso di \func{inotify\_rm\_watch},
- che in maniera implicita per la rimozione
- dell'oggetto osservato o per lo smontaggio del
- filesystem su cui questo si trova.\\
- \const{IN\_ISDIR} & L'evento avvenuto fa riferimento ad una directory
- (consente così di distinguere, quando si pone
- sotto osservazione una directory, fra gli eventi
- relativi ad essa e quelli relativi ai file che
- essa contiene).\\
- \const{IN\_Q\_OVERFLOW}& Si sono eccedute le dimensioni della coda degli
- eventi (\textit{overflow} della coda); in questo
- caso il valore di \var{wd} è $-1$.\footnotemark\\
- \const{IN\_UNMOUNT} & Il filesystem contenente l'oggetto posto sotto
- osservazione è stato smontato.\\
+ \constd{IN\_IGNORED} & L'osservatore è stato rimosso, sia in maniera
+ esplicita con l'uso di \func{inotify\_rm\_watch},
+ che in maniera implicita per la rimozione
+ dell'oggetto osservato o per lo smontaggio del
+ filesystem su cui questo si trova.\\
+ \constd{IN\_ISDIR} & L'evento avvenuto fa riferimento ad una directory
+ (consente così di distinguere, quando si pone
+ sotto osservazione una directory, fra gli eventi
+ relativi ad essa e quelli relativi ai file che
+ essa contiene).\\
+ \constd{IN\_Q\_OVERFLOW}& Si sono eccedute le dimensioni della coda degli
+ eventi (\textit{overflow} della coda); in questo
+ caso il valore di \var{wd} è $-1$.\footnotemark\\
+ \constd{IN\_UNMOUNT} & Il filesystem contenente l'oggetto posto sotto
+ osservazione è stato smontato.\\
\hline
\end{tabular}
\caption{Le costanti che identificano i bit aggiuntivi usati nella maschera
\footnotetext{la coda di notifica ha una dimensione massima che viene
controllata dal parametro di sistema
- \sysctlfile{fs/inotify/max\_queued\_events}, che indica il numero massimo di
+ \sysctlfiled{fs/inotify/max\_queued\_events}, che indica il numero massimo di
eventi che possono essere mantenuti sulla stessa; quando detto valore viene
ecceduto gli ulteriori eventi vengono scartati, ma viene comunque generato
un evento di tipo \const{IN\_Q\_OVERFLOW}.}
Infine due campi \var{name} e \var{len} sono utilizzati soltanto quando
l'evento è relativo ad un file presente in una directory posta sotto
osservazione, in tal caso essi contengono rispettivamente il nome del file
-(come \itindsub{pathname}{relativo} \textit{pathname} relativo alla directory
-osservata) e la relativa dimensione in byte. Il campo \var{name} viene sempre
-restituito come stringa terminata da NUL, con uno o più zeri di terminazione,
-a seconda di eventuali necessità di allineamento del risultato, ed il valore
-di \var{len} corrisponde al totale della dimensione di \var{name}, zeri
-aggiuntivi compresi. La stringa con il nome del file viene restituita nella
-lettura subito dopo la struttura \struct{inotify\_event}; questo significa che
-le dimensioni di ciascun evento di \textit{inotify} saranno pari a
-\code{sizeof(\struct{inotify\_event}) + len}.
+(come \textit{pathname} relativo alla directory osservata) e la relativa
+dimensione in byte. Il campo \var{name} viene sempre restituito come stringa
+terminata da NUL, con uno o più zeri di terminazione, a seconda di eventuali
+necessità di allineamento del risultato, ed il valore di \var{len} corrisponde
+al totale della dimensione di \var{name}, zeri aggiuntivi compresi. La stringa
+con il nome del file viene restituita nella lettura subito dopo la struttura
+\struct{inotify\_event}; questo significa che le dimensioni di ciascun evento
+di \textit{inotify} saranno pari a \code{sizeof(\struct{inotify\_event}) +
+ len}.
Vediamo allora un esempio dell'uso dell'interfaccia di \textit{inotify} con un
semplice programma che permette di mettere sotto osservazione uno o più file e
% TODO trattare fanotify, vedi http://lwn.net/Articles/339399/ e
% http://lwn.net/Articles/343346/ (incluso nel 2.6.36)
+% fanotify_mark() ha FAN_MARK_FILESYSTEM dal 4.20
+% fanotify() ha FAN_OPEN_EXEC dal 4.21/5.0
\subsection{L'interfaccia POSIX per l'I/O asincrono}
Benché la modalità di apertura asincrona di un file vista in
sez.~\ref{sec:signal_driven_io} possa risultare utile in varie occasioni (in
particolar modo con i socket e gli altri file per i quali le funzioni di I/O
-sono \index{system~call~lente} \textit{system call} lente), essa è comunque
-limitata alla notifica della disponibilità del file descriptor per le
-operazioni di I/O, e non ad uno svolgimento asincrono delle medesime. Lo
-standard POSIX.1b definisce una interfaccia apposita per l'I/O asincrono vero
-e proprio,\footnote{questa è stata ulteriormente perfezionata nelle successive
- versioni POSIX.1-2001 e POSIX.1-2008.} che prevede un insieme di funzioni
-dedicate per la lettura e la scrittura dei file, completamente separate
-rispetto a quelle usate normalmente.
+sono \textit{system call} lente), essa è comunque limitata alla notifica della
+disponibilità del file descriptor per le operazioni di I/O, e non ad uno
+svolgimento asincrono delle medesime. Lo standard POSIX.1b definisce una
+interfaccia apposita per l'I/O asincrono vero e proprio,\footnote{questa è
+ stata ulteriormente perfezionata nelle successive versioni POSIX.1-2001 e
+ POSIX.1-2008.} che prevede un insieme di funzioni dedicate per la lettura e
+la scrittura dei file, completamente separate rispetto a quelle usate
+normalmente.
In generale questa interfaccia è completamente astratta e può essere
-implementata sia direttamente nel kernel che in user space attraverso l'uso di
-\itindex{thread} \textit{thread}. Per le versioni del kernel meno recenti
-esiste una implementazione di questa interfaccia fornita completamente delle
-\acr{glibc} a partire dalla versione 2.1, che è realizzata completamente in
-user space, ed è accessibile linkando i programmi con la libreria
+implementata sia direttamente nel kernel che in \textit{user space} attraverso
+l'uso di \textit{thread}. Per le versioni del kernel meno recenti esiste una
+implementazione di questa interfaccia fornita completamente dalla \acr{glibc}
+a partire dalla versione 2.1, che è realizzata completamente in \textit{user
+ space}, ed è accessibile linkando i programmi con la libreria
\file{librt}. A partire dalla versione 2.5.32 è stato introdotto nel kernel
una nuova infrastruttura per l'I/O asincrono, ma ancora il supporto è parziale
ed insufficiente ad implementare tutto l'AIO POSIX.
controllate attraverso l'uso di una apposita struttura \struct{aiocb} (il cui
nome sta per \textit{asyncronous I/O control block}), che viene passata come
argomento a tutte le funzioni dell'interfaccia. La sua definizione, come
-effettuata in \headfile{aio.h}, è riportata in
+effettuata in \headfiled{aio.h}, è riportata in
fig.~\ref{fig:file_aiocb}. Nello steso file è definita la macro
-\macro{\_POSIX\_ASYNCHRONOUS\_IO}, che dichiara la disponibilità
+\macrod{\_POSIX\_ASYNCHRONOUS\_IO}, che dichiara la disponibilità
dell'interfaccia per l'I/O asincrono.
\begin{figure}[!htb]
Il campo \var{aio\_reqprio} permette di impostare la priorità delle operazioni
di I/O, in generale perché ciò sia possibile occorre che la piattaforma
supporti questa caratteristica, questo viene indicato dal fatto che le macro
-\macro{\_POSIX\_PRIORITIZED\_IO}, e \macro{\_POSIX\_PRIORITY\_SCHEDULING} sono
-definite. La priorità viene impostata a partire da quella del processo
+\macrod{\_POSIX\_PRIORITIZED\_IO}, e \macrod{\_POSIX\_PRIORITY\_SCHEDULING}
+sono definite. La priorità viene impostata a partire da quella del processo
chiamante (vedi sez.~\ref{sec:proc_priority}), cui viene sottratto il valore
di questo campo. Il campo \var{aio\_lio\_opcode} è usato solo dalla funzione
\func{lio\_listio}, che, come vedremo, permette di eseguire con una sola
esse.
Infine il campo \var{aio\_sigevent} è una struttura di tipo \struct{sigevent}
-(illustrata in in fig.~\ref{fig:struct_sigevent}) che serve a specificare il
-modo in cui si vuole che venga effettuata la notifica del completamento delle
+(illustrata in fig.~\ref{fig:struct_sigevent}) che serve a specificare il modo
+in cui si vuole che venga effettuata la notifica del completamento delle
operazioni richieste; per la trattazione delle modalità di utilizzo della
stessa si veda quanto già visto in proposito in sez.~\ref{sec:sig_timer_adv}.
operazione può dar luogo a risultati impredicibili, perché l'accesso ai vari
campi per eseguire l'operazione può avvenire in un momento qualsiasi dopo la
richiesta. Questo comporta che non si devono usare per \param{aiocbp}
-\index{variabili!automatiche} variabili automatiche e che non si deve
-riutilizzare la stessa struttura per un'altra operazione fintanto che la
-precedente non sia stata ultimata. In generale per ogni operazione si deve
-utilizzare una diversa struttura \struct{aiocb}.
+variabili automatiche e che non si deve riutilizzare la stessa struttura per
+un'altra operazione fintanto che la precedente non sia stata ultimata. In
+generale per ogni operazione si deve utilizzare una diversa struttura
+\struct{aiocb}.
Dato che si opera in modalità asincrona, il successo di \func{aio\_read} o
\func{aio\_write} non implica che le operazioni siano state effettivamente
di successo, i possibili valori di ritorno per \func{aio\_cancel} (anch'essi
definiti in \headfile{aio.h}) sono tre:
\begin{basedescript}{\desclabelwidth{3.0cm}}
-\item[\const{AIO\_ALLDONE}] indica che le operazioni di cui si è richiesta la
+\item[\constd{AIO\_ALLDONE}] indica che le operazioni di cui si è richiesta la
cancellazione sono state già completate,
-\item[\const{AIO\_CANCELED}] indica che tutte le operazioni richieste sono
+\item[\constd{AIO\_CANCELED}] indica che tutte le operazioni richieste sono
state cancellate,
-\item[\const{AIO\_NOTCANCELED}] indica che alcune delle operazioni erano in
+\item[\constd{AIO\_NOTCANCELED}] indica che alcune delle operazioni erano in
corso e non sono state cancellate.
\end{basedescript}
ognuna di esse dovrà essere specificato il tipo di operazione con il campo
\var{aio\_lio\_opcode}, che può prendere i valori:
\begin{basedescript}{\desclabelwidth{2.0cm}}
-\item[\const{LIO\_READ}] si richiede una operazione di lettura.
-\item[\const{LIO\_WRITE}] si richiede una operazione di scrittura.
-na operazione.
+\item[\constd{LIO\_READ}] si richiede una operazione di lettura.
+\item[\constd{LIO\_WRITE}] si richiede una operazione di scrittura.
+\item[\constd{LIO\_NOP}] non si effettua nessuna operazione.
\end{basedescript}
dove \const{LIO\_NOP} viene usato quando si ha a che fare con un vettore di
dimensione fissa, per poter specificare solo alcune operazioni, o quando si
quelle non completate.
L'argomento \param{mode} controlla il comportamento della funzione, se viene
-usato il valore \const{LIO\_WAIT} la funzione si blocca fino al completamento
-di tutte le operazioni richieste; se si usa \const{LIO\_NOWAIT} la funzione
+usato il valore \constd{LIO\_WAIT} la funzione si blocca fino al completamento
+di tutte le operazioni richieste; se si usa \constd{LIO\_NOWAIT} la funzione
ritorna immediatamente dopo aver messo in coda tutte le richieste. In tal caso
il chiamante può richiedere la notifica del completamento di tutte le
richieste, impostando l'argomento \param{sig} in maniera analoga a come si fa
% http://webfiveoh.com/content/guides/2012/aug/mon-13th/linux-asynchronous-io-and-libaio.html,
% https://code.google.com/p/kernel/wiki/AIOUserGuide,
% http://bert-hubert.blogspot.de/2012/05/on-linux-asynchronous-file-io.html
+% https://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt
+
+% TODO trattare la poll API basata sull'I/O asicrono, introdotta con il kernel
+% 4.18, vedi https://lwn.net/Articles/743714/,
+% https://lwn.net/Articles/742978/, https://lwn.net/Articles/758324/
+% http://git.infradead.org/users/hch/vfs.git/commit/d2d9e26c7cb6d95d521153897910080cf56c7fad
+% Reverted
+% TODO trattare la nuova API per l'I/O asincrono (io_uring), introdotta con il
+% kernel 5.1, vedi https://lwn.net/Articles/776703/,
+% https://lwn.net/ml/linux-fsdevel/20190112213011.1439-1-axboe@kernel.dk/
+% altre feature correlate:
+% dal 5.11: support for the shutdown(), renameat2(), and unlinkat()
\section{Altre modalità di I/O avanzato}
\label{sec:file_advanced_io}
\label{sec:file_memory_map}
\itindbeg{memory~mapping}
+
Una modalità alternativa di I/O, che usa una interfaccia completamente diversa
rispetto a quella classica vista in sez.~\ref{sec:file_unix_interface}, è il
cosiddetto \textit{memory-mapped I/O}, che attraverso il meccanismo della
-\textsl{paginazione} \index{paginazione} usato dalla memoria virtuale (vedi
+\textsl{paginazione} usato dalla memoria virtuale (vedi
sez.~\ref{sec:proc_mem_gen}) permette di \textsl{mappare} il contenuto di un
file in una sezione dello spazio di indirizzi del processo che lo ha allocato.
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{PROT\_EXEC} & Le pagine possono essere eseguite.\\
- \const{PROT\_READ} & Le pagine possono essere lette.\\
- \const{PROT\_WRITE} & Le pagine possono essere scritte.\\
- \const{PROT\_NONE} & L'accesso alle pagine è vietato.\\
+ \constd{PROT\_EXEC} & Le pagine possono essere eseguite.\\
+ \constd{PROT\_READ} & Le pagine possono essere lette.\\
+ \constd{PROT\_WRITE} & Le pagine possono essere scritte.\\
+ \constd{PROT\_NONE} & L'accesso alle pagine è vietato.\\
\hline
\end{tabular}
\caption{Valori dell'argomento \param{prot} di \func{mmap}, relativi alla
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{MAP\_32BIT} & Esegue la mappatura sui primi 2Gb dello spazio
+ \constd{MAP\_32BIT} & Esegue la mappatura sui primi 2Gb dello spazio
degli indirizzi, viene supportato solo sulle
piattaforme \texttt{x86-64} per compatibilità con
le applicazioni a 32 bit. Viene ignorato se si è
richiesto \const{MAP\_FIXED} (dal kernel 2.4.20).\\
- \const{MAP\_ANON} & Sinonimo di \const{MAP\_ANONYMOUS}, deprecato.\\
- \const{MAP\_ANONYMOUS} & La mappatura non è associata a nessun file. Gli
+ \constd{MAP\_ANON} & Sinonimo di \const{MAP\_ANONYMOUS}, deprecato.\\
+ \constd{MAP\_ANONYMOUS}& La mappatura non è associata a nessun file. Gli
argomenti \param{fd} e \param{offset} sono
ignorati. L'uso di questo flag con
\const{MAP\_SHARED} è stato implementato in Linux
a partire dai kernel della serie 2.4.x.\\
- \const{MAP\_DENYWRITE} & In Linux viene ignorato per evitare
+ \constd{MAP\_DENYWRITE}& In Linux viene ignorato per evitare
\textit{DoS}
(veniva usato per segnalare che tentativi di
scrittura sul file dovevano fallire con
\errcode{ETXTBSY}).\\
- \const{MAP\_EXECUTABLE}& Ignorato.\\
- \const{MAP\_FILE} & Valore di compatibilità, ignorato.\\
- \const{MAP\_FIXED} & Non permette di restituire un indirizzo diverso
+ \constd{MAP\_EXECUTABLE}& Ignorato.\\
+ \constd{MAP\_FILE} & Valore di compatibilità, ignorato.\\
+ \constd{MAP\_FIXED} & Non permette di restituire un indirizzo diverso
da \param{start}, se questo non può essere usato
\func{mmap} fallisce. Se si imposta questo flag il
valore di \param{start} deve essere allineato
alle dimensioni di una pagina.\\
- \const{MAP\_GROWSDOWN} & Usato per gli \textit{stack}.
+ \constd{MAP\_GROWSDOWN}& Usato per gli \textit{stack}.
Indica che la mappatura deve essere effettuata
con gli indirizzi crescenti verso il basso.\\
- \const{MAP\_HUGETLB} & Esegue la mappatura usando le cosiddette
+ \constd{MAP\_HUGETLB} & Esegue la mappatura usando le cosiddette
``\textit{huge pages}'' (dal kernel 2.6.32).\\
- \const{MAP\_LOCKED} & Se impostato impedisce lo \textit{swapping} delle
+ \constd{MAP\_LOCKED} & Se impostato impedisce lo \textit{swapping} delle
pagine mappate (dal kernel 2.5.37).\\
- \const{MAP\_NONBLOCK} & Esegue un \textit{prefaulting} più limitato che
+ \constd{MAP\_NONBLOCK} & Esegue un \textit{prefaulting} più limitato che
non causa I/O (dal kernel 2.5.46).\\
- \const{MAP\_NORESERVE} & Si usa con \const{MAP\_PRIVATE}. Non riserva
+ \constd{MAP\_NORESERVE}& Si usa con \const{MAP\_PRIVATE}. Non riserva
delle pagine di \textit{swap} ad uso del meccanismo
del \textit{copy on write}
per mantenere le modifiche fatte alla regione
mappata, in questo caso dopo una scrittura, se
non c'è più memoria disponibile, si ha
l'emissione di un \signal{SIGSEGV}.\\
- \const{MAP\_POPULATE} & Esegue il \textit{prefaulting} delle pagine di
+ \constd{MAP\_POPULATE} & Esegue il \textit{prefaulting} delle pagine di
memoria necessarie alla mappatura (dal kernel
2.5.46).\\
- \const{MAP\_PRIVATE} & I cambiamenti sulla memoria mappata non vengono
+ \constd{MAP\_PRIVATE} & I cambiamenti sulla memoria mappata non vengono
riportati sul file. Ne viene fatta una copia
privata cui solo il processo chiamante ha
accesso. Incompatibile con \const{MAP\_SHARED}.\\
- \const{MAP\_SHARED} & I cambiamenti sulla memoria mappata vengono
+ \constd{MAP\_SHARED} & I cambiamenti sulla memoria mappata vengono
riportati sul file e saranno immediatamente
visibili agli altri processi che mappano lo stesso
file. Incompatibile
con \const{MAP\_PRIVATE}.\\
\const{MAP\_STACK} & Al momento è ignorato, è stato fornito (dal kernel
2.6.27) a supporto della implementazione dei
- thread nelle \acr{glibc}, per allocare memoria in
- uno spazio utilizzabile come \textit{stack} per le
- architetture hardware che richiedono un
- trattamento speciale di quest'ultimo.\\
- \const{MAP\_UNINITIALIZED}& Specifico per i sistemi embedded ed
+ \textit{thread} nella \acr{glibc}, per allocare
+ memoria in uno spazio utilizzabile come
+ \textit{stack} per le architetture hardware che
+ richiedono un trattamento speciale di
+ quest'ultimo.\\
+ \constd{MAP\_UNINITIALIZED}& Specifico per i sistemi embedded ed
utilizzabile dal kernel 2.6.33 solo se è stata
abilitata in fase di compilazione dello stesso
l'opzione
per i sistemi embedded) si ha il completo
controllo dell'uso della memoria da parte degli
utenti.\\
-% \const{MAP\_DONTEXPAND}& Non consente una successiva espansione dell'area
+% \constd{MAP\_DONTEXPAND}& Non consente una successiva espansione dell'area
% mappata con \func{mremap}, proposto ma pare non
% implementato.\\
\hline
% TODO trattare MAP_HUGETLB introdotto con il kernel 2.6.32, e modifiche
% introdotte con il 3.8 per le dimensioni variabili delle huge pages
+% TODO trattare MAP_FIXED_NOREPLACE vedi https://lwn.net/Articles/751651/ e
+% https://lwn.net/Articles/741369/
+
+% TODO: verificare MAP_SYNC e MAP_SHARED_VALIDATE, vedi
+% https://lwn.net/Articles/731706/, https://lwn.net/Articles/758594/ incluse
+% con il 4.15
+
+
L'argomento \param{flags} specifica infine qual è il tipo di oggetto mappato,
le opzioni relative alle modalità con cui è effettuata la mappatura e alle
modalità con cui le modifiche alla memoria mappata vengono condivise o
di \func{msync} le funzioni dell'interfaccia ordinaria troveranno un contenuto
del file aggiornato.
-
\begin{table}[htb]
\centering
\footnotesize
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{MS\_SYNC} & richiede una sincronizzazione e ritorna soltanto
+ \constd{MS\_SYNC} & richiede una sincronizzazione e ritorna soltanto
quando questa è stata completata.\\
- \const{MS\_ASYNC} & richiede una sincronizzazione, ma ritorna subito
+ \constd{MS\_ASYNC} & richiede una sincronizzazione, ma ritorna subito
non attendendo che questa sia finita.\\
- \const{MS\_INVALIDATE} & invalida le pagine per tutte le mappature
+ \constd{MS\_INVALIDATE}& invalida le pagine per tutte le mappature
in memoria così da rendere necessaria una
rilettura immediata delle stesse.\\
\hline
indica la dimensione. Con \param{new\_size} si specifica invece la nuova
dimensione che si vuole ottenere. Infine l'argomento \param{flags} è una
maschera binaria per i flag che controllano il comportamento della funzione.
-Il solo valore utilizzato è \const{MREMAP\_MAYMOVE} che consente di eseguire
+Il solo valore utilizzato è \constd{MREMAP\_MAYMOVE} che consente di eseguire
l'espansione anche quando non è possibile utilizzare il precedente
indirizzo. Per questo motivo, se si è usato questo flag, la funzione può
restituire un indirizzo della nuova zona di memoria che non è detto coincida
database effettuare accessi ai dati in maniera pressoché casuale, mentre un
riproduttore audio o video eseguirà per lo più letture sequenziali.
+\itindend{memory~mapping}
+
Per migliorare le prestazioni a seconda di queste modalità di accesso è
disponibile una apposita funzione, \funcd{madvise},\footnote{tratteremo in
sez.~\ref{sec:file_fadvise} le funzioni che consentono di ottimizzare
l'accesso ai file con l'interfaccia classica.} che consente di fornire al
-kernel delle indicazioni su dette modalità, così che possano essere adottate
-le opportune strategie di ottimizzazione. Il suo prototipo è:
+kernel delle indicazioni su come un processo intende accedere ad un segmento
+di memoria, anche al di là delle mappature dei file, così che possano essere
+adottate le opportune strategie di ottimizzazione. Il suo prototipo è:
\begin{funcproto}{
\fhead{sys/mman.h}
\fdecl{int madvise(void *start, size\_t length, int advice)}
-\fdesc{Fornisce indicazioni sull'uso previsto di un \textit{memory mapping}.}
+\fdesc{Fornisce indicazioni sull'uso previsto di un segmento di memoria.}
}
{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
\item[\errcode{EINVAL}] \param{start} non è allineato alla dimensione di
una pagina, \param{length} ha un valore negativo, o \param{advice} non è
un valore valido, o si è richiesto il rilascio (con
- \const{MADV\_DONTNEED}) di pagine bloccate o condivise.
+ \const{MADV\_DONTNEED}) di pagine bloccate o condivise o si è usato
+ \const{MADV\_MERGEABLE} o \const{MADV\_UNMERGEABLE} ma il kernel non è
+ stato compilato per il relativo supporto.
\item[\errcode{EIO}] la paginazione richiesta eccederebbe i limiti (vedi
sez.~\ref{sec:sys_resource_limit}) sulle pagine residenti in memoria del
processo (solo in caso di \const{MADV\_WILLNEED}).
ed inoltre \errval{EAGAIN} e \errval{ENOSYS} nel loro significato generico.}
\end{funcproto}
-
La sezione di memoria sulla quale si intendono fornire le indicazioni deve
essere indicata con l'indirizzo iniziale \param{start} e l'estensione
\param{length}, il valore di \param{start} deve essere allineato,
L'indicazione viene espressa dall'argomento \param{advice} che deve essere
specificato con uno dei valori riportati in
tab.~\ref{tab:madvise_advice_values}; si tenga presente che i valori indicati
-nella seconda parte sono specifici di Linux e non sono previsti dallo standard
-POSIX.1b.
+nella seconda parte della tabella sono specifici di Linux e non sono previsti
+dallo standard POSIX.1b. La funzione non ha, tranne il caso di
+\const{MADV\_DONTFORK}, nessun effetto sul comportamento di un programma, ma
+può influenzarne le prestazioni fornendo al kernel indicazioni sulle esigenze
+dello stesso, così che sia possibile scegliere le opportune strategie per la
+gestione del \textit{read-ahead} (vedi sez.~\ref{sec:file_fadvise}) e del
+caching dei dati.
-\begin{table}[htb]
+\begin{table}[!htb]
\centering
\footnotesize
\begin{tabular}[c]{|l|p{10 cm}|}
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{MADV\_DONTNEED}& non ci si aspetta nessun accesso nell'immediato
- futuro, pertanto le pagine possono essere
- liberate dal kernel non appena necessario; l'area
- di memoria resterà accessibile, ma un accesso
- richiederà che i dati vengano ricaricati dal file
- a cui la mappatura fa riferimento.\\
- \const{MADV\_NORMAL} & nessuna indicazione specifica, questo è il valore
- di default usato quando non si è chiamato
- \func{madvise}.\\
- \const{MADV\_RANDOM} & ci si aspetta un accesso casuale all'area
- indicata, pertanto l'applicazione di una lettura
- anticipata con il meccanismo del \textit{read-ahead} (vedi
- sez.~\ref{sec:file_fadvise}) è di
- scarsa utilità e verrà disabilitata.\\
- \const{MADV\_SEQUENTIAL}& ci si aspetta un accesso sequenziale al file,
- quindi da una parte sarà opportuno eseguire una
- lettura anticipata, e dall'altra si potranno
- scartare immediatamente le pagine una volta che
- queste siano state lette.\\
+ \constd{MADV\_DONTNEED}& non ci si aspetta nessun accesso nell'immediato
+ futuro, pertanto le pagine possono essere
+ liberate dal kernel non appena necessario; l'area
+ di memoria resterà accessibile, ma un accesso
+ richiederà che i dati vengano ricaricati dal file
+ a cui la mappatura fa riferimento.\\
+ \constd{MADV\_NORMAL} & nessuna indicazione specifica, questo è il valore
+ di default usato quando non si è chiamato
+ \func{madvise}.\\
+ \constd{MADV\_RANDOM} & ci si aspetta un accesso casuale all'area
+ indicata, pertanto l'applicazione di una lettura
+ anticipata con il meccanismo del
+ \textit{read-ahead} (vedi
+ sez.~\ref{sec:file_fadvise}) è di
+ scarsa utilità e verrà disabilitata.\\
+ \constd{MADV\_SEQUENTIAL}& ci si aspetta un accesso sequenziale al file,
+ quindi da una parte sarà opportuno eseguire una
+ lettura anticipata, e dall'altra si potranno
+ scartare immediatamente le pagine una volta che
+ queste siano state lette.\\
\const{MADV\_WILLNEED}& ci si aspetta un accesso nell'immediato futuro,
pertanto l'applicazione del \textit{read-ahead}
deve essere incentivata.\\
\hline
- \const{MADV\_REMOVE} & libera un intervallo di pagine di memoria ed il
- relativo supporto sottostante; è supportato
- soltanto sui filesystem in RAM \textit{tmpfs} e
- \textit{shmfs} se usato su altri tipi di
- filesystem causa un errore di \errcode{ENOSYS}
- (dal kernel 2.6.16).\\
- \const{MADV\_DONTFORK}& impedisce che l'intervallo specificato venga
- ereditato dal processo figlio dopo una
- \func{fork}; questo consente di evitare che il
- meccanismo del \textit{copy on write} effettui la
- rilocazione delle pagine quando il padre scrive
- sull'area di memoria dopo la \func{fork}, cosa che può
- causare problemi per l'hardware che esegue
- operazioni in DMA su quelle pagine (dal kernel
- 2.6.16).\\
- \const{MADV\_DOFORK} & rimuove l'effetto della precedente
- \const{MADV\_DONTFORK} (dal kernel 2.6.16).\\
- \const{MADV\_MERGEABLE}& marca la pagina come accorpabile (indicazione
- principalmente ad uso dei sistemi di
- virtualizzazione).\footnotemark\\
- \const{MADV\_UNMERGEABLE}& \\
- \hline
+ \constd{MADV\_DONTDUMP}& esclude da un \textit{core dump} (vedi
+ sez.~\ref{sec:sig_standard}) le pagine
+ specificate, viene usato per evitare di scrivere
+ su disco dati relativi a zone di memoria che si sa
+ non essere utili in un \textit{core dump}.\\
+ \constd{MADV\_DODUMP} & rimuove l'effetto della precedente
+ \const{MADV\_DONTDUMP} (dal kernel 3.4).\\
+ \constd{MADV\_DONTFORK}& impedisce che l'intervallo specificato venga
+ ereditato dal processo figlio dopo una
+ \func{fork}; questo consente di evitare che il
+ meccanismo del \textit{copy on write} effettui la
+ rilocazione delle pagine quando il padre scrive
+ sull'area di memoria dopo la \func{fork}, cosa che
+ può causare problemi per l'hardware che esegue
+ operazioni in DMA su quelle pagine (dal kernel
+ 2.6.16).\\
+ \constd{MADV\_DOFORK} & rimuove l'effetto della precedente
+ \const{MADV\_DONTFORK} (dal kernel 2.6.16).\\
+ \constd{MADV\_HUGEPAGE}& abilita il meccanismo delle \textit{Transparent
+ Huge Page} (vedi sez.~\ref{sec:huge_pages})
+ sulla regione indicata; se questa è allineata
+ alle relative dimensioni il kernel alloca
+ direttamente delle \textit{huge page}; è
+ utilizzabile solo con mappature anomime private
+ (dal kernel 2.6.38).\\
+ \constd{MADV\_NOHUGEPAGE}& impedisce che la regione indicata venga
+ collassata in eventuali \textit{huge page} (dal
+ kernel 2.6.38).\\
+ \constd{MADV\_HWPOISON} &opzione ad uso di debug per verificare codice
+ che debba gestire errori nella gestione della
+ memoria; richiede una apposita opzione di
+ compilazione del kernel, privilegi amministrativi
+ (la capacità \const{CAP\_SYS\_ADMIN}) e provoca
+ l'emissione di un segnale di \const{SIGBUS} dal
+ programma chiamante e rimozione della mappatura
+ (dal kernel 2.6.32).\\
+ \constd{MADV\_SOFT\_OFFLINE}&opzione utilizzata per il debug del
+ codice di verifica degli errori di gestione
+ memoria, richiede una apposita opzione di
+ compilazione (dal kernel 2.6.33).\\
+ \constd{MADV\_MERGEABLE}& marca la pagina come accorpabile, indicazione
+ principalmente ad uso dei sistemi di
+ virtualizzazione\footnotemark (dal kernel
+ 2.6.32).\\
+ \constd{MADV\_REMOVE} & libera un intervallo di pagine di memoria ed il
+ relativo supporto sottostante; è supportato
+ soltanto sui filesystem in RAM \textit{tmpfs} e
+ \textit{shmfs} se usato su altri tipi di
+ filesystem causa un errore di \errcode{ENOSYS}
+ (dal kernel 2.6.16).\\
+ \constd{MADV\_UNMERGEABLE}& rimuove l'effetto della precedente
+ \const{MADV\_MERGEABLE} (dal kernel 2.6.32). \\
+ \hline
\end{tabular}
\caption{Valori dell'argomento \param{advice} di \func{madvise}.}
\label{tab:madvise_advice_values}
\end{table}
-\footnotetext{.}
+% TODO aggiunta MADV_FREE dal kernel 4.5 (vedi http://lwn.net/Articles/590991/)
+% TODO aggiunta MADV_WIPEONFORK dal kernel 4.14 that causes the affected memory
+% region to appear to be full of zeros in the child process after a fork. It
+% differs from the existing MADV_DONTFORK in that the address range will
+% remain valid in the child (dalla notizia in
+% https://lwn.net/Articles/733256/).
+% TODO aggiunte MADV_COLD e MADV_PAGEOUT dal kernel 5.4, vedi
+% https://git.kernel.org/linus/9c276cc65a58 e
+% https://git.kernel.org/linus/1a4e58cce84e
\footnotetext{a partire dal kernel 2.6.32 è stato introdotto un meccanismo che
identifica pagine di memoria identiche e le accorpa in una unica pagina
la loro occupazione di memoria, ma il meccanismo può essere usato anche in
altre applicazioni in cui sian presenti numerosi processi che usano gli
stessi dati; per maggiori dettagli si veda
- \href{http://kernelnewbies.org/Linux_2_6_32\#head-d3f32e41df508090810388a57efce73f52660ccb}{\texttt{http://kernelnewbies.org/Linux\_2\_6\_32}}.}
-
-La funzione non ha, tranne il caso di \const{MADV\_DONTFORK}, nessun effetto
-sul comportamento di un programma, ma può influenzarne le prestazioni fornendo
-al kernel indicazioni sulle esigenze dello stesso, così che sia possibile
-scegliere le opportune strategie per la gestione del \itindex{read-ahead}
-\textit{read-ahead} e del caching dei dati. A differenza da quanto specificato
-nello standard POSIX.1b, per il quale l'uso di \func{madvise} è a scopo
-puramente indicativo, Linux considera queste richieste come imperative, per
-cui ritorna un errore qualora non possa soddisfarle.\footnote{questo
- comportamento differisce da quanto specificato nello standard.}
+ \href{http://kernelnewbies.org/Linux_2_6_32\#head-d3f32e41df508090810388a57efce73f52660ccb}{\texttt{http://kernelnewbies.org/Linux\_2\_6\_32}}
+ e la documentazione nei sorgenti del kernel
+ (\texttt{Documentation/vm/ksm.txt}).}
+
+
+A differenza da quanto specificato nello standard POSIX.1b, per il quale l'uso
+di \func{madvise} è a scopo puramente indicativo, Linux considera queste
+richieste come imperative, per cui ritorna un errore qualora non possa
+soddisfarle; questo comportamento differisce da quanto specificato nello
+standard.
+
+Nello standard POSIX.1-2001 è prevista una ulteriore funzione
+\funcd{posix\_madvise} che su Linux viene reimplementata utilizzando
+\func{madvise}; il suo prototipo è:
+
+\begin{funcproto}{
+\fhead{sys/mman.h}
+\fdecl{int posix\_madvise(void *start, size\_t lenght, int advice)}
+\fdesc{Fornisce indicazioni sull'uso previsto di un segmento di memoria.}
+}
+
+{La funzione ritorna $0$ in caso di successo ed un valore positivo per un
+ errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
+ \item[\errcode{EINVAL}] \param{start} non è allineato alla dimensione di
+ una pagina, \param{length} ha un valore negativo, o \param{advice} non è
+ un valore valido.
+ \item[\errcode{ENOMEM}] gli indirizzi specificati non sono nello spazio di
+ indirizzi del processo.
+ \end{errlist}
+}
+\end{funcproto}
+
+Gli argomenti \param{start} e \param{lenght} hanno lo stesso identico
+significato degli analoghi di \func{madvise}, a cui si rimanda per la loro
+descrizione ma a differenza di quanto indicato dallo standard per questa
+funzione, su Linux un valore nullo di \param{len} è consentito.
+
+\begin{table}[!htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|l|l|}
+ \hline
+ \textbf{Valore} & \textbf{Significato} \\
+ \hline
+ \hline
+ \constd{POSIX\_MADV\_DONTNEED}& analogo a \const{MADV\_DONTNEED}.\\
+ \constd{POSIX\_MADV\_NORMAL} & identico a \const{MADV\_NORMAL}.\\
+ \constd{POSIX\_MADV\_RANDOM} & identico a \const{MADV\_RANDOM}.\\
+ \constd{POSIX\_MADV\_SEQUENTIAL}& identico a \const{MADV\_SEQUENTIAL}.\\
+ \constd{POSIX\_MADV\_WILLNEED}& identico a \const{MADV\_WILLNEED}.\\
+ \hline
+ \end{tabular}
+ \caption{Valori dell'argomento \param{advice} di \func{posix\_madvise}.}
+ \label{tab:posix_madvise_advice_values}
+\end{table}
+
+
+L'argomento \param{advice} invece può assumere solo i valori indicati in
+tab.~\ref{tab:posix_madvise_advice_values}, che riflettono gli analoghi di
+\func{madvise}, con lo stesso effetto per tutti tranne
+\const{POSIX\_MADV\_DONTNEED}. Infatti a partire dalla \acr{glibc} 2.6
+\const{POSIX\_MADV\_DONTNEED} viene ignorato, in quanto l'uso del
+corrispondente \const{MADV\_DONTNEED} di \func{madvise} ha, per la semantica
+imperativa, l'effetto immediato di far liberare le pagine da parte del kernel,
+che viene considerato distruttivo.
-\itindend{memory~mapping}
\subsection{I/O vettorizzato: \func{readv} e \func{writev}}
\label{sec:file_multiple_io}
-Un caso abbastanza comune è quello in cui ci si trova a dover eseguire una
-serie multipla di operazioni di I/O, come una serie di letture o scritture di
-vari buffer. Un esempio tipico è quando i dati sono strutturati nei campi di
-una struttura ed essi devono essere caricati o salvati su un file. Benché
-l'operazione sia facilmente eseguibile attraverso una serie multipla di
-chiamate a \func{read} e \func{write}, ci sono casi in cui si vuole poter
-contare sulla atomicità delle operazioni.
+Una seconda modalità di I/O diversa da quella ordinaria è il cosiddetto
+\textsl{I/O vettorizzato}, che nasce per rispondere al caso abbastanza comune
+in cui ci si trova nell'esigenza di dover eseguire una serie multipla di
+operazioni di I/O, come una serie di letture o scritture di vari buffer. Un
+esempio tipico è quando i dati sono strutturati nei campi di una struttura ed
+essi devono essere caricati o salvati su un file. Benché l'operazione sia
+facilmente eseguibile attraverso una serie multipla di chiamate a \func{read}
+e \func{write}, ci sono casi in cui si vuole poter contare sulla atomicità
+delle operazioni di lettura e scrittura rispetto all'esecuzione del programma.
Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove
\textit{system call} che permettessero di effettuare con una sola chiamata una
-serie di letture o scritture su una serie di buffer, con quello che viene
-normalmente chiamato \textsl{I/O vettorizzato}. Queste funzioni sono
+serie di letture da, o scritture su, una serie di buffer, quello che poi venne
+chiamato \textsl{I/O vettorizzato}. Queste funzioni di sistema sono
\funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese
da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} ed i
relativi prototipi sono:
-\begin{functions}
- \headdecl{sys/uio.h}
-
- \funcdecl{int readv(int fd, const struct iovec *vector, int count)}
- \funcdecl{int writev(int fd, const struct iovec *vector, int count)}
- Eseguono rispettivamente una lettura o una scrittura vettorizzata.
-
- \bodydesc{Le funzioni restituiscono il numero di byte letti o scritti in
- caso di successo, e -1 in caso di errore, nel qual caso \var{errno}
- assumerà uno dei valori:
+
+\begin{funcproto}{
+\fhead{sys/uio.h}
+\fdecl{int readv(int fd, const struct iovec *vector, int count)}
+\fdecl{int writev(int fd, const struct iovec *vector, int count)}
+\fdesc{Eseguono rispettivamente una lettura o una scrittura vettorizzata.}
+}
+
+{Le funzioni ritornano il numero di byte letti o scritti in caso di successo e
+ $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli
+ \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli
argomenti (ad esempio \param{count} è maggiore di \const{IOV\_MAX}).
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima di
- di avere eseguito una qualunque lettura o scrittura.
- \item[\errcode{EAGAIN}] \param{fd} è stato aperto in modalità non bloccante e
- non ci sono dati in lettura.
- \item[\errcode{EOPNOTSUPP}] la coda delle richieste è momentaneamente piena.
\end{errlist}
- ed anche \errval{EISDIR}, \errval{EBADF}, \errval{ENOMEM}, \errval{EFAULT}
- (se non sono stati allocati correttamente i buffer specificati nei campi
- \var{iov\_base}), più gli eventuali errori delle funzioni di lettura e
- scrittura eseguite su \param{fd}.}
-\end{functions}
+ più tutti i valori, con lo stesso significato, che possono risultare
+ dalle condizioni di errore di \func{read} e \func{write}.
+ }
+\end{funcproto}
+
Entrambe le funzioni usano una struttura \struct{iovec}, la cui definizione è
riportata in fig.~\ref{fig:file_iovec}, che definisce dove i dati devono
\func{sysconf} richiedendo l'argomento \const{\_SC\_IOV\_MAX} (vedi
sez.~\ref{sec:sys_limits}).
-Nel caso di Linux il limite di sistema è di 1024, però se si usano le
-\acr{glibc} queste forniscono un \textit{wrapper} per le \textit{system call}
+Nel caso di Linux il limite di sistema è di 1024, però se si usa la
+\acr{glibc} essa fornisce un \textit{wrapper} per le \textit{system call}
che si accorge se una operazione supererà il precedente limite, in tal caso i
dati verranno letti o scritti con le usuali \func{read} e \func{write} usando
-un buffer di dimensioni sufficienti appositamente allocato e sufficiente a
+un buffer di dimensioni sufficienti appositamente allocato in grado di
contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo
ma si perderà l'atomicità del trasferimento da e verso la destinazione finale.
bit dell'argomento \param{offset}, che varia a seconda delle architetture,
ma queste differenze vengono gestite dalle funzioni di librerie di libreria
che mantengono l'interfaccia delle analoghe tratte da BSD.}
-\begin{functions}
- \headdecl{sys/uio.h}
-
- \funcdecl{int preadv(int fd, const struct iovec *vector, int count, off\_t
+
+
+\begin{funcproto}{
+\fhead{sys/uio.h}
+\fdecl{int preadv(int fd, const struct iovec *vector, int count, off\_t
offset)}
- \funcdecl{int pwritev(int fd, const struct iovec *vector, int count, off\_t
+\fdecl{int pwritev(int fd, const struct iovec *vector, int count, off\_t
offset)}
+\fdesc{Eseguono una lettura o una scrittura vettorizzata a partire da una data
+ posizione sul file.}
+}
- Eseguono una lettura o una scrittura vettorizzata a partire da una data
- posizione sul file.
-
- \bodydesc{Le funzioni hanno gli stessi valori di ritorno delle
- corrispondenti \func{readv} e \func{writev}; anche gli eventuali errori
- sono gli stessi già visti in precedenza, ma ad essi si possono aggiungere
- per \var{errno} anche i valori:
- \begin{errlist}
- \item[\errcode{EOVERFLOW}] \param{offset} ha un valore che non può essere
- usato come \type{off\_t}.
- \item[\errcode{ESPIPE}] \param{fd} è associato ad un socket o una
- \textit{pipe}.
- \end{errlist}
+{ Le funzioni hanno gli stessi valori di ritorno delle corrispondenti
+ \func{readv} e \func{writev} ed anche gli eventuali errori sono gli stessi,
+ con in più quelli che si possono ottenere dalle possibili condizioni di
+ errore di \func{lseek}.
}
-\end{functions}
+\end{funcproto}
Le due funzioni eseguono rispettivamente una lettura o una scrittura
vettorizzata a partire dalla posizione \param{offset} sul file indicato
dell'ulteriore argomento il comportamento delle funzioni è identico alle
precedenti \func{readv} e \func{writev}.
-Con l'uso di queste funzioni si possono evitare eventuali
-\itindex{race~condition} \textit{race condition} quando si deve eseguire la
-una operazione di lettura e scrittura vettorizzata a partire da una certa
-posizione su un file, mentre al contempo si possono avere in concorrenza
-processi che utilizzano lo stesso file descriptor (si ricordi quanto visto in
-sez.~\ref{sec:file_adv_func}) con delle chiamate a \func{lseek}.
+Con l'uso di queste funzioni si possono evitare eventuali \textit{race
+ condition} quando si deve eseguire la una operazione di lettura e scrittura
+vettorizzata a partire da una certa posizione su un file, mentre al contempo
+si possono avere in concorrenza processi che utilizzano lo stesso file
+descriptor (si ricordi quanto visto in sez.~\ref{sec:file_adv_func}) con delle
+chiamate a \func{lseek}.
+% TODO trattare preadv2() e pwritev2(), introdotte con il kernel 4.6, vedi
+% http://lwn.net/Articles/670231/ ed il flag RWF_HIPRI, anche l'aggiunta del
+% flag RWF_APPEND a pwritev2 con il kernel 4.16, vedi
+% https://lwn.net/Articles/746129/
\subsection{L'I/O diretto fra file descriptor: \func{sendfile} e
Benché il kernel ottimizzi la gestione di questo processo quando si ha a che
fare con file normali, in generale quando i dati da trasferire sono molti si
pone il problema di effettuare trasferimenti di grandi quantità di dati da
-kernel space a user space e all'indietro, quando in realtà potrebbe essere più
-efficiente mantenere tutto in kernel space. Tratteremo in questa sezione
-alcune funzioni specialistiche che permettono di ottimizzare le prestazioni in
-questo tipo di situazioni.
+\textit{kernel space} a \textit{user space} e all'indietro, quando in realtà
+potrebbe essere più efficiente mantenere tutto in \textit{kernel
+ space}. Tratteremo in questa sezione alcune funzioni specialistiche che
+permettono di ottimizzare le prestazioni in questo tipo di situazioni.
La prima funzione che è stata ideata per ottimizzare il trasferimento dei dati
-fra due file descriptor è \func{sendfile};\footnote{la funzione è stata
- introdotta con i kernel della serie 2.2, e disponibile dalle \acr{glibc}
- 2.1.} la funzione è presente in diverse versioni di Unix,\footnote{la si
- ritrova ad esempio in FreeBSD, HPUX ed altri Unix.} ma non è presente né in
-POSIX.1-2001 né in altri standard,\footnote{pertanto si eviti di utilizzarla
- se si devono scrivere programmi portabili.} per cui per essa vengono
-utilizzati prototipi e semantiche differenti; nel caso di Linux il prototipo
-di \funcd{sendfile} è:
-\begin{functions}
- \headdecl{sys/sendfile.h}
-
- \funcdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t
- count)}
-
- Copia dei dati da un file descriptor ad un altro.
+fra due file descriptor è \func{sendfile}.\footnote{la funzione è stata
+ introdotta con i kernel della serie 2.2, e disponibile dalla \acr{glibc}
+ 2.1.} La funzione è presente in diverse versioni di Unix (la si ritrova ad
+esempio in FreeBSD, HPUX ed altri Unix) ma non è presente né in POSIX.1-2001
+né in altri standard (pertanto si eviti di utilizzarla se si devono scrivere
+programmi portabili) per cui per essa vengono utilizzati prototipi e
+semantiche differenti. Nel caso di Linux il prototipo di \funcd{sendfile} è:
+
+
+\begin{funcproto}{
+\fhead{sys/sendfile.h}
+\fdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t
+ count)}
+\fdesc{Copia dei dati da un file descriptor ad un altro.}
+}
- \bodydesc{La funzione restituisce il numero di byte trasferiti in caso di
- successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
- dei valori:
- \begin{errlist}
+{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$
+ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EAGAIN}] si è impostata la modalità non bloccante su
\param{out\_fd} e la scrittura si bloccherebbe.
\item[\errcode{EINVAL}] i file descriptor non sono validi, o sono bloccati
\item[\errcode{EIO}] si è avuto un errore di lettura da \param{in\_fd}.
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per la lettura da
\param{in\_fd}.
- \end{errlist}
- ed inoltre \errcode{EBADF} e \errcode{EFAULT}.
- }
-\end{functions}
+ \end{errlist}
+ ed inoltre \errcode{EBADF} e \errcode{EFAULT} nel loro significato
+ generico.}
+\end{funcproto}
La funzione copia direttamente \param{count} byte dal file descriptor
-\param{in\_fd} al file descriptor \param{out\_fd}; in caso di successo
+\param{in\_fd} al file descriptor \param{out\_fd}. In caso di successo la
funzione ritorna il numero di byte effettivamente copiati da \param{in\_fd} a
-\param{out\_fd} o $-1$ in caso di errore; come le ordinarie \func{read} e
-\func{write} questo valore può essere inferiore a quanto richiesto con
-\param{count}.
+\param{out\_fd} e come per le ordinarie \func{read} e \func{write} questo
+valore può essere inferiore a quanto richiesto con \param{count}.
Se il puntatore \param{offset} è nullo la funzione legge i dati a partire
dalla posizione corrente su \param{in\_fd}, altrimenti verrà usata la
nullo la posizione corrente sul file sarà aggiornata tenendo conto dei byte
letti da \param{in\_fd}.
-Fino ai kernel della serie 2.4 la funzione è utilizzabile su un qualunque file
-descriptor, e permette di sostituire la invocazione successiva di una
+Fino ai kernel della serie 2.4 la funzione era utilizzabile su un qualunque
+file descriptor, e permetteva di sostituire la invocazione successiva di una
\func{read} e una \func{write} (e l'allocazione del relativo buffer) con una
-sola chiamata a \funcd{sendfile}. In questo modo si può diminuire il numero di
-chiamate al sistema e risparmiare in trasferimenti di dati da kernel space a
-user space e viceversa. La massima utilità della funzione si ha comunque per
-il trasferimento di dati da un file su disco ad un socket di
-rete,\footnote{questo è il caso classico del lavoro eseguito da un server web,
- ed infatti Apache ha una opzione per il supporto esplicito di questa
- funzione.} dato che in questo caso diventa possibile effettuare il
-trasferimento diretto via DMA dal controller del disco alla scheda di rete,
-senza neanche allocare un buffer nel kernel,\footnote{il meccanismo è detto
- \textit{zerocopy} in quanto i dati non vengono mai copiati dal kernel, che
- si limita a programmare solo le operazioni di lettura e scrittura via DMA.}
+sola chiamata a \funcd{sendfile}. In questo modo si poteva diminuire il numero
+di chiamate al sistema e risparmiare in trasferimenti di dati da
+\textit{kernel space} a \textit{user space} e viceversa. La massima utilità
+della funzione si ottiene comunque per il trasferimento di dati da un file su
+disco ad un socket di rete,\footnote{questo è il caso classico del lavoro
+ eseguito da un server web, ed infatti Apache ha una opzione per il supporto
+ esplicito di questa funzione.} dato che in questo caso diventa possibile
+effettuare il trasferimento diretto via DMA dal controller del disco alla
+scheda di rete, senza neanche allocare un buffer nel kernel (il meccanismo è
+detto \textit{zerocopy} in quanto i dati non vengono mai copiati dal kernel,
+che si limita a programmare solo le operazioni di lettura e scrittura via DMA)
ottenendo la massima efficienza possibile senza pesare neanche sul processore.
-In seguito però ci si è accorti che, fatta eccezione per il trasferimento
+In seguito però ci si accorse che, fatta eccezione per il trasferimento
diretto da file a socket, non sempre \func{sendfile} comportava miglioramenti
significativi delle prestazioni rispetto all'uso in sequenza di \func{read} e
-\func{write},\footnote{nel caso generico infatti il kernel deve comunque
- allocare un buffer ed effettuare la copia dei dati, e in tal caso spesso il
- guadagno ottenibile nel ridurre il numero di chiamate al sistema non
- compensa le ottimizzazioni che possono essere fatte da una applicazione in
- user space che ha una conoscenza diretta su come questi sono strutturati.} e
-che anzi in certi casi si potevano avere anche dei peggioramenti. Questo ha
+\func{write}. Nel caso generico infatti il kernel deve comunque allocare un
+buffer ed effettuare la copia dei dati, e in tal caso spesso il guadagno
+ottenibile nel ridurre il numero di chiamate al sistema non compensa le
+ottimizzazioni che possono essere fatte da una applicazione in \textit{user
+ space} che ha una conoscenza diretta su come questi sono strutturati, per
+cui in certi casi si potevano avere anche dei peggioramenti. Questo ha
portato, per i kernel della serie 2.6,\footnote{per alcune motivazioni di
questa scelta si può fare riferimento a quanto illustrato da Linus Torvalds
in \url{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}.}
alla decisione di consentire l'uso della funzione soltanto quando il file da
cui si legge supporta le operazioni di \textit{memory mapping} (vale a dire
non è un socket) e quello su cui si scrive è un socket; in tutti gli altri
-casi l'uso di \func{sendfile} darà luogo ad un errore di \errcode{EINVAL}.
+casi l'uso di \func{sendfile} da luogo ad un errore di \errcode{EINVAL}.
Nonostante ci possano essere casi in cui \func{sendfile} non migliora le
prestazioni, resta il dubbio se la scelta di disabilitarla sempre per il
di prestazioni infatti si può sempre fare ricorso al metodo ordinario, ma
lasciare a disposizione la funzione consentirebbe se non altro di semplificare
la gestione della copia dei dati fra file, evitando di dover gestire
-l'allocazione di un buffer temporaneo per il loro trasferimento.
-
-Questo dubbio si può comunque ritenere superato con l'introduzione, avvenuta a
-partire dal kernel 2.6.17, della nuova \textit{system call} \func{splice}. Lo
-scopo di questa funzione è quello di fornire un meccanismo generico per il
-trasferimento di dati da o verso un file utilizzando un buffer gestito
-internamente dal kernel. Descritta in questi termini \func{splice} sembra
-semplicemente un ``\textsl{dimezzamento}'' di \func{sendfile}.\footnote{nel
- senso che un trasferimento di dati fra due file con \func{sendfile} non
- sarebbe altro che la lettura degli stessi su un buffer seguita dalla
- relativa scrittura, cosa che in questo caso si dovrebbe eseguire con due
- chiamate a \func{splice}.} In realtà le due \textit{system call} sono
-profondamente diverse nel loro meccanismo di funzionamento;\footnote{questo
- fino al kernel 2.6.23, dove \func{sendfile} è stata reimplementata in
- termini di \func{splice}, pur mantenendo disponibile la stessa interfaccia
- verso l'user space.} \func{sendfile} infatti, come accennato, non necessita
-di avere a disposizione un buffer interno, perché esegue un trasferimento
-diretto di dati; questo la rende in generale più efficiente, ma anche limitata
-nelle sue applicazioni, dato che questo tipo di trasferimento è possibile solo
-in casi specifici.\footnote{e nel caso di Linux questi sono anche solo quelli
- in cui essa può essere effettivamente utilizzata.}
+l'allocazione di un buffer temporaneo per il loro trasferimento. Comunque a
+partire dal kernel 2.6.33 la restrizione su \param{out\_fd} è stata rimossa e
+questo può essere un file qualunque, rimane però quella di non poter usare un
+socket per \param{in\_fd}.
+
+A partire dal kernel 2.6.17 come alternativa a \func{sendfile} è disponibile
+la nuova \textit{system call} \func{splice}. Lo scopo di questa funzione è
+quello di fornire un meccanismo generico per il trasferimento di dati da o
+verso un file, utilizzando un buffer gestito internamente dal
+kernel. Descritta in questi termini \func{splice} sembra semplicemente un
+``\textsl{dimezzamento}'' di \func{sendfile}, nel senso che un trasferimento
+di dati fra due file con \func{sendfile} non sarebbe altro che la lettura
+degli stessi su un buffer seguita dalla relativa scrittura, cosa che in questo
+caso si dovrebbe eseguire con due chiamate a \func{splice}.
+
+In realtà le due \textit{system call} sono profondamente diverse nel loro
+meccanismo di funzionamento;\footnote{questo fino al kernel 2.6.23, dove
+ \func{sendfile} è stata reimplementata in termini di \func{splice}, pur
+ mantenendo disponibile la stessa interfaccia verso l'\textit{user space}.}
+\func{sendfile} infatti, come accennato, non necessita di avere a disposizione
+un buffer interno, perché esegue un trasferimento diretto di dati; questo la
+rende in generale più efficiente, ma anche limitata nelle sue applicazioni,
+dato che questo tipo di trasferimento è possibile solo in casi specifici che
+nel caso di Linux questi sono anche solo quelli in cui essa può essere
+effettivamente utilizzata.
Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in
realtà la proposta originale di Larry Mc Voy non differisce poi tanto negli
dallo stesso Linus Torvalds in \url{http://kerneltrap.org/node/6505}.} si
tratta semplicemente di una funzione che consente di fare in maniera del tutto
generica delle operazioni di trasferimento di dati fra un file e un buffer
-gestito interamente in kernel space. In questo caso il cuore della funzione (e
-delle affini \func{vmsplice} e \func{tee}, che tratteremo più avanti) è
-appunto l'uso di un buffer in kernel space, e questo è anche quello che ne ha
-semplificato l'adozione, perché l'infrastruttura per la gestione di un tale
-buffer è presente fin dagli albori di Unix per la realizzazione delle
-\textit{pipe} (vedi sez.~\ref{sec:ipc_unix}). Dal punto di vista concettuale
-allora \func{splice} non è altro che una diversa interfaccia (rispetto alle
-\textit{pipe}) con cui utilizzare in user space l'oggetto ``\textsl{buffer in
- kernel space}''.
+gestito interamente in \textit{kernel space}. In questo caso il cuore della
+funzione (e delle affini \func{vmsplice} e \func{tee}, che tratteremo più
+avanti) è appunto l'uso di un buffer in \textit{kernel space}, e questo è
+anche quello che ne ha semplificato l'adozione, perché l'infrastruttura per la
+gestione di un tale buffer è presente fin dagli albori di Unix per la
+realizzazione delle \textit{pipe} (vedi sez.~\ref{sec:ipc_unix}). Dal punto di
+vista concettuale allora \func{splice} non è altro che una diversa interfaccia
+(rispetto alle \textit{pipe}) con cui utilizzare in \textit{user space}
+l'oggetto ``\textsl{buffer in kernel space}''.
Così se per una \textit{pipe} o una \textit{fifo} il buffer viene utilizzato
come area di memoria (vedi fig.~\ref{fig:ipc_pipe_singular}) dove appoggiare i
dati che vengono trasferiti da un capo all'altro della stessa per creare un
meccanismo di comunicazione fra processi, nel caso di \func{splice} il buffer
viene usato o come fonte dei dati che saranno scritti su un file, o come
-destinazione dei dati che vengono letti da un file. La funzione \funcd{splice}
-fornisce quindi una interfaccia generica che consente di trasferire dati da un
-buffer ad un file o viceversa; il suo prototipo, accessibile solo dopo aver
-definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa
+destinazione dei dati che vengono letti da un file. La funzione fornisce
+quindi una interfaccia generica che consente di trasferire dati da un buffer
+ad un file o viceversa; il prototipo di \funcd{splice}, accessibile solo dopo
+aver definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa
funzione non è contemplata da nessuno standard, è presente solo su Linux, e
pertanto deve essere evitata se si vogliono scrivere programmi portabili.}
è il seguente:
-\begin{functions}
- \headdecl{fcntl.h}
- \funcdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t
- *off\_out, size\_t len, unsigned int flags)}
-
- Trasferisce dati da un file verso una \textit{pipe} o viceversa.
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t
+ *off\_out, size\_t len, \\
+\phantom{long splice(}unsigned int flags)}
+\fdesc{Trasferisce dati da un file verso una \textit{pipe} o viceversa.}
+}
- \bodydesc{La funzione restituisce il numero di byte trasferiti in caso di
- successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
- dei valori:
- \begin{errlist}
+{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$
+ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] uno o entrambi fra \param{fd\_in} e \param{fd\_out}
non sono file descriptor validi o, rispettivamente, non sono stati
aperti in lettura o scrittura.
richiesta.
\item[\errcode{ESPIPE}] o \param{off\_in} o \param{off\_out} non sono
\val{NULL} ma il corrispondente file descriptor è una \textit{pipe}.
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+}
+\end{funcproto}
+
La funzione esegue un trasferimento di \param{len} byte dal file descriptor
\param{fd\_in} al file descriptor \param{fd\_out}, uno dei quali deve essere
-una \textit{pipe}; l'altro file descriptor può essere
-qualunque.\footnote{questo significa che può essere, oltre che un file di
- dati, anche un altra \textit{pipe}, o un socket.} Come accennato una
-\textit{pipe} non è altro che un buffer in kernel space, per cui a seconda che
-essa sia usata per \param{fd\_in} o \param{fd\_out} si avrà rispettivamente la
-copia dei dati dal buffer al file o viceversa.
+una \textit{pipe}; l'altro file descriptor può essere qualunque, questo
+significa che può essere, oltre che un file di dati, anche un altra
+\textit{pipe}, o un socket. Come accennato una \textit{pipe} non è altro che
+un buffer in \textit{kernel space}, per cui a seconda che essa sia usata
+per \param{fd\_in} o \param{fd\_out} si avrà rispettivamente la copia dei dati
+dal buffer al file o viceversa.
In caso di successo la funzione ritorna il numero di byte trasferiti, che può
essere, come per le normali funzioni di lettura e scrittura su file, inferiore
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{SPLICE\_F\_MOVE} & Suggerisce al kernel di spostare le pagine
- di memoria contenenti i dati invece di
- copiarle;\footnotemark viene usato soltanto
- da \func{splice}.\\
- \const{SPLICE\_F\_NONBLOCK}& Richiede di operare in modalità non
- bloccante; questo flag influisce solo sulle
- operazioni che riguardano l'I/O da e verso la
- \textit{pipe}. Nel caso di \func{splice}
- questo significa che la funzione potrà
- comunque bloccarsi nell'accesso agli altri
- file descriptor (a meno che anch'essi non
- siano stati aperti in modalità non
- bloccante).\\
- \const{SPLICE\_F\_MORE} & Indica al kernel che ci sarà l'invio di
- ulteriori dati in una \func{splice}
- successiva, questo è un suggerimento utile
- che viene usato quando \param{fd\_out} è un
- socket.\footnotemark Attualmente viene usato
- solo da \func{splice}, potrà essere
- implementato in futuro anche per
- \func{vmsplice} e \func{tee}.\\
- \const{SPLICE\_F\_GIFT} & Le pagine di memoria utente sono
- ``\textsl{donate}'' al kernel;\footnotemark
- se impostato una seguente \func{splice} che
- usa \const{SPLICE\_F\_MOVE} potrà spostare le
- pagine con successo, altrimenti esse dovranno
- essere copiate; per usare questa opzione i
- dati dovranno essere opportunamente allineati
- in posizione ed in dimensione alle pagine di
- memoria. Viene usato soltanto da
- \func{vmsplice}.\\
+ \constd{SPLICE\_F\_MOVE} & Suggerisce al kernel di spostare le pagine
+ di memoria contenenti i dati invece di
+ copiarle: per una maggiore efficienza
+ \func{splice} usa quando possibile i
+ meccanismi della memoria virtuale per
+ eseguire i trasferimenti di dati. In maniera
+ analoga a \func{mmap}), qualora le pagine non
+ possano essere spostate dalla \textit{pipe} o
+ il buffer non corrisponda a pagine intere
+ esse saranno comunque copiate. Viene usato
+ soltanto da \func{splice}.\\
+ \constd{SPLICE\_F\_NONBLOCK}& Richiede di operare in modalità non
+ bloccante; questo flag influisce solo sulle
+ operazioni che riguardano l'I/O da e verso la
+ \textit{pipe}. Nel caso di \func{splice}
+ questo significa che la funzione potrà
+ comunque bloccarsi nell'accesso agli altri
+ file descriptor (a meno che anch'essi non
+ siano stati aperti in modalità non
+ bloccante).\\
+ \constd{SPLICE\_F\_MORE} & Indica al kernel che ci sarà l'invio di
+ ulteriori dati in una \func{splice}
+ successiva, questo è un suggerimento utile
+ che viene usato quando \param{fd\_out} è un
+ socket. Questa opzione consente di utilizzare
+ delle opzioni di gestione dei socket che
+ permettono di ottimizzare le trasmissioni via
+ rete (si veda la descrizione di
+ \const{TCP\_CORK} in
+ sez.~\ref{sec:sock_tcp_udp_options} e quella
+ di \const{MSG\_MORE} in
+ sez.~\ref{sec:net_sendmsg}). Attualmente
+ viene usato solo da \func{splice}, potrà essere
+ implementato in futuro anche per
+ \func{vmsplice} e \func{tee}.\\
+ \constd{SPLICE\_F\_GIFT} & Le pagine di memoria utente sono
+ ``\textsl{donate}'' al kernel; questo
+ significa che la cache delle pagine e i dati
+ su disco potranno differire, e che
+ l'applicazione non potrà modificare
+ quest'area di memoria.
+ Se impostato una seguente \func{splice} che
+ usa \const{SPLICE\_F\_MOVE} potrà spostare le
+ pagine con successo, altrimenti esse dovranno
+ essere copiate; per usare questa opzione i
+ dati dovranno essere opportunamente allineati
+ in posizione ed in dimensione alle pagine di
+ memoria. Viene usato soltanto da
+ \func{vmsplice}.\\
\hline
\end{tabular}
\caption{Le costanti che identificano i bit della maschera binaria
\label{tab:splice_flag}
\end{table}
-\footnotetext[120]{per una maggiore efficienza \func{splice} usa quando
- possibile i meccanismi della memoria virtuale per eseguire i trasferimenti
- di dati (in maniera analoga a \func{mmap}), qualora le pagine non possano
- essere spostate dalla \textit{pipe} o il buffer non corrisponda a pagine
- intere esse saranno comunque copiate.}
-
-\footnotetext[121]{questa opzione consente di utilizzare delle opzioni di
- gestione dei socket che permettono di ottimizzare le trasmissioni via rete,
- si veda la descrizione di \const{TCP\_CORK} in
- sez.~\ref{sec:sock_tcp_udp_options} e quella di \const{MSG\_MORE} in
- sez.~\ref{sec:net_sendmsg}.}
-
-\footnotetext{questo significa che la cache delle pagine e i dati su disco
- potranno differire, e che l'applicazione non potrà modificare quest'area di
- memoria.}
Per capire meglio il funzionamento di \func{splice} vediamo un esempio con un
semplice programma che usa questa funzione per effettuare la copia di un file
-su un altro senza utilizzare buffer in user space. Il programma si chiama
-\texttt{splicecp.c} ed il codice completo è disponibile coi sorgenti allegati
-alla guida, il corpo principale del programma, che non contiene la sezione di
-gestione delle opzioni e le funzioni di ausilio è riportato in
-fig.~\ref{fig:splice_example}.
-
-Lo scopo del programma è quello di eseguire la copia dei con \func{splice},
-questo significa che si dovrà usare la funzione due volte, prima per leggere i
-dati e poi per scriverli, appoggiandosi ad un buffer in kernel space (vale a
-dire ad una \textit{pipe}); lo schema del flusso dei dati è illustrato in
-fig.~\ref{fig:splicecp_data_flux}.
+su un altro senza utilizzare buffer in \textit{user space}. Lo scopo del
+programma è quello di eseguire la copia dei dati con \func{splice}, questo
+significa che si dovrà usare la funzione due volte, prima per leggere i dati
+dal file di ingresso e poi per scriverli su quello di uscita, appoggiandosi ad
+una \textit{pipe}: lo schema del flusso dei dati è illustrato in
+fig.~\ref{fig:splicecp_data_flux}.
\begin{figure}[htb]
\centering
- \includegraphics[height=6cm]{img/splice_copy}
+ \includegraphics[height=3.5cm]{img/splice_copy}
\caption{Struttura del flusso di dati usato dal programma \texttt{splicecp}.}
\label{fig:splicecp_data_flux}
\end{figure}
-Una volta trattate le opzioni il programma verifica che restino
-(\texttt{\small 13-16}) i due argomenti che indicano il file sorgente ed il
-file destinazione. Il passo successivo è aprire il file sorgente
-(\texttt{\small 18-22}), quello di destinazione (\texttt{\small 23-27}) ed
-infine (\texttt{\small 28-31}) la \textit{pipe} che verrà usata come buffer.
+Il programma si chiama \texttt{splicecp.c} ed il codice completo è disponibile
+coi sorgenti allegati alla guida, il corpo principale del programma, che non
+contiene la sezione di gestione delle opzioni, le funzioni di ausilio, le
+aperture dei file di ingresso e di uscita passati come argomenti e quella
+della \textit{pipe} intermedia, è riportato in fig.~\ref{fig:splice_example}.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{\codesamplewidth}
\includecodesample{listati/splicecp.c}
\label{fig:splice_example}
\end{figure}
-Il ciclo principale (\texttt{\small 33-58}) inizia con la lettura dal file
-sorgente tramite la prima \func{splice} (\texttt{\small 34-35}), in questo
+Il ciclo principale (\texttt{\small 13-38}) inizia con la lettura dal file
+sorgente tramite la prima \func{splice} (\texttt{\small 14-15}), in questo
caso si è usato come primo argomento il file descriptor del file sorgente e
-come terzo quello del capo in scrittura della \textit{pipe} (il funzionamento
+come terzo quello del capo in scrittura della \textit{pipe}. Il funzionamento
delle \textit{pipe} e l'uso della coppia di file descriptor ad esse associati
è trattato in dettaglio in sez.~\ref{sec:ipc_unix}; non ne parleremo qui dato
che nell'ottica dell'uso di \func{splice} questa operazione corrisponde
-semplicemente al trasferimento dei dati dal file al buffer).
+semplicemente al trasferimento dei dati dal file al buffer in \textit{kernel
+ space}.
La lettura viene eseguita in blocchi pari alla dimensione specificata
dall'opzione \texttt{-s} (il default è 4096); essendo in questo caso
\func{splice} equivalente ad una \func{read} sul file, se ne controlla il
valore di uscita in \var{nread} che indica quanti byte sono stati letti, se
-detto valore è nullo (\texttt{\small 36}) questo significa che si è giunti
+detto valore è nullo (\texttt{\small 16}) questo significa che si è giunti
alla fine del file sorgente e pertanto l'operazione di copia è conclusa e si
può uscire dal ciclo arrivando alla conclusione del programma (\texttt{\small
- 59}). In caso di valore negativo (\texttt{\small 37-44}) c'è stato un
-errore ed allora si ripete la lettura (\texttt{\small 36}) se questo è dovuto
+ 59}). In caso di valore negativo (\texttt{\small 17-24}) c'è stato un
+errore ed allora si ripete la lettura (\texttt{\small 16}) se questo è dovuto
ad una interruzione, o altrimenti si esce con un messaggio di errore
-(\texttt{\small 41-43}).
+(\texttt{\small 21-23}).
Una volta completata con successo la lettura si avvia il ciclo di scrittura
-(\texttt{\small 45-57}); questo inizia (\texttt{\small 46-47}) con la
+(\texttt{\small 25-37}); questo inizia (\texttt{\small 26-27}) con la
seconda \func{splice} che cerca di scrivere gli \var{nread} byte letti, si
noti come in questo caso il primo argomento faccia di nuovo riferimento alla
\textit{pipe} (in questo caso si usa il capo in lettura, per i dettagli si
Di nuovo si controlla il numero di byte effettivamente scritti restituito in
\var{nwrite} e in caso di errore al solito si ripete la scrittura se questo è
dovuto a una interruzione o si esce con un messaggio negli altri casi
-(\texttt{\small 48-55}). Infine si chiude il ciclo di scrittura sottraendo
-(\texttt{\small 57}) il numero di byte scritti a quelli di cui è richiesta la
+(\texttt{\small 28-35}). Infine si chiude il ciclo di scrittura sottraendo
+(\texttt{\small 37}) il numero di byte scritti a quelli di cui è richiesta la
scrittura,\footnote{in questa parte del ciclo \var{nread}, il cui valore
iniziale è dato dai byte letti dalla precedente chiamata a \func{splice},
viene ad assumere il significato di byte da scrivere.} così che il ciclo di
Si noti come il programma sia concettualmente identico a quello che si sarebbe
scritto usando \func{read} al posto della prima \func{splice} e \func{write}
-al posto della seconda, utilizzando un buffer in user space per eseguire la
-copia dei dati, solo che in questo caso non è stato necessario allocare nessun
-buffer e non si è trasferito nessun dato in user space.
-
-Si noti anche come si sia usata la combinazione \texttt{SPLICE\_F\_MOVE |
- SPLICE\_F\_MORE } per l'argomento \param{flags} di \func{splice}, infatti
-anche se un valore nullo avrebbe dato gli stessi risultati, l'uso di questi
-flag, che si ricordi servono solo a dare suggerimenti al kernel, permette in
-genere di migliorare le prestazioni.
+al posto della seconda, utilizzando un buffer in \textit{user space} per
+eseguire la copia dei dati, solo che in questo caso non è stato necessario
+allocare nessun buffer e non si è trasferito nessun dato in \textit{user
+ space}. Si noti anche come si sia usata la combinazione
+\texttt{SPLICE\_F\_MOVE | SPLICE\_F\_MORE } per l'argomento \param{flags} di
+\func{splice}, infatti anche se un valore nullo avrebbe dato gli stessi
+risultati, l'uso di questi flag, che si ricordi servono solo a dare
+suggerimenti al kernel, permette in genere di migliorare le prestazioni.
Come accennato con l'introduzione di \func{splice} sono state realizzate anche
altre due \textit{system call}, \func{vmsplice} e \func{tee}, che utilizzano
la stessa infrastruttura e si basano sullo stesso concetto di manipolazione e
-trasferimento di dati attraverso un buffer in kernel space; benché queste non
-attengono strettamente ad operazioni di trasferimento dati fra file
+trasferimento di dati attraverso un buffer in \textit{kernel space}; benché
+queste non attengono strettamente ad operazioni di trasferimento dati fra file
descriptor, le tratteremo qui, essendo strettamente correlate fra loro.
La prima funzione, \funcd{vmsplice}, è la più simile a \func{splice} e come
indica il suo nome consente di trasferire i dati dalla memoria virtuale di un
processo (ad esempio per un file mappato in memoria) verso una \textit{pipe};
il suo prototipo è:
-\begin{functions}
- \headdecl{fcntl.h}
- \headdecl{sys/uio.h}
- \funcdecl{long vmsplice(int fd, const struct iovec *iov, unsigned long
- nr\_segs, unsigned int flags)}
-
- Trasferisce dati dalla memoria di un processo verso una \textit{pipe}.
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fhead{sys/uio.h}
+\fdecl{long vmsplice(int fd, const struct iovec *iov, unsigned long nr\_segs,\\
+\phantom{long vmsplice(}unsigned int flags)}
+\fdesc{Trasferisce dati dalla memoria di un processo verso una \textit{pipe}.}
+}
- \bodydesc{La funzione restituisce il numero di byte trasferiti in caso di
- successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
- dei valori:
- \begin{errlist}
+{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$
+ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] o \param{fd} non è un file descriptor valido o non
fa riferimento ad una \textit{pipe}.
\item[\errcode{EINVAL}] si è usato un valore nullo per \param{nr\_segs}
oppure si è usato \const{SPLICE\_F\_GIFT} ma la memoria non è allineata.
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
richiesta.
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+}
+\end{funcproto}
La \textit{pipe} indicata da \param{fd} dovrà essere specificata tramite il
file descriptor corrispondente al suo capo aperto in scrittura (di nuovo si
eseguire una copia dei dati che contengono.
La seconda funzione aggiunta insieme a \func{splice} è \func{tee}, che deve il
-suo nome all'omonimo comando in user space, perché in analogia con questo
-permette di duplicare i dati in ingresso su una \textit{pipe} su un'altra
-\textit{pipe}. In sostanza, sempre nell'ottica della manipolazione dei dati su
-dei buffer in kernel space, la funzione consente di eseguire una copia del
-contenuto del buffer stesso. Il prototipo di \funcd{tee} è il seguente:
-\begin{functions}
- \headdecl{fcntl.h}
-
- \funcdecl{long tee(int fd\_in, int fd\_out, size\_t len, unsigned int
+suo nome all'omonimo comando in \textit{user space}, perché in analogia con
+questo permette di duplicare i dati in ingresso su una \textit{pipe} su
+un'altra \textit{pipe}. In sostanza, sempre nell'ottica della manipolazione
+dei dati su dei buffer in \textit{kernel space}, la funzione consente di
+eseguire una copia del contenuto del buffer stesso. Il prototipo di
+\funcd{tee} è il seguente:
+
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{long tee(int fd\_in, int fd\_out, size\_t len, unsigned int
flags)}
-
- Duplica \param{len} byte da una \textit{pipe} ad un'altra.
+\fdesc{Duplica i dati da una \textit{pipe} ad un'altra.}
+}
- \bodydesc{La funzione restituisce il numero di byte copiati in caso di
- successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
- dei valori:
- \begin{errlist}
+{La funzione ritorna restituisce il numero di byte copiati in caso di successo
+ e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EINVAL}] o uno fra \param{fd\_in} e \param{fd\_out} non fa
riferimento ad una \textit{pipe} o entrambi fanno riferimento alla
stessa \textit{pipe}.
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
richiesta.
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+}
+\end{funcproto}
La funzione copia \param{len} byte del contenuto di una \textit{pipe} su di
un'altra; \param{fd\_in} deve essere il capo in lettura della \textit{pipe}
La funzione restituisce il numero di byte copiati da una \textit{pipe}
all'altra (o $-1$ in caso di errore), un valore nullo indica che non ci sono
byte disponibili da copiare e che il capo in scrittura della \textit{pipe} è
-stato chiuso.\footnote{si tenga presente però che questo non avviene se si è
- impostato il flag \const{SPLICE\_F\_NONBLOCK}, in tal caso infatti si
- avrebbe un errore di \errcode{EAGAIN}.} Un esempio di realizzazione del
-comando \texttt{tee} usando questa funzione, ripreso da quello fornito nella
-pagina di manuale e dall'esempio allegato al patch originale, è riportato in
+stato chiuso; si tenga presente però che questo non avviene se si è impostato
+il flag \const{SPLICE\_F\_NONBLOCK}, in tal caso infatti si avrebbe un errore
+di \errcode{EAGAIN}. Un esempio di realizzazione del comando \texttt{tee}
+usando questa funzione, ripreso da quello fornito nella pagina di manuale e
+dall'esempio allegato al patch originale, è riportato in
fig.~\ref{fig:tee_example}. Il programma consente di copiare il contenuto
-dello standard input sullo standard output e su un file specificato come
-argomento, il codice completo si trova nel file \texttt{tee.c} dei sorgenti
-allegati alla guida.
+dello \textit{standard input} sullo \textit{standard output} e su un file
+specificato come argomento, il codice completo si trova nel file
+\texttt{tee.c} dei sorgenti allegati alla guida.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{\codesamplewidth}
\includecodesample{listati/tee.c}
\label{fig:tee_example}
\end{figure}
-La prima parte del programma (\texttt{\small 10-35}) si cura semplicemente di
-controllare (\texttt{\small 11-14}) che sia stato fornito almeno un argomento
-(il nome del file su cui scrivere), di aprirlo ({\small 15-19}) e che sia lo
-standard input (\texttt{\small 20-27}) che lo standard output (\texttt{\small
- 28-35}) corrispondano ad una \textit{pipe}.
+La prima parte del programma, che si è omessa per brevità, si cura
+semplicemente di controllare che sia stato fornito almeno un argomento (il
+nome del file su cui scrivere), di aprirlo e che sia lo standard input che lo
+standard output corrispondano ad una \textit{pipe}.
-Il ciclo principale (\texttt{\small 37-58}) inizia con la chiamata a
+Il ciclo principale (\texttt{\small 11-32}) inizia con la chiamata a
\func{tee} che duplica il contenuto dello standard input sullo standard output
-(\texttt{\small 39}), questa parte è del tutto analoga ad una lettura ed
+(\texttt{\small 13}), questa parte è del tutto analoga ad una lettura ed
infatti come nell'esempio di fig.~\ref{fig:splice_example} si controlla il
valore di ritorno della funzione in \var{len}; se questo è nullo significa che
-non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 40}), se
+non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 14}), se
è negativo c'è stato un errore, ed allora si ripete la chiamata se questo è
-dovuto ad una interruzione (\texttt{\small 42-44}) o si stampa un messaggio
-di errore e si esce negli altri casi (\texttt{\small 44-47}).
-
-Una volta completata la copia dei dati sullo standard output si possono
-estrarre dalla standard input e scrivere sul file, di nuovo su usa un ciclo di
-scrittura (\texttt{\small 50-58}) in cui si ripete una chiamata a
-\func{splice} (\texttt{\small 51}) fintanto che non si sono scritti tutti i
-\var{len} byte copiati in precedenza con \func{tee} (il funzionamento è
-identico all'analogo ciclo di scrittura del precedente esempio di
+dovuto ad una interruzione (\texttt{\small 15-48}) o si stampa un messaggio
+di errore e si esce negli altri casi (\texttt{\small 18-21}).
+
+Una volta completata la copia dei dati sullo \textit{standard output} si
+possono estrarre dallo \textit{standard input} e scrivere sul file, di nuovo
+su usa un ciclo di scrittura (\texttt{\small 24-31}) in cui si ripete una
+chiamata a \func{splice} (\texttt{\small 25}) fintanto che non si sono scritti
+tutti i \var{len} byte copiati in precedenza con \func{tee} (il funzionamento
+è identico all'analogo ciclo di scrittura del precedente esempio di
fig.~\ref{fig:splice_example}).
Infine una nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}:
% TODO?? dal 2.6.25 splice ha ottenuto il supporto per la ricezione su rete
+% TODO trattare qui copy_file_range (vedi http://lwn.net/Articles/659523/),
+% introdotta nel kernel 4.5, vedi anche https://lwn.net/Articles/846403/
+
\subsection{Gestione avanzata dell'accesso ai dati dei file}
\label{sec:file_fadvise}
\itindbeg{read-ahead}
Una prima funzione che può essere utilizzata per modificare la gestione
-ordinaria dell'I/O su un file è \funcd{readahead},\footnote{questa è una
- funzione specifica di Linux, introdotta con il kernel 2.4.13, e non deve
- essere usata se si vogliono scrivere programmi portabili.} che consente di
-richiedere una lettura anticipata del contenuto dello stesso in cache, così
-che le seguenti operazioni di lettura non debbano subire il ritardo dovuto
-all'accesso al disco; il suo prototipo è:
-\begin{functions}
- \headdecl{fcntl.h}
-
- \funcdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)}
-
- Esegue una lettura preventiva del contenuto di un file in cache.
+ordinaria dell'I/O su un file è \funcd{readahead} (questa è una funzione
+specifica di Linux, introdotta con il kernel 2.4.13, e non deve essere usata
+se si vogliono scrivere programmi portabili), che consente di richiedere una
+lettura anticipata del contenuto dello stesso in cache, così che le seguenti
+operazioni di lettura non debbano subire il ritardo dovuto all'accesso al
+disco; il suo prototipo è:
+
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)}
+\fdesc{Esegue una lettura preventiva del contenuto di un file in cache.}
+}
- \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] l'argomento \param{fd} non è un file descriptor
valido o non è aperto in lettura.
\item[\errcode{EINVAL}] l'argomento \param{fd} si riferisce ad un tipo di
file che non supporta l'operazione (come una \textit{pipe} o un socket).
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+}
+\end{funcproto}
La funzione richiede che venga letto in anticipo il contenuto del file
\param{fd} a partire dalla posizione \param{offset} e per un ammontare di
in blocchi corrispondenti alle dimensioni delle pagine di memoria, ed i valori
di \param{offset} e \param{count} vengono arrotondati di conseguenza.
-La funzione estende quello che è un comportamento normale del kernel che
+La funzione estende quello che è un comportamento normale del kernel che,
quando si legge un file, aspettandosi che l'accesso prosegua, esegue sempre
una lettura preventiva di una certa quantità di dati; questo meccanismo di
lettura anticipata viene chiamato \textit{read-ahead}, da cui deriva il nome
\itindend{read-ahead}
Il concetto di \func{readahead} viene generalizzato nello standard
-POSIX.1-2001 dalla funzione \func{posix\_fadvise},\footnote{anche se
- l'argomento \param{len} è stato modificato da \type{size\_t} a \type{off\_t}
- nella revisione POSIX.1-2003 TC5.} che consente di ``\textsl{avvisare}'' il
+POSIX.1-2001 dalla funzione \func{posix\_fadvise} (anche se
+l'argomento \param{len} è stato modificato da \type{size\_t} a \type{off\_t}
+nella revisione POSIX.1-2003 TC1) che consente di ``\textsl{avvisare}'' il
kernel sulle modalità con cui si intende accedere nel futuro ad una certa
-porzione di un file,\footnote{la funzione però è stata introdotta su Linux
- solo a partire dal kernel 2.5.60.} così che esso possa provvedere le
-opportune ottimizzazioni; il prototipo di \funcd{posix\_fadvise}, che è
-disponibile soltanto se è stata definita la macro \macro{\_XOPEN\_SOURCE} ad
-valore di almeno 600, è:
-\begin{functions}
- \headdecl{fcntl.h}
-
- \funcdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)}
-
- Dichiara al kernel le future modalità di accesso ad un file.
+porzione di un file, così che esso possa provvedere le opportune
+ottimizzazioni; il prototipo di \funcd{posix\_fadvise}\footnote{la funzione è
+ stata introdotta su Linux solo a partire dal kernel 2.5.60, ed è disponibile
+ soltanto se è stata definita la macro \macro{\_XOPEN\_SOURCE} ad valore di
+ almeno \texttt{600} o la macro \macro{\_POSIX\_C\_SOURCE} ad valore di
+ almeno \texttt{200112L}.} è:
+
- \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)}
+\fdesc{Dichiara al kernel le future modalità di accesso ad un file.}
+}
+
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] l'argomento \param{fd} non è un file descriptor
valido.
\item[\errcode{EINVAL}] il valore di \param{advice} non è valido o
\param{fd} si riferisce ad un tipo di file che non supporta l'operazione
(come una \textit{pipe} o un socket).
- \item[\errcode{ESPIPE}] previsto dallo standard se \param{fd} è una \textit{pipe} o
- un socket (ma su Linux viene restituito \errcode{EINVAL}).
- \end{errlist}
- }
-\end{functions}
+ \item[\errcode{ESPIPE}] previsto dallo standard se \param{fd} è una
+ \textit{pipe} o un socket (ma su Linux viene restituito
+ \errcode{EINVAL}).
+ \end{errlist}
+}
+\end{funcproto}
La funzione dichiara al kernel le modalità con cui intende accedere alla
regione del file indicato da \param{fd} che inizia alla posizione
\param{offset} e si estende per \param{len} byte. Se per \param{len} si usa un
-valore nullo la regione coperta sarà da \param{offset} alla fine del
-file.\footnote{questo è vero solo per le versioni più recenti, fino al kernel
- 2.6.6 il valore nullo veniva interpretato letteralmente.} Le modalità sono
-indicate dall'argomento \param{advice} che è una maschera binaria dei valori
-illustrati in tab.~\ref{tab:posix_fadvise_flag}, che riprendono il significato
-degli analoghi già visti in sez.~\ref{sec:file_memory_map} per
+valore nullo la regione coperta sarà da \param{offset} alla fine del file, ma
+questo è vero solo per le versioni più recenti, fino al kernel 2.6.6 il valore
+nullo veniva interpretato letteralmente. Le modalità sono indicate
+dall'argomento \param{advice} che è una maschera binaria dei valori illustrati
+in tab.~\ref{tab:posix_fadvise_flag}, che riprendono il significato degli
+analoghi già visti in sez.~\ref{sec:file_memory_map} per
\func{madvise}.\footnote{dato che si tratta dello stesso tipo di funzionalità,
in questo caso applicata direttamente al sistema ai contenuti di un file
invece che alla sua mappatura in memoria.} Si tenga presente comunque che la
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{POSIX\_FADV\_NORMAL} & Non ci sono avvisi specifici da fare
+ \constd{POSIX\_FADV\_NORMAL} & Non ci sono avvisi specifici da fare
riguardo le modalità di accesso, il
comportamento sarà identico a quello che si
avrebbe senza nessun avviso.\\
- \const{POSIX\_FADV\_SEQUENTIAL}& L'applicazione si aspetta di accedere di
+ \constd{POSIX\_FADV\_SEQUENTIAL}& L'applicazione si aspetta di accedere di
accedere ai dati specificati in maniera
sequenziale, a partire dalle posizioni più
basse.\\
- \const{POSIX\_FADV\_RANDOM} & I dati saranno letti in maniera
+ \constd{POSIX\_FADV\_RANDOM} & I dati saranno letti in maniera
completamente causale.\\
- \const{POSIX\_FADV\_NOREUSE} & I dati saranno acceduti una sola volta.\\
- \const{POSIX\_FADV\_WILLNEED}& I dati saranno acceduti a breve.\\
- \const{POSIX\_FADV\_DONTNEED}& I dati non saranno acceduti a breve.\\
+ \constd{POSIX\_FADV\_NOREUSE} & I dati saranno acceduti una sola volta.\\
+ \constd{POSIX\_FADV\_WILLNEED}& I dati saranno acceduti a breve.\\
+ \constd{POSIX\_FADV\_DONTNEED}& I dati non saranno acceduti a breve.\\
\hline
\end{tabular}
\caption{Valori delle costanti usabili per l'argomento \param{advice} di
Questa è una indicazione utile che permette di alleggerire il carico sulla
cache, ed un programma può utilizzare periodicamente questa funzione per
liberare pagine di memoria da dati che non sono più utilizzati per far posto a
-nuovi dati utili.\footnote{la pagina di manuale riporta l'esempio dello
- streaming di file di grosse dimensioni, dove le pagine occupate dai dati già
- inviati possono essere tranquillamente scartate.}
+nuovi dati utili; la pagina di manuale riporta l'esempio dello streaming di
+file di grosse dimensioni, dove le pagine occupate dai dati già inviati
+possono essere tranquillamente scartate.
Sia \func{posix\_fadvise} che \func{readahead} attengono alla ottimizzazione
dell'accesso in lettura; lo standard POSIX.1-2001 prevede anche una funzione
-specifica per le operazioni di scrittura,
-\funcd{posix\_fallocate},\footnote{la funzione è stata introdotta a partire
- dalle glibc 2.1.94.} che consente di preallocare dello spazio disco per
-assicurarsi che una seguente scrittura non fallisca, il suo prototipo,
-anch'esso disponibile solo se si definisce la macro \macro{\_XOPEN\_SOURCE} ad
-almeno 600, è:
-\begin{functions}
- \headdecl{fcntl.h}
-
- \funcdecl{int posix\_fallocate(int fd, off\_t offset, off\_t len)}
-
- Richiede la allocazione di spazio disco per un file.
+specifica per le operazioni di scrittura, \funcd{posix\_fallocate} (la
+funzione è stata introdotta a partire dalle glibc 2.1.94), che consente di
+preallocare dello spazio disco per assicurarsi che una seguente scrittura non
+fallisca, il suo prototipo, anch'esso disponibile solo se si definisce la
+macro \macro{\_XOPEN\_SOURCE} ad almeno 600, è:
+
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{int posix\_fallocate(int fd, off\_t offset, off\_t len)}
+\fdesc{Richiede la allocazione di spazio disco per un file.}
+}
- \bodydesc{La funzione restituisce 0 in caso di successo e direttamente un
- codice di errore, in caso di fallimento, in questo caso \var{errno} non
- viene impostata, ma sarà restituito direttamente uno dei valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e direttamente un codice di
+ errore altrimenti, in tal caso \var{errno} non viene impostato, e si otterrà
+ direttamente uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] l'argomento \param{fd} non è un file descriptor
valido o non è aperto in scrittura.
\item[\errcode{EINVAL}] o \param{offset} o \param{len} sono minori di
l'operazione.
\item[\errcode{ESPIPE}] l'argomento \param{fd} è una \textit{pipe}.
\end{errlist}
- }
-\end{functions}
+}
+\end{funcproto}
La funzione assicura che venga allocato sufficiente spazio disco perché sia
possibile scrivere sul file indicato dall'argomento \param{fd} nella regione
Ci si può chiedere a cosa possa servire una funzione come
\func{posix\_fallocate} dato che è sempre possibile ottenere l'effetto voluto
-eseguendo esplicitamente sul file la scrittura\footnote{usando \funcd{pwrite}
- per evitare spostamenti della posizione corrente sul file.} di una serie di
-zeri per l'estensione di spazio necessaria qualora il \itindex{sparse~file}
-file debba essere esteso o abbia dei \index{file!\textit{hole}}
-buchi.\footnote{si ricordi che occorre scrivere per avere l'allocazione e che
- l'uso di \func{truncate} per estendere un file creerebbe soltanto uno
- \itindex{sparse~file} \textit{sparse file} (vedi sez.~\ref{sec:file_lseek})
- senza una effettiva allocazione dello spazio disco.} In realtà questa è la
-modalità con cui la funzione veniva realizzata nella prima versione fornita
-dalle \acr{glibc}, per cui la funzione costituiva in sostanza soltanto una
-standardizzazione delle modalità di esecuzione di questo tipo di allocazioni.
+eseguendo esplicitamente sul file la scrittura di una serie di zeri (usando
+\funcd{pwrite} per evitare spostamenti della posizione corrente sul file) per
+l'estensione di spazio necessaria qualora il file debba essere esteso o abbia
+dei buchi.\footnote{si ricordi che occorre scrivere per avere l'allocazione e
+ che l'uso di \func{truncate} per estendere un file creerebbe soltanto uno
+ \textit{sparse file} (vedi sez.~\ref{sec:file_lseek}) senza una effettiva
+ allocazione dello spazio disco.} In realtà questa è la modalità con cui la
+funzione veniva realizzata nella prima versione fornita dalla \acr{glibc}, per
+cui la funzione costituiva in sostanza soltanto una standardizzazione delle
+modalità di esecuzione di questo tipo di allocazioni.
Questo metodo, anche se funzionante, comporta però l'effettiva esecuzione una
scrittura su tutto lo spazio disco necessario, da fare al momento della
richiesta di allocazione, pagandone il conseguente prezzo in termini di
prestazioni; il tutto quando in realtà servirebbe solo poter riservare lo
spazio per poi andarci a scrivere, una sola volta, quando il contenuto finale
-diventa effettivamente disponibile.
-
-Per poter fare tutto questo è però necessario il supporto da parte del kernel,
-e questo è divenuto disponibile solo a partire dal kernel 2.6.23 in cui è
-stata introdotta la nuova \textit{system call} \func{fallocate},\footnote{non
- è detto che la funzione sia disponibile per tutti i filesystem, ad esempio
- per XFS il supporto è stato introdotto solo a partire dal kernel 2.6.25.}
-che consente di realizzare direttamente all'interno del kernel l'allocazione
-dello spazio disco così da poter realizzare una versione di
-\func{posix\_fallocate} con prestazioni molto più elevate.\footnote{nelle
- \acr{glibc} la nuova \textit{system call} viene sfruttata per la
- realizzazione di \func{posix\_fallocate} a partire dalla versione 2.10.}
+diventa effettivamente disponibile. Per poter fare tutto questo è però
+necessario il supporto da parte del kernel, e questo è divenuto disponibile
+solo a partire dal kernel 2.6.23 in cui è stata introdotta la nuova
+\textit{system call} \func{fallocate},\footnote{non è detto che la funzione
+ sia disponibile per tutti i filesystem, ad esempio per XFS il supporto è
+ stato introdotto solo a partire dal kernel 2.6.25.} che consente di
+realizzare direttamente all'interno del kernel l'allocazione dello spazio
+disco così da poter realizzare una versione di \func{posix\_fallocate} con
+prestazioni molto più elevate; nella \acr{glibc} la nuova \textit{system call}
+viene sfruttata per la realizzazione di \func{posix\_fallocate} a partire
+dalla versione 2.10.
Trattandosi di una funzione di servizio, ed ovviamente disponibile
esclusivamente su Linux, inizialmente \funcd{fallocate} non era stata definita
come funzione di libreria,\footnote{pertanto poteva essere invocata soltanto
in maniera indiretta con l'ausilio di \func{syscall}, vedi
sez.~\ref{sec:proc_syscall}, come \code{long fallocate(int fd, int mode,
- loff\_t offset, loff\_t len)}.} ma a partire dalle \acr{glibc} 2.10 è
+ loff\_t offset, loff\_t len)}.} ma a partire dalla \acr{glibc} 2.10 è
stato fornito un supporto esplicito; il suo prototipo è:
-\begin{functions}
- \headdecl{linux/fcntl.h}
- \funcdecl{int fallocate(int fd, int mode, off\_t offset, off\_t len)}
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{int fallocate(int fd, int mode, off\_t offset, off\_t len)}
+\fdesc{Prealloca dello spazio disco per un file.}
+}
- Prealloca dello spazio disco per un file.
-
- \bodydesc{La funzione ritorna 0 in caso di successo e $-1$ in caso di errore,
- nel qual caso \var{errno} può assumere i valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] \param{fd} non fa riferimento ad un file descriptor
valido aperto in scrittura.
\item[\errcode{EFBIG}] la somma di \param{offset} e \param{len} eccede le
minore o uguale a zero.
\item[\errcode{ENODEV}] \param{fd} non fa riferimento ad un file ordinario
o a una directory.
- \item[\errcode{ENOSPC}] non c'è spazio disco sufficiente per l'operazione.
+ \item[\errcode{EPERM}] il file è immutabile o \textit{append-only} (vedi
+ sez.~\ref{sec:file_perm_overview}).
\item[\errcode{ENOSYS}] il filesystem contenente il file associato
a \param{fd} non supporta \func{fallocate}.
\item[\errcode{EOPNOTSUPP}] il filesystem contenente il file associato
a \param{fd} non supporta l'operazione \param{mode}.
- \end{errlist}
- ed inoltre \errval{EINTR}, \errval{EIO}.
-}
-\end{functions}
+ \end{errlist}
+ ed inoltre \errval{EINTR}, \errval{EIO} e \errval{ENOSPC} nel loro significato
+ generico.}
+\end{funcproto}
La funzione prende gli stessi argomenti di \func{posix\_fallocate} con lo
stesso significato, a cui si aggiunge l'argomento \param{mode} che indica le
-modalità di allocazione; al momento quest'ultimo può soltanto essere nullo o
-assumere il valore \const{FALLOC\_FL\_KEEP\_SIZE} che richiede che la
-dimensione del file\footnote{quella ottenuta nel campo \var{st\_size} di una
- struttura \struct{stat} dopo una chiamata a \texttt{fstat}.} non venga
-modificata anche quando la somma di \param{offset} e \param{len} eccede la
-dimensione corrente.
-
-Se \param{mode} è nullo invece la dimensione totale del file in caso di
-estensione dello stesso viene aggiornata, come richiesto per
-\func{posix\_fallocate}, ed invocata in questo modo si può considerare
-\func{fallocate} come l'implementazione ottimale di \func{posix\_fallocate} a
-livello di kernel.
+modalità di allocazione; se questo è nullo il comportamento è identico a
+quello di \func{posix\_fallocate} e si può considerare \func{fallocate} come
+l'implementazione ottimale della stessa a livello di kernel.
+
+Inizialmente l'unico altro valore possibile per \param{mode} era
+\const{FALLOC\_FL\_KEEP\_SIZE} che richiede che la dimensione del file
+(quella ottenuta nel campo \var{st\_size} di una struttura \struct{stat} dopo
+una chiamata a \texttt{fstat}) non venga modificata anche quando la somma
+di \param{offset} e \param{len} eccede la dimensione corrente, che serve
+quando si deve comunque preallocare dello spazio per scritture in append. In
+seguito sono stati introdotti altri valori, riassunti in
+tab.\ref{tab:fallocate_mode}, per compiere altre operazioni relative alla
+allocazione dello spazio disco dei file.
+
+\begin{table}[htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|l|p{10cm}|}
+ \hline
+ \textbf{Valore} & \textbf{Significato} \\
+ \hline
+ \hline
+ \constd{FALLOC\_FL\_INSERT} & .\\
+ \constd{FALLOC\_FL\_COLLAPSE\_RANGE}& .\\
+ \constd{FALLOC\_FL\_KEEP\_SIZE} & Mantiene invariata la dimensione del
+ file, pur allocando lo spazio disco anche
+ oltre la dimensione corrente del file.\\
+ \constd{FALLOC\_FL\_PUNCH\_HOLE}& Crea un \textsl{buco} nel file (vedi
+ sez.~\ref{sec:file_lseek}) rendendolo una
+ \textit{sparse file} (dal kernel
+ 2.6.38).\\
+ \constd{FALLOC\_FL\_ZERO\_RANGE}& .\\
+ \hline
+ \end{tabular}
+ \caption{Valori delle costanti usabili per l'argomento \param{mode} di
+ \func{fallocate}.}
+ \label{tab:fallocate_mode}
+\end{table}
+
+In particolare con \const{FALLOC\_FL\_PUNCH\_HOLE} è possibile scartare il
+contenuto della sezione di file indicata da \param{offser} e \param{len},
+creando un \textsl{buco} (si ricordi quanto detto in
+sez.~\ref{sec:file_lseek}); i blocchi del file interamente contenuti
+nell'intervallo verranno disallocati, la parte di intervallo contenuta
+parzialmente in altri blocchi verrà riempita con zeri e la lettura dal file
+restituirà degli zeri per tutto l'intervallo indicato. In sostanza si rende il
+file uno \textit{sparse file} a posteriori.
% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/
% http://kernelnewbies.org/Linux_2_6_23
+
% TODO aggiungere FALLOC_FL_ZERO_RANGE e FALLOC_FL_COLLAPSE_RANGE, inseriti
% nel kernel 3.15 (sul secondo vedi http://lwn.net/Articles/589260/), vedi
% anche http://lwn.net/Articles/629965/
+% TODO aggiungere FALLOC_FL_INSERT vedi http://lwn.net/Articles/629965/
+
+% TODO aggiungere i file hints di fcntl (F_GET_RW_HINT e compagnia)
+% con RWH_WRITE_LIFE_EXTREME e RWH_WRITE_LIFE_SHORT aggiunte con
+% il kernel 4.13 (vedi https://lwn.net/Articles/727385/)
+
+\subsection{Altre funzionalità avanzate}
+\label{sec:file_seal_et_al}
+
+da fare
+
% TODO non so dove trattarli, ma dal 2.6.39 ci sono i file handle, vedi
-% http://lwn.net/Articles/432757/
+% http://lwn.net/Articles/432757/ (probabilmente da associare alle
+% at-functions)
+
+% TODO: trattare i file seal, vedi fcntl / F_ADD_SEAL e memfd_create
+
+% TODO trattare qui ioctl_ficlonerange ?
+
+% TODO trattare qui close_range, vedi https://lwn.net/Articles/789023/
+% dal 5.11 aggiunto CLOSE_RANGE_CLOEXEC, https://lwn.net/Articles/837816/
+
% LocalWords: dell'I locking multiplexing cap sez system call socket BSD GID
% LocalWords: clockid CLOCK MONOTONIC REALTIME itimerspec interval Resource
% LocalWords: ABSTIME gettime temporarily unavailable SIGINT SIGQUIT SIGTERM
% LocalWords: sigfd fifofd break siginf names starting echo Message from Got
-% LocalWords: message kill received means exit
+% LocalWords: message kill received means exit TLOCK ULOCK EPOLLWAKEUP
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "gapil"
%%% End:
+