X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=6f5f8cb2fbd9dea573cf42ca731b0ce0c1d63aa2;hp=f742380f1de5a128c6759d1051e09fc497e9c83c;hb=04a547df13e4c672d95e1060e1ada9ae2e1fcb2f;hpb=9b7af600ff0f73bc946c9d160c320667c7a91347 diff --git a/ipc.tex b/ipc.tex index f742380..6f5f8cb 100644 --- a/ipc.tex +++ b/ipc.tex @@ -228,14 +228,14 @@ direzione del flusso dei dati è data dalle frecce continue. Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato intermedio su un file temporaneo. Questo però non tiene conto del fatto che un \textit{CGI} può essere eseguito più volte in contemporanea, e si avrebbe una -evidente \itindex{race~condition} \textit{race condition} in caso di accesso -simultaneo a detto file da istanze diverse. Il problema potrebbe essere -superato utilizzando un sempre diverso per il file temporaneo, che verrebbe -creato all'avvio di ogni istanza, utilizzato dai sottoprocessi, e cancellato -alla fine della sua esecuzione; ma a questo punto le cose non sarebbero più -tanto semplici. L'uso di una \textit{pipe} invece permette di risolvere il -problema in maniera semplice ed elegante, oltre ad essere molto più -efficiente, dato che non si deve scrivere su disco. +evidente \textit{race condition} in caso di accesso simultaneo a detto file da +istanze diverse. Il problema potrebbe essere superato utilizzando un sempre +diverso per il file temporaneo, che verrebbe creato all'avvio di ogni istanza, +utilizzato dai sottoprocessi, e cancellato alla fine della sua esecuzione; ma +a questo punto le cose non sarebbero più tanto semplici. L'uso di una +\textit{pipe} invece permette di risolvere il problema in maniera semplice ed +elegante, oltre ad essere molto più efficiente, dato che non si deve scrivere +su disco. Il programma ci servirà anche come esempio dell'uso delle funzioni di duplicazione dei file descriptor che abbiamo trattato in @@ -539,9 +539,8 @@ una \textit{fifo} in scrittura anche se non ci sono ancora processi il lettura. Infine è possibile anche usare la \textit{fifo} all'interno di un solo processo, nel qual caso però occorre stare molto attenti alla possibili situazioni di stallo: se si cerca di leggere da una \textit{fifo} che non -contiene dati si avrà infatti un \itindex{deadlock} \textit{deadlock} -immediato, dato che il processo si blocca e quindi non potrà mai eseguire le -funzioni di scrittura. +contiene dati si avrà infatti un \textit{deadlock} immediato, dato che il +processo si blocca e quindi non potrà mai eseguire le funzioni di scrittura. Per la loro caratteristica di essere accessibili attraverso il filesystem, è piuttosto frequente l'utilizzo di una \textit{fifo} come canale di @@ -739,7 +738,7 @@ scrittura e l'apertura si sarebbe bloccata indefinitamente. Verifichiamo allora il comportamento dei nostri programmi, in questo, come in altri esempi precedenti, si fa uso delle varie funzioni di servizio, che sono -state raccolte nella libreria \file{libgapil.so}, per poter usare quest'ultima +state raccolte nella libreria \file{libgapil.so}, e per poterla usare occorrerà definire la variabile di ambiente \envvar{LD\_LIBRARY\_PATH} in modo che il linker dinamico possa accedervi. @@ -865,11 +864,11 @@ connettere i due descrittori, ma in questo caso i soli valori validi che possono essere specificati sono rispettivamente \const{AF\_UNIX}, \const{SOCK\_STREAM} e \val{0}. -A partire dal kernel 2.6.27 la funzione supporta anche l'uso dei flag -\const{SOCK\_NONBLOCK} e \const{SOCK\_CLOEXEC} (trattati in -sez.~\ref{sec:sock_type}) nell'indicazione del tipo di socket, con effetto -identico agli analoghi \const{O\_CLOEXEC} e \const{O\_NONBLOCK} di una -\func{open} (vedi tab.~\ref{tab:open_operation_flag}). +A partire dal kernel 2.6.27 la funzione supporta nell'indicazione del tipo di +socket anche i due flag \const{SOCK\_NONBLOCK} e \const{SOCK\_CLOEXEC} +(trattati in sez.~\ref{sec:sock_type}), con effetto identico agli analoghi +\const{O\_CLOEXEC} e \const{O\_NONBLOCK} di una \func{open} (vedi +tab.~\ref{tab:open_operation_flag}). L'utilità di chiamare questa funzione per evitare due chiamate a \func{pipe} può sembrare limitata; in realtà l'utilizzo di questa funzione (e dei socket @@ -999,9 +998,8 @@ con i 16 bit meno significativi \itindex{inode} dell'inode del file \param{pathname} (che vengono ottenuti attraverso \func{stat}, da cui derivano i possibili errori), e gli 8 bit meno significativi del numero del dispositivo su cui è il file. Diventa perciò relativamente facile ottenere delle -collisioni, specie se i file sono su dispositivi con lo stesso -\itindex{minor~number} \textit{minor number}, come \file{/dev/hda1} e -\file{/dev/sda1}. +collisioni, specie se i file sono su dispositivi con lo stesso \textit{minor + number}, come \file{/dev/hda1} e \file{/dev/sda1}. In genere quello che si fa è utilizzare un file comune usato dai programmi che devono comunicare (ad esempio un header comune, o uno dei programmi che devono @@ -1314,22 +1312,26 @@ l'uso di \func{sysctl} o scrivendo nei file \sysctlrelfile{kernel}{msgmax}, \sysctlrelfile{kernel}{msgmnb} e \sysctlrelfile{kernel}{msgmni} di \file{/proc/sys/kernel/}. -Una coda di messaggi è costituita da una \itindex{linked~list} \textit{linked - list}.\footnote{una \itindex{linked~list} \textit{linked list} è una tipica - struttura di dati, organizzati in una lista in cui ciascun elemento contiene - un puntatore al successivo. In questo modo la struttura è veloce - nell'estrazione ed immissione dei dati dalle estremità dalla lista (basta - aggiungere un elemento in testa o in coda ed aggiornare un puntatore), e - relativamente veloce da attraversare in ordine sequenziale (seguendo i - puntatori), è invece relativamente lenta nell'accesso casuale e nella - ricerca.} I nuovi messaggi vengono inseriti in coda alla lista e vengono -letti dalla cima, in fig.~\ref{fig:ipc_mq_schema} si è riportato uno schema -semplificato con cui queste strutture vengono mantenute dal kernel. Lo schema -illustrato in realtà è una semplificazione di quello usato fino ai kernel -della serie 2.2. A partire della serie 2.4 la gestione delle code di messaggi -è effettuata in maniera diversa (e non esiste una struttura \struct{msqid\_ds} -nel kernel), ma abbiamo mantenuto lo schema precedente dato che illustra in -maniera più che adeguata i principi di funzionamento delle code di messaggi. +\itindbeg{linked~list} + +Una coda di messaggi è costituita da una \textit{linked list}.\footnote{una + \itindex{linked~list} \textit{linked list} è una tipica struttura di dati, + organizzati in una lista in cui ciascun elemento contiene un puntatore al + successivo. In questo modo la struttura è veloce nell'estrazione ed + immissione dei dati dalle estremità dalla lista (basta aggiungere un + elemento in testa o in coda ed aggiornare un puntatore), e relativamente + veloce da attraversare in ordine sequenziale (seguendo i puntatori), è + invece relativamente lenta nell'accesso casuale e nella ricerca.} I nuovi +messaggi vengono inseriti in coda alla lista e vengono letti dalla cima, in +fig.~\ref{fig:ipc_mq_schema} si è riportato uno schema semplificato con cui +queste strutture vengono mantenute dal kernel. Lo schema illustrato in realtà +è una semplificazione di quello usato fino ai kernel della serie 2.2. A +partire della serie 2.4 la gestione delle code di messaggi è effettuata in +maniera diversa (e non esiste una struttura \struct{msqid\_ds} nel kernel), ma +abbiamo mantenuto lo schema precedente dato che illustra in maniera più che +adeguata i principi di funzionamento delle code di messaggi. + +\itindend{linked~list} \begin{figure}[!htb] \centering \includegraphics[width=13cm]{img/mqstruct} @@ -2081,7 +2083,7 @@ specificata con \param{cmd}, ed opera o sull'intero insieme specificato da \includestruct{listati/semun.h} \end{minipage} \normalsize - \caption{La definizione dei possibili valori di una \direct{union} + \caption{La definizione dei possibili valori di una \dirct{union} \structd{semun}, usata come quarto argomento della funzione \func{semctl}.} \label{fig:ipc_semun} @@ -2594,10 +2596,10 @@ portabile. Il secondo flag aggiuntivo, introdotto a partire dal kernel 2.6.15, è \const{SHM\_NORESERVE}, ed ha lo stesso scopo del flag \const{MAP\_NORESERVE} di \func{mmap} (vedi sez.~\ref{sec:file_memory_map}): non vengono riservate -delle pagine di swap ad uso del meccanismo del \textit{copy on write} -\itindex{copy~on~write} per mantenere le modifiche fatte sul segmento. Questo -significa che caso di scrittura sul segmento quando non c'è più memoria -disponibile, si avrà l'emissione di un \signal{SIGSEGV}. +delle pagine di swap ad uso del meccanismo del \textit{copy on write} per +mantenere le modifiche fatte sul segmento. Questo significa che caso di +scrittura sul segmento quando non c'è più memoria disponibile, si avrà +l'emissione di un \signal{SIGSEGV}. Infine l'argomento \param{size} specifica la dimensione del segmento di memoria condivisa; il valore deve essere specificato in byte, ma verrà @@ -3323,7 +3325,7 @@ relativamente poco diffuso. \subsection{I \textsl{file di lock}} \label{sec:ipc_file_lock} -\index{file!di lock|(} +\index{file!di~lock|(} Come illustrato in sez.~\ref{sec:ipc_sysv_sem} i semafori del \textit{SysV-IPC} presentano una interfaccia inutilmente complessa e con alcuni difetti @@ -3341,12 +3343,12 @@ directory, \file{/var/lock}, nella standardizzazione del \textit{Filesystem 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 \itindex{race~condition} \textit{race condition}.} che -essa ritorni un errore quando usata con i flag di \const{O\_CREAT} e -\const{O\_EXCL}. In tal modo la creazione di un \textsl{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}. + possibilità di una \textit{race condition}.} che essa ritorni un errore +quando usata con i flag di \const{O\_CREAT} e \const{O\_EXCL}. In tal modo la +creazione di un \textsl{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}. Un esempio dell'uso di questa funzione è mostrato dalle funzioni \func{LockFile} ed \func{UnlockFile} riportate in fig.~\ref{fig:ipc_file_lock} @@ -3396,23 +3398,23 @@ usa spesso per evitare interferenze sull'uso delle porte seriali da parte di più programmi: qualora si trovi un file di lock il programma che cerca di accedere alla seriale si limita a segnalare che la risorsa non è disponibile. -\index{file!di lock|)} +\index{file!di~lock|)} \subsection{La sincronizzazione con il \textit{file locking}} \label{sec:ipc_lock_file} -Dato che i \index{file!di lock} file di lock presentano gli inconvenienti -illustrati in precedenza, la tecnica alternativa di sincronizzazione più -comune è quella di fare ricorso al \itindex{file~locking} \textit{file - locking} (trattato in sez.~\ref{sec:file_locking}) usando \func{fcntl} su un -file creato per l'occasione per ottenere un write lock. 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. Una richiesta -fatta con un write lock metterà automaticamente il processo in stato di -attesa, senza necessità di ricorrere al \itindex{polling} \textit{polling} per -determinare la disponibilità della risorsa, e al rilascio della stessa da -parte del processo che la occupava si otterrà il nuovo lock atomicamente. +Dato che i file di lock presentano gli inconvenienti illustrati in precedenza, +la tecnica alternativa di sincronizzazione più comune è quella di fare ricorso +al \itindex{file~locking} \textit{file locking} (trattato in +sez.~\ref{sec:file_locking}) usando \func{fcntl} su un file creato per +l'occasione per ottenere un write lock. 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. Una richiesta fatta con un write lock +metterà automaticamente il processo in stato di attesa, senza necessità di +ricorrere al \itindex{polling} \textit{polling} per determinare la +disponibilità della risorsa, e al rilascio della stessa da parte del processo +che la occupava si otterrà il nuovo lock atomicamente. Questo approccio presenta il notevole vantaggio che alla terminazione di un processo tutti i lock acquisiti vengono rilasciati automaticamente (alla @@ -4145,12 +4147,12 @@ che se si vuole mantenere il meccanismo di notifica occorre ripetere la registrazione chiamando nuovamente \func{mq\_notify} all'interno del gestore del segnale di notifica. A differenza della situazione simile che si aveva con i segnali non affidabili (l'argomento è stato affrontato in -\ref{sec:sig_semantics}) questa caratteristica non configura una -\itindex{race~condition} \textit{race condition} perché l'invio di un segnale -avviene solo se la coda è vuota; pertanto se si vuole evitare di correre il -rischio di perdere eventuali ulteriori segnali inviati nel lasso di tempo che -occorre per ripetere la richiesta di notifica basta avere cura di eseguire -questa operazione prima di estrarre i messaggi presenti dalla coda. +\ref{sec:sig_semantics}) questa caratteristica non configura una \textit{race + condition} perché l'invio di un segnale avviene solo se la coda è vuota; +pertanto se si vuole evitare di correre il rischio di perdere eventuali +ulteriori segnali inviati nel lasso di tempo che occorre per ripetere la +richiesta di notifica basta avere cura di eseguire questa operazione prima di +estrarre i messaggi presenti dalla coda. L'invio del segnale di notifica avvalora alcuni campi di informazione restituiti al gestore attraverso la struttura \struct{siginfo\_t} (definita in @@ -4624,8 +4626,8 @@ dall'argomento \param{sem}, se questo era nullo la relativa risorsa risulterà sbloccata, cosicché un altro processo (o \itindex{thread} \textit{thread}) eventualmente bloccato in una \func{sem\_wait} sul semaforo possa essere svegliato e rimesso in esecuzione. Si tenga presente che la funzione è sicura -\index{funzioni!sicure} per l'uso all'interno di un gestore di segnali (si -ricordi quanto detto in sez.~\ref{sec:sig_signal_handler}). +per l'uso all'interno di un gestore di segnali (si ricordi quanto detto in +sez.~\ref{sec:sig_signal_handler}). Se invece di operare su un semaforo se ne volesse semplicemente leggere il valore, si potrà usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: @@ -4875,12 +4877,12 @@ notazione ottale). Infine il semaforo verrà inizializzato ad un valore nullo A questo punto (\texttt{\small 22}) si potrà inizializzare il messaggio posto nel segmento di memoria condivisa usando la stringa passata come argomento al programma. Essendo il semaforo stato creato già bloccato non ci si dovrà -preoccupare di eventuali \itindex{race~condition} \textit{race condition} -qualora il programma di modifica del messaggio venisse lanciato proprio in -questo momento. Una volta inizializzato il messaggio occorrerà però -rilasciare il semaforo (\texttt{\small 24-27}) per consentirne l'uso; in -tutte queste operazioni si provvederà ad uscire dal programma con un opportuno -messaggio in caso di errore. +preoccupare di eventuali \textit{race condition} qualora il programma di +modifica del messaggio venisse lanciato proprio in questo momento. Una volta +inizializzato il messaggio occorrerà però rilasciare il semaforo +(\texttt{\small 24-27}) per consentirne l'uso; in tutte queste operazioni si +provvederà ad uscire dal programma con un opportuno messaggio in caso di +errore. Una volta completate le inizializzazioni il ciclo principale del programma (\texttt{\small 29-47}) viene ripetuto indefinitamente (\texttt{\small 29}) @@ -4951,8 +4953,8 @@ argomento. Una volta completate con successo le precedenti inizializzazioni, il passo seguente (\texttt{\small 21-24}) è quello di acquisire il semaforo, dopo di che sarà possibile eseguire la sostituzione del messaggio (\texttt{\small 25}) -senza incorrere in possibili \itindex{race~condition} \textit{race condition} -con la stampa dello stesso da parte di \file{message\_getter}. +senza incorrere in possibili \textit{race condition} con la stampa dello +stesso da parte di \file{message\_getter}. Una volta effettuata la modifica viene stampato (\texttt{\small 26}) il tempo di attesa impostato con l'opzione ``\texttt{-t}'' dopo di che (\texttt{\small @@ -5009,10 +5011,10 @@ message: ciao \end{Console} %$ -E si noterà come nel momento in cui si è lanciato \file{message\_setter} le -stampe di \file{message\_getter} si bloccheranno, come corretto, dopo aver -registrato un valore nullo per il semaforo. Il programma infatti resterà -bloccato nella \func{sem\_wait} (quella di riga (\texttt{\small 37}) in +E si noterà come nel momento in cui si lancia \file{message\_setter} le stampe +di \file{message\_getter} si bloccheranno, come corretto, dopo aver registrato +un valore nullo per il semaforo. Il programma infatti resterà bloccato nella +\func{sem\_wait} (quella di riga (\texttt{\small 37}) in fig.~\ref{fig:ipc_posix_sem_shm_message_server}) fino alla scadenza dell'attesa di \file{message\_setter} (con l'esecuzione della \func{sem\_post} della riga (\texttt{\small 29}) di