X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=491d29d9587f21c3160231b822b2da3ed9481c82;hp=74b51365d4f7311193f2d8e734121abfe2fcd02f;hb=33a54e1bfa5e62cb90d84c2d5f2d0c53864f6bec;hpb=f03c81e511c5c9c5c65f9b2bcd21dc7664c08b38 diff --git a/ipc.tex b/ipc.tex index 74b5136..491d29d 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,6 +1,6 @@ %% ipc.tex %% -%% Copyright (C) 2000-2007 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2011 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 "Un preambolo", @@ -66,8 +66,9 @@ La funzione restituisce la coppia di file descriptor nel vettore accennato concetto di funzionamento di una pipe è semplice: quello che si scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale nel file descriptor aperto in lettura. I file descriptor infatti non sono -connessi a nessun file reale, ma ad un buffer nel kernel, la cui dimensione è -specificata dal parametro di sistema \const{PIPE\_BUF}, (vedi +connessi a nessun file reale, ma, come accennato in +sez.~\ref{sec:file_sendfile_splice}, ad un buffer nel kernel, la cui +dimensione è specificata dal parametro di sistema \const{PIPE\_BUF}, (vedi sez.~\ref{sec:sys_file_limits}). Lo schema di funzionamento di una pipe è illustrato in fig.~\ref{fig:ipc_pipe_singular}, in cui sono illustrati i due capi della pipe, associati a ciascun file descriptor, con le frecce che @@ -133,7 +134,7 @@ Per capire meglio il funzionamento delle pipe faremo un esempio di quello che è il loro uso più comune, analogo a quello effettuato della shell, e che consiste nell'inviare l'output di un processo (lo standard output) sull'input di un altro. Realizzeremo il programma di esempio nella forma di un -\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un +\textit{CGI}\footnote{un CGI (\textit{Common Gateway Interface}) è un programma che permette la creazione dinamica di un oggetto da inserire all'interno di una pagina HTML.} per Apache, che genera una immagine JPEG di un codice a barre, specificato come argomento in ingresso. @@ -174,10 +175,10 @@ evidente \itindex{race~condition} \textit{race condition} in caso di accesso simultaneo a detto file.\footnote{il problema potrebbe essere superato determinando in anticipo un nome appropriato per il file temporaneo, che verrebbe utilizzato dai vari sotto-processi, e cancellato alla fine della - loro esecuzione; ma a questo le cose non sarebbero più tanto semplici.} -L'uso di una pipe invece permette di risolvere il problema in maniera semplice -ed elegante, oltre ad essere molto più efficiente, dato che non si deve -scrivere su disco. + loro esecuzione; ma a questo punto le cose non sarebbero più tanto + semplici.} L'uso di una pipe invece permette di risolvere il problema in +maniera semplice ed elegante, oltre ad essere molto più efficiente, dato che +non si deve scrivere su disco. Il programma ci servirà anche come esempio dell'uso delle funzioni di duplicazione dei file descriptor che abbiamo trattato in @@ -189,7 +190,6 @@ fig.~\ref{fig:ipc_barcodepage_code} abbiamo riportato il corpo del programma, il cui codice completo è disponibile nel file \file{BarCodePage.c} che si trova nella directory dei sorgenti. - \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} @@ -568,7 +568,7 @@ ricevuta la risposta, uscir A questo punto il server resta (se non ci sono altri client che stanno effettuando richieste) con la fifo chiusa sul lato in lettura, ed in questo stato la funzione \func{read} non si bloccherà in attesa di input, ma -ritornerà in continuazione, restituendo un end-of-file.\footnote{Si è usata +ritornerà in continuazione, restituendo un end-of-file.\footnote{si è usata questa tecnica per compatibilità, Linux infatti supporta l'apertura delle fifo in lettura/scrittura, per cui si sarebbe potuto effettuare una singola apertura con \const{O\_RDWR}, la doppia apertura comunque ha il vantaggio @@ -655,24 +655,26 @@ verificata) che si facciano le prove direttamente nella directory dei sorgenti (dove di norma vengono creati sia i programmi che la libreria), il comando da dare sarà \code{export LD\_LIBRARY\_PATH=./}; a questo punto potremo lanciare il server, facendogli leggere una decina di frasi, con: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./fortuned -n10 -\end{verbatim} +\end{Verbatim} +%$ Avendo usato \func{daemon} per eseguire il server in background il comando ritornerà immediatamente, ma potremo verificare con \cmd{ps} che in effetti il programma resta un esecuzione in background, e senza avere associato un terminale di controllo (si ricordi quanto detto in sez.~\ref{sec:sess_daemon}): -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ps aux ... piccardi 27489 0.0 0.0 1204 356 ? S 01:06 0:00 ./fortuned -n10 piccardi 27492 3.0 0.1 2492 764 pts/2 R 01:08 0:00 ps aux -\end{verbatim}%$ +\end{Verbatim} +%$ e si potrà verificare anche che in \file{/tmp} è stata creata la fifo di ascolto \file{fortune.fifo}. A questo punto potremo interrogare il server con il programma client; otterremo così: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./fortune Linux ext2fs has been stable for a long time, now it's time to break it -- Linuxkongreß '95 in Berlin @@ -691,7 +693,8 @@ Let's call it an accidental feature. [piccardi@gont sources]$ ./fortune Linux ext2fs has been stable for a long time, now it's time to break it -- Linuxkongreß '95 in Berlin -\end{verbatim}%$ +\end{Verbatim} +%$ e ripetendo varie volte il comando otterremo, in ordine casuale, le dieci frasi tenute in memoria dal server. @@ -749,9 +752,9 @@ entrambe le direzioni. Il prototipo della funzione \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EAFNOSUPPORT}] I socket locali non sono supportati. - \item[\errcode{EPROTONOSUPPORT}] Il protocollo specificato non è supportato. - \item[\errcode{EOPNOTSUPP}] Il protocollo specificato non supporta la + \item[\errcode{EAFNOSUPPORT}] i socket locali non sono supportati. + \item[\errcode{EPROTONOSUPPORT}] il protocollo specificato non è supportato. + \item[\errcode{EOPNOTSUPP}] il protocollo specificato non supporta la creazione di coppie di socket. \end{errlist} ed inoltre \errval{EMFILE}, \errval{EFAULT}. @@ -893,8 +896,9 @@ con i 16 bit meno significativi \index{inode} dell'inode del file \param{pathname} (che vengono ottenuti attraverso \func{stat}, da cui derivano i possibili errori), e gli 8 bit meno significativi del numero del dispositivo su cui è il file. Diventa perciò relativamente facile ottenere delle -collisioni, specie se i file sono su dispositivi con lo stesso \textit{minor - number}, come \file{/dev/hda1} e \file{/dev/sda1}. +collisioni, specie se i file sono su dispositivi con lo stesso +\itindex{minor~number} \textit{minor number}, come \file{/dev/hda1} e +\file{/dev/sda1}. In genere quello che si fa è utilizzare un file comune usato dai programmi che devono comunicare (ad esempio un header comune, o uno dei programmi che devono @@ -960,7 +964,7 @@ nullo, nel qual caso l'identificatore sar Il secondo livello di controllo è quello delle varie funzioni che accedono direttamente (in lettura o scrittura) all'oggetto. In tal caso lo schema dei controlli è simile a quello dei file, ed avviene secondo questa sequenza: -\begin{itemize} +\begin{itemize*} \item se il processo ha i privilegi di amministratore l'accesso è sempre consentito. \item se l'user-ID effettivo del processo corrisponde o al valore del campo @@ -972,12 +976,12 @@ controlli valore del campo \var{cgid} o a quello del campo \var{gid} ed il permesso per il gruppo in \var{mode} è appropriato l'accesso è consentito. \item se il permesso per gli altri è appropriato l'accesso è consentito. -\end{itemize} +\end{itemize*} solo se tutti i controlli elencati falliscono l'accesso è negato. Si noti che a differenza di quanto avviene per i permessi dei file, fallire in uno dei passi elencati non comporta il fallimento dell'accesso. Un'ulteriore differenza rispetto a quanto avviene per i file è che per gli oggetti di IPC -il valore di \var{umask} (si ricordi quanto esposto in +il valore di \itindex{umask} \textit{umask} (si ricordi quanto esposto in sez.~\ref{sec:file_perm_management}) non ha alcun significato. @@ -1017,8 +1021,9 @@ Il sistema dispone sempre di un numero fisso di oggetti di IPC,\footnote{fino altri limiti relativi al \textit{SysV IPC}) solo con una ricompilazione del kernel, andando a modificarne la definizione nei relativi header file. A partire dal kernel 2.4.x è possibile cambiare questi valori a sistema attivo - scrivendo sui file \file{shmmni}, \file{msgmni} e \file{sem} di - \file{/proc/sys/kernel} o con l'uso di \func{sysctl}.} e per ciascuno di + scrivendo sui file \procrelfile{/proc/sys/kernel}{shmmni}, + \procrelfile{/proc/sys/kernel}{msgmni} e \procrelfile{/proc/sys/kernel}{sem} + di \file{/proc/sys/kernel} o con l'uso di \func{sysctl}.} e per ciascuno di essi viene mantenuto in \var{seq} un numero di sequenza progressivo che viene incrementato di uno ogni volta che l'oggetto viene cancellato. Quando l'oggetto viene creato usando uno spazio che era già stato utilizzato in @@ -1055,25 +1060,27 @@ inizializzare i valori delle variabili \var{type} al tipo di oggetto voluto, e stampa, cancellazione. I valori di default sono per l'uso delle code di messaggi e un ciclo di 5 volte. Se si lancia il comando si otterrà qualcosa del tipo: -\begin{verbatim} +\begin{Verbatim} piccardi@gont sources]$ ./ipctestid Identifier Value 0 Identifier Value 32768 Identifier Value 65536 Identifier Value 98304 Identifier Value 131072 -\end{verbatim}%$ +\end{Verbatim} +%$ il che ci mostra che abbiamo un kernel della serie 2.4.x nel quale non avevamo ancora usato nessuna coda di messaggi. Se ripetiamo il comando otterremo ancora: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./ipctestid Identifier Value 163840 Identifier Value 196608 Identifier Value 229376 Identifier Value 262144 Identifier Value 294912 -\end{verbatim}%$ +\end{Verbatim} +%$ che ci mostra come il valore di \var{seq} sia in effetti una quantità mantenuta staticamente all'interno del sistema. @@ -1101,15 +1108,15 @@ di messaggi esistente (o di crearne una se questa non esiste) \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCES}] Il processo chiamante non ha i privilegi per accedere + \item[\errcode{EACCES}] il processo chiamante non ha i privilegi per accedere alla coda richiesta. - \item[\errcode{EEXIST}] Si è richiesta la creazione di una coda che già + \item[\errcode{EEXIST}] si è richiesta la creazione di una coda che già esiste, ma erano specificati sia \const{IPC\_CREAT} che \const{IPC\_EXCL}. - \item[\errcode{EIDRM}] La coda richiesta è marcata per essere cancellata. - \item[\errcode{ENOENT}] Si è cercato di ottenere l'identificatore di una coda + \item[\errcode{EIDRM}] la coda richiesta è marcata per essere cancellata. + \item[\errcode{ENOENT}] si è cercato di ottenere l'identificatore di una coda di messaggi specificando una chiave che non esiste e \const{IPC\_CREAT} non era specificato. - \item[\errcode{ENOSPC}] Si è cercato di creare una coda di messaggi quando è + \item[\errcode{ENOSPC}] si è cercato di creare una coda di messaggi quando è stato superato il limite massimo di code (\const{MSGMNI}). \end{errlist} ed inoltre \errval{ENOMEM}. @@ -1160,7 +1167,7 @@ coda. \hline \hline \const{MSGMNI}& 16& \file{msgmni} & Numero massimo di code di - messaggi. \\ + messaggi.\\ \const{MSGMAX}& 8192& \file{msgmax} & Dimensione massima di un singolo messaggio.\\ \const{MSGMNB}&16384& \file{msgmnb} & Dimensione massima del contenuto di @@ -1175,11 +1182,12 @@ Le code di messaggi sono caratterizzate da tre limiti fondamentali, definiti negli header e corrispondenti alle prime tre costanti riportate in tab.~\ref{tab:ipc_msg_limits}, come accennato però in Linux è possibile modificare questi limiti attraverso l'uso di \func{sysctl} o scrivendo nei -file \file{msgmax}, \file{msgmnb} e \file{msgmni} di \file{/proc/sys/kernel/}. - +file \procrelfile{/proc/sys/kernel}{msgmax}, +\procrelfile{/proc/sys/kernel}{msgmnb} e +\procrelfile{/proc/sys/kernel}{msgmni} di \file{/proc/sys/kernel/}. \begin{figure}[htb] - \centering \includegraphics[width=15cm]{img/mqstruct} + \centering \includegraphics[width=13cm]{img/mqstruct} \caption{Schema della struttura di una coda messaggi.} \label{fig:ipc_mq_schema} \end{figure} @@ -1266,13 +1274,13 @@ prototipo Esegue l'operazione specificata da \param{cmd} sulla coda \param{msqid}. - \bodydesc{La funzione restituisce 0 in caso di successo o -1 in caso di + \bodydesc{La funzione restituisce 0 in caso di successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCES}] Si è richiesto \const{IPC\_STAT} ma processo + \item[\errcode{EACCES}] si è richiesto \const{IPC\_STAT} ma processo chiamante non ha i privilegi di lettura sulla coda. - \item[\errcode{EIDRM}] La coda richiesta è stata cancellata. - \item[\errcode{EPERM}] Si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} ma + \item[\errcode{EIDRM}] la coda richiesta è stata cancellata. + \item[\errcode{EPERM}] si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} ma il processo non ha i privilegi, o si è richiesto di aumentare il valore di \var{msg\_qbytes} oltre il limite \const{MSGMNB} senza essere amministratore. @@ -1321,21 +1329,19 @@ messaggio su una coda si utilizza la funzione \funcd{msgsnd}; il suo prototipo Invia un messaggio sulla coda \param{msqid}. - \bodydesc{La funzione restituisce 0, e -1 in caso di errore, nel qual caso + \bodydesc{La funzione restituisce 0, e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCES}] Non si hanno i privilegi di accesso sulla coda. - \item[\errcode{EIDRM}] La coda è stata cancellata. - \item[\errcode{EAGAIN}] Il messaggio non può essere inviato perché si è + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \item[\errcode{EAGAIN}] il messaggio non può essere inviato perché si è superato il limite \var{msg\_qbytes} sul numero massimo di byte presenti sulla coda, e si è richiesto \const{IPC\_NOWAIT} in \param{flag}. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Si è specificato un \param{msgid} invalido, o un + \item[\errcode{EINVAL}] si è specificato un \param{msgid} invalido, o un valore non positivo per \param{mtype}, o un valore di \param{msgsz} maggiore di \const{MSGMAX}. \end{errlist} - ed inoltre \errval{EFAULT} ed \errval{ENOMEM}. -} + ed inoltre \errval{EFAULT}, \errval{EINTR} ed \errval{ENOMEM}. } \end{functions} La funzione inserisce il messaggio sulla coda specificata da \param{msqid}; il @@ -1432,13 +1438,13 @@ La funzione che viene utilizzata per estrarre un messaggio da una coda successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCES}] Non si hanno i privilegi di accesso sulla coda. - \item[\errcode{EIDRM}] La coda è stata cancellata. - \item[\errcode{E2BIG}] Il testo del messaggio è più lungo di \param{msgsz} e + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \item[\errcode{E2BIG}] il testo del messaggio è più lungo di \param{msgsz} e non si è specificato \const{MSG\_NOERROR} in \param{msgflg}. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale mentre + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale mentre era in attesa di ricevere un messaggio. - \item[\errcode{EINVAL}] Si è specificato un \param{msgid} invalido o un + \item[\errcode{EINVAL}] si è specificato un \param{msgid} invalido o un valore di \param{msgsz} negativo. \end{errlist} ed inoltre \errval{EFAULT}. @@ -1724,7 +1730,7 @@ pertanto una realizzazione di un oggetto di questo tipo demandata al kernel. La forma più semplice di semaforo è quella del \textsl{semaforo binario}, o \textit{mutex}, in cui un valore diverso da zero (normalmente 1) indica la libertà di accesso, e un valore nullo l'occupazione -della risorsa; in generale però si possono usare semafori con valori interi, +della risorsa. In generale però si possono usare semafori con valori interi, utilizzando il valore del contatore come indicatore del ``numero di risorse'' ancora disponibili. @@ -1745,15 +1751,15 @@ permette di creare o ottenere l'identificatore di un insieme di semafori \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 in caso di errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{ENOSPC}] Si è cercato di creare una insieme di semafori + \item[\errcode{ENOSPC}] si è cercato di creare una insieme di semafori quando è stato superato o il limite per il numero totale di semafori (\const{SEMMNS}) o quello per il numero totale degli insiemi (\const{SEMMNI}) nel sistema. - \item[\errcode{EINVAL}] L'argomento \param{nsems} è minore di zero o + \item[\errcode{EINVAL}] l'argomento \param{nsems} è minore di zero o maggiore del limite sul numero di semafori per ciascun insieme (\const{SEMMSL}), o se l'insieme già esiste, maggiore del numero di semafori che contiene. - \item[\errcode{ENOMEM}] Il sistema non ha abbastanza memoria per poter + \item[\errcode{ENOMEM}] il sistema non ha abbastanza memoria per poter contenere le strutture per un nuovo insieme di semafori. \end{errlist} ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, @@ -1788,7 +1794,6 @@ del sistema. Come vedremo esistono delle modalit diventa necessario indicare esplicitamente che si vuole il ripristino del semaforo all'uscita del processo. - \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} @@ -1819,7 +1824,6 @@ quanto riguarda gli altri campi invece: effettuata, viene inizializzato a zero. \end{itemize*} - Ciascun semaforo dell'insieme è realizzato come una struttura di tipo \struct{sem} che ne contiene i dati essenziali, la sua definizione\footnote{si è riportata la definizione originaria del kernel 1.0, che contiene la prima @@ -1862,16 +1866,16 @@ indicano rispettivamente: \textbf{Costante} & \textbf{Valore} & \textbf{Significato} \\ \hline \hline - \const{SEMMNI}& 128 & Numero massimo di insiemi di semafori. \\ + \const{SEMMNI}& 128 & Numero massimo di insiemi di semafori.\\ \const{SEMMSL}& 250 & Numero massimo di semafori per insieme.\\ \const{SEMMNS}&\const{SEMMNI}*\const{SEMMSL}& Numero massimo di semafori - nel sistema .\\ + nel sistema.\\ \const{SEMVMX}& 32767 & Massimo valore per un semaforo.\\ \const{SEMOPM}& 32 & Massimo numero di operazioni per chiamata a \func{semop}. \\ \const{SEMMNU}&\const{SEMMNS}& Massimo numero di strutture di ripristino.\\ \const{SEMUME}&\const{SEMOPM}& Massimo numero di voci di ripristino.\\ - \const{SEMAEM}&\const{SEMVMX}& valore massimo per l'aggiustamento + \const{SEMAEM}&\const{SEMVMX}& Valore massimo per l'aggiustamento all'uscita. \\ \hline \end{tabular} @@ -1884,7 +1888,7 @@ Come per le code di messaggi anche per gli insiemi di semafori esistono una serie di limiti, i cui valori sono associati ad altrettante costanti, che si sono riportate in tab.~\ref{tab:ipc_sem_limits}. Alcuni di questi limiti sono al solito accessibili e modificabili attraverso \func{sysctl} o scrivendo -direttamente nel file \file{/proc/sys/kernel/sem}. +direttamente nel file \procfile{/proc/sys/kernel/sem}. La funzione che permette di effettuare le varie operazioni di controllo sui semafori (fra le quali, come accennato, è impropriamente compresa anche la @@ -1904,12 +1908,12 @@ loro inizializzazione) quattro. In caso di errore restituisce -1, ed \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCES}] Il processo non ha i privilegi per eseguire + \item[\errcode{EACCES}] il processo non ha i privilegi per eseguire l'operazione richiesta. - \item[\errcode{EIDRM}] L'insieme di semafori è stato cancellato. - \item[\errcode{EPERM}] Si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{EPERM}] si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} ma il processo non ha privilegi sufficienti ad eseguire l'operazione. - \item[\errcode{ERANGE}] Si è richiesto \const{SETALL} \const{SETVAL} ma il + \item[\errcode{ERANGE}] si è richiesto \const{SETALL} \const{SETVAL} ma il valore a cui si vuole impostare il semaforo è minore di zero o maggiore di \const{SEMVMX}. \end{errlist} @@ -2010,10 +2014,10 @@ tutti i semafori il cui valore viene modificato. \textbf{Operazione} & \textbf{Valore restituito} \\ \hline \hline - \const{GETNCNT}& valore di \var{semncnt}.\\ - \const{GETPID} & valore di \var{sempid}.\\ - \const{GETVAL} & valore di \var{semval}.\\ - \const{GETZCNT}& valore di \var{semzcnt}.\\ + \const{GETNCNT}& Valore di \var{semncnt}.\\ + \const{GETPID} & Valore di \var{sempid}.\\ + \const{GETVAL} & Valore di \var{semval}.\\ + \const{GETZCNT}& Valore di \var{semzcnt}.\\ \hline \end{tabular} \caption{Valori di ritorno della funzione \func{semctl}.} @@ -2042,18 +2046,18 @@ vengono effettuate con la funzione \funcd{semop}, il cui prototipo \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCES}] Il processo non ha i privilegi per eseguire + \item[\errcode{EACCES}] il processo non ha i privilegi per eseguire l'operazione richiesta. - \item[\errcode{EIDRM}] L'insieme di semafori è stato cancellato. - \item[\errcode{ENOMEM}] Si è richiesto un \const{SEM\_UNDO} ma il sistema + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{ENOMEM}] si è richiesto un \const{SEM\_UNDO} ma il sistema non ha le risorse per allocare la struttura di ripristino. - \item[\errcode{EAGAIN}] Un'operazione comporterebbe il blocco del processo, + \item[\errcode{EAGAIN}] un'operazione comporterebbe il blocco del processo, ma si è specificato \const{IPC\_NOWAIT} in \var{sem\_flg}. - \item[\errcode{EINTR}] La funzione, bloccata in attesa dell'esecuzione + \item[\errcode{EINTR}] la funzione, bloccata in attesa dell'esecuzione dell'operazione, viene interrotta da un segnale. - \item[\errcode{E2BIG}] L'argomento \param{nsops} è maggiore del numero + \item[\errcode{E2BIG}] l'argomento \param{nsops} è maggiore del numero massimo di operazioni \const{SEMOPM}. - \item[\errcode{ERANGE}] Per alcune operazioni il valore risultante del + \item[\errcode{ERANGE}] per alcune operazioni il valore risultante del semaforo viene a superare il limite massimo \const{SEMVMX}. \end{errlist} ed inoltre \errval{EFAULT} ed \errval{EINVAL}. @@ -2336,14 +2340,14 @@ ed il suo prototipo \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 in caso di errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{ENOSPC}] Si è superato il limite (\const{SHMMNI}) sul numero + \item[\errcode{ENOSPC}] si è superato il limite (\const{SHMMNI}) sul numero di segmenti di memoria nel sistema, o cercato di allocare un segmento le cui dimensioni fanno superare il limite di sistema (\const{SHMALL}) per la memoria ad essi riservata. - \item[\errcode{EINVAL}] Si è richiesta una dimensione per un nuovo segmento + \item[\errcode{EINVAL}] si è richiesta una dimensione per un nuovo segmento maggiore di \const{SHMMAX} o minore di \const{SHMMIN}, o se il segmento già esiste \param{size} è maggiore delle sue dimensioni. - \item[\errcode{ENOMEM}] Il sistema non ha abbastanza memoria per poter + \item[\errcode{ENOMEM}] il sistema non ha abbastanza memoria per poter contenere le strutture per un nuovo segmento di memoria condivisa. \end{errlist} ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, @@ -2430,23 +2434,25 @@ che permettono di cambiarne il valore. & \textbf{Significato} \\ \hline \hline - \const{SHMALL}& 0x200000&\file{shmall}& Numero massimo di pagine che - possono essere usate per i segmenti di - memoria condivisa. \\ - \const{SHMMAX}&0x2000000&\file{shmmax}& Dimensione massima di un segmento - di memoria condivisa.\\ - \const{SHMMNI}& 4096&\file{msgmni}& Numero massimo di segmenti di - memoria condivisa presenti nel - kernel.\\ + \const{SHMALL}& 0x200000&\procrelfile{/proc/sys/kernel}{shmall} + & Numero massimo di pagine che + possono essere usate per i segmenti di + memoria condivisa.\\ + \const{SHMMAX}&0x2000000&\procrelfile{/proc/sys/kernel}{shmmax} + & Dimensione massima di un segmento di memoria + condivisa.\\ + \const{SHMMNI}& 4096&\procrelfile{/proc/sys/kernel}{msgmni} + & Numero massimo di segmenti di memoria condivisa + presenti nel kernel.\\ \const{SHMMIN}& 1& --- & Dimensione minima di un segmento di - memoria condivisa. \\ + memoria condivisa.\\ \const{SHMLBA}&\const{PAGE\_SIZE}&--- & Limite inferiore per le dimensioni minime di un segmento (deve essere allineato alle dimensioni di una - pagina di memoria). \\ + pagina di memoria).\\ \const{SHMSEG}& --- & --- & Numero massimo di segmenti di - memoria condivisa - per ciascun processo.\\ + memoria condivisa per ciascun + processo.\\ \hline @@ -2470,18 +2476,18 @@ un segmento di memoria condivisa \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{EACCES}] Si è richiesto \const{IPC\_STAT} ma i permessi non + \item[\errcode{EACCES}] si è richiesto \const{IPC\_STAT} ma i permessi non consentono l'accesso in lettura al segmento. - \item[\errcode{EINVAL}] O \param{shmid} non è un identificatore valido o + \item[\errcode{EINVAL}] o \param{shmid} non è un identificatore valido o \param{cmd} non è un comando valido. - \item[\errcode{EIDRM}] L'argomento \param{shmid} fa riferimento ad un + \item[\errcode{EIDRM}] l'argomento \param{shmid} fa riferimento ad un segmento che è stato cancellato. - \item[\errcode{EPERM}] Si è specificato un comando con \const{IPC\_SET} o + \item[\errcode{EPERM}] si è specificato un comando con \const{IPC\_SET} o \const{IPC\_RMID} senza i permessi necessari. - \item[\errcode{EOVERFLOW}] Si è tentato il comando \const{IPC\_STAT} ma il + \item[\errcode{EOVERFLOW}] si è tentato il comando \const{IPC\_STAT} ma il valore del group-ID o dell'user-ID è troppo grande per essere memorizzato nella struttura puntata da \param{buf}. - \item[\errcode{EFAULT}] L'indirizzo specificato con \param{buf} non è + \item[\errcode{EFAULT}] l'indirizzo specificato con \param{buf} non è valido. \end{errlist} } @@ -2542,9 +2548,9 @@ il suo prototipo successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{EACCES}] Il processo non ha i privilegi per accedere al + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al segmento nella modalità richiesta. - \item[\errcode{EINVAL}] Si è specificato un identificatore invalido per + \item[\errcode{EINVAL}] si è specificato un identificatore invalido per \param{shmid}, o un indirizzo non allineato sul confine di una pagina per \param{shmaddr}. \end{errlist} @@ -2569,9 +2575,9 @@ stato marcato per la cancellazione. \label{fig:ipc_shmem_layout} \end{figure} -L'argomento \param{shmaddr} specifica a quale indirizzo\footnote{Lo standard +L'argomento \param{shmaddr} specifica a quale indirizzo\footnote{lo standard SVID prevede che l'argomento \param{shmaddr} sia di tipo \ctyp{char *}, così - come il valore di ritorno della funzione. In Linux è stato così con le + come il valore di ritorno della funzione; in Linux è stato così con le \acr{libc4} e le \acr{libc5}, con il passaggio alle \acr{glibc} il tipo di \param{shmaddr} è divenuto un \ctyp{const void *} e quello del valore di ritorno un \ctyp{void *}.} deve essere associato il segmento, se il valore @@ -2601,12 +2607,12 @@ indirizzo come arrotondamento, in Linux L'uso di \const{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 \const{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. +caso un tentativo di scrivere sul segmento comporterà una +\itindex{segment~violation} violazione di accesso con l'emissione di un +segnale di \const{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 \struct{shmid\_ds}: @@ -2898,13 +2904,14 @@ il mutex, prima di uscire. Verifichiamo allora il funzionamento dei nostri programmi; al solito, usando le funzioni di libreria occorre definire opportunamente \code{LD\_LIBRARY\_PATH}; poi si potrà lanciare il server con: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./dirmonitor ./ -\end{verbatim}%$ +\end{Verbatim} +%$ ed avendo usato \func{daemon} il comando ritornerà immediatamente. Una volta che il server è in esecuzione, possiamo passare ad invocare il client per verificarne i risultati, in tal caso otterremo: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./readmon Ci sono 68 file dati Ci sono 3 directory @@ -2914,12 +2921,13 @@ Ci sono 0 socket Ci sono 0 device a caratteri Ci sono 0 device a blocchi Totale 71 file, per 489831 byte -\end{verbatim}%$ +\end{Verbatim} +%$ ed un rapido calcolo (ad esempio con \code{ls -a | wc} per contare i file) ci permette di verificare che il totale dei file è giusto. Un controllo con \cmd{ipcs} ci permette inoltre di verificare la presenza di un segmento di memoria condivisa e di un semaforo: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ipcs ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status @@ -2931,12 +2939,13 @@ key semid owner perms nsems ------ Message Queues -------- key msqid owner perms used-bytes messages -\end{verbatim}%$ +\end{Verbatim} +%$ Se a questo punto aggiungiamo un file, ad esempio con \code{touch prova}, potremo verificare che, passati nel peggiore dei casi almeno 10 secondi (o l'eventuale altro intervallo impostato per la rilettura dei dati) avremo: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./readmon Ci sono 69 file dati Ci sono 3 directory @@ -2946,18 +2955,20 @@ Ci sono 0 socket Ci sono 0 device a caratteri Ci sono 0 device a blocchi Totale 72 file, per 489887 byte -\end{verbatim}%$ +\end{Verbatim} +%$ A questo punto possiamo far uscire il server inviandogli un segnale di \const{SIGTERM} con il comando \code{killall dirmonitor}, a questo punto ripetendo la lettura, otterremo un errore: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ./readmon Cannot find shared memory: No such file or directory -\end{verbatim}%$ +\end{Verbatim} +%$ e inoltre potremo anche verificare che anche gli oggetti di intercomunicazione visti in precedenza sono stati regolarmente cancellati: -\begin{verbatim} +\begin{Verbatim} [piccardi@gont sources]$ ipcs ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status @@ -2967,8 +2978,8 @@ key semid owner perms nsems ------ Message Queues -------- key msqid owner perms used-bytes messages -\end{verbatim}%$ - +\end{Verbatim} +%$ %% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta @@ -3236,6 +3247,7 @@ pi sez.~\ref{sec:ipc_sysv_shm} che possa restituisca i risultati via rete. \itindend{memory~mapping} +% TODO fare esempio di mmap anonima \section{Il sistema di comunicazione fra processi di POSIX} \label{sec:ipc_posix} @@ -3250,31 +3262,31 @@ una interfaccia completamente nuova, che tratteremo in questa sezione. \subsection{Considerazioni generali} \label{sec:ipc_posix_generic} -In Linux non tutti gli oggetti del POSIX IPC sono pienamente supportati nel -kernel ufficiale; solo la memoria condivisa è presente con l'interfaccia -completa, ma solo a partire dal kernel 2.4.x, i semafori sono forniti dalle -\acr{glibc} nella sezione che implementa i thread POSIX, le code di messaggi -non hanno alcun tipo di supporto ufficiale. Per queste ultime esistono -tuttavia dei patch e una libreria aggiuntiva. +Oggi Linux supporta tutti gli oggetti definito nello standard POSIX per l'IPC, +ma a lungo non è stato così; la memoria condivisa è presente a partire dal +kernel 2.4.x, i semafori sono forniti dalle \acr{glibc} nella sezione che +implementa i \itindex{thread} \textit{thread} POSIX di nuova generazione che +richiedono il kernel 2.6, le code di messaggi sono supportate a partire dal +kernel 2.6.6. La caratteristica fondamentale dell'interfaccia POSIX è l'abbandono dell'uso degli identificatori e delle chiavi visti nel SysV IPC, per passare ai -\textit{POSIX IPC names}\itindex{POSIX~IPC~names}, che sono sostanzialmente +\itindex{POSIX~IPC~names} \textit{POSIX IPC names}, che sono sostanzialmente equivalenti ai nomi dei file. Tutte le funzioni che creano un oggetto di IPC POSIX prendono come primo argomento una stringa che indica uno di questi nomi; lo standard è molto generico riguardo l'implementazione, ed i nomi stessi possono avere o meno una corrispondenza sul filesystem; tutto quello che è richiesto è che: -\begin{itemize} +\begin{itemize*} \item i nomi devono essere conformi alle regole che caratterizzano i - \itindex{pathname}\textit{pathname}, in particolare non essere più lunghi di + \itindex{pathname} \textit{pathname}, in particolare non essere più lunghi di \const{PATH\_MAX} byte e terminati da un carattere nullo. \item se il nome inizia per una \texttt{/} chiamate differenti allo stesso nome fanno riferimento allo stesso oggetto, altrimenti l'interpretazione del nome dipende dall'implementazione. \item l'interpretazione di ulteriori \texttt{/} presenti nel nome dipende dall'implementazione. -\end{itemize} +\end{itemize*} Data la assoluta genericità delle specifiche, il comportamento delle funzioni è subordinato in maniera quasi completa alla relativa @@ -3287,64 +3299,63 @@ directory (rispettivamente \file{/dev/shm} e \file{/dev/mqueue}, per i dettagli si faccia riferimento a sez.~\ref{sec:ipc_posix_shm}, sez.~\ref{sec:ipc_posix_sem} e sez.~\ref{sec:ipc_posix_mq}) ed i nomi specificati nelle relative funzioni sono considerati come un -\itindsub{pathname}{assoluto}\textit{pathname} assoluto (comprendente +\itindsub{pathname}{assoluto} \textit{pathname} assoluto (comprendente eventuali sottodirectory) rispetto a queste radici. Il vantaggio degli oggetti di IPC POSIX è comunque che essi vengono inseriti nell'albero dei file, e possono essere maneggiati con le usuali funzioni e comandi di accesso ai file,\footnote{questo è vero nel caso di Linux, che usa una implementazione che lo consente, non è detto che altrettanto valga per - altri kernel; in particolare sia la memoria condivisa che per le code di - messaggi, come si può facilmente evincere con uno \cmd{strace}, le system - call utilizzate sono le stesse, in quanto detti oggetti sono realizzati con - dei file in speciali filesystem.} che funzionano come su dei file normali. + altri kernel; in particolare, come si può facilmente verificare con uno + \cmd{strace}, sia per la memoria condivisa che per le code di messaggi le + system call utilizzate da Linux sono le stesse di quelle dei file, essendo + detti oggetti realizzati come tali in appositi filesystem.} che funzionano +come su dei file normali. In particolare i permessi associati agli oggetti di IPC POSIX sono identici ai -permessi dei file, e il controllo di accesso segue esattamente la stessa -semantica (quella illustrata in sez.~\ref{sec:file_access_control}), invece di +permessi dei file, ed il controllo di accesso segue esattamente la stessa +semantica (quella illustrata in sez.~\ref{sec:file_access_control}), e non quella particolare (si ricordi quanto visto in -sez.~\ref{sec:ipc_sysv_access_control}) usata per gli oggetti del SysV IPC. -Per quanto riguarda l'attribuzione dell'utente e del gruppo proprietari -dell'oggetto alla creazione di quest'ultimo essa viene effettuata secondo la -semantica SysV; essi corrispondono cioè a userid e groupid effettivi del -processo che esegue la creazione. - +sez.~\ref{sec:ipc_sysv_access_control}) che viene usata per gli oggetti del +SysV IPC. Per quanto riguarda l'attribuzione dell'utente e del gruppo +proprietari dell'oggetto alla creazione di quest'ultimo essa viene effettuata +secondo la semantica SysV: corrispondono cioè a user-ID e group-ID effettivi +del processo che esegue la creazione. \subsection{Code di messaggi} \label{sec:ipc_posix_mq} Le code di messaggi POSIX sono supportate da Linux a partire dalla versione -2.6.6-rc1 del kerne;, \footnote{l'implementazione è dovuta a Michal Wronski e +2.6.6-rc1 del kernel,\footnote{l'implementazione è dovuta a Michal Wronski e Krzysztof Benedyczak, e le relative informazioni si possono trovare su \href{http://www.geocities.com/wronski12/posix_ipc/index.html} - {\texttt{http://www.geocities.com/wronski12/posix\_ipc/index.html}}.} In + {\textsf{http://www.geocities.com/wronski12/posix\_ipc/index.html}}.} In generale, come le corrispettive del SysV IPC, le code di messaggi sono poco usate, dato che i socket, nei casi in cui sono sufficienti, sono più comodi, e che in casi più complessi la comunicazione può essere gestita direttamente con -mutex e memoria condivisa con tutta la flessibilità che occorre. +mutex (o semafori) e memoria condivisa con tutta la flessibilità che occorre. Per poter utilizzare le code di messaggi, oltre ad utilizzare un kernel -superiore al 2.6.6 (o precedente, purché cui siano stati opportunamente -applicati i relativi patch) occorre utilizzare la libreria -\file{libmqueue}\footnote{i programmi che usano le code di messaggi cioè - devono essere compilati aggiungendo l'opzione \code{-lmqueue} al comando - \cmd{gcc}, dato che le funzioni non fanno parte della libreria standard, in - corrispondenza all'inclusione del supporto nel kernel ufficiale, anche le - relative funzioni sono state inserite nelle \acr{glibc}, e presenti a - partire dalla versione 2.3.4 delle medesime.} che contiene le funzioni -dell'interfaccia POSIX.\footnote{in realtà l'implementazione è realizzata - tramite delle speciali chiamate ad \func{ioctl} sui file del filesystem - speciale su cui vengono mantenuti questi oggetti di IPC.} +superiore al 2.6.6 (o precedente, se sono stati opportunamente applicati i +relativi patch) occorre utilizzare la libreria \file{libmqueue}\footnote{i + programmi che usano le code di messaggi cioè devono essere compilati + aggiungendo l'opzione \code{-lmqueue} al comando \cmd{gcc}; in + corrispondenza all'inclusione del supporto nel kernel ufficiale anche + \file{libmqueue} è stata inserita nelle \acr{glibc}, a partire dalla + versione 2.3.4 delle medesime.} che contiene le funzioni dell'interfaccia +POSIX.\footnote{in realtà l'implementazione è realizzata tramite delle + opportune chiamate ad \func{ioctl} sui file del filesystem speciale su cui + vengono mantenuti questi oggetti di IPC.} La libreria inoltre richiede la presenza dell'apposito filesystem di tipo \texttt{mqueue} montato su \file{/dev/mqueue}; questo può essere fatto -aggiungendo ad \file{/etc/fstab} una riga come: +aggiungendo ad \conffile{/etc/fstab} una riga come: \begin{verbatim} mqueue /dev/mqueue mqueue defaults 0 0 \end{verbatim} ed esso sarà utilizzato come radice sulla quale vengono risolti i nomi delle -code di messaggi che iniziano con una \texttt{/}. Le opzioni di mount +code di messaggi che iniziano con una ``\texttt{/}''. Le opzioni di mount accettate sono \texttt{uid}, \texttt{gid} e \texttt{mode} che permettono rispettivamente di impostare l'utente, il gruppo ed i permessi associati al filesystem. @@ -3363,37 +3374,42 @@ di messaggi POSIX Apre una coda di messaggi POSIX impostandone le caratteristiche. \bodydesc{La funzione restituisce il descrittore associato alla coda in caso - di successo e -1 in caso di errore; nel quel caso \var{errno} assumerà i + di successo e -1 per un errore; nel quel caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{EACCES}] Il processo non ha i privilegi per accedere al + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al alla memoria secondo quanto specificato da \param{oflag}. - \item[\errcode{EEXIST}] Si è specificato \const{O\_CREAT} e + \item[\errcode{EEXIST}] si è specificato \const{O\_CREAT} e \const{O\_EXCL} ma la coda già esiste. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Il file non supporta la funzione, o si è + \item[\errcode{EINVAL}] il file non supporta la funzione, o si è specificato \const{O\_CREAT} con una valore non nullo di \param{attr} e valori non validi di \var{mq\_maxmsg} e \var{mq\_msgsize}. - \item[\errcode{ENOENT}] Non si è specificato \const{O\_CREAT} ma la coda + \item[\errcode{ENOENT}] non si è specificato \const{O\_CREAT} ma la coda non esiste. \end{errlist} ed inoltre \errval{ENOMEM}, \errval{ENOSPC}, \errval{EFAULT}, - \errval{EMFILE} ed \errval{ENFILE}.} + \errval{EMFILE}, \errval{EINTR} ed \errval{ENFILE}. +} \end{functions} La funzione apre la coda di messaggi identificata dall'argomento \param{name} restituendo il descrittore ad essa associato, del tutto analogo ad un file descriptor, con l'unica differenza che lo standard prevede un apposito tipo -\type{mqd\_t}.\footnote{nella implementazione citata questo è definito come - \ctyp{int}.} Se la coda esiste già il descrittore farà riferimento allo -stesso oggetto, consentendo così la comunicazione fra due processi diversi. +\type{mqd\_t}.\footnote{nel caso di Linux si tratta in effetti proprio di un + normale file descriptor; pertanto, anche se questo comportamento non è + portabile, lo si può tenere sotto osservazione con le funzioni dell'I/O + multiplexing (vedi sez.~\ref{sec:file_multiplexing}) come possibile + alternativa all'uso dell'interfaccia di notifica di \func{mq\_notify} (che + vedremo a breve).} Se la coda esiste già il descrittore farà riferimento +allo stesso oggetto, consentendo così la comunicazione fra due processi +diversi. La funzione è del tutto analoga ad \func{open} ed analoghi sono i valori che possono essere specificati per \param{oflag}, che deve essere specificato come maschera binaria; i valori possibili per i vari bit sono quelli visti in tab.~\ref{tab:file_open_flags} dei quali però \func{mq\_open} riconosce solo i seguenti: -\begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}} +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} \item[\const{O\_RDONLY}] Apre la coda solo per la ricezione di messaggi. Il processo potrà usare il descrittore con \func{mq\_receive} ma non con \func{mq\_send}. @@ -3421,13 +3437,14 @@ per i file normali. Se la coda non esiste e la si vuole creare si deve specificare \const{O\_CREAT}, in tal caso occorre anche specificare i permessi di -creazione con l'argomento \param{mode}; i valori di quest'ultimo sono identici -a quelli usati per \func{open}, anche se per le code di messaggi han senso -solo i permessi di lettura e scrittura. Oltre ai permessi di creazione possono -essere specificati anche gli attributi specifici della coda tramite -l'argomento \param{attr}; quest'ultimo è un puntatore ad una apposita -struttura \struct{mq\_attr}, la cui definizione è riportata in -fig.~\ref{fig:ipc_mq_attr}. +creazione con l'argomento \param{mode};\footnote{fino al 2.6.14 per un bug i + valori della \textit{umask} del processo non venivano applicati a questi + permessi.} i valori di quest'ultimo sono identici a quelli usati per +\func{open}, anche se per le code di messaggi han senso solo i permessi di +lettura e scrittura. Oltre ai permessi di creazione possono essere specificati +anche gli attributi specifici della coda tramite l'argomento \param{attr}; +quest'ultimo è un puntatore ad una apposita struttura \struct{mq\_attr}, la +cui definizione è riportata in fig.~\ref{fig:ipc_mq_attr}. \begin{figure}[!htb] \footnotesize \centering @@ -3440,14 +3457,14 @@ fig.~\ref{fig:ipc_mq_attr}. \label{fig:ipc_mq_attr} \end{figure} -Per ls creazione della coda i campi della struttura che devono essere -specificati sono \var{mq\_msgsize} e \var{mq\_maxmsg}, che indicano -rispettivamente la dimensione massima di un messaggio ed il numero massimo di -messaggi che essa può contenere. Il valore dovrà essere positivo e minore dei -rispettivi limiti di sistema \const{MQ\_MAXMSG} e \const{MQ\_MSGSIZE}, -altrimenti la funzione fallirà con un errore di \errcode{EINVAL}. Qualora si -specifichi per \param{attr} un puntatore nullo gli attributi della coda -saranno impostati ai valori predefiniti. +Per la creazione della coda i campi della struttura che devono essere +specificati sono \var{mq\_maxmsg} e \var{mq\_msgsize}, che indicano +rispettivamente il numero massimo di messaggi che può contenere e la +dimensione massima di un messaggio. Il valore dovrà essere positivo e minore +dei rispettivi limiti di sistema \const{MQ\_MAXMSG} e \const{MQ\_MSGSIZE}, +altrimenti la funzione fallirà con un errore di \errcode{EINVAL}. +Se \param{attr} è un puntatore nullo gli attributi della coda saranno +impostati ai valori predefiniti. Quando l'accesso alla coda non è più necessario si può chiudere il relativo descrittore con la funzione \funcd{mq\_close}, il cui prototipo è: @@ -3456,8 +3473,8 @@ descrittore con la funzione \funcd{mq\_close}, il cui prototipo Chiude la coda \param{mqdes}. -\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore; nel quel caso \var{errno} assumerà i valori \errval{EBADF} o +\bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore; + nel quel caso \var{errno} assumerà i valori \errval{EBADF} o \errval{EINTR}.} \end{prototype} @@ -3497,15 +3514,13 @@ coda rester di essa. Allo stesso modo una coda ed i suoi contenuti resteranno disponibili all'interno del sistema anche quando quest'ultima non è aperta da nessun processo (questa è una delle differenze più rilevanti nei confronti di pipe e -fifo). - -La sola differenza fra code di messaggi POSIX e file normali è che, essendo il -filesystem delle code di messaggi virtuale e basato su oggetti interni al -kernel, il suo contenuto viene perduto con il riavvio del sistema. +fifo). La sola differenza fra code di messaggi POSIX e file normali è che, +essendo il filesystem delle code di messaggi virtuale e basato su oggetti +interni al kernel, il suo contenuto viene perduto con il riavvio del sistema. -Come accennato in precedenza ad ogni coda di messaggi è associata una -struttura \struct{mq\_attr}, che può essere letta e modificata attraverso le -due funzioni \funcd{mq\_getattr} e \funcd{mq\_setattr}, i cui prototipi sono: +Come accennato ad ogni coda di messaggi è associata una struttura +\struct{mq\_attr}, che può essere letta e modificata attraverso le due +funzioni \funcd{mq\_getattr} e \funcd{mq\_setattr}, i cui prototipi sono: \begin{functions} \headdecl{mqueue.h} @@ -3554,22 +3569,20 @@ Per inserire messaggi su di una coda sono previste due funzioni, \param{abs\_timeout}. - \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso di + \bodydesc{Le funzioni restituiscono 0 in caso di successo e $-1$ per un errore; nel quel caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{EAGAIN}] Si è aperta la coda con \const{O\_NONBLOCK}, e la + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la coda è piena. - \item[\errcode{EMSGSIZE}] La lunghezza del messaggio \param{msg\_len} + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio \param{msg\_len} eccede il limite impostato per la coda. - \item[\errcode{ENOMEM}] Il kernel non ha memoria sufficiente. Questo - errore può avvenire quando l'inserimento del messaggio - \item[\errcode{EINVAL}] Si è specificato un valore nullo per + \item[\errcode{EINVAL}] si è specificato un valore nullo per \param{msg\_len}, o un valore di \param{msg\_prio} fuori dai limiti, o un valore non valido per \param{abs\_timeout}. - \item[\errcode{ETIMEDOUT}] L'inserimento del messaggio non è stato + \item[\errcode{ETIMEDOUT}] l'inserimento del messaggio non è stato effettuato entro il tempo stabilito. \end{errlist} - ed inoltre \errval{EBADF} ed \errval{EINTR}.} + ed inoltre \errval{EBADF}, \errval{ENOMEM} ed \errval{EINTR}.} \end{functions} Entrambe le funzioni richiedono un puntatore al testo del messaggio @@ -3585,11 +3598,16 @@ valore della priorit \const{MQ\_PRIO\_MAX}, che nel caso è pari a 32768. Qualora la coda sia piena, entrambe le funzioni si bloccano, a meno che non -sia stata selezionata in fase di apertura la modalità non bloccante, nel qual -caso entrambe ritornano \errcode{EAGAIN}. La sola differenza fra le due -funzioni è che la seconda, passato il tempo massimo impostato con l'argomento -\param{abs\_timeout}, ritorna comunque con un errore di \errcode{ETIMEDOUT}. - +sia stata selezionata in fase di apertura la modalità non +bloccante,\footnote{o si sia impostato il flag \const{O\_NONBLOCK} sul file + descriptor della coda.} nel qual caso entrambe ritornano \errcode{EAGAIN}. +La sola differenza fra le due funzioni è che la seconda, passato il tempo +massimo impostato con l'argomento \param{abs\_timeout},\footnote{deve essere + specificato un tempo assoluto tramite una struttura \struct{timespec} (vedi + fig.~\ref{fig:sys_timespec_struct}) indicato in numero di secondi e + nanosecondi a partire dal 1 gennaio 1970.} ritorna comunque con un errore di +\errcode{ETIMEDOUT}, se invece il tempo è già scaduto al momento della +chiamata e la coda è vuota la funzione ritorna immediatamente. Come per l'inserimento, anche per l'estrazione dei messaggi da una coda sono previste due funzioni, \funcd{mq\_receive} e \funcd{mq\_timedreceive}, i cui @@ -3610,13 +3628,13 @@ prototipi sono: di successo e -1 in caso di errore; nel quel caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{EAGAIN}] Si è aperta la coda con \const{O\_NONBLOCK}, e la + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la coda è vuota. - \item[\errcode{EMSGSIZE}] La lunghezza del messaggio sulla coda eccede il + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio sulla coda eccede il valore \param{msg\_len} specificato per la ricezione. - \item[\errcode{EINVAL}] Si è specificato un valore nullo per + \item[\errcode{EINVAL}] si è specificato un valore nullo per \param{msg\_ptr}, o un valore non valido per \param{abs\_timeout}. - \item[\errcode{ETIMEDOUT}] La ricezione del messaggio non è stata + \item[\errcode{ETIMEDOUT}] la ricezione del messaggio non è stata effettuata entro il tempo stabilito. \end{errlist} ed inoltre \errval{EBADF}, \errval{EINTR}, \errval{ENOMEM}, o @@ -3626,7 +3644,11 @@ prototipi sono: La funzione estrae dalla coda il messaggio a priorità più alta, o il più vecchio fra quelli della stessa priorità. Una volta ricevuto il messaggio viene tolto dalla coda e la sua dimensione viene restituita come valore di -ritorno. +ritorno.\footnote{si tenga presente che 0 è una dimensione valida e che la + condizione di errore è restituita dal valore -1; Stevens in \cite{UNP2} fa + notare che questo è uno dei casi in cui vale ciò che lo standard + \textsl{non} dice, una dimensione nulla infatti, pur non essendo citata, non + viene proibita.} Se la dimensione specificata da \param{msg\_len} non è sufficiente a contenere il messaggio, entrambe le funzioni, al contrario di quanto avveniva nelle code @@ -3645,10 +3667,8 @@ Qualora non interessi usare la priorit Si noti che con le code di messaggi POSIX non si ha la possibilità di selezionare quale messaggio estrarre con delle condizioni sulla priorità, a differenza di quanto avveniva con le code di messaggi di SysV che permettono -invece la selezione in base al valore del campo \var{mtype}. Qualora non -interessi usare la priorità dei messaggi si +invece la selezione in base al valore del campo \var{mtype}. -% TODO vericare questa interruzione di paragrafo % TODO inserire i dati di /proc/sys/fs/mqueue Qualora la coda sia vuota entrambe le funzioni si bloccano, a meno che non si @@ -3678,8 +3698,8 @@ Attiva il meccanismo di notifica per la coda \param{mqdes}. \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di errore; nel quel caso \var{errno} assumerà i valori: \begin{errlist} - \item[\errcode{EBUSY}] C'è già un processo registrato per la notifica. - \item[\errcode{EBADF}] Il descrittore non fa riferimento ad una coda di + \item[\errcode{EBUSY}] c'è già un processo registrato per la notifica. + \item[\errcode{EBADF}] il descrittore non fa riferimento ad una coda di messaggi. \end{errlist}} \end{prototype} @@ -3692,32 +3712,36 @@ processo alla volta per ciascuna coda. Il comportamento di \func{mq\_notify} dipende dal valore dell'argomento \param{notification}, che è un puntatore ad una apposita struttura -\struct{sigevent}, (definita in fig.~\ref{fig:file_sigevent}) introdotta dallo -standard POSIX.1b per gestire la notifica di eventi; per altri dettagli si può -vedere quanto detto in sez.~\ref{sec:file_asyncronous_io} a proposito dell'uso -della stessa struttura per l'invio dei segnali usati per l'I/O asincrono. +\struct{sigevent}, (definita in fig.~\ref{fig:struct_sigevent}) introdotta +dallo standard POSIX.1b per gestire la notifica di eventi; per altri dettagli +si può vedere quanto detto in sez.~\ref{sec:sig_timer_adv} a proposito +dell'uso della stessa struttura per la notifica delle scadenze dei +\textit{timer}. Attraverso questa struttura si possono impostare le modalità con cui viene -effettuata la notifica; in particolare il campo \var{sigev\_notify} deve -essere posto a \const{SIGEV\_SIGNAL}\footnote{il meccanismo di notifica basato - sui thread, specificato tramite il valore \const{SIGEV\_THREAD}, non è - implementato.} ed il campo \var{sigev\_signo} deve indicare il valore del -segnale che sarà inviato al processo. Inoltre il campo \var{sigev\_value} è il -puntatore ad una struttura \struct{sigval\_t} (definita in -fig.~\ref{fig:sig_sigval}) che permette di restituire al gestore del segnale un -valore numerico o un indirizzo,\footnote{per il suo uso si riveda la +effettuata la notifica nel campo \var{sigev\_notify}, che può assumere i +valori di tab.~\ref{tab:sigevent_sigev_notify}.\footnote{la pagina di manuale + riporta soltanto i primi tre (inizialmente era possibile solo + \const{SIGEV\_SIGNAL}).} Il metodo consigliato è quello di usare +\const{SIGEV\_SIGNAL} usando il campo \var{sigev\_signo} per indicare il quale +segnale deve essere inviato al processo. Inoltre il campo \var{sigev\_value} è +un puntatore ad una struttura \struct{sigval\_t} (definita in +fig.~\ref{fig:sig_sigval}) che permette di restituire al gestore del segnale +un valore numerico o un indirizzo,\footnote{per il suo uso si riveda la trattazione fatta in sez.~\ref{sec:sig_real_time} a proposito dei segnali - real-time.} posto che questo sia installato nella forma estesa vista in -sez.~\ref{sec:sig_sigaction}. + \textit{real-time}.} posto che questo sia installato nella forma estesa +vista in sez.~\ref{sec:sig_sigaction}. La funzione registra il processo chiamante per la notifica se \param{notification} punta ad una struttura \struct{sigevent} opportunamente inizializzata, o cancella una precedente registrazione se è \val{NULL}. Dato che un solo processo alla volta può essere registrato, la funzione fallisce -con \errcode{EBUSY} se c'è un altro processo già registrato. Si tenga -presente inoltre che alla chiusura del descrittore associato alla coda (e -quindi anche all'uscita del processo) ogni eventuale registrazione di notifica -presente viene cancellata. +con \errcode{EBUSY} se c'è un altro processo già registrato.\footnote{questo + significa anche che se si registra una notifica con \const{SIGEV\_NONE} il + processo non la riceverà, ma impedirà anche che altri possano registrarsi + per poterlo fare.} Si tenga presente inoltre che alla chiusura del +descrittore associato alla coda (e quindi anche all'uscita del processo) ogni +eventuale registrazione di notifica presente viene cancellata. La notifica del segnale avviene all'arrivo di un messaggio in una coda vuota (cioè solo se sulla coda non ci sono messaggi) e se non c'è nessun processo @@ -3734,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 @@ -3748,71 +3772,29 @@ all'userid effettivo, \var{si\_code} a \const{SI\_MESGQ}, e \var{si\_errno} a 0. Questo ci dice che, se si effettua la ricezione dei messaggi usando esclusivamente il meccanismo di notifica, è possibile ottenere le informazioni sul processo che ha inserito un messaggio usando un gestore per il segnale in -forma estesa\footnote{di nuovo si faccia riferimento a quanto detto al +forma estesa.\footnote{di nuovo si faccia riferimento a quanto detto al proposito in sez.~\ref{sec:sig_sigaction} e sez.~\ref{sec:sig_real_time}.} -\subsection{Semafori} -\label{sec:ipc_posix_sem} - -Dei semafori POSIX esistono sostanzialmente due implementazioni; una è fatta a -livello di libreria ed è fornita dalla libreria dei thread; questa però li -implementa solo a livello di thread e non di processi.\footnote{questo - significa che i semafori sono visibili solo all'interno dei thread creati da - un singolo processo, e non possono essere usati come meccanismo di - sincronizzazione fra processi diversi.} Esiste però anche una libreria -realizzata da Konstantin Knizhnik, che reimplementa l'interfaccia POSIX usando -i semafori di SysV IPC, e che non vale comunque la pena di usare visto che i -problemi sottolineati in sez.~\ref{sec:ipc_sysv_sem} rimangono, anche se -mascherati. - -In realtà a partire dal kernel 2.5.7 è stato introdotto un meccanismo di -sincronizzazione completamente nuovo, basato sui cosiddetti -\textit{futex}\footnote{la sigla sta per \textit{fast user mode mutex}.}, con -il quale è stato possibile implementare una versione nativa dei semafori; esso -è già stato usato con successo per reimplementare in maniera più efficiente -tutte le direttive di sincronizzazione previste per i thread POSIX; e con il -kernel della serie 2.6 e le nuove versioni delle \acr{glibc} che usano quella -che viene chiamata la \textit{New Posix Thread Library} sono state -implementate tutte le funzioni occorrenti. - -Anche in questo caso (come per le code di messaggi) è necessario appoggiarsi -alla libreria per le estensioni \textit{real-time} \texttt{librt}, questo -significa che se si vuole utilizzare questa interfaccia, oltre ad utilizzare -gli opportuni file di definizione, occorrerà compilare i programmi con -l'opzione \texttt{-lrt}. - -% TODO trattare l'argomento a partire da man sem_overview. - - - \subsection{Memoria condivisa} \label{sec:ipc_posix_shm} La memoria condivisa è stato il primo degli oggetti di IPC POSIX inserito nel kernel ufficiale; il supporto a questo tipo di oggetti è realizzato attraverso il filesystem \texttt{tmpfs}, uno speciale filesystem che mantiene tutti i -suoi contenuti in memoria,\footnote{il filesystem \texttt{tmpfs} è diverso da - un normale RAM disk, anch'esso disponibile attraverso il filesystem - \texttt{ramfs}, proprio perché realizza una interfaccia utilizzabile anche - per la memoria condivisa; esso infatti non ha dimensione fissa, ed usa - direttamente la cache interna del kernel (che viene usata anche per la - shared memory in stile SysV). In più i suoi contenuti, essendo trattati - direttamente dalla memoria virtuale\index{memoria~virtuale} possono essere - salvati sullo swap automaticamente.} che viene attivato abilitando l'opzione +suoi contenuti in memoria, che viene attivato abilitando l'opzione \texttt{CONFIG\_TMPFS} in fase di compilazione del kernel. - -Per potere utilizzare l'interfaccia POSIX per le code di messaggi le +Per potere utilizzare l'interfaccia POSIX per la memoria condivisa le \acr{glibc}\footnote{le funzioni sono state introdotte con le glibc-2.2.} richiedono di compilare i programmi con l'opzione \code{-lrt}; inoltre è necessario che in \file{/dev/shm} sia montato un filesystem \texttt{tmpfs}; -questo di norma viene eseguita aggiungendo una riga tipo: +questo di norma viene fatto aggiungendo una riga del tipo di: \begin{verbatim} tmpfs /dev/shm tmpfs defaults 0 0 \end{verbatim} -ad \file{/etc/fstab}. In realtà si può montare un filesystem \texttt{tmpfs} +ad \conffile{/etc/fstab}. In realtà si può montare un filesystem \texttt{tmpfs} dove si vuole, per usarlo come RAM disk, con un comando del tipo: \begin{verbatim} mount -t tmpfs -o size=128M,nr_inodes=10k,mode=700 tmpfs /mytmpfs @@ -3827,24 +3809,29 @@ questo caso La funzione che permette di aprire un segmento di memoria condivisa POSIX, ed eventualmente di crearlo se non esiste ancora, è \funcd{shm\_open}; il suo prototipo è: -\begin{prototype}{mqueue.h} -{int shm\_open(const char *name, int oflag, mode\_t mode)} +\begin{functions} + \headdecl{sys/mman.h} + \headdecl{sys/stat.h} + \headdecl{fcntl.h} + + \funcdecl{int shm\_open(const char *name, int oflag, mode\_t mode)} -Apre un segmento di memoria condivisa. + Apre un segmento di memoria condivisa. -\bodydesc{La funzione restituisce un file descriptor positivo in caso di - successo e -1 in caso di errore; nel quel caso \var{errno} assumerà gli - stessi valori riportati da \func{open}.} -\end{prototype} + \bodydesc{La funzione restituisce un file descriptor positivo in caso di + successo e -1 in caso di errore; nel quel caso \var{errno} assumerà gli + stessi valori riportati da \func{open}.} +\end{functions} La funzione apre un segmento di memoria condivisa identificato dal nome -\param{name}. Come già spiegato in sez.~\ref{sec:ipc_posix_generic} questo nome -può essere specificato in forma standard solo facendolo iniziare per \file{/} -e senza ulteriori \file{/}, Linux supporta comunque nomi generici, che -verranno interpretati prendendo come radice \file{/dev/shm}.\footnote{occorre - pertanto evitare di specificare qualcosa del tipo \file{/dev/shm/nome} - all'interno di \param{name}, perché questo comporta, da parte delle funzioni - di libreria, il tentativo di accedere a \file{/dev/shm/dev/shm/nome}.} +\param{name}. Come già spiegato in sez.~\ref{sec:ipc_posix_generic} questo +nome può essere specificato in forma standard solo facendolo iniziare per +``\file{/}'' e senza ulteriori ``\file{/}''. Linux supporta comunque nomi +generici, che verranno interpretati prendendo come radice +\file{/dev/shm}.\footnote{occorre pertanto evitare di specificare qualcosa del + tipo \file{/dev/shm/nome} all'interno di \param{name}, perché questo + comporta, da parte delle funzioni di libreria, il tentativo di accedere a + \file{/dev/shm/dev/shm/nome}.} La funzione è del tutto analoga ad \func{open} ed analoghi sono i valori che possono essere specificati per \param{oflag}, che deve essere specificato come @@ -3876,7 +3863,7 @@ segmento di memoria condiviso con le stesse modalit il flag \const{FD\_CLOEXEC}. Chiamate effettuate da diversi processi usando lo stesso nome, restituiranno file descriptor associati allo stesso segmento (così come, nel caso di file di dati, essi sono associati allo stesso -\index{inode}inode). In questo modo è possibile effettuare una chiamata ad +\index{inode} inode). In questo modo è possibile effettuare una chiamata ad \func{mmap} sul file descriptor restituito da \func{shm\_open} ed i processi vedranno lo stesso segmento di memoria condivisa. @@ -3888,10 +3875,9 @@ sez.~\ref{sec:file_file_size}), prima di mapparlo in memoria con \func{mmap}. Si tenga presente che una volta chiamata \func{mmap} si può chiudere il file descriptor (con \func{close}), senza che la mappatura ne risenta. - Come per i file, quando si vuole effettivamente rimuovere segmento di memoria condivisa, occorre usare la funzione \funcd{shm\_unlink}, il cui prototipo è: -\begin{prototype}{mqueue.h} +\begin{prototype}{sys/mman.h} {int shm\_unlink(const char *name)} Rimuove un segmento di memoria condivisa. @@ -3964,6 +3950,624 @@ fare (\texttt{\small 44}) in questo caso restituendo al chiamante il valore di ritorno. + + +\subsection{Semafori} +\label{sec:ipc_posix_sem} + +Fino alla serie 2.4.x del kernel esisteva solo una implementazione parziale +dei semafori POSIX che li realizzava solo a livello di \itindex{thread} +\textit{thread} e non di processi,\footnote{questo significava che i semafori + erano visibili solo all'interno dei \itindex{thread} \textit{thread} creati + da un singolo processo, e non potevano essere usati come meccanismo di + sincronizzazione fra processi diversi.} fornita attraverso la sezione delle +estensioni \textit{real-time} delle \acr{glibc}.\footnote{quelle che si + accedono collegandosi alla libreria \texttt{librt}.} Esisteva inoltre una +libreria che realizzava (parzialmente) l'interfaccia POSIX usando le funzioni +dei semafori di SysV IPC (mantenendo così tutti i problemi sottolineati in +sez.~\ref{sec:ipc_sysv_sem}). + +A partire dal kernel 2.5.7 è stato introdotto un meccanismo di +sincronizzazione completamente nuovo, basato sui cosiddetti +\textit{futex},\footnote{la sigla sta per \textit{fast user mode mutex}.} con +il quale è stato possibile implementare una versione nativa dei semafori +POSIX. Grazie a questo con i kernel della serie 2.6 e le nuove versioni delle +\acr{glibc} che usano questa nuova infrastruttura per quella che viene quella +che viene chiamata \textit{New Posix Thread Library}, sono state implementate +anche tutte le funzioni dell'interfaccia dei semafori POSIX. + +Anche in questo caso è necessario appoggiarsi alla libreria per le estensioni +\textit{real-time} \texttt{librt}, questo significa che se si vuole utilizzare +questa interfaccia, oltre ad utilizzare gli opportuni file di definizione, +occorrerà compilare i programmi con l'opzione \texttt{-lrt}. + +La funzione che permette di creare un nuovo semaforo POSIX, creando il +relativo file, o di accedere ad uno esistente, è \funcd{sem\_open}, questa +prevede due forme diverse a seconda che sia utilizzata per aprire un semaforo +esistente o per crearne uno nuovi, i relativi prototipi sono: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{sem\_t *sem\_open(const char *name, int oflag)} + + \funcdecl{sem\_t *sem\_open(const char *name, int oflag, mode\_t mode, + unsigned int value)} + + Crea un semaforo o ne apre uno esistente. + + \bodydesc{La funzione restituisce l'indirizzo del semaforo in caso di + successo e \const{SEM\_FAILED} in caso di errore; nel quel caso + \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCESS}] il semaforo esiste ma non si hanno permessi + sufficienti per accedervi. + \item[\errcode{EEXIST}] si sono specificati \const{O\_CREAT} e + \const{O\_EXCL} ma il semaforo esiste. + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENAMETOOLONG}] si è utilizzato un nome troppo lungo. + \item[\errcode{ENOENT}] non si è usato \const{O\_CREAT} ed il nome + specificato non esiste. + \end{errlist} + ed inoltre \errval{ENFILE} ed \errval{ENOMEM}.} +\end{functions} + +L'argomento \param{name} definisce il nome del semaforo che si vuole +utilizzare, ed è quello che permette a processi diversi di accedere allo +stesso semaforo. Questo deve essere specificato con un pathname nella forma +\texttt{/qualchenome}, che non ha una corrispondenza diretta con un pathname +reale; con Linux infatti i file associati ai semafori sono mantenuti nel +filesystem virtuale \texttt{/dev/shm}, e gli viene assegnato automaticamente +un nome nella forma \texttt{sem.qualchenome}.\footnote{si ha cioè una + corrispondenza per cui \texttt{/qualchenome} viene rimappato, nella + creazione tramite \func{sem\_open}, su \texttt{/dev/shm/sem.qualchenome}.} + +L'argomento \param{oflag} è quello che controlla le modalità con cui opera la +funzione, ed è passato come maschera binaria; i bit corrispondono a quelli +utilizzati per l'analogo argomento di \func{open}, anche se dei possibili +valori visti in sez.~\ref{sec:file_open} sono utilizzati soltanto +\const{O\_CREAT} e \const{O\_EXCL}. + +Se si usa \const{O\_CREAT} si richiede la creazione del semaforo qualora +questo non esista, ed in tal caso occorre utilizzare la seconda forma della +funzione, in cui si devono specificare sia un valore iniziale con l'argomento +\param{value},\footnote{e si noti come così diventa possibile, differenza di + quanto avviene per i semafori del \textit{SysV IPC}, effettuare in maniera + atomica creazione ed inizializzazione di un semaforo usando una unica + funzione.} che una maschera dei permessi con l'argomento +\param{mode};\footnote{anche questo argomento prende gli stessi valori + utilizzati per l'analogo di \func{open}, che si sono illustrati in dettaglio + sez.~\ref{sec:file_perm_overview}.} questi verranno assegnati al semaforo +appena creato. Se il semaforo esiste già i suddetti valori saranno invece +ignorati. Usando il flag \const{O\_EXCL} si richiede invece la verifica che il +semaforo non esiste, usandolo insieme ad \const{O\_CREAT} la funzione fallisce +qualora un semaforo con lo stesso nome sia già presente. + +La funzione restituisce in caso di successo un puntatore all'indirizzo del +semaforo con un valore di tipo \ctyp{sem\_t *}, è questo valore che dovrà +essere passato alle altre funzioni per operare sul semaforo stesso. Si tenga +presente che, come accennato in sez.~\ref{sec:ipc_posix_generic}, i semafori +usano la semantica standard dei file per quanto riguarda i controlli di +accesso. + +Questo significa che un nuovo semaforo viene sempre creato con l'user-ID ed il +group-ID effettivo del processo chiamante, e che i permessi indicati con +\param{mode} vengono filtrati dal valore della \itindex{umask} \textit{umask} +del processo. Inoltre per poter aprire un semaforo è necessario avere su di +esso sia il permesso di lettura che quello di scrittura. + +Una volta che si sia ottenuto l'indirizzo di un semaforo, sarà possibile +utilizzarlo; se si ricorda quanto detto all'inizio di +sez.~\ref{sec:ipc_sysv_sem}, dove si sono introdotti i concetti generali +relativi ai semafori, le operazioni principali sono due, quella che richiede +l'uso di una risorsa bloccando il semaforo e quella che rilascia la risorsa +liberando il semaforo. La prima operazione è effettuata dalla funzione +\funcd{sem\_wait}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_wait(sem\_t *sem)} + + Blocca il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione cerca di decrementare il valore del semaforo indicato dal +puntatore \param{sem}, se questo ha un valore positivo, cosa che significa che +la risorsa è disponibile, la funzione ha successo, il valore del semaforo +viene diminuito di 1 ed essa ritorna immediatamente; se il valore è nullo la +funzione si blocca fintanto che il valore del semaforo non torni +positivo\footnote{ovviamente per opera di altro processo che lo rilascia + chiamando \func{sem\_post}.} così che poi essa possa decrementarlo con +successo e proseguire. + +Si tenga presente che la funzione può sempre essere interrotta da un segnale +(nel qual caso si avrà un errore di \const{EINTR}) e che questo avverrà +comunque, anche se si è richiesta la semantica BSD installando il relativo +gestore con \const{SA\_RESTART} (vedi sez.~\ref{sec:sig_sigaction}) per +riavviare le system call interrotte. + +Della funzione \func{sem\_wait} esistono due varianti che consentono di +gestire diversamente le modalità di attesa in caso di risorsa occupata, la +prima di queste è \funcd{sem\_trywait}, che serve ad effettuare un tentativo +di acquisizione senza bloccarsi; il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_trywait(sem\_t *sem)} + + Tenta di bloccare il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori: + \begin{errlist} + \item[\errcode{EAGAIN}] il semaforo non può essere acquisito senza + bloccarsi. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione è identica a \func{sem\_wait} ed se la risorsa è libera ha lo +stesso effetto, vale a dire che in caso di semaforo diverso da zero la +funzione lo decrementa e ritorna immediatamente; la differenza è che nel caso +in cui il semaforo è occupato essa non si blocca e di nuovo ritorna +immediatamente, restituendo però un errore di \errval{EAGAIN}, così che il +programma possa proseguire. + +La seconda variante di \func{sem\_wait} è una estensione specifica che può +essere utilizzata soltanto se viene definita la macro \macro{\_XOPEN\_SOURCE} +ad un valore di 600 prima di includere \texttt{semaphore.h}, la funzione è +\func{sem\_timedwait}, ed il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_timedwait(sem\_t *sem, const struct timespec + *abs\_timeout)} + + Blocca il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori: + \begin{errlist} + \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \end{errlist} +} +\end{functions} + +Anche in questo caso il comportamento della funzione è identico a quello di +\func{sem\_wait}, la sola differenza consiste nel fatto che con questa +funzione è possibile impostare tramite l'argomento \param{abs\_timeout} un +tempo limite per l'attesa, scaduto il quale la funzione ritorna comunque, +anche se non è possibile acquisire il semaforo. In tal caso la funzione +fallirà, riportando un errore di \errval{ETIMEDOUT}. + +La seconda funzione principale utilizzata per l'uso dei semafori è +\funcd{sem\_post}, che viene usata per rilasciare un semaforo occupato o, in +generale, per aumentare di una unità il valore dello stesso anche qualora non +fosse occupato;\footnote{si ricordi che in generale un semaforo viene usato + come indicatore di un numero di risorse disponibili.} il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_post(sem\_t *sem)} + + Rilascia il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione incrementa di uno il valore corrente del semaforo indicato +dall'argomento \param{sem}, se questo era nullo la relativa risorsa risulterà +sbloccata, cosicché un altro processo (o \itindex{thread} \textit{thread}) +eventualmente bloccato in una \func{sem\_wait} sul semaforo potrà essere +svegliato e rimesso in esecuzione. Si tenga presente che la funzione è sicura +\index{funzioni!sicure} per l'uso all'interno di un gestore di segnali (si +ricordi quanto detto in sez.~\ref{sec:sig_signal_handler}). + +Se invece di operare su un semaforo se ne vuole solamente leggere il valore, +si può usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_getvalue(sem\_t *sem, int *sval)} + + Richiede il valore del semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione legge il valore del semaforo indicato dall'argomento \param{sem} e +lo restituisce nella variabile intera puntata dall'argomento +\param{sval}. Qualora ci siano uno o più processi bloccati in attesa sul +semaforo lo standard prevede che la funzione possa restituire un valore nullo +oppure il numero di processi bloccati in una \func{sem\_wait} sul suddetto +semaforo; nel caso di Linux vale la prima opzione. + +Questa funzione può essere utilizzata per avere un suggerimento sullo stato di +un semaforo, ovviamente non si può prendere il risultato riportato in +\param{sval} che come indicazione, il valore del semaforo infatti potrebbe +essere già stato modificato al ritorno della funzione. + +% TODO verificare comportamento sem_getvalue + +Una volta che non ci sia più la necessità di operare su un semaforo se ne può +terminare l'uso con la funzione \funcd{sem\_close}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_close(sem\_t *sem)} + + Chiude il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione chiude il semaforo indicato dall'argomento \param{sem}; questo +comporta che tutte le risorse che il sistema può avere assegnato al processo +nell'uso dello stesso vengono rilasciate. Questo significa che un altro +processo bloccato sul semaforo a causa della acquisizione da parte del +processo che chiama \func{sem\_close} potrà essere riavviato. + +Si tenga presente poi che come per i file all'uscita di un processo tutti i +semafori che questo aveva aperto vengono automaticamente chiusi; questo +comportamento risolve il problema che si aveva con i semafori del \textit{SysV + IPC} (di cui si è parlato in sez.~\ref{sec:ipc_sysv_sem}) per i quali le +risorse possono restare bloccate. Si tenga poi presente che, a differenza di +quanto avviene per i file, in caso di una chiamata ad \func{execve} tutti i +semafori vengono chiusi automaticamente. + +Come per i semafori del \textit{SysV IPC} anche quelli POSIX hanno una +persistenza di sistema; questo significa che una volta che si è creato un +semaforo con \func{sem\_open} questo continuerà ad esistere fintanto che il +kernel resta attivo (vale a dire fino ad un successivo riavvio) a meno che non +lo si cancelli esplicitamente. Per far questo si può utilizzare la funzione +\funcd{sem\_unlink}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_unlink(const char *name)} + + Rimuove il semaforo \param{name}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCESS}] non si hanno i permessi necessari a cancellare il + semaforo. + \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. + \item[\errcode{ENOENT}] il semaforo \param{name} non esiste. + \end{errlist} +} +\end{functions} + +La funzione rimuove il semaforo indicato dall'argomento \param{name}, che +prende un valore identico a quello usato per creare il semaforo stesso con +\func{sem\_open}. Il semaforo viene rimosso dal filesystem immediatamente; ma +il semaforo viene effettivamente cancellato dal sistema soltanto quando tutti +i processi che lo avevano aperto lo chiudono. Si segue cioè la stessa +semantica usata con \func{unlink} per i file, trattata in dettaglio in +sez.~\ref{sec:file_link}. + +Una delle caratteristiche peculiari dei semafori POSIX è che questi possono +anche essere utilizzati anche in forma anonima, senza necessità di fare +ricorso ad un nome sul filesystem o ad altri indicativi. In questo caso si +dovrà porre la variabile che contiene l'indirizzo del semaforo in un tratto di +memoria che sia accessibile a tutti i processi in gioco. La funzione che +consente di inizializzare un semaforo anonimo è \funcd{sem\_init}, il cui +prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} + + Inizializza il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENOSYS}] il valore di \param{pshared} non è nullo ed il + sistema non supporta i semafori per i processi. + \end{errlist} +} +\end{functions} + +La funzione inizializza un semaforo all'indirizzo puntato dall'argomento +\param{sem}, e come per \func{sem\_open} consente di impostare un valore +iniziale con \param{value}. L'argomento \param{pshared} serve ad indicare se +il semaforo deve essere utilizzato dai \itindex{thread} \textit{thread} di uno +stesso processo (con un valore nullo) o condiviso fra processi diversi (con un +valore non nullo). + +Qualora il semaforo debba essere condiviso dai \itindex{thread} +\textit{thread} di uno stesso processo (nel qual caso si parla di +\textit{thread-shared semaphore}), occorrerà che \param{sem} sia l'indirizzo +di una variabile visibile da tutti i \itindex{thread} \textit{thread}, si +dovrà usare cioè una variabile globale o una variabile allocata dinamicamente +nello \itindex{heap} \textit{heap}. + +Qualora il semaforo debba essere condiviso fra più processi (nel qual caso si +parla di \textit{process-shared semaphore}) la sola scelta possibile per +renderlo visibile a tutti è di porlo in un tratto di memoria condivisa. Questo +potrà essere ottenuto direttamente sia con \func{shmget} (vedi +sez.~\ref{sec:ipc_sysv_shm}) che con \func{shm\_open} (vedi +sez.~\ref{sec:ipc_posix_shm}), oppure, nel caso che tutti i processi in gioco +abbiano un genitore comune, con una mappatura anonima con \func{mmap} (vedi +sez.~\ref{sec:file_memory_map}),\footnote{si ricordi che i tratti di memoria + condivisa vengono mantenuti nei processi figli attraverso la funzione + \func{fork}.} a cui essi poi potranno accedere. + +Una volta inizializzato il semaforo anonimo con \func{sem\_init} lo si potrà +utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e +\func{sem\_post}. Si tenga presente però che inizializzare due volte lo stesso +semaforo può dar luogo ad un comportamento indefinito. + +Una volta che non si intenda più utilizzare un semaforo anonimo questo può +essere eliminato dal sistema; per far questo di deve utilizzare una apposita +funzione, \funcd{sem\_destroy}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_destroy(sem\_t *sem)} + + Elimina il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \end{errlist} +} +\end{functions} + +La funzione prende come unico argomento l'indirizzo di un semaforo che deve +essere stato inizializzato con \func{sem\_init}; non deve quindi essere +applicata a semafori creati con \func{sem\_open}. Inoltre si deve essere +sicuri che il semaforo sia effettivamente inutilizzato, la distruzione di un +semaforo su cui sono presenti processi (o \itindex{thread} \textit{thread}) in +attesa (cioè bloccati in una \func{sem\_wait}) provoca un comportamento +indefinito. + +Si tenga presente infine che utilizzare un semaforo che è stato distrutto con +\func{sem\_destroy} di nuovo può dare esito a comportamenti indefiniti. Nel +caso ci si trovi in una tale evenienza occorre reinizializzare il semaforo una +seconda volta con \func{sem\_init}. + +Come esempio di uso sia della memoria condivisa che dei semafori POSIX si sono +scritti due semplici programmi con i quali è possibile rispettivamente +monitorare il contenuto di un segmento di memoria condivisa e modificarne il +contenuto. + +\begin{figure}[!h] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/message_getter.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del programma + \file{message\_getter.c}.} + \label{fig:ipc_posix_sem_shm_message_server} +\end{figure} + +Il corpo principale del primo dei due, il cui codice completo è nel file +\file{message\_getter.c} dei sorgenti allegati, è riportato in +fig.~\ref{fig:ipc_posix_sem_shm_message_server}; si è tralasciata la parte che +tratta la gestione delle opzioni a riga di comando (che consentono di +impostare un nome diverso per il semaforo e il segmento di memoria condivisa) +ed il controllo che al programma venga fornito almeno un argomento, contenente +la stringa iniziale da inserire nel segmento di memoria condivisa. + +Lo scopo del programma è quello di creare un segmento di memoria condivisa su +cui registrare una stringa, e tenerlo sotto osservazione stampando la stessa +una volta al secondo. Si utilizzerà un semaforo per proteggere l'accesso in +lettura alla stringa, in modo che questa non possa essere modificata +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 (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 +(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 acquisire 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 semaforo 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 richiama 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{lanciare 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: Architecture descriptor kernel unistd int filedes errno EMFILE % LocalWords: ENFILE EFAULT BUF sez fig fork Stevens siblings EOF read SIGPIPE @@ -3974,7 +4578,7 @@ restituendo al chiamante il valore di ritorno. % LocalWords: PDF EPS lseek ESPIPE PPM Portable PixMap format pnmcrop PNG pnm % LocalWords: pnmmargin png BarCode inode filesystem l'inode mknod mkfifo RDWR % LocalWords: ENXIO deadlock client reinviate fortunes fortunefilename daemon -% LocalWords: FortuneServer FortuneParse FortuneClient pid libgapil LD +% LocalWords: FortuneServer FortuneParse FortuneClient pid libgapil LD librt % LocalWords: PATH linker pathname ps tmp killall fortuned crash socket domain % LocalWords: socketpair BSD sys protocol sv EAFNOSUPPORT EPROTONOSUPPORT AF % LocalWords: EOPNOTSUPP SOCK SysV IPC Process Comunication ipc perm key exec @@ -3987,7 +4591,7 @@ restituendo al chiamante il valore di ritorno. % LocalWords: cmd struct buf EPERM RMID msgsnd msgbuf msgp msgsz msgflg EAGAIN % LocalWords: NOWAIT EINTR mtype mtext long message sizeof LENGTH ts sleep BIG % LocalWords: msgrcv ssize msgtyp NOERROR EXCEPT ENOMSG multiplexing select ls -% LocalWords: poll polling queue MQFortuneServer write init HandSIGTERM +% LocalWords: poll polling queue MQFortuneServer write init HandSIGTERM l'IPC % LocalWords: MQFortuneClient mqfortuned mutex risorse' inter semaphore semget % LocalWords: nsems SEMMNS SEMMSL semid otime semval sempid semncnt semzcnt nr % LocalWords: SEMVMX SEMOPM semop SEMMNU SEMUME SEMAEM semnum union semun arg @@ -4006,13 +4610,20 @@ restituendo al chiamante il valore di ritorno. % LocalWords: SHARED ANONYMOUS thread patch names strace system call userid Di % LocalWords: groupid Michal Wronski Krzysztof Benedyczak wrona posix mqueue % LocalWords: lmqueue gcc mount mqd name oflag attr maxmsg msgsize receive ptr -% LocalWords: send WRONLY NONBLOCK close mqdes EBADF getattr setattr mqstat +% LocalWords: send WRONLY NONBLOCK close mqdes EBADF getattr setattr mqstat to % LocalWords: omqstat curmsgs flags timedsend len prio timespec abs EMSGSIZE % LocalWords: ETIMEDOUT timedreceive getaddr notify sigevent notification l'I % LocalWords: EBUSY sigev SIGNAL signo value sigval siginfo all'userid MESGQ % LocalWords: Konstantin Knizhnik futex tmpfs ramfs cache shared swap CONFIG % LocalWords: lrt blocks PAGECACHE TRUNC CLOEXEC mmap ftruncate munmap FindShm -% LocalWords: CreateShm RemoveShm LIBRARY +% LocalWords: CreateShm RemoveShm LIBRARY Library libmqueue FAILED EACCESS has +% LocalWords: ENAMETOOLONG qualchenome RESTART trywait XOPEN SOURCE timedwait +% LocalWords: process getvalue sval execve pshared ENOSYS heap PAGE destroy it +% LocalWords: xffffffff Arrays owner perms Queues used bytes messages device +% LocalWords: Cannot find such Segments getter Signal MSGMAXSIZE been stable +% LocalWords: for now it's break Berlin sources Let's an accidental feature +% LocalWords: Larry Wall Escape the Hell William ipctestid Identifier segment +% LocalWords: violation dell'I SIGINT setter Fri Dec Sleeping seconds %%% Local Variables: