X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=ca655f9432357b967bc502f1383d0b324bd451d9;hp=ce4842e602ec37500c52304567dc7bc919a4b483;hb=bff17765a4aa5e979e0a72b70917c70254e97c98;hpb=4263734ffe237b1a7249c1f8121bb7ebe8b60a50 diff --git a/ipc.tex b/ipc.tex index ce4842e..ca655f9 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,3 +1,13 @@ +%% ipc.tex +%% +%% Copyright (C) 2000-2002 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 "Prefazione", +%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the +%% license is included in the section entitled "GNU Free Documentation +%% License". +%% \chapter{La comunicazione fra processi} \label{cha:IPC} @@ -1953,7 +1963,7 @@ soffrono di altri due, ben pi 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 dei semafori con \func{semget} e poi inizializzarlo con \func{semctl}, si -perde così ogni possibilità di eseguire atomicamente questa operazione. +perde così ogni possibilità di eseguire l'operazione atomicamente. Il secondo difetto deriva dalla caratteristica generale degli oggetti del \textit{SysV IPC} di essere risorse globali di sistema, che non vengono @@ -2111,12 +2121,6 @@ specificata con \param{cmd}, ed opera o sull'intero insieme specificato da \param{semid} o sul singolo semaforo di un insieme, specificato da \param{semnum}. -Qualora la funzione operi con quattro argomenti \param{arg} è -un argomento generico, che conterrà un dato diverso a seconda dell'azione -richiesta; per unificare l'argomento esso deve essere passato come una -\var{union semun}, la cui definizione, con i possibili valori che può -assumere, è riportata in \figref{fig:ipc_semun}. - \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} @@ -2136,6 +2140,12 @@ union semun { \label{fig:ipc_semun} \end{figure} +Qualora la funzione operi con quattro argomenti \param{arg} è +un argomento generico, che conterrà un dato diverso a seconda dell'azione +richiesta; per unificare l'argomento esso deve essere passato come una +\var{union semun}, la cui definizione, con i possibili valori che può +assumere, è riportata in \figref{fig:ipc_semun}. + Come già accennato sia il comportamento della funzione che il numero di parametri con cui deve essere invocata, dipendono dal valore dell'argomento \param{cmd}, che specifica l'azione da intraprendere; i valori validi (che @@ -2441,7 +2451,7 @@ ripristino non Come esempio di uso dell'interfaccia dei semafori vediamo come implementare con essa dei semplici \textit{mutex} (cioè semafori binari), tutto il codice -in questione, contenuto nel file \file{wrappers.h} allegato ai sorgenti, è +in questione, contenuto nel file \file{Mutex.c} allegato ai sorgenti, è riportato in \figref{fig:ipc_mutex_create}. Utilizzeremo l'interfaccia per creare un insieme contenente un singolo semaforo, per il quale poi useremo un valore unitario per segnalare la disponibilità della risorsa, ed un valore @@ -2454,7 +2464,7 @@ nullo per segnalarne l'indisponibilit /* * Function MutexCreate: create a mutex/semaphore */ -inline int MutexCreate(key_t ipc_key) +int MutexCreate(key_t ipc_key) { const union semun semunion={1}; /* semaphore union structure */ int sem_id, ret; @@ -2471,14 +2481,14 @@ inline int MutexCreate(key_t ipc_key) /* * Function MutexFind: get the semaphore/mutex Id given the IPC key value */ -inline int MutexFind(key_t ipc_key) +int MutexFind(key_t ipc_key) { return semget(ipc_key,1,0); } /* * Function MutexRead: read the current value of the mutex/semaphore */ -inline int MutexRead(int sem_id) +int MutexRead(int sem_id) { return semctl(sem_id, 0, GETVAL); } @@ -2492,18 +2502,18 @@ struct sembuf sem_lock={ /* to lock semaphore */ struct sembuf sem_ulock={ /* to unlock semaphore */ 0, /* semaphore number (only one so 0) */ 1, /* operation (1 to release resource) */ - SEM_UNO}; /* flag (in this case 0) */ + SEM_UNDO}; /* flag (in this case 0) */ /* * Function MutexLock: to lock a mutex/semaphore */ -inline int MutexLock(int sem_id) +int MutexLock(int sem_id) { return semop(sem_id, &sem_lock, 1); } /* * Function MutexUnlock: to unlock a mutex/semaphore */ -inline int MutexUnlock(int sem_id) +int MutexUnlock(int sem_id) { return semop(sem_id, &sem_ulock, 1); } @@ -2551,21 +2561,21 @@ il mutex. Entrambe fanno da wrapper per \func{semop}, utilizzando le due strutture \var{sem\_lock} e \var{sem\_unlock} definite in precedenza (\texttt{\small 32--42}). Si noti come per queste ultime si sia fatto uso dell'opzione \macro{SEM\_UNDO} per evitare che il semaforo resti bloccato in -caso di terminazione imprevista del processo. Si noti infine come, essendo -tutte le funzioni riportate in \figref{fig:ipc_mutex_create} estremamente -semplici, se si sono definite tutte come \ctyp{inline}.\footnote{la direttiva - \func{inline} viene usata per dire al compilatore di non trattare la - funzione cui essa fa riferimento come una funzione, ma di inserire il codice - direttamente nel testo del programma. Anche se i compilatori più moderni - sono in grado di effettuare da soli queste manipolazioni (impostando le - opportune ottimizzazioni) questa è una tecnica usata per migliorare le - prestazioni per le funzioni piccole ed usate di frequente, in tal caso - infatti le istruzioni per creare un nuovo frame nello stack per chiamare la - funzione costituirebbero una parte rilevante del codice, appesantendo - inutilmente il programma. Originariamente questa era fatto utilizzando delle - macro, ma queste hanno tutta una serie di problemi di sintassi nel passaggio - degli argomenti (si veda ad esempio \cite{PratC} che in questo modo possono - essere evitati.} +caso di terminazione imprevista del processo.%% Si noti infine come, essendo +%% tutte le funzioni riportate in \figref{fig:ipc_mutex_create} estremamente +%% semplici, se si sono definite tutte come \ctyp{inline}.\footnote{la direttiva +%% \func{inline} viene usata per dire al compilatore di non trattare la +%% funzione cui essa fa riferimento come una funzione, ma di inserire il codice +%% direttamente nel testo del programma. Anche se i compilatori più moderni +%% sono in grado di effettuare da soli queste manipolazioni (impostando le +%% opportune ottimizzazioni) questa è una tecnica usata per migliorare le +%% prestazioni per le funzioni piccole ed usate di frequente, in tal caso +%% infatti le istruzioni per creare un nuovo frame nello stack per chiamare la +%% funzione costituirebbero una parte rilevante del codice, appesantendo +%% inutilmente il programma. Originariamente questa era fatto utilizzando delle +%% macro, ma queste hanno tutta una serie di problemi di sintassi nel passaggio +%% degli argomenti (si veda ad esempio \cite{PratC} che in questo modo possono +%% essere evitati.} Chiamare \func{MutexLock} decrementa il valore del semaforo: se questo è @@ -2584,8 +2594,6 @@ analoga senza questo problemi usando il file locking. - - \subsection{Memoria condivisa} \label{sec:ipc_sysv_shm} @@ -2638,7 +2646,7 @@ quest'ultimo non acceda al segmento di memoria condivisa prima che il primo non abbia completato le operazioni di scrittura, inoltre nel corso di una lettura si deve essere sicuri che i dati restano coerenti e non vengono sovrascritti da un accesso in scrittura sullo stesso segmento da parte di un -altro processo; per questo in genere la memoria condivisa viene sempre +altro processo. Per questo in genere la memoria condivisa viene sempre utilizzata in abbinamento ad un meccanismo di sincronizzazione, il che, di norma, significa insieme a dei semafori. @@ -2677,7 +2685,7 @@ invece: inizializzato al valore di \param{size}. \item il campo \var{shm\_ctime}, che esprime il tempo di creazione del segmento, viene inizializzato al tempo corrente. -\item i campi \var{shm\_atime} e \var{shm\_atime}, che esprimono +\item i campi \var{shm\_atime} e \var{shm\_dtime}, che esprimono rispettivamente il tempo dell'ultima volta che il segmento è stato agganciato o sganciato da un processo, vengono inizializzati a zero. \item il campo \var{shm\_lpid}, che esprime il \acr{pid} del processo che ha @@ -2689,11 +2697,14 @@ invece: \end{itemize*} Come per le code di messaggi e gli insiemi di semafori, anche per i segmenti -di memoria condivisa esistono una serie di limiti, i cui valori, riportati in -\tabref{tab:ipc_shm_limits} sono associati ad altrettante costanti. Alcuni di -questi limiti sono al solito accessibili e modificabili attraverso +di memoria condivisa esistono una serie di limiti imposti dal sistema. Alcuni +di questi limiti sono al solito accessibili e modificabili attraverso \func{sysctl} o scrivendo direttamente nei rispettivi file di -\file{/proc/sys/kernel/}. +\file{/proc/sys/kernel/}. In \tabref{tab:ipc_shm_limits} si sono riportate le +costanti simboliche associate a ciascuno di essi, il loro significato, i +valori preimpostati, e, quando presente, il file in \file{/proc/sys/kernel/} +che permettono di cambiarne il valore. + \begin{table}[htb] \footnotesize @@ -2704,15 +2715,25 @@ questi limiti sono al solito accessibili e modificabili attraverso & \textbf{Significato} \\ \hline \hline - \macro{SHMALL}&0x200000&\file{shmall}& Numero massimo di pagine che + \macro{SHMALL}& 0x200000&\file{shmall}& Numero massimo di pagine che possono essere usate per i segmenti di memoria condivisa. \\ \macro{SHMMAX}&0x2000000&\file{shmmax}& Dimensione massima di un segmento - di memoria condivisa.\\ - \macro{SHMMNI}&4096&\file{msgmni}& Numero massimo di segmenti di memoria - condivisa presenti nel kernel.\\ - \macro{SHMMIN}& 1& --- & Dimensione minima di un segmento di - memoria condivisa. \\ + di memoria condivisa.\\ + \macro{SHMMNI}& 4096&\file{msgmni}& Numero massimo di segmenti di + memoria condivisa presenti nel + kernel.\\ + \macro{SHMMIN}& 1& --- & Dimensione minima di un segmento di + memoria condivisa. \\ + \macro{SHMLBA}&\macro{PAGE\_SIZE}&--- & Limite inferiore per le dimensioni + minime di un segmento (deve essere + allineato alle dimensioni di una + pagina di memoria). \\ + \macro{SHMSEG}& --- & --- & Numero massimo di segmenti di + memoria condivisa + per ciascun processo.\\ + + \hline \end{tabular} \caption{Valori delle costanti associate ai limiti dei segmenti di memoria @@ -2757,18 +2778,20 @@ attraverso l'argomento \param{cmd}, i valori possibili sono i seguenti: \item[\macro{IPC\_RMID}] Marca il segmento di memoria condivisa per la rimozione, questo verrà cancellato effettivamente solo quando l'ultimo processo ad esso agganciato si sarà staccato. Questo comando può essere - eseguito solo da un processo con userid effettivo, corrispondente al - creatore o al proprietario della coda, o all'amministratore. + eseguito solo da un processo con userid effettivo corrispondente o al + creatore della coda, o al proprietario della coda, o all'amministratore. \item[\macro{IPC\_SET}] Permette di modificare i permessi ed il proprietario del segmento. Per modificare i valori di \var{shm\_perm.mode}, \var{shm\_perm.uid} e \var{shm\_perm.gid} occorre essere il proprietario o il creatore della coda, oppure l'amministratore. Compiuta l'operazione aggiorna anche il valore del campo \var{shm\_ctime}. \item[\macro{SHM\_LOCK}] Abilita il \textit{memory locking}\index{memory - locking} (vedi \secref{sec:proc_mem_lock}) sul segmento di memoria + locking}\footnote{impedisce cioè che la memoria usata per il segmento + venga salvata su disco dal meccanismo della memoria virtuale; si ricordi + quanto trattato in \secref{sec:proc_mem_lock}.} sul segmento di memoria condivisa. Solo l'amministratore può utilizzare questo comando. -\item[\macro{SHM\_UNLOCK}] Disabilita il \textit{memory locking}. Solo - l'amministratore può utilizzare questo comando. +\item[\macro{SHM\_UNLOCK}] Disabilita il \textit{memory locking} sul segmento + di memoria condivisa. Solo l'amministratore può utilizzare questo comando. \end{basedescript} i primi tre comandi sono gli stessi già visti anche per le code ed i semafori, gli ultimi due sono delle estensioni previste da Linux. @@ -2801,8 +2824,10 @@ La funzione inserisce un segmento di memoria condivisa all'interno dello spazio di indirizzi del processo, in modo che questo possa accedervi direttamente, la situazione dopo l'esecuzione di \func{shmat} è illustrata in \figref{fig:ipc_shmem_layout} (per la comprensione del resto dello schema si -ricordi quanto illustrato al proposito in \secref{sec:proc_mem_layout}). Si -tenga presente che la funzione ha successo anche se il segmento è stato +ricordi quanto illustrato al proposito in \secref{sec:proc_mem_layout}). In +particolare l'indirizzo finale del segmento dati (quello impostato da +\func{brk}, vedi \secref{sec:proc_mem_sbrk}) non viene influenzato. Si tenga +presente infine che la funzione ha successo anche se il segmento è stato marcato per la cancellazione. \begin{figure}[htb] @@ -2824,7 +2849,13 @@ memoria libera (questo Altrimenti il kernel aggancia il segmento all'indirizzo specificato da \param{shmaddr}; questo però può avvenire solo se l'indirizzo coincide con il limite di una pagina, cioè se è un multiplo esatto del parametro di sistema -\macro{SHMLBA}, che in Linux è sempre uguale \macro{PAGE\_SIZE}. +\macro{SHMLBA}, che in Linux è sempre uguale \macro{PAGE\_SIZE}. + +Si tenga presente però che quando si usa \macro{NULL} come valore di +\param{shmaddr}, l'indirizzo restituito da \func{shmat} può cambiare da +processo a processo; pertanto se nell'area di memoria condivisa si salvano +anche degli indirizzi, si deve avere cura di usare valori relativi (in genere +riferiti all'indirizzo di partenza del segmento). L'argomento \param{shmflg} permette di cambiare il comportamento della funzione; esso va specificato come maschera binaria, i bit utilizzati sono @@ -2835,15 +2866,16 @@ solo due e sono identificati dalle costanti \macro{SHM\_RND} e un valore qualunque per \param{shmaddr}, e il segmento verrà comunque agganciato, ma al più vicino multiplo di \macro{SHMLBA} (il nome della costante sta infatti per \textit{rounded}, e serve per specificare un -indirizzo come arrotondamento). +indirizzo come arrotondamento, in Linux è equivalente a \macro{PAGE\_SIZE}). -Il secondo bit permette di agganciare il segmento in sola lettura (si ricordi -che anche le pagine di memoria hanno dei permessi), in tal caso un tentativo -di scrivere sul segmento comporterà una violazione di accesso con l'emissione -di un segnale di \macro{SIGSEGV}. Il comportamento usuale di \func{shmat} è -quello di agganciare il segmento con l'accesso in lettura e scrittura (ed il -processo deve aver questi permessi in \var{shm\_perm}), non è prevista la -possibilità di agganciare un segmento in sola scrittura. +L'uso di \macro{SHM\_RDONLY} permette di agganciare il segmento in sola +lettura (si ricordi che anche le pagine di memoria hanno dei permessi), in tal +caso un tentativo di scrivere sul segmento comporterà una violazione di +accesso con l'emissione di un segnale di \macro{SIGSEGV}. Il comportamento +usuale di \func{shmat} è quello di agganciare il segmento con l'accesso in +lettura e scrittura (ed il processo deve aver questi permessi in +\var{shm\_perm}), non è prevista la possibilità di agganciare un segmento in +sola scrittura. In caso di successo la funzione aggiorna anche i seguenti campi di \var{shmid\_ds}: @@ -2865,7 +2897,6 @@ diverso, tutti i segmenti agganciati al processo originario vengono automaticamente sganciati. Lo stesso avviene all'uscita del processo attraverso una \func{exit}. - Una volta che un segmento di memoria condivisa non serve più, si può sganciarlo esplicitamente dal processo usando l'altra funzione dell'interfaccia, \func{shmdt}, il cui prototipo è: @@ -2887,18 +2918,32 @@ memoria condivisa; questo viene identificato con l'indirizzo \param{shmaddr} restituito dalla precedente chiamata a \func{shmat} con il quale era stato agganciato al processo. -Per capire meglio il funzionamento delle funzioni facciamo ancora una volta -riferimento alle strutture con cui il kernel implementa i segmenti di memoria -condivisa; uno schema semplificato della struttura è illustrato in -\figref{fig:ipc_shm_struct}. +In caso di successo la funzione aggiorna anche i seguenti campi di +\var{shmid\_ds}: +\begin{itemize*} +\item il tempo \var{shm\_dtime} dell'ultima operazione di sganciamento viene + impostato al tempo corrente. +\item il \acr{pid} \var{shm\_lpid} dell'ultimo processo che ha operato sul + segmento viene impostato a quello del processo corrente. +\item il numero \var{shm\_nattch} di processi agganciati al segmento viene + decrementato di uno. +\end{itemize*} +inoltre la regione di indirizzi usata per il segmento di memoria condivisa +viene tolta dallo spazio di indirizzi del processo. + -\begin{figure}[htb] - \centering - \includegraphics[width=10cm]{img/shmstruct} - \caption{Schema dell'implementazione dei segmenti di memoria condivisa in - Linux.} - \label{fig:ipc_shm_struct} -\end{figure} +%% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta +%% riferimento alle strutture con cui il kernel implementa i segmenti di memoria +%% condivisa; uno schema semplificato della struttura è illustrato in +%% \figref{fig:ipc_shm_struct}. + +%% \begin{figure}[htb] +%% \centering +%% \includegraphics[width=10cm]{img/shmstruct} +%% \caption{Schema dell'implementazione dei segmenti di memoria condivisa in +%% Linux.} +%% \label{fig:ipc_shm_struct} +%% \end{figure} @@ -2906,11 +2951,12 @@ condivisa; uno schema semplificato della struttura \section{Tecniche alternative} \label{sec:ipc_alternatives} -Come abbiamo visto in \secref{sec:ipc_sysv_generic} il \textit{SysV IPC} +Come abbiamo detto in \secref{sec:ipc_sysv_generic}, e ripreso nella +descrizione dei signoli oggetti che ne fan parte, 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. + capitolo 14.} Stevens ne eeffettua una accurata analisi (alcuni dei +concetti sono già stati accennati in precedenza) ed elenca alcune possibili +tecniche alternative, che vogliamo riprendere in questa sezione. \subsection{Alternative alle code di messaggi} @@ -2933,7 +2979,7 @@ cui alla fine l'uso delle code di messaggi classiche -\subsection{La sincronizzazione con il \textit{file locking}} +\subsection{I \textsl{file di lock}} \label{sec:ipc_file_lock} Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC} @@ -2953,66 +2999,209 @@ usa la caratteristica della funzione \func{open} (illustrata in 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ò +\macro{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}.\footnote{abbiamo già accennato in \secref{sec:file_open} che - questa tecnica può non funzionare se il filesystem su cui si va ad operare è - su NFS; in tal caso si può adottare una tecnica alternativa che prevede - l'uso di \func{link} per creare come file di lock un hard link ad un file - esistente; se il link esiste già e la funzione fallisce, significa che la - risorsa è bloccata e potrà essere sbloccata solo con un \func{unlink}, - altrimenti il link è creato ed il lock acquisito; il controllo e l'eventuale - acquisizione sono atomici; il difetto di questa soluzione è che funziona - solo se si opera all'interno di uno stesso filesystem.} - -L'uso di un file di lock presenta però parecchi problemi, che non lo rendono -una alternativa praticabile per la sincronizzazione:\footnote{ma può essere - una tecnica usata con successo quando l'esigenza è solo quella di segnalare - l'occupazione di una risorsa, senza necessità di attendere che questa si - liberi; ad esempio la si 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.} anzitutto anche in questo caso in caso di -terminazione imprevista del processo lascia allocata la risorsa (il file di -lock) e questa deve essere sempre cancellata esplicitamente. Inoltre il -controllo della disponibilità può essere fatto solo con una tecnica di -\textit{polling}\index{polling}, che è molto inefficiente. - -Per questo motivo la tecnica alternativa più pulita è quella di fare ricorso -al \textit{file locking} trattato in \secref{sec:file_locking} ed utilizzare -\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 -\textit{polling}\index{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. +il rilascio si può eseguire con una chiamata ad \func{unlink}. -Questo approccio presenta il notevole vantaggio che alla terminazione di un -processo tutti i lock acquisiti vengono rilasciati automaticamente (alla -chiusura dei relativi file) e non ci si deve preoccupare di niente, e non -consuma risorse permanentemente allocate nel sistema, lo svantaggio è che -dovendo fare ricorso a delle operazioni sul filesystem esso è in genere -leggermente più lento. +Un esempio dell'uso di questa funzione è mostrato dalle funzioni +\func{LockFile} ed \func{UnlockFile} riportate in \figref{fig:ipc_file_lock} +(sono contenute in \file{LockFile.c}, un'altro dei sorgenti allegati alla +guida) che permettono rispettivamente di creare e rimuovere un \textsl{file di + lock}. Come si può notare entrambe le funzioni sono elementari; la prima +(\texttt{\small 4--10}) si limita ad aprire il file di lock (\texttt{\small + 9}) nella modalità descritta, mentre la seconda (\texttt{\small 11--17}) lo +cancella con \func{unlink}. -Il codice per implementare un mutex utilizzando il file locking è riportato in +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} +#include +#include +#include /* unix standard functions */ +/* + * Function LockFile: + */ +int LockFile(const char* path_name) +{ + return open(path_name, O_EXCL|O_CREAT); +} +/* + * Function UnlockFile: + */ +int UnlockFile(const char* path_name) +{ + return unlink(path_name); +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni \func{LockFile} e \func{UnlockFile} che + permettono di creare e rimuovere un \textsl{file di lock}.} + \label{fig:ipc_file_lock} +\end{figure} +Uno dei limiti di questa tecnica è che, come abbiamo già accennato in +\secref{sec:file_open}, questo comportamento di \func{open} può non funzionare +(la funzione viene eseguita, ma non è garantita l'atomicità dell'operazione) +se il filesystem su cui si va ad operare è su NFS; in tal caso si può adottare +una tecnica alternativa che prevede l'uso della \func{link} per creare come +file di lock un hard link ad un file esistente; se il link esiste già e la +funzione fallisce, significa che la risorsa è bloccata e potrà essere +sbloccata solo con un \func{unlink}, altrimenti il link è creato ed il lock +acquisito; il controllo e l'eventuale acquisizione sono atomici; la soluzione +funziona anche su NFS, ma ha un'altro difetto è che è quello di poterla usare +solo se si opera all'interno di uno stesso filesystem. + +Un generale comunque l'uso di un \textsl{file di lock} presenta parecchi +problemi, che non lo rendono una alternativa praticabile per la +sincronizzazione: anzitutto anche in questo caso, in caso di terminazione +imprevista del processo, si lascia allocata la risorsa (il file di lock) e +questa deve essere sempre cancellata esplicitamente. Inoltre il controllo +della disponibilità può essere eseguito solo con una tecnica di +\textit{polling}\index{polling}, ed è quindi molto inefficiente. + +La tecnica dei file di lock non di meno ha una sua utilità, e può essere usata +con successo quando l'esigenza è solo quella di segnalare l'occupazione di una +risorsa, senza necessità di attendere che questa si liberi; ad esempio la si +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. +\subsection{La sincronizzazione con il \textit{file locking}} +\label{sec:ipc_lock_file} + +Dato che i file di lock presentano gli inconvenienti illustrati in precedenza, +la tecnica alternativa più comune è quella di fare ricorso al \textit{file + locking} (trattato in \secref{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 \textit{polling}\index{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 +chiusura dei relativi file) e non ci si deve preoccupare di niente, inoltre +non consuma risorse permanentemente allocate nel sistema, lo svantaggio è che +dovendo fare ricorso a delle operazioni sul filesystem esso è in genere +leggermente più lento. -\subsection{Il \textit{memory mapping} anonimo} +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} +/* + * Function LockMutex: lock a file (creating it if not existent). + */ +int LockMutex(const char *path_name) +{ + int fd, res; + struct flock lock; /* file lock structure */ + /* first open the file (creating it if not existent) */ + if ( (fd = open(path_name, O_EXCL|O_CREAT)) < 0) { /* first open file */ + return fd; + } + /* set flock structure */ + lock.l_type = F_WRLCK; /* set type: read or write */ + lock.l_whence = SEEK_SET; /* start from the beginning of the file */ + lock.l_start = 0; /* set the start of the locked region */ + lock.l_len = 0; /* set the length of the locked region */ + /* do locking */ + if ( (res = fcntl(fd, F_SETLKW, &lock)) < 0 ) { + return res; + } + return 0; +} +/* + * Function UnLockMutex: unlock a file. + */ +int UnlockMutex(const char *path_name) +{ + int fd, res; + struct flock lock; /* file lock structure */ + /* first open the file */ + if ( (fd = open(path_name, O_RDWR)) < 0) { /* first open file */ + return fd; + } + /* set flock structure */ + lock.l_type = F_UNLCK; /* set type: unlock */ + lock.l_whence = SEEK_SET; /* start from the beginning of the file */ + lock.l_start = 0; /* set the start of the locked region */ + lock.l_len = 0; /* set the length of the locked region */ + /* do locking */ + if ( (res = fcntl(fd, F_SETLK, &lock)) < 0 ) { + return res; + } + return 0; +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni che permettono di creare un + \textit{mutex} utilizzando il file locking.} + \label{fig:ipc_flock_mutex} +\end{figure} + +Il codice per implementare un mutex utilizzando il file locking è riportato in +\figref{fig:ipc_flock_mutex}; a differenza del precedente caso in cui si sono +usati i semafori le funzioni questa volta sono sufficienti due funzioni, +\func{LockMutex} e \func{UnlockMutex}, usate rispettivamente per acquisire e +rilasciare il mutex. + +La prima funzione (\texttt{\small 1--22}) serve per acquisire il mutex. +Anzitutto si apre (\texttt{\small 9--11}), creandolo se non esiste, il file +specificato dall'argomento \param{pathname}. In caso di errore si ritorna +immediatamente, altrimenti si prosegue impostando (\texttt{\small 12--16}) la +struttura \var{lock} in modo da poter acquisire un write lock sul file. Infine +si richiede (\texttt{\small 17--20}) il file lock (restituendo il codice di +ritorno di \func{fcntl} caso di errore). Se il file è libero il lock è +acquisito e la funzione ritorna immediatamente; altrimenti \func{fcntl} si +bloccherà (si noti che la si è chiamata con \func{F\_SETLKW}) fino al rilascio +del lock. + +La seconda funzione (\texttt{\small 23--44}) serve a rilasciare il mutex. Di +nuovo si apre (\texttt{\small 30--33}) il file specificato dall'argomento +\param{pathname} (che stavolta deve esistere), ritornando immediatamente in +caso di errore. Poi si passa ad inizializzare (\texttt{\small 34--38}) la +struttura \var{lock} per il rilascio del lock, che viene effettuato +(\texttt{\small 39--42}) subito dopo. + + \subsection{Il \textit{memory mapping} anonimo} \label{sec:ipc_mmap_anonymous} -Abbiamo visto in \secref{sec:file_memory_map} come sia possibile mappare il -contenuto di un file nella memoria di un processo. Una della opzioni possibili -utilizzabili con Linux è quella del \textit{memory mapping} -anonimo\footnote{in altri sistemi una funzionalità simile a questa viene - implementata mappando il file speciale \file{/dev/zero}.}, in tal caso -infatti +Abbiamo già visto che quando i processi sono \textsl{correlati}\footnote{se + cioè hanno almeno un progenitore comune.} l'uso delle pipe può costituire +una valida alternativa alle code di messaggi; nella stessa situazione si può +evitare l'uso di una memoria condivisa facendo ricorso al cosiddetto +\textit{memory mapping} anonimo. + +Abbiamo visto in \secref{sec:file_memory_map} che è possibile mappare il +contenuto di un file nella memoria di un processo, e che, quando viene usato +il flag \macro{MAP\_SHARED}, le modifiche effettuate al contenuto del file +vengono viste da tutti i processi che lo hanno mappato. Utilizzare questa +tecnica per creare una memoria condivisa fra processi diversi è estremamente +inefficiente, in quanto occorre passare attraverso il disco. Però abbiamo +visto anche che se si esegue la mappatura con il flag \macro{MAP\_ANONYMOUS} +la regione mappata non viene associata a nessun file, anche se quanto scritto +rimane in memoria e può essere riletto; allora, dato che un processo figlio +mantiene nel suo spazio degli indirizzi anche le regioni mappate, esso sarà +anche in grado di accedere a quanto in esse è contenuto. + +In questo modo diventa possibile creare una memoria condivisa fra processi +diversi, purché questi abbiano almeno un progenitore comune che ha effettuato +il \textit{memory mapping} anonimo.\footnote{nei sistemi derivati da SysV una + funzionalità simile a questa viene implementata mappando il file speciale + \file{/dev/zero}. In tal caso i valori scritti nella regione mappata non + vengono ignorati (come accade qualora si scriva direttamente sul file), ma + restano in memoria e possono essere riletti secondo le stesse modalità usate + nele \textit{memory mapping} anonimo.} Un esempio di utilizzo di questa +tecnica è mostrato in + \section{La comunicazione fra processi di POSIX} @@ -3031,9 +3220,9 @@ una interfaccia completamente nuova, che tratteremo in questa sezione. Il Linux non tutti gli oggetti del POSIX IPC sono supportati nel kernel ufficiale; solo la memoria condivisa è presente, ma solo a partire dal kernel -2.4.x, per gli altri oggetti esistono patch e librerie non -ufficiali. Nonostante questo è importante esaminare questa interfaccia per la -sua netta superiorità nei confronti di quella del \textit{SysV IPC}. +2.4.x, per gli altri oggetti esistono patch e librerie non ufficiali. +Nonostante questo è importante esaminare questa interfaccia per la sua netta +superiorità nei confronti di quella del \textit{SysV IPC}. \subsection{Code di messaggi}