X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=9676970e57dcf5372c91c85d63d8ff00e8ada585;hp=9d8ebe6d88822dd1788a9782dd6fcaaec37e8792;hb=aa6e7fc3085168384700494a4bd22208f71e089e;hpb=06604d9d9800ff3f095bda01f9c93bc2e82d50eb diff --git a/ipc.tex b/ipc.tex index 9d8ebe6..9676970 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1967,7 +1967,7 @@ Let's call it an accidental feature. \end{verbatim} con un risultato del tutto equivalente al precedente. Infine potremo chiudere il server inviando il segnale di terminazione con il comando \code{killall - mqfortuned} verficando che effettivamente la coda di messaggi viene rimossa. + mqfortuned} verificando che effettivamente la coda di messaggi viene rimossa. Benché funzionante questa architettura risente dello stesso inconveniente visto anche nel caso del precedente server basato sulle fifo; se il client @@ -2670,7 +2670,7 @@ caso di terminazione imprevista del processo. L'ultima funzione (\texttt{\small 46--49}) della serie, è \func{MutexRemove}, che rimuove il mutex. Anche in questo caso si ha un wrapper per una chiamata a \func{semctl} con il comando \const{IPC\_RMID}, che permette di cancellare il -smemaforo; il valore di ritorno di quest'ultima viene passato all'indietro. +semaforo; il valore di ritorno di quest'ultima viene passato all'indietro. Chiamare \func{MutexLock} decrementa il valore del semaforo: se questo è libero (ha già valore 1) sarà bloccato (valore nullo), se è bloccato la @@ -2872,7 +2872,7 @@ un segmento di memoria condivisa Il comando specificato attraverso l'argomento \param{cmd} determina i diversi effetti della funzione; i possibili valori che esso può assumere, ed il -corripondente comportamento della funzione, sono i seguenti: +corrispondente comportamento della funzione, sono i seguenti: \begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} \item[\const{IPC\_STAT}] Legge le informazioni riguardo il segmento di memoria @@ -3197,8 +3197,8 @@ l'accesso alla memoria condivisa. Una volta completata l'inizializzazione e la creazione degli oggetti di intercomunicazione il programma entra nel ciclo principale (\texttt{\small - 45--54}) dove vengono eseguitw indefinitamente le attività di monitoraggio. -Il primo passo (\texttt{\small 46}) è esguire \func{daemon} per proseguire con + 45--54}) dove vengono eseguite indefinitamente le attività di monitoraggio. +Il primo passo (\texttt{\small 46}) è eseguire \func{daemon} per proseguire con l'esecuzione in background come si conviene ad un programma demone; si noti che si è mantenuta, usando un valore non nullo del primo argomento, la directory di lavoro corrente. @@ -3328,8 +3328,8 @@ int main(int argc, char *argv[]) \end{lstlisting} \end{minipage} \normalsize - \caption{Codice del programma client del monitori di directory, - \file{ReadMonitor.c}.} + \caption{Codice del programma client del monitor delle proprietà di una + directory, \file{ReadMonitor.c}.} \label{fig:ipc_dirmonitor_client} \end{figure} @@ -3361,7 +3361,7 @@ stampano i vari valori mantenuti nella memoria condivisa attraverso l'uso di il mutex, prima di uscire. Verifichiamo allora il funzionamento dei nostri programmi; al solito, usando -le funzioni di libreira occorre definire opportunamente +le funzioni di libreria occorre definire opportunamente \code{LD\_LIBRARY\_PATH}; poi si potrà lanciare il server con: \begin{verbatim} [piccardi@gont sources]$ ./dirmonitor ./ @@ -3419,7 +3419,7 @@ nel qual caso, ripetendo la lettura otterremo che: [piccardi@gont sources]$ ./readmon Cannot find shared memory: No such file or directory \end{verbatim}%$ -e potremo verificare che anche gli oggetti di intercomunicazion e sono stati +e potremo verificare che anche gli oggetti di intercomunicazione sono stati cancellati: \begin{verbatim} [piccardi@gont sources]$ ipcs @@ -3615,7 +3615,6 @@ int FindMutex(const char *path_name) int LockMutex(int fd) { struct flock lock; /* file lock structure */ - /* first open the file (creating it if not existent) */ /* set flock structure */ lock.l_type = F_WRLCK; /* set type: read or write */ lock.l_whence = SEEK_SET; /* start from the beginning of the file */ @@ -3686,53 +3685,54 @@ aprire il file da usare per il file locking, solo che in questo caso le opzioni di \func{open} sono tali che il file in questione deve esistere di già. -La terza funzione (\texttt{\small 11--23}) è \func{LockMutex} e serve per +La terza funzione (\texttt{\small 11--22}) è \func{LockMutex} e serve per acquisire il mutex. La funzione definisce (\texttt{\small 14}) e inizializza -(\texttt{\small 17--20}) la struttura \var{lock} da usare per acquisire un +(\texttt{\small 16--19}) la struttura \var{lock} da usare per acquisire un write lock sul file, che poi (\texttt{\small 21}) viene richiesto con \func{fcntl}, restituendo il valore di ritorno di quest'ultima. Se il file è libero il lock viene acquisito e la funzione ritorna immediatamente; altrimenti \func{fcntl} si bloccherà (si noti che la si è chiamata con \func{F\_SETLKW}) fino al rilascio del lock. -La quarta funzione (\texttt{\small 24--35}) è \func{UnlockMutex} e serve a +La quarta funzione (\texttt{\small 24--34}) è \func{UnlockMutex} e serve a rilasciare il mutex. La funzione è analoga alla precedente, solo che in questo -caso si inizializza (\texttt{\small 29--32}) la struttura \var{lock} per il -rilascio del lock, che viene effettuato (\texttt{\small 34}) con la opportuna +caso si inizializza (\texttt{\small 28--31}) la struttura \var{lock} per il +rilascio del lock, che viene effettuato (\texttt{\small 33}) con la opportuna chiamata a \func{fcntl}. Avendo usato il file locking in semantica POSIX (si riveda quanto detto \secref{sec:file_posix_lock}) solo il processo che ha precedentemente eseguito il lock può sbloccare il mutex. -La quinta funzione (\texttt{\small 36--40}) è \func{RemoveMutex} e serve a +La quinta funzione (\texttt{\small 36--39}) è \func{RemoveMutex} e serve a cancellare il mutex. Anche questa funzione è stata definita per mantenere una analogia con le funzioni basate sui semafori, e si limita a cancellare -(\texttt{\small 39}) il file con una chiamata ad \func{unlink}. Si noti che in +(\texttt{\small 38}) il file con una chiamata ad \func{unlink}. Si noti che in questo caso la funzione non ha effetto sui mutex già ottenuti con precedenti chiamate a \func{FindMutex} o \func{CreateMutex}, che continueranno ad essere disponibili fintanto che i relativi file descriptor restano aperti. Pertanto per rilasciare un mutex occorrerà prima chiamare \func{UnlockMutex} oppure chiudere il file usato per il lock. -La sesta funzione (\texttt{\small 41--56}) è \func{ReadMutex} e serve a -leggere lo stato del mutex. In questo caso si prepara (\texttt{\small 47--50}) +La sesta funzione (\texttt{\small 41--55}) è \func{ReadMutex} e serve a +leggere lo stato del mutex. In questo caso si prepara (\texttt{\small 46--49}) la solita struttura \var{lock} come l'acquisizione del lock, ma si effettua -(\texttt{\small 52}) la chiamata a \func{fcntl} usando il comando +(\texttt{\small 51}) la chiamata a \func{fcntl} usando il comando \const{F\_GETLK} per ottenere lo stato del lock, e si restituisce -(\texttt{\small 53}) il valore di ritorno in caso di errore, ed il valore del -campo \var{l\_type} (che descrive lo stato del lock) altrimenti. Per questo -motivo la funzione restituirà -1 in caso di errore e uno dei due valori -\const{F\_UNLCK} o \const{F\_WRLCK}\footnote{non si dovrebbe mai avere il - terzo valore possibile, \const{F\_RDLCK}, dato che la nostra interfaccia usa - solo i write lock. Però è sempre possibile che siano richiesti altri lock - sul file al di fuori dell'interfaccia, nel qual caso si potranno avere, - ovviamente, interferenze indesiderate.} in caso di successo, ad indicare che -il mutex è, rispettivamente, libero o occupato. +(\texttt{\small 52}) il valore di ritorno in caso di errore, ed il valore del +campo \var{l\_type} (che descrive lo stato del lock) altrimenti +(\texttt{\small 54}). Per questo motivo la funzione restituirà -1 in caso di +errore e uno dei due valori \const{F\_UNLCK} o \const{F\_WRLCK}\footnote{non + si dovrebbe mai avere il terzo valore possibile, \const{F\_RDLCK}, dato che + la nostra interfaccia usa solo i write lock. Però è sempre possibile che + siano richiesti altri lock sul file al di fuori dell'interfaccia, nel qual + caso si potranno avere, ovviamente, interferenze indesiderate.} in caso di +successo, ad indicare che il mutex è, rispettivamente, libero o occupato. Basandosi sulla semantica dei file lock POSIX valgono tutte le considerazioni relative al comportamento di questi ultimi fatte in \secref{sec:file_posix_lock}; questo significa ad esempio che, al contrario di quanto avveniva con l'interfaccia basata sui semafori, chiamate multiple a -\func{UnlockMutex} o \func{LockMutex} non hanno nessun inconveniente. +\func{UnlockMutex} o \func{LockMutex} non si cumulano e non danno perciò +nessun inconveniente. \subsection{Il \textit{memory mapping} anonimo} @@ -3744,7 +3744,7 @@ una valida alternativa alle code di messaggi; nella stessa situazione si pu evitare l'uso di una memoria condivisa facendo ricorso al cosiddetto \textit{memory mapping} anonimo. -Abbiamo visto in \secref{sec:file_memory_map} che è possibile mappare il +In \secref{sec:file_memory_map} abbiamo visto come sia possibile mappare il contenuto di un file nella memoria di un processo, e che, quando viene usato il flag \const{MAP\_SHARED}, le modifiche effettuate al contenuto del file vengono viste da tutti i processi che lo hanno mappato. Utilizzare questa @@ -3763,8 +3763,9 @@ il \textit{memory mapping} anonimo.\footnote{nei sistemi derivati da SysV una \file{/dev/zero}. In tal caso i valori scritti nella regione mappata non vengono ignorati (come accade qualora si scriva direttamente sul file), ma restano in memoria e possono essere riletti secondo le stesse modalità usate - nel \textit{memory mapping} anonimo.} Un esempio di utilizzo di questa -tecnica è mostrato in + nel \textit{memory mapping} anonimo.} Vedremo come utilizzare questa tecnica +più avanti, quando realizzeremo una nuova versione del monitor visto in +\secref{sec:ipc_sysv_shm} che possa restituisca i risultati via rete. @@ -3778,29 +3779,511 @@ meccanismi di comunicazione, che vanno sotto il nome di POSIX IPC, definendo una interfaccia completamente nuova, che tratteremo in questa sezione. - \subsection{Considerazioni generali} \label{sec:ipc_posix_generic} -Il Linux non tutti gli oggetti del POSIX IPC sono supportati nel kernel -ufficiale; solo la memoria condivisa è presente, ma solo a partire dal kernel -2.4.x, per gli altri oggetti esistono patch e librerie non ufficiali. -Nonostante questo è importante esaminare questa interfaccia per la sua netta -superiorità nei confronti di quella del \textit{SysV IPC}. +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. + +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}\index{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} +\item i nomi devono essere conformi alle regole che caratterizzano i + \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} + +Data la assoluta genericità delle specifiche, il comportamento delle funzioni +è pertanto subordinato in maniera quasi completa alla relativa +implementazione.\footnote{tanto che Stevens in \cite{UNP2} cita questo caso + come un esempio della maniera standard usata dallo standard POSIX per + consentire implementazioni non standardizzabili.} Nel caso di Linux per +quanto riguarda la memoria condivisa, tutto viene creato nella directory +\file{/dev/shm}, ed i nomi sono presi come pathname assoluto (comprendente +eventuali sottodirectory) rispetto a questa radice (per maggiori dettagli si +veda quanto illustrato in \secref{sec:ipc_posix_shm}). Lo stesso accade per +l'implementazione sperimentale delle code di messaggi, che però fa riferimento +alla directory \file{/dev/mqueue}. + +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 per la memoria condivisa, come si può + facilmente evincere con uno \cmd{strace}, le system call utilizzate sono le + stesse, in quanto essa è realizzata con file in uno speciale 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 \secref{sec:file_access_control}), invece di +quella particolare (si ricordi quanto visto in +\secref{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). + \subsection{Code di messaggi} \label{sec:ipc_posix_mq} -Le code di messaggi non sono ancora supportate nel kernel -ufficiale;\footnote{esiste però una proposta di implementazione di Krzysztof - Benedyczak, a partire dal kernel 2.5.50.} inoltre esse possono essere -implementate, usando la memoria condivisa ed i mutex, con funzioni di -libreria. In generale, come le corrispettive del SysV IPC, sono poco usate, -dato che i socket\index{socket}, nei casi in cui sono sufficienti, sono più -comodi, e negli altri casi la comunicazione può essere gestita direttamente -con mutex e memoria condivisa. Per questo ci limiteremo ad una descrizione -essenziale. +Le code di messaggi non sono ancora supportate nel kernel ufficiale, esiste +però una implementazione sperimentale di Michal Wronski e Krzysztof +Benedyczak,\footnote{i patch al kernel e la relativa libreria possono essere +trovati su +\href{http://www.mat.uni.torun.pl/~wrona/posix_ipc} +{http://www.mat.uni.torun.pl/\~{}wrona/posix\_ipc}.}. +In generale, come le corrispettive del SysV IPC, le code di messaggi sono poco +usate, dato che i socket\index{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. + +Per poter utilizzare le code di messaggi, oltre ad utilizzare un kernel cui +siano stati opportunamente applicati i relativi patch, occorre utilizzare la +libreria \file{mqueue}\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.} +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.} + + +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: +\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 +accettate sono \texttt{uid}, \texttt{gid} e \texttt{mode} che permettono +rispettivamente di impostare l'utente, il gruppo ed i permessi associati al +filesystem. + + +La funzione che permette di aprire (e crearla se non esiste ancora) una coda +di messaggi POSIX è \funcd{mq\_open}, ed il suo prototipo è: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{mqd\_t mq\_open(const char *name, int oflag)} + + \funcdecl{mqd\_t mq\_open(const char *name, int oflag, unsigned long mode, + struct mq\_attr *attr)} + + 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 + valori: + \begin{errlist} + \item[\errcode{EACCESS}] 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 + \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 è + 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 + non esiste. + \end{errlist} + ed inoltre \errval{ENOMEM}, \errval{ENOSPC}, \errval{EFAULT}, + \errval{EMFILE} 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. + +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 +\tabref{tab:file_open_flags} dei quali però \func{mq\_open} riconosce solo i +seguenti: +\begin{basedescript}{\desclabelwidth{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}. +\item[\const{O\_WRONLY}] Apre la coda solo per la trasmissione di messaggi. Il + processo potrà usare il descrittore con \func{mq\_send} ma non con + \func{mq\_receive}. +\item[\const{O\_RDWR}] Apre la coda solo sia per la trasmissione che per la + ricezione. +\item[\const{O\_CREAT}] Necessario qualora si debba creare la coda; la + presenza di questo bit richiede la presenza degli ulteriori argomenti + \param{mode} e \param{attr}. +\item[\const{O\_EXCL}] Se usato insieme a \const{O\_CREAT} fa fallire la + chiamata se la coda esiste già, altrimenti esegue la creazione atomicamente. +\item[\const{O\_NONBLOCK}] Imposta la coda in modalità non bloccante, le + funzioni di ricezione e trasmissione non si bloccano quando non ci sono le + risorse richieste, ma ritornano immediatamente con un errore di + \errcode{EAGAIN}. +\end{basedescript} + +I primi tre bit specificano la modalità di apertura della coda, e sono fra +loro esclusivi. Ma qualunque sia la modalità in cui si è aperta una coda, +questa potrà essere riaperta più volte in una modalità diversa, e vi si potrà +sempre accedere attraverso descrittori diversi, esattamente come si può fare +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 +\figref{fig:ipc_mq_attr}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{} +struct mq_attr { + long mq_flags; /* message queue flags */ + long mq_maxmsg; /* maximum number of messages */ + long mq_msgsize; /* maximum message size */ + long mq_curmsgs; /* number of messages currently queued */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \structd{mq\_attr}, contenente gli attributi di una + coda di messaggi POSIX.} + \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. + +Quando l'accesso alla coda non è più necessario si può chiudere il relativo +descrittore con la funzione \funcd{mq\_close}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_close(mqd\_t mqdes)} + +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 + \errval{EINTR}.} +\end{prototype} + +La funzione è analoga a \func{close},\footnote{in Linux, dove le code sono + implementate come file su un filesystem dedicato, è esattamente la stessa + funzione.} dopo la sua esecuzione il processo non sarà più in grado di usare +il descrittore della coda, ma quest'ultima continuerà ad esistere nel sistema +e potrà essere acceduta con un'altra chiamata a \func{mq\_open}. All'uscita di +un processo tutte le code aperte, così come i file, vengono chiuse +automaticamente. Inoltre se il processo aveva agganciato una richiesta di +notifica sul descrittore che viene chiuso, questa sarà rilasciata e potrà +essere richiesta da qualche altro processo. + + +Quando si vuole effettivamente rimuovere una coda dal sistema occorre usare la +funzione \funcd{mq\_unlink}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_unlink(const char *name)} + +Rimuove una coda di messaggi. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori riportati da + \func{unlink}.} +\end{prototype} + +Anche in questo caso il comportamento della funzione è analogo a quello di +\func{unlink} per i file,\footnote{di nuovo l'implementazione di Linux usa + direttamente \func{unlink}.} la funzione rimove la coda \param{name}, così +che una successiva chiamata a \func{mq\_open} fallisce o crea una coda +diversa. + +Come per i file ogni coda di messaggi ha un contatore di riferimenti, per cui +la coda non viene effettivamente rimossa dal sistema fin quando questo non si +annulla. Pertanto anche dopo aver eseguito con successo \func{mq\_unlink} la +coda resterà accessibile a tutti i processi che hanno un descrittore aperto su +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. + +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: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{int mq\_getattr(mqd\_t mqdes, struct mq\_attr *mqstat)} + Legge gli attributi di una coda di messaggi POSIX. + + \funcdecl{int mq\_setattr(mqd\_t mqdes, const struct mq\_attr *mqstat, + struct mq\_attr *omqstat)} + Modifica gli attributi di una coda di messaggi POSIX. + + \bodydesc{Entrambe le funzioni restituiscono 0 in caso di successo e -1 in + caso di errore; nel quel caso \var{errno} assumerà i valori \errval{EBADF} + o \errval{EINVAL}.} +\end{functions} + +La funzione \func{mq\_getattr} legge i valori correnti degli attributi della +coda nella struttura puntata da \param{mqstat}; di questi l'unico relativo +allo stato corrente della coda è \var{mq\_curmsgs} che indica il numero di +messaggi da essa contenuti, gli altri indicano le caratteristiche generali +della stessa. + +La funzione \func{mq\_setattr} permette di modificare gli attributi di una +coda tramite i valori contenuti nella struttura puntata da \param{mqstat}, ma +può essere modificato solo il campo \var{mq\_flags}, gli altri campi vengono +ignorati. In particolare i valori di \var{mq\_maxmsg} e \var{mq\_msgsize} +possono essere specificati solo in fase ci creazione della coda. Inoltre i +soli valori possibili per \var{mq\_flags} sono 0 e \const{O\_NONBLOCK}, per +cui alla fine la funzione può essere utilizzata solo per abilitare o +disabilitare la modalità non bloccante. L'argomento \param{omqstat} viene +usato, quando diverso da \val{NULL}, per specificare l'indirizzo di una +struttura su cui salvare i valori degli attributi precedenti alla chiamata +della funzione. + +Per inserire messaggi su di una coda sono previste due funzioni, +\funcd{mq\_send} e \funcd{mq\_timedsend}, i cui prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{int mq\_send(mqd\_t mqdes, const char *msg\_ptr, size\_t msg\_len, + unsigned int msg\_prio)} + Esegue l'inserimento di un messaggio su una coda. + + \funcdecl{int mq\_timedsend(mqd\_t mqdes, const char *msg\_ptr, size\_t + msg\_len, unsigned msg\_prio, const struct timespec *abs\_timeout)} + Esegue l'inserimento di un messaggio su una coda entro il tempo + \param{abs\_timeout}. + + + \bodydesc{Le funzioni restituiscono 0 in caso 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 + coda è piena. + \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 + \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 + effettuato entro il tempo stabilito. + \end{errlist} + ed inoltre \errval{EBADF} ed \errval{EINTR}.} +\end{functions} + +Entrambe le funzioni richiedono un puntatore al testo del messaggio +nell'argomento \param{msg\_ptr} e la relativa lunghezza in \param{msg\_len}. +Se quest'ultima eccede la dimensione massima specificata da \var{mq\_msgsize} +le funzioni ritornano immediatamente con un errore di \errcode{EMSGSIZE}. + +L'argomento \param{msg\_prio} indica la priorità dell'argomento; i messaggi di +priorità maggiore vengono inseriti davanti a quelli di priorità inferiore (e +quindi saranno riletti per primi). A parità del valore della priorità il +messaggio sarà inserito in coda a tutti quelli con la stessa priorità. Il +valore della priorità non può eccedere il limite di sistema +\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}. + + +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 +prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{ssize\_t mq\_receive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio)} + Effettua la ricezione di un messaggio da una coda. + + \funcdecl{ssize\_t mq\_timedreceive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio, const struct timespec *abs\_timeout)} + Effettua la ricezione di un messaggio da una coda entro il tempo + \param{abs\_timeout}. + + \bodydesc{Le funzioni restituiscono il numero di byte del messaggio in caso + 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 + coda è vuota. + \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 + \param{msg\_ptr}, o un valore non valido per \param{abs\_timeout}. + \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 + \errval{EINVAL}.} +\end{functions} + +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. + +Se la dimensione specificata da \param{msg\_len} non è sufficiente a contenere +il messaggio, entrambe le funzioni, al contrario di quanto avveniva nelle code +di messaggi di SysV, ritornano un errore di \errcode{EMSGSIZE} senza estrarre +il messaggio. È pertanto opportuno eseguire sempre una chiamata a +\func{mq\_getaddr} prima di eseguire una ricezione, in modo da ottenere la +dimensione massima dei messaggi sulla coda, per poter essere in grado di +allocare dei buffer sufficientemente ampi per la lettura. + +Se si specifica un puntatore per l'argomento \param{msg\_prio} il valore della +priorità del messaggio viene memorizzato all'indirizzo da esso indicato. +Qualora non interessi usare la priorità dei messaggi si può specificare +\var{NULL}, ed usare un valore nullo della priorità nelle chiamate a +\func{mq\_send}. + +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 + +Qualora la coda sia vuota entrambe le funzioni si bloccano, a meno che non si +sia selezionata la modalità non bloccante; in tal caso entrambe ritornano +immediatamente con l'errore \errcode{EAGAIN}. Anche in questo caso la sola +differenza fra le due funzioni è che la seconda non attende indefinitamente e +passato il tempo massimo \param{abs\_timeout} ritorna comunque con un errore +di \errcode{ETIMEDOUT}. + +Uno dei problemi sottolineati da Stevens in \cite{UNP2}, comuni ad entrambe le +tipologie di code messaggi, è che non è possibile per chi riceve identificare +chi è che ha inviato il messaggio, in particolare non è possibile sapere da +quale utente esso provenga. Infatti, in mancanza di un meccanismo interno al +kernel, anche se si possono inserire delle informazioni nel messaggio, queste +non possono essere credute, essendo completamente dipendenti da chi lo invia. +Vedremo però come, attraverso l'uso del meccanismo di notifica, sia possibile +superare in parte questo problema. + +Una caratteristica specifica delle code di messaggi POSIX è la possibilità di +usufruire di un meccanismo di notifica asincrono; questo può essere attivato +usando la funzione \funcd{mq\_notify}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_notify(mqd\_t mqdes, const struct sigevent *notification)} + +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 + messaggi. + \end{errlist}} +\end{prototype} + +Il meccanismo di notifica permette di segnalare in maniera asincrona ad un +processo la presenza di dati sulla coda, in modo da evitare la necessità di +bloccarsi nell'attesa. Per far questo un processo deve registrarsi con la +funzione \func{mq\_notify}, ed il meccanismo è disponibile per un solo +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 \figref{fig:file_sigevent}) introdotta dallo +standard POSIX.1b per gestire la notifica di eventi; per altri dettagli si può +vedere quanto detto in \secref{sec:file_asyncronous_io} a proposito dell'uso +della stessa struttura per l'invio dei segnali usati per l'I/O asincrono. + +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 +\figref{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 \secref{sec:sig_real_time} a proposito dei segnali + real-time.} posto che questo sia installato nella forma estesa vista in +\secref{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. + +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 +bloccato in una chiamata a \func{mq\_receive}, in questo caso infatti il +processo bloccato ha la precedenza ed il messaggio gli viene immediatamente +inviato, mentre per il meccanismo di notifica tutto funziona come se la coda +fosse rimasta vuota. + +Quando un messaggio arriva su una coda vuota al processo che si era registrato +viene inviato il segnale specificato da \code{notification->sigev\_signo}, e +la coda diventa disponibile per una ulteriore registrazione. Questo comporta +che se si vuole mantenere il meccanismo di notifica occorre ripetere la +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. + +L'invio del segnale di notifica avvalora alcuni campi di informazione +restituiti al gestore attraverso la struttura \struct{siginfo\_t} (definita in +\figref{fig:sig_siginfo_t}). In particolare \var{si\_pid} viene impostato al +valore del \acr{pid} del processo che ha emesso il segnale, \var{si\_uid} +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 + proposito in \secref{sec:sig_sigaction} e \secref{sec:sig_real_time}.} @@ -3809,16 +4292,59 @@ essenziale. 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. Esiste un'altra -versione, realizzata da Konstantin Knizhnik, che reimplementa l'interfaccia -POSIX usando i semafori di SysV IPC. +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 \secref{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{faxt user mode mutex}.}, con +il quale dovrebbe essere 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. L'interfaccia corrente è stata stabilizzata a partire dal kernel +2.5.40. + + \subsection{Memoria condivisa} \label{sec:ipc_posix_shm} La memoria condivisa è l'unico degli oggetti di IPC POSIX già presente nel -kernel ufficiale. +kernel ufficiale. Per poterla utilizzare occorre abilitare 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 (viene usato anche per la SysV shared memory). In più i suoi + contenuti, essendo trattati direttamente dalla memoria + virtuale\index{memoria virtuale} e possono essere salvati sullo swap + automaticamente.} abilitando l'opzione \texttt{CONFIG\_TMPFS} in fase di +compilazione del kernel, e montando il filesystem aggiungendo una riga tipo: +\begin{verbatim} +tmpfs /dev/shm tmpfs defaults 0 0 +\end{verbatim} +ad \file{/etc/fstab}, oppure dove si preferisce con un comando del +tipo:\footnote{il filesystem riconosce, oltre quelle mostrate, le opzioni + \texttt{uid} e \texttt{gid} che identificano rispettivamente utente e gruppo + cui assegnarne la titolarità, e \texttt{nr\_blocks} che permette di + specificarne la dimensione in blocchi, cioè in multipli di + \const{PAGECACHE\_SIZE}.} +\begin{verbatim} +mount -t tmpfs -o size=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs +\end{verbatim} + + + la memoria +condivisa è trattata come un filesystem separato, con tutte le caratteristiche +di un qualunque filesystem, %%% Local Variables: