From 9fc37da8fe691ce7ca6222773aace47c5edef3d1 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 31 Dec 2010 13:30:45 +0000 Subject: [PATCH] Risistemati ed insiriti nella relativa sezione gli esempi di uso dei segmenti di memoria condivisa e dei semafori POSIX. --- ipc.tex | 184 +++++++++++++++++++++++++++++++++++++-- listati/HandSigInt.c | 6 ++ listati/message_getter.c | 49 +++++++++++ listati/message_setter.c | 34 ++++++++ sources/message_getter.c | 48 +++++----- sources/message_setter.c | 10 ++- 6 files changed, 293 insertions(+), 38 deletions(-) create mode 100644 listati/HandSigInt.c create mode 100644 listati/message_getter.c create mode 100644 listati/message_setter.c diff --git a/ipc.tex b/ipc.tex index 69286e9..d22c3ba 100644 --- a/ipc.tex +++ b/ipc.tex @@ -3758,11 +3758,11 @@ registrazione chiamando nuovamente \func{mq\_notify} all'interno del gestore del segnale di notifica. A differenza della situazione simile che si aveva con i segnali non affidabili,\footnote{l'argomento è stato affrontato in \ref{sec:sig_semantics}.} questa caratteristica non configura una -race-condition perché l'invio di un segnale avviene solo se la coda è vuota; -pertanto se si vuole evitare di correre il rischio di perdere eventuali -ulteriori segnali inviati nel lasso di tempo che occorre per ripetere la -richiesta di notifica basta avere cura di eseguire questa operazione prima di -estrarre i messaggi presenti dalla coda. +\itindex{race~condition} \textit{race condition} perché l'invio di un segnale +avviene solo se la coda è vuota; pertanto se si vuole evitare di correre il +rischio di perdere eventuali ulteriori segnali inviati nel lasso di tempo che +occorre per ripetere la richiesta di notifica basta avere cura di eseguire +questa operazione prima di estrarre i messaggi presenti dalla coda. L'invio del segnale di notifica avvalora alcuni campi di informazione restituiti al gestore attraverso la struttura \struct{siginfo\_t} (definita in @@ -4393,13 +4393,179 @@ dall'altro programma prima di averla finita di stampare. La parte iniziale del programma contiene le definizioni (\texttt{\small 1--8}) del gestore del segnale usato per liberare le risorse utilizzate, delle variabili globali contenenti i nomi di default del segmento di memoria -condivisa e del semaforo, e delle altre variabili utilizzate dal programma. +condivisa e del semaforo (il default scelto è \texttt{messages}), e delle +altre variabili utilizzate dal programma. Come prima istruzione (\texttt{\small 10}) si è provveduto ad installare un -gestore di segnale che consentirà di effettuare le operazioni di pulizia, dopo -di che +gestore di segnale che consentirà di effettuare le operazioni di pulizia +(usando la funzione \func{Signal} illustrata in +fig.~\ref{fig:sig_Signal_code}), dopo di che (\texttt{\small 10--16}) si è +creato il segmento di memoria condivisa con la funzione \func{CreateShm} che +abbiamo appena trattato in sez.~\ref{sec:ipc_posix_shm}, uscendo con un +messaggio in caso di errore. + +Si tenga presente che la funzione \func{CreateShm} richiede che il segmento +non sia già presente e fallirà qualora un'altra istanza, o un altro programma +abbia già allocato un segmento con quello stesso nome. Per semplicità di +gestione si è usata una dimensione fissa pari a 256 byte, definita tramite la +costante \texttt{MSGMAXSIZE}. + +Il passo successivo (\texttt{\small 17--21}) è quello della creazione del +semaforo che regola l'accesso al segmento di memoria condivisa con +\func{sem\_open}; anche in questo caso si gestisce l'uscita con stampa di un +messaggio in caso di errore. Anche per il semaforo, avendo specificato la +combinazione di flag \code{O\_CREAT|O\_EXCL} come secondo argomento, si esce +qualora fosse già esistente; altrimenti esso verrà creato con gli opportuni +permessi specificati dal terzo argomento, (indicante lettura e scrittura in +notazione ottale). Infine il semaforo verrà inizializzato ad un valore nullo +(il quarto argomento), corrispondete allo stato in cui risulta bloccato. + +A questo punto (\texttt{\small 23}) si potrà inizializzare il messaggio posto +nel segmento di memoria condivisa usando la stringa passata come argomento al +programma. Essendo il semaforo stato creato già bloccato non ci si dovrà +preoccupare di eventuali \itindex{race~condition} \textit{race condition} +qualora il programma di modifica del messaggio venisse lanciato proprio in +questo momento. Una volta inizializzato il messaggio occorrerà però +rilasciare il semaforo (\texttt{\small 25--28}) per consentirne l'uso; in +tutte queste operazioni si provvederà ad uscire dal programma con un opportuno +messaggio in caso di errore. + +Una volta completate le inizializzazioni il ciclo principale del programma +(\texttt{\small 29--47}) viene ripetuto indefinitamente (\texttt{\small 29}) +per stampare sia il contenuto del messaggio che una serie di informazioni di +controllo. Il primo passo (\texttt{\small 30--34}) è quello di acquisire (con +\func{sem\_getvalue}, con uscita in caso di errore) e stampare il valore del +semaforo ad inizio del ciclo; seguito (\texttt{\small 35--36}) dal tempo +corrente. +\begin{figure}[!h] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/HandSigInt.c} + \end{minipage} + \normalsize + \caption{Codice del gestore di segnale del programma + \file{message\_getter.c}.} + \label{fig:ipc_posix_sem_shm_message_server_handler} +\end{figure} + +Prima della stampa del messaggio invece si deve aquisire il semaforo +(\texttt{\small 31--34}) per evitare accessi concorrenti alla stringa da parte +del programma di modifica. Una volta eseguita la stampa (\texttt{\small 41}) +il semforo dovrà essere rilasciato (\texttt{\small 42--45}). Il passo finale +(\texttt{\small 46}) è attendere per un secondo prima di eseguire da capo il +ciclo. + +Per uscire in maniera corretta dal programma sarà necessario interromperlo con +il break da tastiera (\texttt{C-c}), che corrisponde all'invio del segnale +\const{SIGINT}, per il quale si è installato (\texttt{\small 10}) una +opportuna funzione di gestione, riportata in +fig.~\ref{fig:ipc_posix_sem_shm_message_server_handler}. La funzione è molto +semplice e richiame le funzioni di rimozione sia per il segmento di memoria +condivisa che per il semaforo, garantendo così che possa essere riaperto +ex-novo senza errori in un futuro riutilizzo del comando. + +\begin{figure}[!h] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/message_setter.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del programma + \file{message\_setter.c}.} + \label{fig:ipc_posix_sem_shm_message_setter} +\end{figure} + +Il secondo programma di esempio è \file{message\_setter.c}, di cui si è +riportato il corpo principale in +fig.~\ref{fig:ipc_posix_sem_shm_message_setter},\footnote{al solito il codice + completo è nel file dei sorgenti allegati.} dove si è tralasciata, non +essendo significativa per quanto si sta trattando, la parte relativa alla +gestione delle opzioni a riga di comando e degli argomenti, che sono identici +a quelli usati da \file{message\_getter}, con l'unica aggiunta di un'opzione +``\texttt{-t}'' che consente di indicare un tempo di attesa (in secondi) in +cui il programma si ferma tenendo bloccato il semaforo. + +Una volta completata la gestione delle opzioni e degli argomenti (ne deve +essere presente uno solo, contenente la nuova stringa da usare come +messaggio), il programma procede (\texttt{\small 10--14}) con l'acquisizione +del segmento di memoria condivisa usando la funzione \func{FindShm} (trattata +in sez.~\ref{sec:ipc_posix_shm}) che stavolta deve già esistere. Il passo +successivo (\texttt{\small 16--19}) è quello di aprire il semaforo, e a +differenza di \file{message\_getter}, in questo caso si richiede a +\func{sem\_open} che questo esista, passando uno zero come secondo ed unico +argomento. + +Una volta completate con successo le precedenti inizializzazioni, il passo +seguente (\texttt{\small 21--24}) è quello di acquisire il semaforo, dopo di +che sarà possibile eseguire la sostituzione del messaggio (\texttt{\small 25}) +senza incorrere in possibili \itindex{race~condition} \textit{race condition} +con la stampa dello stesso da parte di \file{message\_getter}. + +Una volta effettuata la modifica viene stampato (\texttt{\small 26}) il tempo +di attesa impostato con l'opzione ``\texttt{-t}'' dopo di che (\texttt{\small + 27}) viene eseguita la stessa, senza rilasciare il semaforo che resterà +quindi bloccato (causando a questo punto una interruzione delle stampe +eseguite da \file{message\_getter}). Terminato il tempo di attesa si rilascerà +(\texttt{\small 29--32}) il semaforo per poi uscire. + +Per verificare il funzionamento dei programmi occorrerà lanciare per primo +\file{message\_getter}\footnote{lanciando per primo \file{message\_setter} + darà luogo ad un errore, non essendo stati creati il semaforo ed il segmento + di memoria condivisa.} che inizierà a stampare una volta al secondo il +contenuto del messaggio ed i suoi dati, con qualcosa del tipo: +\begin{Verbatim} +piccardi@hain:~/gapil/sources$ ./message_getter messaggio +sem=1, Fri Dec 31 14:12:41 2010 +message: messaggio +sem=1, Fri Dec 31 14:12:42 2010 +message: messaggio +... +\end{Verbatim} +%$ +proseguendo indefinitamente fintanto che non si prema \texttt{C-c} per farlo +uscire. Si noti come il valore del semaforo risulti sempre pari ad 1 (in +quanto al momento esso sarà sempre libero). + +A questo punto si potrà lanciare \file{message\_setter} per cambiare il +messaggio, nel nostro caso per rendere evidente il funzionamento del blocco +richiederemo anche una attesa di 3 secondi, ed otterremo qualcosa del tipo: +\begin{Verbatim} +piccardi@hain:~/gapil/sources$ ./message_setter -t 3 ciao +Sleeping for 3 seconds +\end{Verbatim} +%$ +dove il programma si fermerà per 3 secondi prima di rilasciare il semaforo e +terminare. + +L'effetto di questo programma si potrà però apprezzare meglio nell'uscita di +\file{message\_getter}, che verrà interrotta per questo stesso tempo, prima di +ricominciare con il nuovo testo: +\begin{Verbatim} +... +sem=1, Fri Dec 31 14:16:27 2010 +message: messaggio +sem=1, Fri Dec 31 14:16:28 2010 +message: messaggio +sem=0, Fri Dec 31 14:16:29 2010 +message: ciao +sem=1, Fri Dec 31 14:16:32 2010 +message: ciao +sem=1, Fri Dec 31 14:16:33 2010 +message: ciao +... +\end{Verbatim} +%$ +E si noterà come nel momento in cui si è lanciato \file{message\_setter} le +stampe di \file{message\_getter} si bloccheranno, come corretto, dopo aver +registrato un valore nullo per il semaforo. Il programma infatti resterà +bloccato nella \func{sem\_wait} (quella di riga (\texttt{\small 37}) in +fig.~\ref{fig:ipc_posix_sem_shm_message_server}) fino alla scadenza +dell'attesa di \file{message\_setter} (con l'esecuzione della \func{sem\_post} +della riga (\texttt{\small 29}) di +fig.~\ref{fig:ipc_posix_sem_shm_message_setter}), e riprenderanno con il nuovo +testo alla terminazione di quest'ultimo. % LocalWords: like fifo System POSIX RPC Calls Common Object Request Brocker @@ -4453,6 +4619,8 @@ di che % LocalWords: CreateShm RemoveShm LIBRARY Library libmqueue FAILED EACCESS % LocalWords: ENAMETOOLONG qualchenome RESTART trywait XOPEN SOURCE timedwait % LocalWords: process getvalue sval execve pshared ENOSYS heap PAGE destroy +% LocalWords: xffffffff Arrays owner perms Queues used bytes messages device +% LocalWords: Cannot find such Segments getter Signal MSGMAXSIZE %%% Local Variables: diff --git a/listati/HandSigInt.c b/listati/HandSigInt.c new file mode 100644 index 0000000..3212934 --- /dev/null +++ b/listati/HandSigInt.c @@ -0,0 +1,6 @@ +void HandSigInt(int sig) +{ + if (RemoveShm(shmname) != 0) perror("Cannot remove shared memory"); + if (sem_unlink(semname)!= 0) perror("Cannot remove semaphore") ; + exit(0); +} diff --git a/listati/message_getter.c b/listati/message_getter.c new file mode 100644 index 0000000..4b19bef --- /dev/null +++ b/listati/message_getter.c @@ -0,0 +1,49 @@ +void HandSigInt(int sig); +#define MSGMAXSIZE 256 +char *shmname = "messages"; +char *semname = "messages"; + +int main(int argc, char *argv[]) +{ + sem_t * sem, void * shm_ptr, time_t t; + ... + Signal(SIGINT, HandSigInt); + // get a shared memory segment + if ((shm_ptr = CreateShm(shmname, MSGMAXSIZE, 0666, 0)) == NULL) { + perror("Cannot find shared memory"); + exit(1); + } + // get a locked semaphore + if ((sem = sem_open(semname, O_CREAT|O_EXCL, 0666, 0)) == SEM_FAILED) { + perror("Cannot open semaphore"); + exit(1); + } + // set initial string + strncpy((char *) shm_ptr, argv[optind], MSGMAXSIZE); + // do initial release + if (sem_post(sem) != 0) { + perror("cannot do semaphore initial release"); + exit(1); + } + // main loop + while(1) { + if (sem_getvalue(sem, &i) !=0) { // get sem values + perror("cannot get semaphore value"); + exit(1); + } + printf("sem=%i, ", i); // print sem values + t = time(NULL); // get time + printf("%s", ctime(&t)); // print time + if (sem_wait(sem) != 0) { // acquire semaphore + perror("cannot use semaphore"); + exit(1); + } + printf("message: %s\n", (char *) shm_ptr ); // print message + if (sem_post(sem) != 0) { // release semaphore + perror("cannot release semaphore"); + exit(1); + } + sleep(1); + } +exit(0); +} diff --git a/listati/message_setter.c b/listati/message_setter.c new file mode 100644 index 0000000..1094e39 --- /dev/null +++ b/listati/message_setter.c @@ -0,0 +1,34 @@ +#define MSGMAXSIZE 256 + +int main(int argc, char *argv[]) +{ + int t = 0, sem_t * sem, void *shm_ptr; + char *shmname = "messages"; + char *semname = "messages"; + ... + // get shared memory segment + shm_ptr = FindShm(shmname, MSGMAXSIZE); + if ( shm_ptr == NULL) { + perror("Cannot find shared memory"); + exit(1); + } + // open semaphore + if ( (sem = sem_open(semname, 0)) == SEM_FAILED ) { + perror("Cannot open semaphore"); + exit(1); + } + // get semaphore + if ( sem_wait(sem) != 0) { + perror("cannot use semaphore"); + exit(1); + } + strncpy((char *) shm_ptr, argv[optind], MSGMAXSIZE); // modify message + printf("Sleeping for %i seconds\n", t); // print wait time + sleep(t); // sleep + // release semaphore + if ( sem_post(sem) != 0) { + perror("cannot release semaphore"); + exit(1); + } + exit(0); +} diff --git a/sources/message_getter.c b/sources/message_getter.c index 5ffba09..8b3ec2d 100644 --- a/sources/message_getter.c +++ b/sources/message_getter.c @@ -93,42 +93,38 @@ int main(int argc, char *argv[]) usage(); } Signal(SIGINT, HandSigInt); - // Get shared memory segment - shm_ptr = CreateShm(shmname, MSGMAXSIZE, 0666, 0); - if ( shm_ptr == NULL) { + // get a shared memory segment + if ((shm_ptr = CreateShm(shmname, MSGMAXSIZE, 0666, 0)) == NULL) { perror("Cannot find shared memory"); exit(1); } - // set initial string - strncpy((char *) shm_ptr, argv[optind], MSGMAXSIZE); - // open the semaphore or create it locked - if ( (sem = sem_open(semname, O_CREAT, 0666, 0)) == SEM_FAILED ) { + // get a locked semaphore + if ((sem = sem_open(semname, O_CREAT|O_EXCL, 0666, 0)) == SEM_FAILED) { perror("Cannot open semaphore"); exit(1); } - // check if first time creation, and unlock - if ( sem_getvalue(sem, &i) != 0) { - perror("cannot get initial semaphore value"); + // set initial string + strncpy((char *) shm_ptr, argv[optind], MSGMAXSIZE); + // do initial release + if (sem_post(sem) != 0) { + perror("cannot do semaphore initial release"); exit(1); - } else - if (i == 0) - if ( sem_post(sem) != 0) { - perror("cannot do semaphore initial release"); - exit(1); - } + } // main loop while(1) { - // acquire semaphore - if ( sem_wait(sem) != 0) { + if (sem_getvalue(sem, &i) !=0) { // get sem values + perror("cannot get semaphore value"); + exit(1); + } + printf("sem=%i, ", i); // print sem values + t = time(NULL); // get time + printf("%s", ctime(&t)); // print time + if (sem_wait(sem) != 0) { // acquire semaphore perror("cannot use semaphore"); exit(1); } - t = time(NULL); - printf("%s", ctime(&t)); - sem_getvalue(sem, &i); - printf("sem=%i, ", i); - printf("message: %s\n", (char *) shm_ptr ); - if ( sem_post(sem) != 0) { + printf("message: %s\n", (char *) shm_ptr ); // print message + if (sem_post(sem) != 0) { // release semaphore perror("cannot release semaphore"); exit(1); } @@ -152,7 +148,7 @@ void usage(void) { void HandSigInt(int sig) { - RemoveShm(shmname); - sem_unlink(semname); + if (RemoveShm(shmname) != 0) perror("Cannot remove shared memory"); + if (sem_unlink(semname)!= 0) perror("Cannot remove semaphore") ; exit(0); } diff --git a/sources/message_setter.c b/sources/message_setter.c index f6a8408..823f9f8 100644 --- a/sources/message_setter.c +++ b/sources/message_setter.c @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) printf("Wrong number of arguments %d\n", argc - optind); usage(); } - // Get shared memory segment + // get shared memory segment shm_ptr = FindShm(shmname, MSGMAXSIZE); if ( shm_ptr == NULL) { perror("Cannot find shared memory"); @@ -103,13 +103,15 @@ int main(int argc, char *argv[]) perror("Cannot open semaphore"); exit(1); } + // get semaphore if ( sem_wait(sem) != 0) { perror("cannot use semaphore"); exit(1); } - strncpy((char *) shm_ptr, argv[optind], MSGMAXSIZE); - printf("Sleeping for %i seconds\n", t); - sleep(t); + strncpy((char *) shm_ptr, argv[optind], MSGMAXSIZE); // modify message + printf("Sleeping for %i seconds\n", t); // print sleep value + sleep(t); // sleep + // release semaphore if ( sem_post(sem) != 0) { perror("cannot release semaphore"); exit(1); -- 2.30.2