X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;ds=sidebyside;f=ipc.tex;h=978e3b19e399cad38e84949e380ac68b63530fb9;hb=cfb949627c354806c241631fbcbd98b81f9014b8;hp=69c67608adf68d9d8cca93f5c97194bc6060fd5f;hpb=1950828363ce9bfcb68b2646e68b82d162e4313d;p=gapil.git diff --git a/ipc.tex b/ipc.tex index 69c6760..978e3b1 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,4 +1,4 @@ -<%% ipc.tex +%% ipc.tex %% %% Copyright (C) 2000-2013 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free @@ -690,9 +690,9 @@ stringa di richiesta dalla \textit{fifo} nota (che a questo punto si bloccherà tutte le volte che non ci sono richieste). Dopo di che, una volta terminata la stringa (\texttt{\small 40}) e selezionato (\texttt{\small 41}) un numero casuale per ricavare la frase da inviare, si procederà (\texttt{\small - 42--46}) all'apertura della \textit{fifo} per la risposta, che poi -\texttt{\small 47--48}) vi sarà scritta. Infine (\texttt{\small 49}) si chiude -la \textit{fifo} di risposta che non serve più. + 42--46}) all'apertura della \textit{fifo} per la risposta, che poi +(\texttt{\small 47--48}) vi sarà scritta. Infine (\texttt{\small 49}) si +chiude la \textit{fifo} di risposta che non serve più. Il codice del client è invece riportato in fig.~\ref{fig:ipc_fifo_client}, anche in questo caso si è omessa la gestione delle opzioni e la funzione che @@ -1422,7 +1422,7 @@ file; il suo prototipo è: \var{msg\_qbytes} oltre il limite \const{MSGMNB} senza essere amministratore. \end{errlist} - ed inoltre \errval{EFAULT} ed \errval{EINVAL} nel loro significato + ed inoltre \errval{EFAULT} ed \errval{EINVAL} nel loro significato generico.} \end{funcproto} @@ -1839,7 +1839,6 @@ dei \ids{PID} da parte dei processi, un client eseguito in un momento successivo potrebbe ricevere un messaggio non indirizzato a lui. - \subsection{I semafori} \label{sec:ipc_sysv_sem} @@ -1974,7 +1973,6 @@ quanto riguarda gli altri campi invece: effettuata, viene inizializzato a zero. \end{itemize*} - \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{.80\textwidth} @@ -2079,10 +2077,9 @@ specificata con \param{cmd}, ed opera o sull'intero insieme specificato da \param{semid} o sul singolo semaforo di un insieme, specificato da \param{semnum}. - \begin{figure}[!htb] \footnotesize \centering - \begin{minipage}[c]{.80\textwidth} + \begin{minipage}[c]{0.80\textwidth} \includestruct{listati/semun.h} \end{minipage} \normalsize @@ -2318,7 +2315,7 @@ possa essere ripristinato all'uscita del processo. Infine \var{sem\_op} è il campo che controlla qual'è l'operazione che viene eseguita e determina in generale il comportamento della chiamata a \func{semop}. I casi possibili per il valore di questo campo sono tre: -\begin{basedescript}{\desclabelwidth{2.0cm}} +\begin{basedescript}{\desclabelwidth{1.8cm}} \item[\var{sem\_op} $>0$] In questo caso il valore viene aggiunto al valore corrente di \var{semval} per il semaforo indicato. Questa operazione non causa mai un blocco del processo, ed eventualmente \func{semop} ritorna @@ -2410,7 +2407,7 @@ a queste strutture restano per compatibilità (in particolare con le vecchie versioni delle librerie del C, come le \acr{libc5}). \begin{figure}[!htb] - \centering \includegraphics[width=13cm]{img/semtruct} + \centering \includegraphics[width=12cm]{img/semtruct} \caption{Schema della struttura di un insieme di semafori.} \label{fig:ipc_sem_schema} \end{figure} @@ -2635,7 +2632,7 @@ norma, significa insieme a dei semafori. \begin{figure}[!htb] \footnotesize \centering - \begin{minipage}[c]{\textwidth} + \begin{minipage}[c]{0.80\textwidth} \includestruct{listati/shmid_ds.h} \end{minipage} \normalsize @@ -2652,7 +2649,7 @@ campo \var{shm\_perm} viene inizializzato come illustrato in sez.~\ref{sec:ipc_sysv_access_control}, e valgono le considerazioni ivi fatte relativamente ai permessi di accesso; per quanto riguarda gli altri campi invece: -\begin{itemize} +\begin{itemize*} \item il campo \var{shm\_segsz}, che esprime la dimensione del segmento, viene inizializzato al valore di \param{size}. \item il campo \var{shm\_ctime}, che esprime il tempo di creazione del @@ -2666,7 +2663,7 @@ invece: creato il segmento, viene inizializzato al \ids{PID} del processo chiamante. \item il campo \var{shm\_nattac}, che esprime il numero di processi agganciati al segmento viene inizializzato a zero. -\end{itemize} +\end{itemize*} Come per le code di messaggi e gli insiemi di semafori, anche per i segmenti di memoria condivisa esistono una serie di limiti imposti dal sistema. Alcuni @@ -2791,13 +2788,16 @@ si ha a cuore la portabilità. Questi comandi aggiuntivi sono: sez.~\ref{sec:sys_resource_limit}). \item[\const{SHM\_UNLOCK}] Disabilita il \itindex{memory~locking} \textit{memory locking} sul segmento di memoria condivisa. Fino al kernel - 2.6.9 solo l'amministratore poteva utilizzare questo comando. + 2.6.9 solo l'amministratore poteva utilizzare questo comando in + corrispondenza di un segmento da lui bloccato. \end{basedescript} -I primi tre comandi sono gli stessi già visti anche per le code di messaggi e -gli insiemi di semafori, gli ultimi due sono delle estensioni specifiche -previste da Linux, che permettono di abilitare e disabilitare il meccanismo -della \index{memoria~virtuale} memoria virtuale per il segmento. +A questi due, come per \func{msgctl} e \func{semctl}, si aggiungono tre +ulteriori valori, \const{IPC\_INFO}, \const{MSG\_STAT} e \const{MSG\_INFO}, +introdotti ad uso del programma \cmd{ipcs} per ottenere le informazioni +generali relative alle risorse usate dai segmenti di memoria condivisa. Dato +che potranno essere modificati o rimossi in favore dell'uso di \texttt{/proc}, +non devono essere usati e non li tratteremo. L'argomento \param{buf} viene utilizzato solo con i comandi \const{IPC\_STAT} e \const{IPC\_SET} nel qual caso esso dovrà puntare ad una struttura @@ -2810,25 +2810,28 @@ l'interfaccia prevede due funzioni, \funcd{shmat} e \func{shmdt}. La prima di queste serve ad agganciare un segmento al processo chiamante, in modo che quest'ultimo possa inserirlo nel suo spazio di indirizzi per potervi accedere; il suo prototipo è: -\begin{functions} - \headdecl{sys/types.h} - \headdecl{sys/shm.h} - - \funcdecl{void *shmat(int shmid, const void *shmaddr, int shmflg)} - Aggancia al processo un segmento di memoria condivisa. - - \bodydesc{La funzione restituisce l'indirizzo del segmento in caso di - successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà i - valori: - \begin{errlist} + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/shm.h} +\fdecl{void *shmat(int shmid, const void *shmaddr, int shmflg)} + +\fdesc{Aggancia un segmento di memoria condivisa al processo chiamante.} +} + +{La funzione ritorna l'indirizzo del segmento in caso di successo e $-1$ (in + un cast a \type{void *}) per un errore, nel qual caso \var{errno} assumerà + uno dei valori: + \begin{errlist} \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 \param{shmid}, o un indirizzo non allineato sul confine di una pagina - per \param{shmaddr}. - \end{errlist} - ed inoltre \errval{ENOMEM}.} -\end{functions} + per \param{shmaddr} o il valore \val{NULL} indicando \const{SHM\_REMAP}. + \end{errlist} + ed inoltre \errval{ENOMEM} nel suo significato generico. +} +\end{funcproto} La funzione inserisce un segmento di memoria condivisa all'interno dello spazio di indirizzi del processo, in modo che questo possa accedervi @@ -2852,13 +2855,14 @@ L'argomento \param{shmaddr} specifica a quale indirizzo\footnote{lo standard come il valore di ritorno della funzione; in Linux è stato così con le \acr{libc4} e le \acr{libc5}, con il passaggio alla \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 -specificato è \val{NULL} è il sistema a scegliere opportunamente un'area di -memoria libera (questo è il modo più portabile e sicuro di usare la funzione). -Altrimenti il kernel aggancia il segmento all'indirizzo specificato da -\param{shmaddr}; questo però può avvenire solo se l'indirizzo coincide con il -limite di una pagina, cioè se è un multiplo esatto del parametro di sistema -\const{SHMLBA}, che in Linux è sempre uguale \const{PAGE\_SIZE}. + ritorno un \ctyp{void *} seguendo POSIX.1-2001.} deve essere associato il +segmento, se il valore specificato è \val{NULL} è il sistema a scegliere +opportunamente un'area di memoria libera (questo è il modo più portabile e +sicuro di usare la funzione). Altrimenti il kernel aggancia il segmento +all'indirizzo specificato da \param{shmaddr}; questo però può avvenire solo se +l'indirizzo coincide con il limite di una pagina, cioè se è un multiplo esatto +del parametro di sistema \const{SHMLBA}, che in Linux è sempre uguale +\const{PAGE\_SIZE}. Si tenga presente però che quando si usa \val{NULL} come valore di \param{shmaddr}, l'indirizzo restituito da \func{shmat} può cambiare da @@ -2867,15 +2871,17 @@ anche degli indirizzi, si deve avere cura di usare valori relativi (in genere riferiti all'indirizzo di partenza del segmento). L'argomento \param{shmflg} permette di cambiare il comportamento della -funzione; esso va specificato come maschera binaria, i bit utilizzati sono -solo due e sono identificati dalle costanti \const{SHM\_RND} e -\const{SHM\_RDONLY}, che vanno combinate con un OR aritmetico. Specificando -\const{SHM\_RND} si evita che \func{shmat} ritorni un errore quando -\param{shmaddr} non è allineato ai confini di una pagina. Si può quindi usare -un valore qualunque per \param{shmaddr}, e il segmento verrà comunque -agganciato, ma al più vicino multiplo di \const{SHMLBA} (il nome della +funzione; esso va specificato come maschera binaria, i bit utilizzati al +momento sono sono tre e sono identificati dalle costanti \const{SHM\_RND}, +\const{SHM\_RDONLY} e \const{SHM\_REMAP} che vanno combinate con un OR +aritmetico. + +Specificando \const{SHM\_RND} si evita che \func{shmat} ritorni un errore +quando \param{shmaddr} non è allineato ai confini di una pagina. Si può quindi +usare un valore qualunque per \param{shmaddr}, e il segmento verrà comunque +agganciato, ma al più vicino multiplo di \const{SHMLBA}; il nome della costante sta infatti per \textit{rounded}, e serve per specificare un -indirizzo come arrotondamento, in Linux è equivalente a \const{PAGE\_SIZE}). +indirizzo come arrotondamento. 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 @@ -2886,8 +2892,16 @@ 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}: +Infine \const{SHM\_REMAP} è una estensione specifica di Linux (quindi non +portabile) che indica che la mappatura del segmento deve rimpiazzare ogni +precedente mappatura esistente nell'intervallo iniziante +all'indirizzo \param{shmaddr} e di dimensione pari alla lunghezza del +segmento. In condizioni normali questo tipo di richiesta fallirebbe con un +errore di \errval{EINVAL}. Ovviamente usando \const{SHM\_REMAP} +l'argomento \param{shmaddr} non può essere nullo. + +In caso di successo la funzione \func{shmat} aggiorna anche i seguenti campi +della struttura \struct{shmid\_ds}: \begin{itemize*} \item il tempo \var{shm\_atime} dell'ultima operazione di aggancio viene impostato al tempo corrente. @@ -2895,7 +2909,7 @@ In caso di successo la funzione aggiorna anche i seguenti campi di segmento viene impostato a quello del processo corrente. \item il numero \var{shm\_nattch} di processi agganciati al segmento viene aumentato di uno. -\end{itemize*} +\end{itemize*} Come accennato in sez.~\ref{sec:proc_fork} un segmento di memoria condivisa agganciato ad un processo viene ereditato da un figlio attraverso una @@ -2909,18 +2923,21 @@ attraverso una \func{exit}. Una volta che un segmento di memoria condivisa non serve più, si può sganciarlo esplicitamente dal processo usando l'altra funzione dell'interfaccia, \funcd{shmdt}, il cui prototipo è: -\begin{functions} - \headdecl{sys/types.h} - \headdecl{sys/shm.h} - \funcdecl{int shmdt(const void *shmaddr)} - Sgancia dal processo un segmento di memoria condivisa. - - \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di - errore, la funzione fallisce solo quando non c'è un segmento agganciato - all'indirizzo \param{shmaddr}, con \var{errno} che assume il valore - \errval{EINVAL}.} -\end{functions} +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/shm.h} +\fdecl{int shmdt(const void *shmaddr)} + +\fdesc{Sgancia dal processo un segmento di memoria condivisa.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, la funzione + fallisce solo quando non c'è un segmento agganciato + all'indirizzo \param{shmaddr}, con \var{errno} che assume il valore + \errval{EINVAL}. +} +\end{funcproto} La funzione sgancia dallo spazio degli indirizzi del processo un segmento di memoria condivisa; questo viene identificato con l'indirizzo \param{shmaddr} @@ -3056,13 +3073,14 @@ si esegue (\texttt{\small 24--26}) su di esso una \func{chdir}, uscendo immediatamente in caso di errore. Questa funzione serve anche per impostare la \index{directory~di~lavoro} directory di lavoro del programma nella directory da tenere sotto controllo, in vista del successivo uso della -funzione \func{daemon}.\footnote{si noti come si è potuta fare questa scelta, - nonostante le indicazioni illustrate in sez.~\ref{sec:sess_daemon}, per il - particolare scopo del programma, che necessita comunque di restare - all'interno di una directory.} Infine (\texttt{\small 27--29}) si installano -i gestori per i vari segnali di terminazione che, avendo a che fare con un -programma che deve essere eseguito come server, sono il solo strumento -disponibile per concluderne l'esecuzione. +funzione \func{daemon}. Si noti come si è potuta fare questa scelta, +nonostante le indicazioni illustrate in sez.~\ref{sec:sess_daemon}, per il +particolare scopo del programma, che necessita comunque di restare all'interno +di una directory. + +Infine (\texttt{\small 27--29}) si installano i gestori per i vari segnali di +terminazione che, avendo a che fare con un programma che deve essere eseguito +come server, sono il solo strumento disponibile per concluderne l'esecuzione. Il passo successivo (\texttt{\small 30--39}) è quello di creare gli oggetti di intercomunicazione necessari. Si inizia costruendo (\texttt{\small 30}) la @@ -3098,16 +3116,18 @@ Il primo passo (\texttt{\small 41}) è 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 \index{directory~di~lavoro} directory di lavoro corrente. Una volta che il -programma è andato in background l'esecuzione prosegue (\texttt{\small - 42--48}) all'interno di un ciclo infinito: si inizia (\texttt{\small 43}) -bloccando il mutex con \func{MutexLock} per poter accedere alla memoria -condivisa (la funzione si bloccherà automaticamente se qualche client sta -leggendo), poi (\texttt{\small 44}) si cancellano i valori precedentemente -immagazzinati nella memoria condivisa con \func{memset}, e si esegue -(\texttt{\small 45}) un nuovo calcolo degli stessi utilizzando la funzione -\myfunc{dir\_scan}; infine (\texttt{\small 46}) si sblocca il mutex con -\func{MutexUnlock}, e si attende (\texttt{\small 47}) per il periodo di tempo -specificato a riga di comando con l'opzione \code{-p} con una \func{sleep}. +programma è andato in background l'esecuzione prosegue all'interno di un ciclo +infinito (\texttt{\small 42--48}). + +Si inizia (\texttt{\small 43}) bloccando il mutex con \func{MutexLock} per +poter accedere alla memoria condivisa (la funzione si bloccherà +automaticamente se qualche client sta leggendo), poi (\texttt{\small 44}) si +cancellano i valori precedentemente immagazzinati nella memoria condivisa con +\func{memset}, e si esegue (\texttt{\small 45}) un nuovo calcolo degli stessi +utilizzando la funzione \myfunc{dir\_scan}; infine (\texttt{\small 46}) si +sblocca il mutex con \func{MutexUnlock}, e si attende (\texttt{\small 47}) per +il periodo di tempo specificato a riga di comando con l'opzione \code{-p} +usando una \func{sleep}. Si noti come per il calcolo dei valori da mantenere nella memoria condivisa si sia usata ancora una volta la funzione \myfunc{dir\_scan}, già utilizzata (e @@ -3176,15 +3196,15 @@ 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} -[piccardi@gont sources]$ ./dirmonitor ./ -\end{Verbatim} +\begin{Console} +[piccardi@gont sources]$ \textbf{./dirmonitor ./} +\end{Console} %$ 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} -[piccardi@gont sources]$ ./readmon +\begin{Console} +[piccardi@gont sources]$ \textbf{./readmon} Ci sono 68 file dati Ci sono 3 directory Ci sono 0 link @@ -3193,14 +3213,14 @@ 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{Console} %$ 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} -[piccardi@gont sources]$ ipcs +\begin{Console} +[piccardi@gont sources]$ \textbf{ipcs} ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0xffffffff 54067205 piccardi 666 4096 1 @@ -3211,14 +3231,14 @@ key semid owner perms nsems ------ Message Queues -------- key msqid owner perms used-bytes messages -\end{Verbatim} +\end{Console} %$ 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} -[piccardi@gont sources]$ ./readmon +\begin{Console} +[piccardi@gont sources]$ \textbf{./readmon} Ci sono 69 file dati Ci sono 3 directory Ci sono 0 link @@ -3227,21 +3247,21 @@ 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{Console} %$ A questo punto possiamo far uscire il server inviandogli un segnale di \signal{SIGTERM} con il comando \code{killall dirmonitor}, a questo punto ripetendo la lettura, otterremo un errore: -\begin{Verbatim} -[piccardi@gont sources]$ ./readmon +\begin{Console} +[piccardi@gont sources]$ \textbf{./readmon} Cannot find shared memory: No such file or directory -\end{Verbatim} +\end{Console} %$ e inoltre potremo anche verificare che anche gli oggetti di intercomunicazione visti in precedenza sono stati regolarmente cancellati: -\begin{Verbatim} -[piccardi@gont sources]$ ipcs +\begin{Console} +[piccardi@gont sources]$ \textbf{ipcs} ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status @@ -3250,7 +3270,7 @@ key semid owner perms nsems ------ Message Queues -------- key msqid owner perms used-bytes messages -\end{Verbatim} +\end{Console} %$ @@ -3301,7 +3321,7 @@ diversa con un uso combinato della memoria condivisa e dei meccanismi di sincronizzazione, per cui alla fine l'uso delle code di messaggi classiche è relativamente poco diffuso. -% TODO: trattare qui, se non ssis trova posto migliore, copy_from_process e +% TODO: trattare qui, se non si trova posto migliore, copy_from_process e % copy_to_process, introdotte con il kernel 3.2. Vedi % http://lwn.net/Articles/405346/ e % http://ozlabs.org/~cyeoh/cma/process_vm_readv.txt @@ -3321,19 +3341,19 @@ necessità di un contatore come i semafori, si possono utilizzare metodi alternativi. La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare -dei \textsl{file di lock} (per i quali esiste anche una opportuna directory, -\file{/var/lock}, nel filesystem standard). Per questo si usa la -caratteristica della funzione \func{open} (illustrata in -sez.~\ref{sec:file_open_close}) che prevede\footnote{questo è quanto dettato dallo - standard POSIX.1, ciò non toglie che in alcune implementazioni questa - tecnica possa non funzionare; in particolare per Linux, nel caso di NFS, si - è comunque soggetti alla possibilità di una \itindex{race~condition} - \textit{race condition}.} che essa ritorni un errore quando usata con i -flag di \const{O\_CREAT} e \const{O\_EXCL}. In tal modo la creazione di un -\textsl{file di lock} può essere eseguita atomicamente, il processo che crea -il file con successo si può considerare come titolare del lock (e della -risorsa ad esso associata) mentre il rilascio si può eseguire con una chiamata -ad \func{unlink}. +dei \textsl{file di lock} (per i quali è stata anche riservata una opportuna +directory, \file{/var/lock}, nella standardizzazione del \textit{Filesystem + Hyerarchy Standard}). Per questo si usa la caratteristica della funzione +\func{open} (illustrata in sez.~\ref{sec:file_open_close}) che +prevede\footnote{questo è quanto dettato dallo standard POSIX.1, ciò non + toglie che in alcune implementazioni questa tecnica possa non funzionare; in + particolare per Linux, nel caso di NFS, si è comunque soggetti alla + possibilità di una \itindex{race~condition} \textit{race condition}.} che +essa ritorni un errore quando usata con i flag di \const{O\_CREAT} e +\const{O\_EXCL}. In tal modo la creazione di un \textsl{file di lock} può +essere eseguita atomicamente, il processo che crea il file con successo si può +considerare come titolare del lock (e della risorsa ad esso associata) mentre +il rilascio si può eseguire con una chiamata ad \func{unlink}. Un esempio dell'uso di questa funzione è mostrato dalle funzioni \func{LockFile} ed \func{UnlockFile} riportate in fig.~\ref{fig:ipc_file_lock} @@ -3496,22 +3516,24 @@ nessun inconveniente. \label{sec:ipc_mmap_anonymous} \itindbeg{memory~mapping} Abbiamo già visto che quando i processi sono -\textsl{correlati}\footnote{se cioè hanno almeno un progenitore comune.} l'uso -delle \textit{pipe} può costituire 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. +\textsl{correlati}, se cioè hanno almeno un progenitore comune, l'uso delle +\textit{pipe} può costituire 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. In sez.~\ref{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 tecnica per creare una memoria condivisa fra processi diversi è estremamente -inefficiente, in quanto occorre passare attraverso il disco. Però abbiamo -visto anche che se si esegue la mappatura con il flag \const{MAP\_ANONYMOUS} -la regione mappata non viene associata a nessun file, anche se quanto scritto -rimane in memoria e può essere riletto; allora, dato che un processo figlio -mantiene nel suo spazio degli indirizzi anche le regioni mappate, esso sarà -anche in grado di accedere a quanto in esse è contenuto. +inefficiente, in quanto occorre passare attraverso il disco. + +Però abbiamo visto anche che se si esegue la mappatura con il flag +\const{MAP\_ANONYMOUS} la regione mappata non viene associata a nessun file, +anche se quanto scritto rimane in memoria e può essere riletto; allora, dato +che un processo figlio mantiene nel suo spazio degli indirizzi anche le +regioni mappate, esso sarà anche in grado di accedere a quanto in esse è +contenuto. In questo modo diventa possibile creare una memoria condivisa fra processi diversi, purché questi abbiano almeno un progenitore comune che ha effettuato @@ -3560,7 +3582,7 @@ 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 \textit{pathname}, in particolare non essere più lunghi di \const{PATH\_MAX} byte e terminati da un carattere nullo. @@ -3569,31 +3591,32 @@ richiesto è che: 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 -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, sia per -quanto riguarda la memoria condivisa ed i semafori, che per quanto riguarda le -code di messaggi, tutto viene creato usando come radici delle opportune -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 -eventuali sottodirectory) rispetto a queste radici. +è subordinato in maniera quasi completa alla relativa implementazione, 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, sia per quanto riguarda la memoria condivisa ed i semafori, +che per quanto riguarda le code di messaggi, tutto viene creato usando come +radici delle opportune 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 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, 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. +comandi di accesso ai file, che funzionano come su dei file normali; questo +però è vero nel caso di Linux, che usa una implementazione che lo consente, +non è detto che altrettanto valga per 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. In particolare i permessi associati agli oggetti di IPC POSIX sono identici ai permessi dei file, ed il controllo di accesso segue esattamente la stessa @@ -3626,16 +3649,16 @@ relativi patch) occorre utilizzare la libreria \file{libmqueue}\footnote{i corrispondenza all'inclusione del supporto nel kernel ufficiale anche \file{libmqueue} è stata inserita nella \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.} +POSIX (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 \conffile{/etc/fstab} una riga come: -\begin{verbatim} +\begin{Example} mqueue /dev/mqueue mqueue defaults 0 0 -\end{verbatim} +\end{Example} 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 @@ -3645,20 +3668,19 @@ 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, + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{mqd\_t mq\_open(const char *name, int oflag)} +\fdecl{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 per un errore; nel quel caso \var{errno} assumerà i - valori: - \begin{errlist} + +\fdesc{Apre una coda di messaggi POSIX impostandone le caratteristiche.} +} + +{La funzione ritorna il descrittore associato alla coda in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} \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 @@ -3668,23 +3690,23 @@ di messaggi POSIX è \funcd{mq\_open}, ed il suo prototipo è: 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}, \errval{EINTR} ed \errval{ENFILE}. -} -\end{functions} + \end{errlist} + ed inoltre \errval{ENOMEM}, \errval{ENOSPC}, \errval{EFAULT}, + \errval{EMFILE}, \errval{EINTR} ed \errval{ENFILE} nel loro significato + generico. +} +\end{funcproto} 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{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. +\type{mqd\_t}. 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 @@ -3730,7 +3752,7 @@ cui definizione è riportata in fig.~\ref{fig:ipc_mq_attr}. \begin{figure}[!htb] \footnotesize \centering - \begin{minipage}[c]{\textwidth} + \begin{minipage}[c]{0.90\textwidth} \includestruct{listati/mq_attr.h} \end{minipage} \normalsize @@ -3750,44 +3772,49 @@ 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 per un errore; - nel quel caso \var{errno} assumerà i valori \errval{EBADF} o - \errval{EINTR}.} -\end{prototype} +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_close(mqd\_t mqdes)} + +\fdesc{Chiude una coda di messaggi.} +} -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 +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori \errval{EBADF} o \errval{EINTR} nel + loro significato generico. +} +\end{funcproto} + +La funzione è analoga a \func{close} (e 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} +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_unlink(const char *name)} + +\fdesc{Rimuove una coda di messaggi.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà gli stessi valori riportati da \func{unlink}. +} +\end{funcproto} 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 rimuove la coda \param{name}, così -che una successiva chiamata a \func{mq\_open} fallisce o crea una coda -diversa. +\func{unlink} per i file, (e di nuovo l'implementazione di Linux usa +direttamente \func{unlink}) la funzione rimuove 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 @@ -3804,20 +3831,21 @@ riavvio del sistema. 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} - - \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, + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_getattr(mqd\_t mqdes, struct mq\_attr *mqstat)} +\fdesc{Legge gli attributi di una coda di messaggi POSIX.} +\fdecl{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} +\fdesc{Modifica gli attributi di una coda di messaggi POSIX.} +} + +{Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, + nel qual caso \var{errno} assumerà i valori \errval{EBADF} + o \errval{EINVAL} nel loro significato generico. +} +\end{funcproto} La funzione \func{mq\_getattr} legge i valori correnti degli attributi della coda nella struttura puntata da \param{mqstat}; di questi l'unico relativo @@ -3839,22 +3867,21 @@ 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$ per un - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_send(mqd\_t mqdes, const char *msg\_ptr, size\_t msg\_len, + unsigned int msg\_prio)} +\fdesc{Esegue l'inserimento di un messaggio su una coda.} +\fdecl{int mq\_timedsend(mqd\_t mqdes, const char *msg\_ptr, size\_t + msg\_len, unsigned msg\_prio, const struct timespec *abs\_timeout)} +\fdesc{Esegue l'inserimento di un messaggio su una coda entro un tempo + specificato} +} + +{Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, + nel qual caso \var{errno} assumerà uno dei 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} @@ -3864,9 +3891,11 @@ Per inserire messaggi su di una coda sono previste due funzioni, 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}, \errval{ENOMEM} ed \errval{EINTR}.} -\end{functions} + \end{errlist} + ed inoltre \errval{EBADF}, \errval{ENOMEM} ed \errval{EINTR} nel loro + significato generico. +} +\end{funcproto} Entrambe le funzioni richiedono un puntatore al testo del messaggio nell'argomento \param{msg\_ptr} e la relativa lunghezza in \param{msg\_len}.