From: Simone Piccardi Date: Sun, 10 Nov 2002 18:28:51 +0000 (+0000) Subject: File lock e sincronizzazione con i file. X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=commitdiff_plain;h=a99dd56a21d2f6cf86da2e1f9e27cdaab010b5a5;p=gapil.git File lock e sincronizzazione con i file. --- diff --git a/fileadv.tex b/fileadv.tex index 454d91b..a9ba273 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -46,27 +46,28 @@ l'errore \macro{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 \textit{polling}, è estremamente inefficiente: si tiene costantemente -impiegata la CPU solo per eseguire in continuazione delle system call che -nella gran parte dei casi falliranno. Per evitare questo, come vedremo in -\secref{sec:file_multiplexing}, è stata introdotta una nuova interfaccia di -programmazione, che comporta comunque l'uso della modalità di I/O non -bloccante. +detta \textit{polling}\index{polling}, è estremamente inefficiente: si tiene +costantemente impiegata la CPU solo per eseguire in continuazione delle system +call che nella gran parte dei casi falliranno. Per evitare questo, come +vedremo in \secref{sec:file_multiplexing}, è stata introdotta una nuova +interfaccia di programmazione, che comporta comunque l'uso della modalità di +I/O non bloccante. \subsection{L'I/O multiplexing} \label{sec:file_multiplexing} -Per superare il problema di dover usare il \textit{polling} per controllare la -possibilità di effettuare operazioni su un file aperto in modalità non -bloccante, sia BSD che System V hanno introdotto delle nuove funzioni in grado -di sospendere l'esecuzione di un processo in attesa che l'accesso diventi -possibile. Il primo ad introdurre questa modalità di operazione, chiamata -usualmente \textit{I/O multiplexing}, è stato BSD,\footnote{la funzione è - apparsa in BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i - sistemi che supportano i \textit{socket}, compreso le varianti di System V.} -con la funzione \func{select}, il cui prototipo è: +Per superare il problema di dover usare il \textit{polling}\index{polling} per +controllare la possibilità di effettuare operazioni su un file aperto in +modalità non bloccante, sia BSD che System V hanno introdotto delle nuove +funzioni in grado di sospendere l'esecuzione di un processo in attesa che +l'accesso diventi possibile. Il primo ad introdurre questa modalità di +operazione, chiamata usualmente \textit{I/O multiplexing}, è stato +BSD,\footnote{la funzione è apparsa in BSD4.2 e standardizzata in BSD4.4, ma è + stata portata su tutti i sistemi che supportano i \textit{socket}, compreso + le varianti di System V.} con la funzione \func{select}, il cui prototipo +è: \begin{functions} \headdecl{sys/time.h} \headdecl{sys/types.h} diff --git a/fileunix.tex b/fileunix.tex index a36652d..eae73cb 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -295,9 +295,10 @@ sempre il file descriptor con il valore pi \footnotetext[2]{la pagina di manuale di \func{open} segnala che questa opzione è difettosa su NFS, e che i programmi che la usano per stabilire un - file di lock possono incorrere in una race condition\index{race condition}. - Si consiglia come alternativa di usare un file con un nome univoco e la - funzione \func{link} per verificarne l'esistenza.} + file di lock (vedi \secref{sec:ipc_file_lock}) possono incorrere in una race + condition\index{race condition}. Si consiglia come alternativa di usare un + file con un nome univoco e la funzione \func{link} per verificarne + l'esistenza.} \footnotetext[3]{\textit{Denial of Service}, si chiamano così attacchi miranti ad impedire un servizio causando una qualche forma di carico eccessivo per diff --git a/ipc.tex b/ipc.tex index 74d69db..ee3270c 100644 --- a/ipc.tex +++ b/ipc.tex @@ -879,20 +879,20 @@ molti altri devono poter leggere non pu Per questo nello sviluppo di System V vennero introdotti una serie di nuovi oggetti per la comunicazione fra processi ed una nuova interfaccia di programmazione, che fossero in grado di garantire una maggiore flessibilità. -In questa sezione esamineremo come Linux supporta quello che viene ormai -chiamato il \textsl{Sistema di comunicazione inter-processo} di System V, o -\textit{System V IPC (Inter-Process Comunication)}. +In questa sezione esamineremo come Linux supporta quello che viene chiamato il +\textsl{Sistema di comunicazione inter-processo} di System V, cui da qui in +avanti faremo riferimento come \textit{SysV IPC} (dove IPC è la sigla di +\textit{Inter-Process Comunication})}. \subsection{Considerazioni generali} \label{sec:ipc_sysv_generic} -La principale caratteristica del sistema di IPC di System V è quella di essere -basato su oggetti permanenti che risiedono nel kernel. Questi, a differenza di -quanto avviene per i file descriptor, non mantengono un contatore dei -riferimenti, e non vengono cancellati dal sistema una volta che non sono più -in uso. +La principale caratteristica del \textit{SysV IPC} è quella di essere basato +su oggetti permanenti che risiedono nel kernel. Questi, a differenza di quanto +avviene per i file descriptor, non mantengono un contatore dei riferimenti, e +non vengono cancellati dal sistema una volta che non sono più in uso. Questo comporta due problemi: il primo è che, al contrario di quanto avviene per pipe e fifo, la memoria allocata per questi oggetti non viene rilasciata @@ -903,13 +903,13 @@ file, un contatore del numero di riferimenti che ne indichi l'essere in uso, essi possono essere cancellati anche se ci sono dei processi che li stanno utilizzando, con tutte le conseguenze (negative) del caso. -Un'ulteriore caratteristica negativa è che gli oggetti usati nel System V IPC -vengono creati direttamente dal kernel, e sono accessibili solo specificando -il relativo \textsl{identificatore}. Questo è un numero progressivo (un po' -come il \acr{pid} dei processi) che il kernel assegna a ciascuno di essi -quanto vengono creati (sul procedimento di assegnazione torneremo in -\secref{sec:ipc_sysv_id_use}). L'identificatore viene restituito dalle -funzioni che creano l'oggetto, ed è quindi locale al processo che le ha +Un'ulteriore caratteristica negativa è che gli oggetti usati nel \textit{SysV + IPC} vengono creati direttamente dal kernel, e sono accessibili solo +specificando il relativo \textsl{identificatore}. Questo è un numero +progressivo (un po' come il \acr{pid} dei processi) che il kernel assegna a +ciascuno di essi quanto vengono creati (sul procedimento di assegnazione +torneremo in \secref{sec:ipc_sysv_id_use}). L'identificatore viene restituito +dalle funzioni che creano l'oggetto, ed è quindi locale al processo che le ha eseguite. Dato che l'identificatore viene assegnato dinamicamente dal kernel non è possibile prevedere quale sarà, né utilizzare un qualche valore statico, si pone perciò il problema di come processi diversi possono accedere allo @@ -922,7 +922,7 @@ primitivo \type{key\_t}, da specificare in fase di creazione dell'oggetto, e tramite la quale è possibile ricavare l'identificatore.\footnote{in sostanza si sposta il problema dell'accesso dalla classificazione in base all'identificatore alla classificazione in base alla chiave, una delle tante - complicazioni inutili presenti nell'IPC di System V.} Oltre la chiave, la + complicazioni inutili presenti nel \textit{SysV IPC}.} Oltre la chiave, la struttura, la cui definizione è riportata in \figref{fig:ipc_ipc_perm}, mantiene varie proprietà ed informazioni associate all'oggetto. @@ -973,7 +973,7 @@ file ed un numero di versione; il suo prototipo \funcdecl{key\_t ftok(const char *pathname, int proj\_id)} - Restituisce una chiave per identificare un oggetto del System V IPC. + Restituisce una chiave per identificare un oggetto del \textit{SysV IPC}. \bodydesc{La funzione restituisce la chiave in caso di successo e -1 altrimenti, nel qual caso \var{errno} sarà uno dei possibili codici di @@ -1011,11 +1011,11 @@ creato da chi ci si aspetta. Questo è, insieme al fatto che gli oggetti sono permanenti e non mantengono un contatore di riferimenti per la cancellazione automatica, il principale -problema del sistema di IPC di System V. Non esiste infatti una modalità -chiara per identificare un oggetto, come sarebbe stato se lo si fosse -associato ad in file, e tutta l'interfaccia è inutilmente complessa. Per -questo ne è stata effettuata una revisione completa nello standard POSIX.1b, -che tratteremo in \secref{sec:ipc_posix}. +problema del \textit{SysV IPC}. Non esiste infatti una modalità chiara per +identificare un oggetto, come sarebbe stato se lo si fosse associato ad in +file, e tutta l'interfaccia è inutilmente complessa. Per questo ne è stata +effettuata una revisione completa nello standard POSIX.1b, che tratteremo in +\secref{sec:ipc_posix}. \subsection{Il controllo di accesso} @@ -1116,10 +1116,10 @@ un identificatore pu Il sistema dispone sempre di un numero fisso di oggetti di IPC,\footnote{fino al kernel 2.2.x questi valori, definiti dalle costanti \macro{MSGMNI}, \macro{SEMMNI} e \macro{SHMMNI}, potevano essere cambiati (come tutti gli - altri limiti relativi al \textit{System V IPC}) solo con una ricompilazione - del kernel, andando a modificarne la definizione nei relativi header file. - A partire dal kernel 2.4.x è possibile cambiare questi valori a sistema - attivo scrivendo sui file \file{shmmni}, \file{msgmni} e \file{sem} di + altri limiti relativi al \textit{SysV IPC}) solo con una ricompilazione del + kernel, andando a modificarne la definizione nei relativi header file. A + partire dal kernel 2.4.x è possibile cambiare questi valori a sistema attivo + scrivendo sui file \file{shmmni}, \file{msgmni} e \file{sem} di \file{/proc/sys/kernel} o con l'uso di \texttt{syscntl}.} e per ciascuno di essi viene mantenuto in \var{seq} un numero di sequenza progressivo che viene incrementato di uno ogni volta che l'oggetto viene cancellato. Quando @@ -1217,7 +1217,7 @@ mantenuta staticamente all'interno del sistema. \subsection{Code di messaggi} \label{sec:ipc_sysv_mq} -Il primo oggetto introdotto dal \textit{System V IPC} è quello delle code di +Il primo oggetto introdotto dal \textit{SysV IPC} è quello delle code di messaggi. Le code di messaggi sono oggetti analoghi alle pipe o alle fifo, anche se la loro struttura è diversa. La funzione che permette di ottenerne una è \func{msgget} ed il suo prototipo è: @@ -1651,6 +1651,24 @@ modificati: \item Il valore \var{msg\_rtime}, che viene impostato al tempo corrente. \end{itemize*} +Le code di messaggi presentano il solito problema di tutti gli oggetti del +SysV IPC; essendo questi permanenti restano nel sistema occupando risorse +anche quando un processo è terminato, al contrario delle pipe per le quali +tutte le risorse occupate vengono rilasciate quanto l'ultimo processo che le +utilizzava termina. Questo comporta che in caso di errori si può saturare il +sistema, e che devono comunque essere esplicitamente previste delle funzioni +di rimozione in caso di interruzioni o uscite dal programma (come vedremo in +\figref{fig:ipc_mq_fortune_server}). + +L'altro problema è non facendo uso di file descriptor le tecniche di +\textit{I/O multiplexing} descritte in \secref{sec:file_multiplexing} non +possono essere utilizzate, e non si ha a disposizione niente di analogo alle +funzioni \func{select} e \func{poll}. Questo rende molto scomodo usare più di +una di queste strutture alla volta; ad esempio non si può scrivere un server +che aspetti un messaggio su più di una coda senza fare ricorso ad una tecnica +di \textit{polling}\index{polling} che esegua un ciclo di attesa su ciascuna +di esse. + Come esempio dell'uso delle code di messaggi possiamo riscrivere il nostro server di \textit{fortunes} usando queste al posto delle fifo. In questo caso useremo una sola coda di messaggi, usando il tipo di messaggio per comunicare @@ -1886,7 +1904,7 @@ della risorsa; in generale per utilizzando il valore del contatore come indicatore del ``numero di risorse'' ancora disponibili. -Il sistema di comunicazione interprocesso di System V IPC prevede anche i +Il sistema di comunicazione interprocesso di \textit{SysV IPC} prevede anche i semafori, ma gli oggetti utilizzati non sono semafori singoli, ma gruppi di semafori detti \textsl{insiemi} (o \textit{semaphore set}); la funzione che permette di creare o ottenere l'identificatore di un insieme di semafori è @@ -1929,8 +1947,8 @@ richiesta dell'identificatore di un insieme gi Purtroppo questa implementazione complica inutilmente lo schema elementare che abbiamo descritto, dato che non è possibile definire un singolo semaforo, ma se ne deve creare per forza un insieme. Ma questa in definitiva è solo una -complicazione inutile, il problema è che i semafori del System V IPC soffrono -di altri due, ben più gravi, difetti. +complicazione inutile, il problema è che i semafori del \textit{SysV IPC} +soffrono di altri due, ben più gravi, difetti. Il primo difetto è che non esiste una funzione che permetta di creare ed inizializzare un semaforo in un'unica chiamata; occorre prima creare l'insieme @@ -1938,8 +1956,8 @@ dei semafori con \func{semget} e poi inizializzarlo con \func{semctl}, si perde così ogni possibilità di eseguire atomicamente questa operazione. Il secondo difetto deriva dalla caratteristica generale degli oggetti del -System V IPC di essere risorse globali di sistema, che non vengono cancellate -quando nessuno le usa più; ci si così a trova a dover affrontare +\textit{SysV IPC} di essere risorse globali di sistema, che non vengono +cancellate quando nessuno le usa più; ci si così a trova a dover affrontare esplicitamente il caso in cui un processo termina per un qualche errore, lasciando un semaforo occupato, che resterà tale fino al successivo riavvio del sistema. Come vedremo esistono delle modalità per evitare tutto ciò, ma @@ -1988,8 +2006,8 @@ semaforo), per quanto riguarda gli altri campi invece: Ciascun semaforo dell'insieme è realizzato come una struttura di tipo \var{sem} che ne contiene i dati essenziali, la sua definizione\footnote{si è riportata la definizione originaria del kernel 1.0, che contiene la prima - realizzazione del System V IPC in Linux. In realtà questa struttura ormai è - ridotta ai soli due primi membri, e gli altri vengono calcolati + realizzazione del \textit{SysV IPC} in Linux. In realtà questa struttura + ormai è ridotta ai soli due primi membri, e gli altri vengono calcolati dinamicamente. La si è utilizzata a scopo di esempio, perché indica tutti i valori associati ad un semaforo, restituiti dalle funzioni di controllo, e citati dalle pagine di manuale.} è riportata in \figref{fig:ipc_sem}. Questa @@ -2360,8 +2378,8 @@ occorre fare riferimento all'implementazione usata in Linux, che in maniera semplificata nello schema di \figref{fig:ipc_sem_schema}. Si è presa come riferimento l'architettura usata fino al kernel 2.2.x che è più semplice (ed illustrata in dettaglio in \cite{tlk}); nel kernel 2.4.x la -struttura del System V IPC è stata modificata, ma le definizioni relative a -queste strutture restano per compatibilità.\footnote{in particolare con le +struttura del \textit{SysV IPC} è stata modificata, ma le definizioni relative +a queste strutture restano per compatibilità.\footnote{in particolare con le vecchie versioni delle librerie del C, come le libc5.} \begin{figure}[htb] @@ -2425,7 +2443,7 @@ ripristino non \subsection{Memoria condivisa} \label{sec:ipc_sysv_shm} -Il terzo oggetto introdotto dal \textit{System V IPC} è quello dei segmenti di +Il terzo oggetto introdotto dal \textit{SysV IPC} è quello dei segmenti di memoria condivisa. La funzione che permette di ottenerne uno è \func{shmget}, ed il suo prototipo è: \begin{functions} @@ -2743,42 +2761,72 @@ condivisa; uno schema semplificato della struttura \section{Tecniche alternative} \label{sec:ipc_alternatives} -Come abbiamo visto in \secref{sec:ipc_sysv_generic} il sistema di IPC di -System V presenta numerosi problemi; in \cite{APUE}\footnote{in particolare - nel capitolo 14.} Stevens effettua una accurata analisi (alcuni dei -concetti sono già stati accennati in precedenza) ed elenca alcune possibili +Come abbiamo visto in \secref{sec:ipc_sysv_generic} il \textit{SysV IPC} +presenta numerosi problemi; in \cite{APUE}\footnote{in particolare nel + capitolo 14.} Stevens effettua una accurata analisi (alcuni dei concetti +sono già stati accennati in precedenza) ed elenca alcune possibili alternative, che vogliamo riprendere in questa sezione. \subsection{Alternative alle code di messaggi} \label{sec:ipc_mq_alternative} -Le code di messaggi sono probabilmente il meno usato degli oggetti di IPC di -System V; esse infatti nacquero principalmente come meccanismo di +Le code di messaggi sono probabilmente il meno usato degli oggetti del +\textit{SysV IPC}; esse infatti nacquero principalmente come meccanismo di comunicazione bidirezionale quando ancora le pipe erano unidirezionali; con la disponibilità di \func{socketpair} (vedi \secref{sec:ipc_socketpair}) si può ottenere lo stesso risultato senza incorrere nelle complicazioni introdotte -dal sistema di IPC di System V. +dal \textit{SysV IPC}. In realtà, grazie alla presenza del campo \var{mtype}, le code di messaggi hanno delle caratteristiche ulteriori, consentendo una classificazione dei -messaggi ed un accesso non rigidamente sequenziale, due cose che sono -impossibili da ottenere con le pipe e i socket di \func{socketpair}. - -È però possibile implementare un meccanismo analogo attraverso l'uso di -memoria condivisa e di meccanismi di sincronizzazione, (un esempio di -reimplementazione di code di messaggi usando il \textit{memory mapping} e i -semafori si trova in \cite{UNP2}). pertanto non è +messaggi ed un accesso non rigidamente sequenziale, due caratteristiche che +sono impossibili da ottenere con le pipe e i socket di \func{socketpair}; +a queste esigenze però si può comunque ovviare in maniera diversa con un uso +combinato della memoria condivisa e dei meccanismi di sincronizzazione, per +cui alla fine l'uso delle code di messaggi classiche è poco diffuso. \subsection{La sincronizzazione con il \textit{file locking}} \label{sec:ipc_file_lock} - - - - - +Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC} +presentano una interfaccia inutilmente complessa e con alcuni difetti +strutturali, per questo quando si ha una semplice esigenza di sincronizzazione +per la quale basterebbe un semaforo binario (quello che abbiamo definito come +\textit{mutex}, che indica la disponibilità o meno di una risorsa, e non ha +associato un contatore come i semafori) si possono utilizzare metodi +alternativi. + +La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare +dei \textsl{file di lock} (per i quali esiste anche una opportuna directory, +\file{/var/lock}, nel filesystem standard). Per questo si usa la +caratteristica della funzione \func{open} (illustrata in +\secref{sec:file_open}) che prevede\footnote{questo è quanto dettato dallo + standard POSIX.1, ciò non toglie che in alcune implementazioni questa + tecnica possa non funzionare; in particolare per Linux, nel caso di NFS, si + è comunque soggetti alla possibilità di una race condition.} che essa +ritorni un errore quando usata con i flag di \macro{O\_CREAT} e +\macro{O\_EXCL}. In tal modo la creazione di un file di lock può essere +eseguita atomicamente, il processo che crea il file con successo si può +considerare come titolare del lock (e della risorsa ad esso associata) mentre +il rilascio si può eseguire con una chiamata ad \func{unlink}. + +Questa tecnica ha però parecchi problemi, che non la rendono una alternativa +praticabile; anzitutto anche in questo caso in caso di terminazione imprevista +del processo lascia allocata la risorsa (il file di lock) che deve comunque +essere cancellata esplicitamente. Inoltre il controllo della disponibilità può +essere fatto solo con una tecnica di polling, che è molto inefficiente. +Modalità alternative prevedono l'uso di \func{link}, che fallisce se il nome +esiste già, ma i problemi sono gli stessi. + +Per questo motivo la tecnica più pulita è quella di utilizzare \func{fcntl} su +un file creato per l'occazione per ottenere un write lock sul primo byte del +file (che non è detto debba esistere); in questo modo potremo usare il lock +come un \textit{mutex}: per bloccare la risorsa basterà acquisire il lock, per +sbloccarla basterà rilasciare il lock; l'accesso sarà controllabile, senza +necessità di ricorrere al \textit{polling}\index{polling}. + Una possibile alternativa all'uso dei semafori come meccanismo di