X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=0da0c53c77b3e3a378d3180d54bf75f009cb4f31;hp=63b1557f1286eaae7ab126408548eff3d25000cf;hb=76c815519021c83247a87d9f4318fc4aaa533f56;hpb=6483a787322c614bc6282a0bf0ee001f1bf54b44 diff --git a/ipc.tex b/ipc.tex index 63b1557..0da0c53 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} @@ -857,7 +867,7 @@ sull'altro e viceversa. I parametri \param{domain}, \param{type} e \param{protocol} derivano dall'interfaccia dei socket (che è quella che fornisce il substrato per connettere i due descrittori), ma in questo caso i soli valori validi che possono essere specificati sono rispettivamente -\macro{AF\_UNIX}, \macro{SOCK\_STREAM} e \macro{0}. +\macro{AF\_UNIX}, \macro{SOCK\_STREAM} e \var{0}. 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 @@ -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 @@ -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,6 +2918,20 @@ 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. +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. + + %% 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 @@ -2906,11 +2951,12 @@ agganciato al processo. \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,36 +2999,79 @@ 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: 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. - -La tecnica può comunque 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; in \file{LockFile.c} (un'altro dei -sorgenti allegati alla guida) si sono predisposte due funzioni, -\func{LockFile} e \func{UnlockFile}, da utilizzare allo scopo. +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 \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}. + +\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 @@ -3002,11 +3091,54 @@ non consuma risorse permanentemente allocate nel sistema, lo svantaggio dovendo fare ricorso a delle operazioni sul filesystem esso è in genere leggermente più lento. -\begin{figure}[!bht] +\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 @@ -3016,21 +3148,60 @@ leggermente pi \end{figure} Il codice per implementare un mutex utilizzando il file locking è riportato in -\figref{fig:ipc_flock_mutex}; come nel precedente caso dei mutex implementato -con i semafori le funzioni sono tre - - - - -\subsection{Il \textit{memory mapping} anonimo} +\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} @@ -3049,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}