X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=91d38feb5ab5534d0928d7bdfb3402ae1fdc16e4;hp=092d40fdc6bc27f655f8a1b35802a49ac7399613;hb=5afbcf1d6a84ab2a527859d8fd05b75a31e39736;hpb=7b43a7843d483c826a6ed13224208c615a23c4d6 diff --git a/ipc.tex b/ipc.tex index 092d40f..91d38fe 100644 --- a/ipc.tex +++ b/ipc.tex @@ -505,12 +505,12 @@ essere in una relazione di \textsl{parentela}. Utilizzando una \textit{fifo} tutti i dati passeranno, come per le \textit{pipe}, attraverso un buffer nel kernel, senza transitare dal -filesystem. Il fatto che siano associate ad un \itindex{inode} -\textit{inode} presente sul filesystem serve infatti solo a fornire un punto -di accesso per i processi, che permetta a questi ultimi di accedere alla -stessa \textit{fifo} senza avere nessuna relazione, con una semplice -\func{open}. Il comportamento delle funzioni di lettura e scrittura è identico -a quello illustrato per le \textit{pipe} in sez.~\ref{sec:ipc_pipes}. +filesystem. Il fatto che siano associate ad un \textit{inode} presente sul +filesystem serve infatti solo a fornire un punto di accesso per i processi, +che permetta a questi ultimi di accedere alla stessa \textit{fifo} senza avere +nessuna relazione, con una semplice \func{open}. Il comportamento delle +funzioni di lettura e scrittura è identico a quello illustrato per le +\textit{pipe} in sez.~\ref{sec:ipc_pipes}. Abbiamo già trattato in sez.~\ref{sec:file_mknod} le funzioni \func{mknod} e \func{mkfifo} che permettono di creare una \textit{fifo}. Per utilizzarne una @@ -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 @@ -947,7 +946,7 @@ mantiene varie proprietà ed informazioni associate all'oggetto. \end{minipage} \normalsize \caption{La struttura \structd{ipc\_perm}, come definita in - \headfile{sys/ipc.h}.} + \headfiled{sys/ipc.h}.} \label{fig:ipc_ipc_perm} \end{figure} @@ -995,13 +994,12 @@ meno significativi. Il problema è che anche così non c'è la sicurezza che il valore della chiave sia univoco, infatti esso è costruito combinando il byte di \param{proj\_id)} -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}. +con i 16 bit meno significativi 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 \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 @@ -1090,7 +1088,7 @@ solo se tutti i controlli elencati falliscono l'accesso è negato. Si noti che a differenza di quanto avviene per i permessi dei file, fallire in uno dei passi elencati non comporta il fallimento dell'accesso. Un'ulteriore differenza rispetto a quanto avviene per i file è che per gli oggetti di IPC -il valore di \itindex{umask} \textit{umask} (si ricordi quanto esposto in +il valore di \textit{umask} (si ricordi quanto esposto in sez.~\ref{sec:file_perm_management}) non ha alcun significato. @@ -1314,33 +1312,38 @@ 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 + \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 \kstruct{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} - \caption{Schema della struttura di una coda messaggi.} + \caption{Schema delle strutture di una coda di messaggi + (\kstructd{msqid\_ds} e \kstructd{msg}).} \label{fig:ipc_mq_schema} \end{figure} -A ciascuna coda è associata una struttura \struct{msqid\_ds} la cui +A ciascuna coda è associata una struttura \kstruct{msqid\_ds} la cui definizione è riportata in fig.~\ref{fig:ipc_msqid_ds} ed a cui si accede -includendo \headfile{sys/msg.h}; +includendo \headfiled{sys/msg.h}; % % INFO: sotto materiale obsoleto e non interessante % In questa struttura il @@ -1534,13 +1537,13 @@ dovrà essere pari a \const{LENGTH}). Per capire meglio il funzionamento della funzione riprendiamo in considerazione la struttura della coda illustrata in -fig.~\ref{fig:ipc_mq_schema}. Alla chiamata di \func{msgsnd} il nuovo messaggio -sarà aggiunto in fondo alla lista inserendo una nuova struttura \struct{msg}, -il puntatore \var{msg\_last} di \struct{msqid\_ds} verrà aggiornato, come pure -il puntatore al messaggio successivo per quello che era il precedente ultimo -messaggio; il valore di \var{mtype} verrà mantenuto in \var{msg\_type} ed il -valore di \param{msgsz} in \var{msg\_ts}; il testo del messaggio sarà copiato -all'indirizzo specificato da \var{msg\_spot}. +fig.~\ref{fig:ipc_mq_schema}. Alla chiamata di \func{msgsnd} il nuovo +messaggio sarà aggiunto in fondo alla lista inserendo una nuova struttura +\kstruct{msg}, il puntatore \var{msg\_last} di \kstruct{msqid\_ds} verrà +aggiornato, come pure il puntatore al messaggio successivo per quello che era +il precedente ultimo messaggio; il valore di \var{mtype} verrà mantenuto in +\var{msg\_type} ed il valore di \param{msgsz} in \var{msg\_ts}; il testo del +messaggio sarà copiato all'indirizzo specificato da \var{msg\_spot}. Il valore dell'argomento \param{flag} permette di specificare il comportamento della funzione. Di norma, quando si specifica un valore nullo, la funzione @@ -1833,9 +1836,9 @@ lettura della risposta, quest'ultima resta nella coda (così come per le \textit{fifo} si aveva il problema delle \textit{fifo} che restavano nel filesystem). In questo caso però il problemi sono maggiori, sia perché è molto più facile esaurire la memoria dedicata ad una coda di messaggi che gli -\itindex{inode} \textit{inode} di un filesystem, sia perché, con il riutilizzo -dei \ids{PID} da parte dei processi, un client eseguito in un momento -successivo potrebbe ricevere un messaggio non indirizzato a lui. +\textit{inode} di un filesystem, sia perché, con il riutilizzo dei \ids{PID} +da parte dei processi, un client eseguito in un momento successivo potrebbe +ricevere un messaggio non indirizzato a lui. \subsection{I semafori} @@ -2081,7 +2084,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} @@ -2406,18 +2409,20 @@ versioni delle librerie del C, come le \acr{libc5}). \begin{figure}[!htb] \centering \includegraphics[width=12cm]{img/semtruct} - \caption{Schema della struttura di un insieme di semafori.} + \caption{Schema delle varie strutture di un insieme di semafori + (\kstructd{semid\_ds}, \kstructd{sem}, \kstructd{sem\_queue} e + \kstructd{sem\_undo}).} \label{fig:ipc_sem_schema} \end{figure} Alla creazione di un nuovo insieme viene allocata una nuova strutture -\struct{semid\_ds} ed il relativo vettore di strutture \struct{sem}. Quando si -richiede una operazione viene anzitutto verificato che tutte le operazioni +\kstruct{semid\_ds} ed il relativo vettore di strutture \kstruct{sem}. Quando +si richiede una operazione viene anzitutto verificato che tutte le operazioni possono avere successo; se una di esse comporta il blocco del processo il kernel crea una struttura \kstruct{sem\_queue} che viene aggiunta in fondo alla coda di attesa associata a ciascun insieme di semafori, che viene referenziata tramite i campi \var{sem\_pending} e \var{sem\_pending\_last} di -\struct{semid\_ds}. Nella struttura viene memorizzato il riferimento alle +\kstruct{semid\_ds}. Nella struttura viene memorizzato il riferimento alle operazioni richieste (nel campo \var{sops}, che è un puntatore ad una struttura \struct{sembuf}) e al processo corrente (nel campo \var{sleeper}) poi quest'ultimo viene messo stato di attesa e viene invocato lo @@ -3323,7 +3328,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 @@ -3396,23 +3401,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 @@ -3700,7 +3705,7 @@ una coda di messaggi POSIX è \funcd{mq\_open}, ed il suo prototipo è: La funzione apre la coda di messaggi identificata dall'argomento \param{name} restituendo il descrittore ad essa associato, del tutto analogo ad un file descriptor, con l'unica differenza che lo standard prevede un apposito tipo -\type{mqd\_t}. Nel caso di Linux si tratta in effetti proprio di un normale +\typed{mqd\_t}. Nel caso di Linux si tratta in effetti proprio di un normale file descriptor; pertanto, anche se questo comportamento non è portabile, lo si può tenere sotto osservazione con le funzioni dell'I/O multiplexing (vedi sez.~\ref{sec:file_multiplexing}) come possibile alternativa all'uso @@ -4260,9 +4265,9 @@ sez.~\ref{sec:file_open_close}. Inoltre sul file descriptor viene sempre impostato il flag \const{FD\_CLOEXEC}. Chiamate effettuate da diversi processi usando lo stesso nome restituiranno file descriptor associati allo stesso segmento, così come, nel caso di file ordinari, essi sono associati -allo stesso \itindex{inode} inode. In questo modo è possibile effettuare una -chiamata ad \func{mmap} sul file descriptor restituito da \func{shm\_open} ed -i processi vedranno lo stesso segmento di memoria condivisa. +allo stesso inode. In questo modo è possibile effettuare una chiamata ad +\func{mmap} sul file descriptor restituito da \func{shm\_open} ed i processi +vedranno lo stesso segmento di memoria condivisa. Quando il nome non esiste si può creare un nuovo segmento specificando \const{O\_CREAT}; in tal caso il segmento avrà (così come i nuovi file) @@ -4477,10 +4482,9 @@ Si tenga presente che, come accennato in sez.~\ref{sec:ipc_posix_generic}, i semafori usano la semantica standard dei file per quanto riguarda i controlli di accesso, questo significa che un nuovo semaforo viene sempre creato con l'\ids{UID} ed il \ids{GID} effettivo del processo chiamante, e che i permessi -indicati con \param{mode} vengono filtrati dal valore della \itindex{umask} -\textit{umask} del processo. Inoltre per poter aprire un semaforo è -necessario avere su di esso sia il permesso di lettura che quello di -scrittura. +indicati con \param{mode} vengono filtrati dal valore della \textit{umask} del +processo. Inoltre per poter aprire un semaforo è necessario avere su di esso +sia il permesso di lettura che quello di scrittura. La funzione restituisce in caso di successo un puntatore all'indirizzo del semaforo con un valore di tipo \ctyp{sem\_t *}, è questo valore che dovrà @@ -4564,7 +4568,7 @@ La seconda variante di \func{sem\_wait} è una estensione specifica che può essere utilizzata soltanto se viene definita la macro \macro{\_XOPEN\_SOURCE} ad un valore di almeno 600 o la macro \macro{\_POSIX\_C\_SOURCE} ad un valore uguale o maggiore di \texttt{200112L} prima di includere -\headfile{semaphore.h}, la funzione è \funcd{sem\_timedwait}, ed il suo +\headfiled{semaphore.h}, la funzione è \funcd{sem\_timedwait}, ed il suo prototipo è: \begin{funcproto}{ @@ -4624,8 +4628,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 è: @@ -5009,10 +5013,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