X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=7bf418608b81f17be4eaf00503d9878574f86576;hp=9b04f516ff264eea8f358a29be2d5e1917aa3d0f;hb=78a82bdb333ca71e395ba39a5c03745761341150;hpb=e99034c22f44153d9260bd9075319a3804ab91a9 diff --git a/ipc.tex b/ipc.tex index 9b04f51..7bf4186 100644 --- a/ipc.tex +++ b/ipc.tex @@ -2440,7 +2440,149 @@ ignorare silenziosamente le altre; questo per ripristino non è comunque garantito in tutte le occasioni. Come esempio di uso dell'interfaccia dei semafori vediamo come implementare -con essa dei semplici \textit{mutex} (cioè semafori binari), +con essa dei semplici \textit{mutex} (cioè semafori binari), tutto il codice +in questione, contenuto nel file \file{wrappers.h} 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 +nullo per segnalarne l'indisponibilità. + +\begin{figure}[!bht] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} +/* + * Function MutexCreate: create a mutex/semaphore + */ +inline int MutexCreate(key_t ipc_key) +{ + const union semun semunion={1}; /* semaphore union structure */ + int sem_id, ret; + sem_id = semget(ipc_key, 1, IPC_CREAT|0666); /* get semaphore ID */ + if (sem_id == -1) { /* if error return code */ + return sem_id; + } + ret = semctl(sem_id, 0, SETVAL, semunion); /* init semaphore */ + if (ret == -1) { + return ret; + } + return sem_id; +} +/* + * Function MutexFind: get the semaphore/mutex Id given the IPC key value + */ +inline 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) +{ + return semctl(sem_id, 0, GETVAL); +} +/* + * Define sembuf structures to lock and unlock the semaphore + */ +struct sembuf sem_lock={ /* to lock semaphore */ + 0, /* semaphore number (only one so 0) */ + -1, /* operation (-1 to use resource) */ + SEM_UNDO}; /* flag (set for undo at exit) */ +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) */ +/* + * Function MutexLock: to lock a mutex/semaphore + */ +inline 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) +{ + return semop(sem_id, &sem_ulock, 1); +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni che permettono di creare o recuperare + l'identificatore di un semaforo da utilizzare come \textit{mutex}.} + \label{fig:ipc_mutex_create} +\end{figure} + +La prima funzione (\texttt{\small 1--17}) è \func{MutexCreate} che data una +chiave crea il semaforo usato per il mutex e lo inizializza, restituendone +l'identificatore. Il primo passo (\texttt{\small 8}) è chiamare \func{semget} +con \macro{IPC\_CREATE} per creare il semaforo qualora non esista, +assegnandogli i privilegi di lettura e scrittura per tutti. In caso di errore +(\texttt{\small 9--11}) si ritorna subito il risultato di \func{semget}, +altrimenti (\texttt{\small 12}) si inizializza il semaforo chiamando +\func{semctl} con il comando \macro{SETVAL}, utilizzando l'unione +\var{semunion} dichiarata ed avvalorata in precedenza (\texttt{\small 6}) ad 1 +per significare che risorsa è libera. In caso di errore (\texttt{\small + 13--16}) si restituisce il valore di ritorno di \func{semctl}, altrimenti si +ritorna l'identificatore del semaforo. + +La seconda funzione (\texttt{\small 18--24}) è \func{MutexFind}, che data una +chiave, restituisce l'identificatore del semaforo ad essa associato. La +comprensione del suo funzionamento è immediata in quanto è solo un +\textit{wrapper}\footnote{si chiama così una funzione usata per fare da + \textsl{involucro} alla chiamata di un altra, usata in genere per + semplificare un'interfaccia (come in questo caso) o per utilizzare con la + stessa funzione diversi substrati (librerie, ecc.) che possono fornire le + stesse funzionalità.} di \func{semget} per cercare l'identificatore +associato alla chiave, restituendo direttamente il valore di ritorno della +funzione. + +La terza funzione (\texttt{\small 25--31}) è \func{MutexRead} che, dato +l'identificatore, restituisce il valore del mutex. Anche in questo caso la +funzione è un \textit{wrapper} per la chiamata di \func{semctl}, questa volta +con il comando \macro{GETVAL}, che permette di restituire il valore del +semaforo. + +La quarta e la quinta funzione (\texttt{\small 43--56}) sono \func{MutexLock}, +e \func{MutexUnlock}, che permettono rispettivamente di bloccare e sbloccare +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.} + + +Chiamare \func{MutexLock} decrementa il valore del semaforo: se questo è +libero (ha già valore 1) sarà bloccato (valore nullo), se è bloccato la +chiamata a \func{semop} si bloccherà fintanto che la risorsa non venga +rilasciata. Chiamando \func{MutexUnlock} il valore del semaforo sarà +incrementato di uno, sbloccandolo qualora fosse bloccato. Si noti che occorre +eseguire sempre prima \func{MutexLock} e poi \func{MutexUnlock}, perché se per +un qualche errore si esegue più volte quest'ultima il valore del semaforo +crescerebbe oltre 1, e \func{MutexLock} non avrebbe più l'effetto aspettato +(bloccare la risorsa quando questa è considerata libera). Si tenga presente +che usare \func{MutexRead} per controllare il valore dei mutex prima di +proseguire non servirebbe comunque, dato che l'operazione non sarebbe atomica. +Vedremo in \secref{sec:ipc_posix_sem} come è possibile ottenere un'interfaccia +analoga senza questo problemi usando il file locking. + + @@ -2498,7 +2640,7 @@ 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 utilizzata in abbinamento ad un meccanismo di sincronizzazione, il che, di -norma, significa insime a dei semafori. +norma, significa insieme a dei semafori. \begin{figure}[!htb] \footnotesize \centering @@ -2535,7 +2677,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 esprimno +\item i campi \var{shm\_atime} e \var{shm\_atime}, 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 @@ -2715,7 +2857,7 @@ In caso di successo la funzione aggiorna anche i seguenti campi di \end{itemize*} Come accennato in \secref{sec:proc_fork} un segmento di memoria condivisa -agganciato ad un precesso viene ereditato da un figlio attraverso una +agganciato ad un processo viene ereditato da un figlio attraverso una \func{fork}, dato che quest'ultimo riceve una copia dello spazio degli indirizzi del padre. Invece, dato che attraverso una \func{exec} viene eseguito un diverso programma con uno spazio di indirizzi completamente @@ -2783,13 +2925,14 @@ dal \textit{SysV IPC}. In realtà, grazie alla presenza del campo \var{mtype}, le code di messaggi hanno delle caratteristiche ulteriori, consentendo una classificazione dei -messaggi ed un accesso non rigidamente sequenziale, due caratteristiche che -sono impossibili da ottenere con le pipe e i socket di \func{socketpair}; -a queste esigenze però si può comunque ovviare in maniera diversa con un uso +messaggi ed un accesso non rigidamente sequenziale; due caratteristiche che +sono impossibili da ottenere con le pipe e i socket di \func{socketpair}. A +queste esigenze però si può comunque ovviare in maniera diversa con un uso combinato della memoria condivisa e dei meccanismi di sincronizzazione, per cui alla fine l'uso delle code di messaggi classiche è poco diffuso. + \subsection{La sincronizzazione con il \textit{file locking}} \label{sec:ipc_file_lock} @@ -2797,14 +2940,14 @@ Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC} presentano una interfaccia inutilmente complessa e con alcuni difetti strutturali, per questo quando si ha una semplice esigenza di sincronizzazione per la quale basterebbe un semaforo binario (quello che abbiamo definito come -\textit{mutex}, che indica la disponibilità o meno di una risorsa, e non ha -associato un contatore come i semafori) si possono utilizzare metodi +\textit{mutex}), per indicare la disponibilità o meno di una risorsa, senza la +necessità di un contatore come i semafori, si possono utilizzare metodi alternativi. La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare -dei \textsl{file di lock} (per i quali esiste anche una opportuna directory, -\file{/var/lock}, nel filesystem standard). Per questo si usa la -caratteristica della funzione \func{open} (illustrata in +dei \textsl{file di lock}\index{file di lock} (per i quali esiste anche una +opportuna directory, \file{/var/lock}, nel filesystem standard). Per questo si +usa la caratteristica della funzione \func{open} (illustrata in \secref{sec:file_open}) che prevede\footnote{questo è quanto dettato dallo standard POSIX.1, ciò non toglie che in alcune implementazioni questa tecnica possa non funzionare; in particolare per Linux, nel caso di NFS, si @@ -2818,50 +2961,76 @@ il rilascio si pu 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, la risorsa - 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.} + 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 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 -polling\index{polling}, che è molto inefficiente. - -Per questo motivo la tecnica alternativa più pulita è quella di fare ricorso -al \textit{file locking} visto in \secref{sec:file_locking} ed utilizzare -\func{fcntl} su un file creato per l'occazione 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 determimanare la dispobilità della -risorsa, e al rilascio della stessa da parte del processo che la occupava si -otterrà il nuovo lock atomicamente. +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; sempre in \file{wrapper.h} si sono +predisposte due funzioni, \func{LockFile} e \func{UnlockFile}, da utilizzare +allo scopo. + +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, e non -consuma risorse permanentemente allocate nel sistema, lo svantaggio è che +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. +\begin{figure}[!bht] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} + + \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}; come nel precedente caso dei mutex implementato +con i semafori le funzioni sono tre + + \subsection{Il \textit{memory mapping} anonimo} \label{sec:ipc_mmap_anonymous} -Abbiamo visto in \secref{sec:file_memory_map} come sia possibile +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 \section{La comunicazione fra processi di POSIX}