From: Simone Piccardi Date: Wed, 20 Nov 2002 23:34:02 +0000 (+0000) Subject: Completata la descrizione dell'esempio dei mutex realizzati con i semafori. X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=3498a6fc0fd13e07cacdea210cb99126d5052fbc Completata la descrizione dell'esempio dei mutex realizzati con i semafori. --- diff --git a/biblio.bib b/biblio.bib index f30a1c2..a258933 100644 --- a/biblio.bib +++ b/biblio.bib @@ -90,6 +90,22 @@ OPTnote = {}, OPTannote = {} } +@Book{PratC, + author = {S. Oullaine}, + editor = {O'Reilly}, + title = {Pratical C}, + publisher = {O'Reilly}, + year = {2002}, + OPTkey = {}, + OPTvolume = {}, + OPTnumber = {}, + OPTseries = {}, + OPTaddress = {}, + OPTedition = {2nd}, + OPTmonth = {}, + OPTnote = {}, + OPTannote = {} +} @Book{glibc, author = {Sandra Loosemore Richard M. Stallman Roland McGrath Andrew Oram and Ulrich Drepper}, editor = {Free Software Foundation}, diff --git a/ipc.tex b/ipc.tex index 1060b49..9dec79a 100644 --- a/ipc.tex +++ b/ipc.tex @@ -2440,66 +2440,72 @@ 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), tutte le funzioni -relative sono definite 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[oullaine]) che in questo modo possono - essere evitati.} nel file \file{wrappers.h}. In -\secref{fig:ipc_mutex_create} si è riportata la definizione delle due funzioni -che permettono di acquisire il \textit{mutex}; la prima è \func{MutexCreate} -che crea il semaforo usato per il mutex e lo inizializza, restituendone -l'identificatore; la seconda è \func{MutexFind}, che data una chiave -restitituisce l'identificatore del semaforo ad essa associato. +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}{} + \begin{lstlisting}{} /* - * Function MutexCreate: - * - * Input: an IPC key value (to create an unique semaphore) - * Return: the semaphore id# + * Function MutexCreate: create a mutex/semaphore */ -const union semun semunion={1}; /* semaphore union structure */ inline int MutexCreate(key_t ipc_key) { - int sem_id; - if( (sem_id=semget(ipc_key,1,IPC_CREAT|0666))<0 ){ /* get sem ID */ - perror("cannot create semaphore"); /* a sem_id <0 is an error */ - printf("semid=%d",sem_id); - exit(1); + 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; } - if ( (semctl(sem_id,0,SETVAL,semunion)) < 0 ) { - perror("cannot init semaphore"); /* <0 is an error */ - printf("on semid=%d",sem_id); - exit(1); + ret = semctl(sem_id, 0, SETVAL, semunion); /* init semaphore */ + if (ret == -1) { + return ret; } return sem_id; } /* - * Find Mutex - * get the semaphore/mutex Id given the IPC key value - * - * Input: an IPC key value + * Function MutexFind: get the semaphore/mutex Id given the IPC key value */ inline int MutexFind(key_t ipc_key) { - int sem_id; - if( (sem_id=semget(ipc_key,1,0))<0 ){ /* find sem .ID */ - perror("cannot find semaphore"); - exit(1); - } - return sem_id; + 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} @@ -2509,16 +2515,72 @@ inline int MutexFind(key_t ipc_key) \label{fig:ipc_mutex_create} \end{figure} - -La prima funzione definita (\texttt{\small 8--22}) è \func{MutexCreate}, -anzitutto (\texttt{\small 11--15}) si chiama \func{semget} con -\macro{IPC\_CREATE} per creare il semaforo qualora non esista, assegnandogli i -privilegi di lettura e scrittura per l'utente. In caso di errore si -scrive un errore e si esce. - - - - +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. @@ -2578,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 @@ -2615,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 @@ -2795,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 @@ -2920,12 +2982,12 @@ polling\index{polling}, che 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 +\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 determimanare la dispobilità della +\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. diff --git a/sources/wrappers.h b/sources/wrappers.h index ee8fa89..15ddd82 100644 --- a/sources/wrappers.h +++ b/sources/wrappers.h @@ -5,7 +5,7 @@ * * Author: S. Piccardi * - * $Id: wrappers.h,v 1.3 2002/08/18 23:24:44 piccardi Exp $ + * $Id: wrappers.h,v 1.4 2002/11/20 23:34:02 piccardi Exp $ * ***************************************************************/ #include /* IPC semaphore declarations */ @@ -32,45 +32,8 @@ union semun { }; #endif /* - * Define the sem_lock and sem_ulock sembuf structures to - * lock and unlock the semaphore (used to implement a mutex) - */ -struct sembuf sem_lock={ /* to lock semaphore */ - 0, /* semaphore number (only one so 0) */ - -1, /* semaphore operation (-1 to use resource) */ - 0}; /* semaphore flag (in this case 0) */ -struct sembuf sem_ulock={ /* to unlock semaphore */ - 0, /* semaphore number (only one so 0) */ - 1, /* semaphore operation (-1 to release resource) */ - 0}; /* semaphore flag (in this case 0) */ -/* - * Function MutexLock: - * to lock a mutex/semaphore - * - * Input: a semaphore id # - */ -inline void MutexLock(int sem_id) -{ - if (semop(sem_id,&sem_lock,1)) { - perror("Cannot lock the semaphore"); - exit(1); - } -} -/* - * Function MutexUnlock - * to unlock a mutex/semaphore + * Function MutexCreate: create a mutex/semaphore * - * Input: a semaphore id # - */ -inline void MutexUnlock(int sem_id) -{ - if (semop(sem_id,&sem_ulock,1)) { - perror("Cannot unlock the semaphore"); - exit(1); - } -} -/* - * Function MutexCreate: * First call create a semaphore, using the given key. * We want only one semaphore so we set second argument to 1; third * parameter is the flag argument, and is set to create a semaphore @@ -78,55 +41,72 @@ inline void MutexUnlock(int sem_id) * Second call initialize the semaphore to 1 (unlocked) * * Input: an IPC key value (to create an unique semaphore) - * Return: the semaphore id# + * Return: the semaphore id# or -1 on error */ -const union semun semunion={1}; /* semaphore union structure */ inline int MutexCreate(key_t ipc_key) { - int sem_id; - if( (sem_id=semget(ipc_key,1,IPC_CREAT|0666))<0 ){ /* get sem ID */ - perror("cannot create semaphore"); /* a sem_id <0 is an error */ - printf("semid=%d",sem_id); - exit(1); + 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; } - if ( (semctl(sem_id,0,SETVAL,semunion)) < 0 ) { - perror("cannot init semaphore"); /* <0 is an error */ - printf("on semid=%d",sem_id); - exit(1); + ret = semctl(sem_id, 0, SETVAL, semunion); /* init semaphore */ + if (ret == -1) { + return ret; } return sem_id; } /* - * Find Mutex - * get the semaphore/mutex Id given the IPC key value + * Function MutexFind: get the semaphore/mutex Id given the IPC key value * * Input: an IPC key value */ inline int MutexFind(key_t ipc_key) { - int sem_id; - if( (sem_id=semget(ipc_key,1,0))<0 ){ /* find sem .ID */ - perror("cannot find semaphore"); - exit(1); - } - return sem_id; + return semget(ipc_key,1,0); } /* - * Function MutexRead: - * Read the current value of the mutex/semaphore + * Function MutexRead: read the current value of the mutex/semaphore * * Input: a semaphore id # * Return: the semaphore value */ inline int MutexRead(int sem_id) { - int value; - if ( (value=semctl(sem_id,0,GETVAL,semunion)) < 0 ) { - perror("cannot read semaphore"); /* a <0 is an error */ - printf("on semid=%d\n",sem_id); - exit(1); - } - return value; + return semctl(sem_id, 0, GETVAL); +} +/* + * Define sembuf structures to lock and unlock the semaphore + * (used to implement a mutex) + */ +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 + * + * Input: a semaphore id # + * Output: semop return code (0 OK, -1 KO) + */ +inline int MutexLock(int sem_id) +{ + return semop(sem_id, &sem_lock, 1); +} +/* + * Function MutexUnlock: to unlock a mutex/semaphore + * + * Input: a semaphore id # + * Return: semop return code (0 OK, -1 KO) + */ +inline int MutexUnlock(int sem_id) +{ + return semop(sem_id, &sem_ulock, 1); } /* * Function ShmCreate: