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
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
% 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:
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);
}
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);
}