%% ipc.tex
%%
-%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to
+%% Copyright (C) 2000-2005 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 "Prefazione",
+%% Free Software Foundation; with the Invariant Sections being "Un preambolo",
%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the
%% license is included in the section entitled "GNU Free Documentation
%% License".
Le \textit{pipe} nascono sostanzialmente con Unix, e sono il primo, e tuttora
uno dei più usati, meccanismi di comunicazione fra processi. Si tratta in
-sostanza di una una coppia di file descriptor\footnote{si tenga presente che
+sostanza di una coppia di file descriptor\footnote{si tenga presente che
le pipe sono oggetti creati dal kernel e non risiedono su disco.} connessi
fra di loro in modo che se quanto scrive su di uno si può rileggere
dall'altro. Si viene così a costituire un canale di comunicazione tramite i
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
-\secref{sec:sys_file_limits}). Lo schema di funzionamento di una pipe è
-illustrato in \figref{fig:ipc_pipe_singular}, in cui sono illustrati i due
+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
indicano la direzione del flusso dei dati.
\end{figure}
Chiaramente creare una pipe all'interno di un singolo processo non serve a
-niente; se però ricordiamo quanto esposto in \secref{sec:file_sharing}
+niente; se però ricordiamo quanto esposto in sez.~\ref{sec:file_sharing}
riguardo al comportamento dei file descriptor nei processi figli, è immediato
capire come una pipe possa diventare un meccanismo di intercomunicazione. Un
processo figlio infatti condivide gli stessi file descriptor del padre,
compresi quelli associati ad una pipe (secondo la situazione illustrata in
-\figref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un
+fig.~\ref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un
capo della pipe, l'altro può leggere.
\begin{figure}[htb]
Tutto ciò ci mostra come sia immediato realizzare un meccanismo di
comunicazione fra processi attraverso una pipe, utilizzando le proprietà
-ordinarie dei file, ma ci mostra anche qual'è il principale\footnote{Stevens
+ordinarie dei file, ma ci mostra anche qual è il principale\footnote{Stevens
in \cite{APUE} riporta come limite anche il fatto che la comunicazione è
unidirezionale, ma in realtà questo è un limite facilmente superabile usando
una coppia di pipe.} limite nell'uso delle pipe. È necessario infatti che i
pipe il cui capo in scrittura è stato chiuso, si avrà la ricezione di un EOF
(vale a dire che la funzione \func{read} ritornerà restituendo 0). Se invece
si esegue una scrittura su una pipe il cui capo in lettura non è aperto il
-processo riceverà il segnale \errcode{EPIPE}, e la funzione di scrittura
+processo riceverà il segnale \const{SIGPIPE}, e la funzione di scrittura
restituirà un errore di \errcode{EPIPE} (al ritorno del gestore, o qualora il
segnale sia ignorato o bloccato).
\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 parametro di input.
+di un codice a barre, specificato come argomento in ingresso.
Un programma che deve essere eseguito come \textit{CGI} deve rispondere a
delle caratteristiche specifiche, esso infatti non viene lanciato da una
shell, ma dallo stesso web server, alla richiesta di una specifica URL, che di
solito ha la forma:
\begin{verbatim}
- http://www.sito.it/cgi-bin/programma?parametro
+ http://www.sito.it/cgi-bin/programma?argomento
\end{verbatim}
ed il risultato dell'elaborazione deve essere presentato (con una intestazione
che ne descrive il mime-type) sullo standard output, in modo che il web-server
codici a barre corrispondenti ad una qualunque stringa, mentre il secondo
serve per poter effettuare la conversione della stessa immagine in formato
JPEG. Usando una pipe potremo inviare l'output del primo sull'input del
-secondo, secondo lo schema mostrato in \figref{fig:ipc_pipe_use}, in cui la
+secondo, secondo lo schema mostrato in fig.~\ref{fig:ipc_pipe_use}, in cui la
direzione del flusso dei dati è data dalle frecce continue.
\begin{figure}[htb]
\centering
\includegraphics[height=5cm]{img/pipeuse}
\caption{Schema dell'uso di una pipe come mezzo di comunicazione fra
- due processi attraverso attraverso l'esecuzione una \func{fork} e la
- chiusura dei capi non utilizzati.}
+ due processi attraverso l'esecuzione una \func{fork} e la chiusura dei
+ capi non utilizzati.}
\label{fig:ipc_pipe_use}
\end{figure}
Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato
intermedio su un file temporaneo. Questo però non tiene conto del fatto che un
\textit{CGI} deve poter gestire più richieste in concorrenza, e si avrebbe una
-evidente race condition\index{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.
+evidente \textit{race condition}\index{\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.
Il programma ci servirà anche come esempio dell'uso delle funzioni di
duplicazione dei file descriptor che abbiamo trattato in
-\secref{sec:file_dup}, in particolare di \func{dup2}. È attraverso queste
+sez.~\ref{sec:file_dup}, in particolare di \func{dup2}. È attraverso queste
funzioni infatti che è possibile dirottare gli stream standard dei processi
-(che abbiamo visto in \secref{sec:file_std_descr} e
-\secref{sec:file_std_stream}) sulla pipe. In \figref{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.
+(che abbiamo visto in sez.~\ref{sec:file_std_descr} e
+sez.~\ref{sec:file_std_stream}) sulla pipe. In
+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}
- \begin{lstlisting}{}
-int main(int argc, char *argv[], char *envp[])
-{
- ...
- /* create two pipes, pipein and pipeout, to handle communication */
- if ( (retval = pipe(pipein)) ) {
- WriteMess("input pipe creation error");
- exit(0);
- }
- if ( (retval = pipe(pipeout)) ) {
- WriteMess("output pipe creation error");
- exit(0);
- }
- /* First fork: use child to run barcode program */
- if ( (pid = fork()) == -1) { /* on error exit */
- WriteMess("child creation error");
- exit(0);
- }
- /* if child */
- if (pid == 0) {
- close(pipein[1]); /* close pipe write end */
- dup2(pipein[0], STDIN_FILENO); /* remap stdin to pipe read end */
- close(pipeout[0]);
- dup2(pipeout[1], STDOUT_FILENO); /* remap stdout in pipe output */
- execlp("barcode", "barcode", size, NULL);
- }
- close(pipein[0]); /* close input side of input pipe */
- write(pipein[1], argv[1], strlen(argv[1])); /* write parameter to pipe */
- close(pipein[1]); /* closing write end */
- waitpid(pid, NULL, 0); /* wait child completion */
- /* Second fork: use child to run ghostscript */
- if ( (pid = fork()) == -1) {
- WriteMess("child creation error");
- exit(0);
- }
- /* second child, convert PS to JPEG */
- if (pid == 0) {
- close(pipeout[1]); /* close write end */
- dup2(pipeout[0], STDIN_FILENO); /* remap read end to stdin */
- /* send mime type */
- write(STDOUT_FILENO, content, strlen(content));
- execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL);
- }
- /* still parent */
- close(pipeout[1]);
- waitpid(pid, NULL, 0);
- exit(0);
-}
- \end{lstlisting}
+ \includecodesample{listati/BarCodePage.c}
\end{minipage}
\normalsize
\caption{Sezione principale del codice del \textit{CGI}
per produrre il codice a barre; si ha cura di controllare la riuscita della
chiamata, inviando in caso di errore un messaggio invece dell'immagine
richiesta.\footnote{la funzione \func{WriteMess} non è riportata in
- \secref{fig:ipc_barcodepage_code}; essa si incarica semplicemente di
+ fig.~\ref{fig:ipc_barcodepage_code}; essa si incarica semplicemente di
formattare l'uscita alla maniera dei CGI, aggiungendo l'opportuno
\textit{mime type}, e formattando il messaggio in HTML, in modo che
quest'ultimo possa essere visualizzato correttamente da un browser.}
quindi associato allo standard input) in caso di \code{"w"}.
Lo stream restituito da \func{popen} è identico a tutti gli effetti ai file
-stream visti in \capref{cha:files_std_interface}, anche se è collegato ad una
-pipe e non ad un file, e viene sempre aperto in modalità
-\textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica differenza
-con gli usuali stream è che dovrà essere chiuso dalla seconda delle due nuove
-funzioni, \funcd{pclose}, il cui prototipo è:
+stream visti in cap.~\ref{cha:files_std_interface}, anche se è collegato ad
+una pipe e non ad un file, e viene sempre aperto in modalità
+\textit{fully-buffered} (vedi sez.~\ref{sec:file_buffering}); l'unica
+differenza con gli usuali stream è che dovrà essere chiuso dalla seconda delle
+due nuove funzioni, \funcd{pclose}, il cui prototipo è:
\begin{prototype}{stdio.h}
{int pclose(FILE *stream)}
\func{popen}.
Per illustrare l'uso di queste due funzioni riprendiamo il problema
-precedente: il programma mostrato in \figref{fig:ipc_barcodepage_code} per
+precedente: il programma mostrato in fig.~\ref{fig:ipc_barcodepage_code} per
quanto funzionante, è (volutamente) codificato in maniera piuttosto complessa,
inoltre nella pratica sconta un problema di \cmd{gs} che non è in
grado\footnote{nella versione GNU Ghostscript 6.53 (2002-02-13).} di
Nel nostro caso, dato che ciascun processo deve scrivere il suo output sullo
standard input del successivo, occorrerà usare \func{popen} aprendo la pipe in
scrittura. Il codice del nuovo programma è riportato in
-\figref{fig:ipc_barcode_code}. Come si può notare l'ordine di invocazione dei
-programmi è l'inverso di quello in cui ci si aspetta che vengano
+fig.~\ref{fig:ipc_barcode_code}. Come si può notare l'ordine di invocazione
+dei programmi è l'inverso di quello in cui ci si aspetta che vengano
effettivamente eseguiti. Questo non comporta nessun problema dato che la
lettura su una pipe è bloccante, per cui ciascun processo, per quanto lanciato
per primo, si bloccherà in attesa di ricevere sullo standard input il
-risultato dell'elaborazione del precedente, benchè quest'ultimo venga
-invocato dopo.
+risultato dell'elaborazione del precedente, benché quest'ultimo venga invocato
+dopo.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-int main(int argc, char *argv[], char *envp[])
-{
- FILE *pipe[4];
- FILE *pipein;
- char *cmd_string[4]={
- "pnmtopng",
- "pnmmargin -white 10",
- "pnmcrop",
- "gs -sDEVICE=ppmraw -sOutputFile=- -sNOPAUSE -q - -c showpage -c quit"
- };
- char content[]="Content-type: image/png\n\n";
- int i;
- /* write mime-type to stdout */
- write(STDOUT_FILENO, content, strlen(content));
- /* execute chain of command */
- for (i=0; i<4; i++) {
- pipe[i] = popen(cmd_string[i], "w");
- dup2(fileno(pipe[i]), STDOUT_FILENO);
- }
- /* create barcode (in PS) */
- pipein = popen("barcode", "w");
- /* send barcode string to barcode program */
- write(fileno(pipein), argv[1], strlen(argv[1]));
- /* close all pipes (in reverse order) */
- for (i=4; i==0; i--) {
- pclose((pipe[i]));
- }
- exit(0);
-}
- \end{lstlisting}
+ \includecodesample{listati/BarCode.c}
\end{minipage}
\normalsize
\caption{Codice completo del \textit{CGI} \file{BarCode.c}.}
\subsection{Le \textit{pipe} con nome, o \textit{fifo}}
\label{sec:ipc_named_pipe}
-Come accennato in \secref{sec:ipc_pipes} il problema delle \textit{pipe} è che
-esse possono essere utilizzate solo da processi con un progenitore comune o
-nella relazione padre/figlio; per superare questo problema lo standard POSIX.1
-ha definito dei nuovi oggetti, le \textit{fifo}, che hanno le stesse
+Come accennato in sez.~\ref{sec:ipc_pipes} il problema delle \textit{pipe} è
+che esse possono essere utilizzate solo da processi con un progenitore comune
+o nella relazione padre/figlio; per superare questo problema lo standard
+POSIX.1 ha definito dei nuovi oggetti, le \textit{fifo}, che hanno le stesse
caratteristiche delle pipe, ma che invece di essere strutture interne del
kernel, visibili solo attraverso un file descriptor, sono accessibili
attraverso un inode\index{inode} che risiede sul filesystem, così che i
l'inode\index{inode} allocato sul filesystem serve infatti solo a fornire un
punto di riferimento per i processi, che permetta loro di accedere alla stessa
fifo; il comportamento delle funzioni di lettura e scrittura è identico a
-quello illustrato per le pipe in \secref{sec:ipc_pipes}.
+quello illustrato per le pipe in sez.~\ref{sec:ipc_pipes}.
-Abbiamo già visto in \secref{sec:file_mknod} le funzioni \func{mknod} e
+Abbiamo già visto in sez.~\ref{sec:file_mknod} le funzioni \func{mknod} e
\func{mkfifo} che permettono di creare una fifo; per utilizzarne una un
processo non avrà che da aprire il relativo file speciale o in lettura o
scrittura; nel primo caso sarà collegato al capo di uscita della fifo, e dovrà
lettura; è possibile anche usare la fifo all'interno di un solo processo, nel
qual caso però occorre stare molto attenti alla possibili situazioni di
stallo.\footnote{se si cerca di leggere da una fifo che non contiene dati si
- avrà un deadlock\index{deadlock} immediato, dato che il processo si blocca e
- non potrà quindi mai eseguire le funzioni di scrittura.}
+ avrà un deadlock\index{\textit{deadlock}} immediato, dato che il processo si
+ blocca e non potrà quindi mai eseguire le funzioni di scrittura.}
Per la loro caratteristica di essere accessibili attraverso il filesystem, è
piuttosto frequente l'utilizzo di una fifo come canale di comunicazione nelle
fondamentale che le operazioni di scrittura siano atomiche; per questo si deve
sempre tenere presente che questo è vero soltanto fintanto che non si supera
il limite delle dimensioni di \const{PIPE\_BUF} (si ricordi quanto detto in
-\secref{sec:ipc_pipes}).
+sez.~\ref{sec:ipc_pipes}).
A parte il caso precedente, che resta probabilmente il più comune, Stevens
riporta in \cite{APUE} altre due casistiche principali per l'uso delle fifo:
sull'input di parecchi altri (attraverso l'uso del comando \cmd{tee}).
\item Come canale di comunicazione fra client ed server (il modello
- \textit{client-server} è illustrato in \secref{sec:net_cliserv}).
+ \textit{client-server} è illustrato in sez.~\ref{sec:net_cliserv}).
\end{itemize}
Nel primo caso quello che si fa è creare tante fifo, da usare come standard
leggerli, quando i dati inviati sono destinati a loro.
Per risolvere questo problema, si può usare un'architettura come quella
-illustrata in \figref{fig:ipc_fifo_server_arch} in cui i client inviano le
+illustrata in fig.~\ref{fig:ipc_fifo_server_arch} in cui i client inviano le
richieste al server su una fifo nota mentre le risposte vengono reinviate dal
server a ciascuno di essi su una fifo temporanea creata per l'occasione.
un detto a caso estratto da un insieme di frasi; sia il numero delle frasi
dell'insieme, che i file da cui esse vengono lette all'avvio, sono importabili
da riga di comando. Il corpo principale del server è riportato in
-\figref{fig:ipc_fifo_server}, dove si è tralasciata la parte che tratta la
+fig.~\ref{fig:ipc_fifo_server}, dove si è tralasciata la parte che tratta la
gestione delle opzioni a riga di comando, che effettua il settaggio delle
variabili \var{fortunefilename}, che indica il file da cui leggere le frasi,
ed \var{n}, che indica il numero di frasi tenute in memoria, ad un valore
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-char *fifoname = "/tmp/fortune.fifo";
-int main(int argc, char *argv[])
-{
-/* Variables definition */
- int i, n = 0;
- char *fortunefilename = "/usr/share/games/fortunes/linux";
- char **fortune;
- char line[80];
- int fifo_server, fifo_client;
- int nread;
- ...
- if (n==0) usage(); /* if no pool depth exit printing usage info */
- Signal(SIGTERM, HandSIGTERM); /* set handlers for termination */
- Signal(SIGINT, HandSIGTERM);
- Signal(SIGQUIT, HandSIGTERM);
- i = FortuneParse(fortunefilename, fortune, n); /* parse phrases */
- if (mkfifo(fifoname, 0622)) { /* create well known fifo if does't exist */
- if (errno!=EEXIST) {
- perror("Cannot create well known fifo");
- exit(1);
- }
- }
- daemon(0, 0);
- /* open fifo two times to avoid EOF */
- fifo_server = open(fifoname, O_RDONLY);
- if (fifo_server < 0) {
- perror("Cannot open read only well known fifo");
- exit(1);
- }
- if (open(fifoname, O_WRONLY) < 0) {
- perror("Cannot open write only well known fifo");
- exit(1);
- }
- /* Main body: loop over requests */
- while (1) {
- nread = read(fifo_server, line, 79); /* read request */
- if (nread < 0) {
- perror("Read Error");
- exit(1);
- }
- line[nread] = 0; /* terminate fifo name string */
- n = random() % i; /* select random value */
- fifo_client = open(line, O_WRONLY); /* open client fifo */
- if (fifo_client < 0) {
- perror("Cannot open");
- exit(1);
- }
- nread = write(fifo_client, /* write phrase */
- fortune[n], strlen(fortune[n])+1);
- close(fifo_client); /* close client fifo */
- }
-}
- \end{lstlisting}
+ \includecodesample{listati/FortuneServer.c}
\end{minipage}
\normalsize
\caption{Sezione principale del codice del server di \textit{fortunes}
presenza di un valore nullo provoca l'uscita dal programma attraverso la
routine (non riportata) che ne stampa le modalità d'uso. Dopo di che installa
(\texttt{\small 13--15}) la funzione che gestisce i segnali di interruzione
-(anche questa non è riportata in \figref{fig:ipc_fifo_server}) che si limita a
-rimuovere dal filesystem la fifo usata dal server per comunicare.
+(anche questa non è riportata in fig.~\ref{fig:ipc_fifo_server}) che si limita
+a rimuovere dal filesystem la fifo usata dal server per comunicare.
Terminata l'inizializzazione (\texttt{\small 16}) si effettua la chiamata alla
funzione \code{FortuneParse} che legge dal file specificato in
sarà scritta. Infine (\texttt{\small 49}) si chiude la fifo di risposta che
non serve più.
-Il codice del client è invece riportato in \figref{fig:ipc_fifo_client}, anche
-in questo caso si è omessa la gestione delle opzioni e la funzione che stampa
-a video le informazioni di utilizzo ed esce, riportando solo la sezione
+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
+stampa a video le informazioni di utilizzo ed esce, riportando solo la sezione
principale del programma e le definizioni delle variabili. Il codice completo
è nel file \file{FortuneClient.c} dei sorgenti allegati.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-int main(int argc, char *argv[])
-{
-/* Variables definition */
- int n = 0;
- char *fortunefilename = "/tmp/fortune.fifo";
- char line[80];
- int fifo_server, fifo_client;
- char fifoname[80];
- int nread;
- char buffer[PIPE_BUF];
- ...
- snprintf(fifoname, 80, "/tmp/fortune.%d", getpid()); /* compose name */
- if (mkfifo(fifoname, 0622)) { /* open client fifo */
- if (errno!=EEXIST) {
- perror("Cannot create well known fifo");
- exit(-1);
- }
- }
- fifo_server = open(fortunefilename, O_WRONLY); /* open server fifo */
- if (fifo_server < 0) {
- perror("Cannot open well known fifo");
- exit(-1);
- }
- nread = write(fifo_server, fifoname, strlen(fifoname)+1); /* write name */
- close(fifo_server); /* close server fifo */
- fifo_client = open(fifoname, O_RDONLY); /* open client fifo */
- if (fifo_client < 0) {
- perror("Cannot open well known fifo");
- exit(-1);
- }
- nread = read(fifo_client, buffer, sizeof(buffer)); /* read answer */
- printf("%s", buffer); /* print fortune */
- close(fifo_client); /* close client */
- close(fifo_server); /* close server */
- unlink(fifoname); /* remove client fifo */
-}
- \end{lstlisting}
+ \includecodesample{listati/FortuneClient.c}
\end{minipage}
\normalsize
\caption{Sezione principale del codice del client di \textit{fortunes}
occorrerà definire la speciale variabile di ambiente \code{LD\_LIBRARY\_PATH}
in modo che il linker dinamico possa accedervi.
-In generale questa variabile indica il pathname della directory contenente la
+In generale questa variabile indica il
+\index{\textit{pathname}}\textit{pathname} della directory contenente la
libreria. Nell'ipotesi (che daremo sempre per 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
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 \secref{sec:sess_daemon}):
+terminale di controllo (si ricordi quanto detto in sez.~\ref{sec:sess_daemon}):
\begin{verbatim}
[piccardi@gont sources]$ ps aux
...
fifo non è adatta a risolvere questo tipo di problemi, che possono essere
affrontati in maniera più semplice ed efficace o usando i
\textit{socket}\index{socket} (che tratteremo in dettaglio a partire da
-\capref{cha:socket_intro}) o ricorrendo a meccanismi di comunicazione diversi,
-come quelli che esamineremo in seguito.
+cap.~\ref{cha:socket_intro}) o ricorrendo a meccanismi di comunicazione
+diversi, come quelli che esamineremo in seguito.
Un meccanismo di comunicazione molto simile alle pipe, ma che non presenta il
problema della unidirezionalità del flusso dei dati, è quello dei cosiddetti
\textsl{socket locali} (o \textit{Unix domain socket}). Tratteremo l'argomento
-dei \textit{socket}\index{socket} in \capref{cha:socket_intro},\footnote{si
+dei \textit{socket}\index{socket} in cap.~\ref{cha:socket_intro},\footnote{si
tratta comunque di oggetti di comunicazione che, come le pipe, sono
utilizzati attraverso dei file descriptor.} nell'ambito dell'interfaccia
generale che essi forniscono per la programmazione di rete; e vedremo anche
-(in~\secref{sec:sock_sa_local}) come si possono definire dei file speciali (di
-tipo \textit{socket}, analoghi a quello associati alle fifo) cui si accede
+(in~sez.~\ref{sec:sock_sa_local}) come si possono definire dei file speciali
+(di tipo \textit{socket}, analoghi a quello associati alle fifo) cui si accede
però attraverso quella medesima interfaccia; vale però la pena esaminare qui
una modalità di uso dei socket locali\footnote{la funzione \func{socketpair} è
stata introdotta in BSD4.4, ma è supportata in genere da qualunque sistema
La funzione restituisce in \param{sv} la coppia di descrittori connessi fra di
loro: quello che si scrive su uno di essi sarà ripresentato in input
-sull'altro e viceversa. I parametri \param{domain}, \param{type} e
-\param{protocol} derivano dall'interfaccia dei socket\index{socket} (che è
-quella che fornisce il substrato per connettere i due descrittori), ma in
-questo caso i soli valori validi che possono essere specificati sono
-rispettivamente \const{AF\_UNIX}, \const{SOCK\_STREAM} e \val{0}.
+sull'altro e viceversa. Gli argomenti \param{domain}, \param{type} e
+\param{protocol} derivano dall'interfaccia dei socket\index{socket} (vedi
+sez.~\ref{sec:sock_creation}) che è quella che fornisce il substrato per
+connettere i due descrittori, ma in questo caso i soli valori validi che
+possono essere specificati sono rispettivamente \const{AF\_UNIX},
+\const{SOCK\_STREAM} e \val{0}.
L'utilità di chiamare questa funzione per evitare due chiamate a \func{pipe}
può sembrare limitata; in realtà l'utilizzo di questa funzione (e dei
linea non solo dei dati, ma anche dei file descriptor: si può cioè passare da
un processo ad un altro un file descriptor, con una sorta di duplicazione
dello stesso non all'interno di uno stesso processo, ma fra processi distinti
-(torneremo su questa funzionalità in \secref{sec:xxx_fd_passing}).
+(torneremo su questa funzionalità in sez.~\ref{sec:xxx_fd_passing}).
-\section{La comunicazione fra processi di System V}
+\section{Il sistema di comunicazione fra processi di System V}
\label{sec:ipc_sysv}
Benché le pipe e le fifo siano ancora ampiamente usate, esse scontano il
oggetti per la comunicazione fra processi ed una nuova interfaccia di
programmazione, che fossero in grado di garantire una maggiore flessibilità.
In questa sezione esamineremo come Linux supporta quello che viene chiamato il
-\textsl{Sistema di comunicazione inter-processo} di System V, cui da qui in
+\textsl{Sistema di comunicazione fra processi} di System V, cui da qui in
avanti faremo riferimento come \textit{SysV IPC} (dove IPC è la sigla di
\textit{Inter-Process Comunication}).
specificando il relativo \textsl{identificatore}. Questo è un numero
progressivo (un po' come il \acr{pid} dei processi) che il kernel assegna a
ciascuno di essi quanto vengono creati (sul procedimento di assegnazione
-torneremo in \secref{sec:ipc_sysv_id_use}). L'identificatore viene restituito
+torneremo in sez.~\ref{sec:ipc_sysv_id_use}). L'identificatore viene restituito
dalle funzioni che creano l'oggetto, ed è quindi locale al processo che le ha
eseguite. Dato che l'identificatore viene assegnato dinamicamente dal kernel
non è possibile prevedere quale sarà, né utilizzare un qualche valore statico,
si sposta il problema dell'accesso dalla classificazione in base
all'identificatore alla classificazione in base alla chiave, una delle tante
complicazioni inutili presenti nel \textit{SysV IPC}.} Oltre la chiave, la
-struttura, la cui definizione è riportata in \figref{fig:ipc_ipc_perm},
+struttura, la cui definizione è riportata in fig.~\ref{fig:ipc_ipc_perm},
mantiene varie proprietà ed informazioni associate all'oggetto.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm ]{}
-struct ipc_perm
-{
- key_t key; /* Key. */
- uid_t uid; /* Owner's user ID. */
- gid_t gid; /* Owner's group ID. */
- uid_t cuid; /* Creator's user ID. */
- gid_t cgid; /* Creator's group ID. */
- unsigned short int mode; /* Read/write permission. */
- unsigned short int seq; /* Sequence number. */
-};
- \end{lstlisting}
+ \includestruct{listati/ipc_perm.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{ipc\_perm}, come definita in
Usando la stessa chiave due processi diversi possono ricavare l'identificatore
associato ad un oggetto ed accedervi. Il problema che sorge a questo punto è
come devono fare per accordarsi sull'uso di una stessa chiave. Se i processi
-sono \textsl{parenti} la soluzione è relativamente semplice, in tal caso
+sono \textsl{imparentati} la soluzione è relativamente semplice, in tal caso
infatti si può usare il valore speciale \texttt{IPC\_PRIVATE} per creare un
nuovo oggetto nel processo padre, l'identificatore così ottenuto sarà
-disponibile in tutti i figli, e potrà essere passato come parametro attraverso
+disponibile in tutti i figli, e potrà essere passato come argomento attraverso
una \func{exec}.
-Però quando i processi non sono \textsl{parenti} (come capita tutte le volte
-che si ha a che fare con un sistema client-server) tutto questo non è
+Però quando i processi non sono \textsl{imparentati} (come capita tutte le
+volte che si ha a che fare con un sistema client-server) tutto questo non è
possibile; si potrebbe comunque salvare l'identificatore su un file noto, ma
questo ovviamente comporta lo svantaggio di doverselo andare a rileggere. Una
alternativa più efficace è quella che i programmi usino un valore comune per
\end{functions}
La funzione determina un valore della chiave sulla base di \param{pathname},
-che deve specificare il pathname di un file effettivamente esistente e di un
-numero di progetto \param{proj\_id)}, che di norma viene specificato come
-carattere, dato che ne vengono utilizzati solo gli 8 bit meno
-significativi.\footnote{nelle libc4 e libc5, come avviene in SunOS,
- l'argomento \param{proj\_id} è dichiarato tipo \ctyp{char}, le \acr{glibc}
- usano il prototipo specificato da XPG4, ma vengono lo stesso utilizzati gli
- 8 bit meno significativi.}
+che deve specificare il \index{\textit{pathname}}\textit{pathname} di un file
+effettivamente esistente e di un numero di progetto \param{proj\_id)}, che di
+norma viene specificato come carattere, dato che ne vengono utilizzati solo
+gli 8 bit meno significativi.\footnote{nelle libc4 e libc5, come avviene in
+ SunOS, l'argomento \param{proj\_id} è dichiarato tipo \ctyp{char}, le
+ \acr{glibc} usano il prototipo specificato da XPG4, ma vengono lo stesso
+ utilizzati gli 8 bit meno significativi.}
Il problema è che anche così non c'è la sicurezza che il valore della chiave
sia univoco, infatti esso è costruito combinando il byte di \param{proj\_id)}
identificare un oggetto, come sarebbe stato se lo si fosse associato ad in
file, e tutta l'interfaccia è inutilmente complessa. Per questo ne è stata
effettuata una revisione completa nello standard POSIX.1b, che tratteremo in
-\secref{sec:ipc_posix}.
+sez.~\ref{sec:ipc_posix}.
\subsection{Il controllo di accesso}
(nei campi \var{cuid} e \var{cgid}) e del proprietario (nei campi \var{uid} e
\var{gid}) dello stesso, e un insieme di permessi (nel campo \var{mode}). In
questo modo è possibile definire un controllo di accesso sugli oggetti di IPC,
-simile a quello che si ha per i file (vedi \secref{sec:file_perm_overview}).
+simile a quello che si ha per i file (vedi sez.~\ref{sec:file_perm_overview}).
Benché questo controllo di accesso sia molto simile a quello dei file, restano
delle importanti differenze. La prima è che il permesso di esecuzione non
permessi di lettura e scrittura (nel caso dei semafori poi quest'ultimo è più
propriamente un permesso di modifica). I valori di \var{mode} sono gli stessi
ed hanno lo stesso significato di quelli riportati in
-\secref{tab:file_mode_flags}\footnote{se però si vogliono usare le costanti
+tab.~\ref{tab:file_mode_flags}\footnote{se però si vogliono usare le costanti
simboliche ivi definite occorrerà includere il file \file{sys/stat.h},
alcuni sistemi definiscono le costanti \const{MSG\_R} (\texttt{0400}) e
\const{MSG\_W} (\texttt{0200}) per indicare i permessi base di lettura e
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
-\secref{sec:file_umask}) non ha alcun significato.
+sez.~\ref{sec:file_umask}) non ha alcun significato.
\subsection{Gli identificatori ed il loro utilizzo}
\label{sec:ipc_sysv_id_use}
L'unico campo di \struct{ipc\_perm} del quale non abbiamo ancora parlato è
-\var{seq}, che in \figref{fig:ipc_ipc_perm} è qualificato con un criptico
+\var{seq}, che in fig.~\ref{fig:ipc_ipc_perm} è qualificato con un criptico
``\textsl{numero di sequenza}'', ne parliamo adesso dato che esso è
strettamente attinente alle modalità con cui il kernel assegna gli
identificatori degli oggetti del sistema di IPC.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-int main(int argc, char *argv[])
-{
- ...
- switch (type) {
- case 'q': /* Message Queue */
- debug("Message Queue Try\n");
- for (i=0; i<n; i++) {
- id = msgget(IPC_PRIVATE, IPC_CREAT|0666);
- printf("Identifier Value %d \n", id);
- msgctl(id, IPC_RMID, NULL);
- }
- break;
- case 's': /* Semaphore */
- debug("Semaphore\n");
- for (i=0; i<n; i++) {
- id = semget(IPC_PRIVATE, 1, IPC_CREAT|0666);
- printf("Identifier Value %d \n", id);
- semctl(id, 0, IPC_RMID);
- }
- break;
- case 'm': /* Shared Memory */
- debug("Shared Memory\n");
- for (i=0; i<n; i++) {
- id = shmget(IPC_PRIVATE, 1000, IPC_CREAT|0666);
- printf("Identifier Value %d \n", id);
- shmctl(id, IPC_RMID, NULL);
- }
- break;
- default: /* should not reached */
- return -1;
- }
- return 0;
-}
- \end{lstlisting}
+ \includecodesample{listati/IPCTestId.c}
\end{minipage}
\normalsize
\caption{Sezione principale del programma di test per l'assegnazione degli
\label{fig:ipc_sysv_idtest}
\end{figure}
-In \figref{fig:ipc_sysv_idtest} è riportato il codice di un semplice programma
-di test che si limita a creare un oggetto (specificato a riga di comando),
-stamparne il numero di identificatore e cancellarlo per un numero specificato
-di volte. Al solito non si è riportato il codice della gestione delle opzioni
-a riga di comando, che permette di specificare quante volte effettuare il
-ciclo \var{n}, e su quale tipo di oggetto eseguirlo.
+In fig.~\ref{fig:ipc_sysv_idtest} è riportato il codice di un semplice
+programma di test che si limita a creare un oggetto (specificato a riga di
+comando), stamparne il numero di identificatore e cancellarlo per un numero
+specificato di volte. Al solito non si è riportato il codice della gestione
+delle opzioni a riga di comando, che permette di specificare quante volte
+effettuare il ciclo \var{n}, e su quale tipo di oggetto eseguirlo.
La figura non riporta il codice di selezione delle opzioni, che permette di
inizializzare i valori delle variabili \var{type} al tipo di oggetto voluto, e
essere nullo e deve essere fornito come maschera binaria, impostando il bit
corrispondente al valore \const{IPC\_CREAT}. In questo caso i nove bit meno
significativi di \param{flag} saranno usati come permessi per il nuovo
-oggetto, secondo quanto illustrato in \secref{sec:ipc_sysv_access_control}.
+oggetto, secondo quanto illustrato in sez.~\ref{sec:ipc_sysv_access_control}.
Se si imposta anche il bit corrispondente a \const{IPC\_EXCL} la funzione avrà
successo solo se l'oggetto non esiste già, fallendo con un errore di
\errcode{EEXIST} altrimenti.
Le code di messaggi sono caratterizzate da tre limiti fondamentali, definiti
negli header e corrispondenti alle prime tre costanti riportate in
-\tabref{tab:ipc_msg_limits}, come accennato però in Linux è possibile
+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/}.
aggiornare un puntatore), e relativamente veloce da attraversare in ordine
sequenziale (seguendo i puntatori), è invece relativamente lenta
nell'accesso casuale e nella ricerca.} i nuovi messaggi vengono inseriti in
-coda alla lista e vengono letti dalla cima, in \figref{fig:ipc_mq_schema} si è
-riportato lo schema con cui queste strutture vengono mantenute dal
-kernel.\footnote{lo schema illustrato in \figref{fig:ipc_mq_schema} è in
+coda alla lista e vengono letti dalla cima, in fig.~\ref{fig:ipc_mq_schema} si
+è riportato lo schema con cui queste strutture vengono mantenute dal
+kernel.\footnote{lo schema illustrato in fig.~\ref{fig:ipc_mq_schema} è in
realtà una semplificazione di quello usato effettivamente fino ai kernel
della serie 2.2.x, nei kernel della serie 2.4.x la gestione delle code di
messaggi è stata modificata ed è effettuata in maniera diversa; abbiamo
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}
-struct msqid_ds {
- struct ipc_perm msg_perm; /* structure for operation permission */
- time_t msg_stime; /* time of last msgsnd command */
- time_t msg_rtime; /* time of last msgrcv command */
- time_t msg_ctime; /* time of last change */
- msgqnum_t msg_qnum; /* number of messages currently on queue */
- msglen_t msg_qbytes; /* max number of bytes allowed on queue */
- pid_t msg_lspid; /* pid of last msgsnd() */
- pid_t msg_lrpid; /* pid of last msgrcv() */
- struct msg *msg_first; /* first message on queue, unused */
- struct msg *msg_last; /* last message in queue, unused */
- unsigned long int msg_cbytes; /* current number of bytes on queue */
-};
- \end{lstlisting}
+ \includestruct{listati/msqid_ds.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{msqid\_ds}, associata a ciascuna coda di
\end{figure}
A ciascuna coda è associata una struttura \struct{msgid\_ds}, la cui
-definizione, è riportata in \secref{fig:ipc_msqid_ds}. In questa struttura il
+definizione, è riportata in fig.~\ref{fig:ipc_msqid_ds}. In questa struttura il
kernel mantiene le principali informazioni riguardo lo stato corrente della
coda.\footnote{come accennato questo vale fino ai kernel della serie 2.2.x,
essa viene usata nei kernel della serie 2.4.x solo per compatibilità in
quanto è quella restituita dalle funzioni dell'interfaccia. Si noti come ci
sia una differenza con i campi mostrati nello schema di
- \figref{fig:ipc_mq_schema} che sono presi dalla definizione di
+ fig.~\ref{fig:ipc_mq_schema} che sono presi dalla definizione di
\file{linux/msg.h}, e fanno riferimento alla definizione della omonima
- struttura usata nel kernel.} In \figref{fig:ipc_msqid_ds} sono elencati i
+ struttura usata nel kernel.} In fig.~\ref{fig:ipc_msqid_ds} sono elencati i
campi significativi definiti in \file{sys/msg.h}, a cui si sono aggiunti gli
ultimi tre campi che sono previsti dalla implementazione originale di System
V, ma non dallo standard Unix98.
Quando si crea una nuova coda con \func{msgget} questa struttura viene
inizializzata, in particolare il campo \var{msg\_perm} viene inizializzato
-come illustrato in \secref{sec:ipc_sysv_access_control}, per quanto riguarda
+come illustrato in sez.~\ref{sec:ipc_sysv_access_control}, per quanto riguarda
gli altri campi invece:
-\begin{itemize}
+\begin{itemize*}
\item il campo \var{msg\_qnum}, che esprime il numero di messaggi presenti
sulla coda, viene inizializzato a 0.
\item i campi \var{msg\_lspid} e \var{msg\_lrpid}, che esprimono
\var{msg\_cbytes}, che esprime la dimensione in byte dei messaggi presenti è
inizializzato a zero. Questi campi sono ad uso interno dell'implementazione
e non devono essere utilizzati da programmi in user space).
-\end{itemize}
+\end{itemize*}
Una volta creata una coda di messaggi le operazioni di controllo vengono
effettuate con la funzione \funcd{msgctl}, che (come le analoghe \func{semctl}
\item[\const{IPC\_RMID}] Rimuove la coda, cancellando tutti i dati, con
effetto immediato. Tutti i processi che cercheranno di accedere alla coda
riceveranno un errore di \errcode{EIDRM}, e tutti processi in attesa su
- funzioni di di lettura o di scrittura sulla coda saranno svegliati ricevendo
+ funzioni di lettura o di scrittura sulla coda saranno svegliati ricevendo
il medesimo errore. Questo comando può essere eseguito solo da un processo
con user-ID effettivo corrispondente al creatore o al proprietario della
coda, o all'amministratore.
messaggio ha lunghezza specificata da \param{msgsz} ed è passato attraverso il
l'argomento \param{msgp}. Quest'ultimo deve venire passato sempre come
puntatore ad una struttura \struct{msgbuf} analoga a quella riportata in
-\figref{fig:ipc_msbuf} che è quella che deve contenere effettivamente il
+fig.~\ref{fig:ipc_msbuf} che è quella che deve contenere effettivamente il
messaggio. La dimensione massima per il testo di un messaggio non può
comunque superare il limite \const{MSGMAX}.
-La struttura di \figref{fig:ipc_msbuf} è comunque solo un modello, tanto che
+La struttura di fig.~\ref{fig:ipc_msbuf} è comunque solo un modello, tanto che
la definizione contenuta in \file{sys/msg.h} usa esplicitamente per il secondo
campo il valore \code{mtext[1]}, che non è di nessuna utilità ai fini pratici.
La sola cosa che conta è che la struttura abbia come primo membro un campo
dimensione, e serve a contenere il testo del messaggio.
In generale pertanto per inviare un messaggio con \func{msgsnd} si usa
-ridefinire una struttura simile a quella di \figref{fig:ipc_msbuf}, adattando
+ridefinire una struttura simile a quella di fig.~\ref{fig:ipc_msbuf}, adattando
alle proprie esigenze il campo \var{mtype}, (o ridefinendo come si vuole il
corpo del messaggio, anche con più campi o con strutture più complesse) avendo
però la cura di mantenere nel primo campo un valore di tipo \ctyp{long} che ne
argomento è solo quella del messaggio, non quella di tutta la struttura, se
cioè \var{message} è una propria struttura che si passa alla funzione,
\param{msgsz} dovrà essere uguale a \code{sizeof(message)-sizeof(long)}, (se
-consideriamo il caso dell'esempio in \figref{fig:ipc_msbuf}, \param{msgsz}
+consideriamo il caso dell'esempio in fig.~\ref{fig:ipc_msbuf}, \param{msgsz}
dovrà essere pari a \const{LENGTH}).
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}
- struct msgbuf {
- long mtype; /* message type, must be > 0 */
- char mtext[LENGTH]; /* message data */
- };
- \end{lstlisting}
+ \includestruct{listati/msgbuf.h}
\end{minipage}
\normalsize
\caption{Schema della struttura \structd{msgbuf}, da utilizzare come
Per capire meglio il funzionamento della funzione riprendiamo in
considerazione la struttura della coda illustrata in
-\figref{fig:ipc_mq_schema}. Alla chiamata di \func{msgsnd} il nuovo messaggio
+fig.~\ref{fig:ipc_mq_schema}. Alla chiamata di \func{msgsnd} il nuovo messaggio
sarà aggiunto in fondo alla lista inserendo una nuova struttura \struct{msg},
il puntatore \var{msg\_last} di \struct{msqid\_ds} verrà aggiornato, come pure
il puntatore al messaggio successivo per quello che era il precedente ultimo
La funzione legge un messaggio dalla coda specificata, scrivendolo sulla
struttura puntata da \param{msgp}, che dovrà avere un formato analogo a quello
-di \figref{fig:ipc_msbuf}. Una volta estratto, il messaggio sarà rimosso dalla
-coda. L'argomento \param{msgsz} indica la lunghezza massima del testo del
-messaggio (equivalente al valore del parametro \const{LENGTH} nell'esempio di
-\figref{fig:ipc_msbuf}).
+di fig.~\ref{fig:ipc_msbuf}. Una volta estratto, il messaggio sarà rimosso
+dalla coda. L'argomento \param{msgsz} indica la lunghezza massima del testo
+del messaggio (equivalente al valore del parametro \const{LENGTH} nell'esempio
+di fig.~\ref{fig:ipc_msbuf}).
Se il testo del messaggio ha lunghezza inferiore a \param{msgsz} esso viene
rimosso dalla coda; in caso contrario, se \param{msgflg} è impostato a
L'argomento \param{msgtyp} permette di restringere la ricerca ad un
sottoinsieme dei messaggi presenti sulla coda; la ricerca infatti è fatta con
-una scansione della struttura mostrata in \figref{fig:ipc_mq_schema},
+una scansione della struttura mostrata in fig.~\ref{fig:ipc_mq_schema},
restituendo il primo messaggio incontrato che corrisponde ai criteri
specificati (che quindi, visto come i messaggi vengono sempre inseriti dalla
coda, è quello meno recente); in particolare:
\begin{itemize}
\item se \param{msgtyp} è 0 viene estratto il messaggio in cima alla coda, cioè
- quello fra i presenti che è stato inserito inserito per primo.
+ quello fra i presenti che è stato inserito per primo.
\item se \param{msgtyp} è positivo viene estratto il primo messaggio il cui
tipo (il valore del campo \var{mtype}) corrisponde al valore di
\param{msgtyp}.
utilizzava termina. Questo comporta che in caso di errori si può saturare il
sistema, e che devono comunque essere esplicitamente previste delle funzioni
di rimozione in caso di interruzioni o uscite dal programma (come vedremo in
-\figref{fig:ipc_mq_fortune_server}).
+fig.~\ref{fig:ipc_mq_fortune_server}).
L'altro problema è non facendo uso di file descriptor le tecniche di
-\textit{I/O multiplexing} descritte in \secref{sec:file_multiplexing} non
+\textit{I/O multiplexing} descritte in sez.~\ref{sec:file_multiplexing} non
possono essere utilizzate, e non si ha a disposizione niente di analogo alle
funzioni \func{select} e \func{poll}. Questo rende molto scomodo usare più di
una di queste strutture alla volta; ad esempio non si può scrivere un server
che aspetti un messaggio su più di una coda senza fare ricorso ad una tecnica
-di \textit{polling}\index{polling} che esegua un ciclo di attesa su ciascuna
-di esse.
+di \textit{polling}\index{\textit{polling}} che esegua un ciclo di attesa su
+ciascuna di esse.
Come esempio dell'uso delle code di messaggi possiamo riscrivere il nostro
server di \textit{fortunes} usando queste al posto delle fifo. In questo caso
\begin{figure}[!bht]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-int msgid; /* Message queue identifier */
-int main(int argc, char *argv[])
-{
-/* Variables definition */
- int i, n = 0;
- char **fortune; /* array of fortune message string */
- char *fortunefilename = "/usr/share/games/fortunes/linux"; /* file name */
- struct msgbuf_read { /* message struct to read request from clients */
- long mtype; /* message type, must be 1 */
- long pid; /* message data, must be the pid of the client */
- } msg_read;
- struct msgbuf_write { /* message struct to write result to clients */
- long mtype; /* message type, will be the pid of the client*/
- char mtext[MSGMAX]; /* message data, will be the fortune */
- } msg_write;
- key_t key; /* Message queue key */
- int size; /* message size */
- ...
- Signal(SIGTERM, HandSIGTERM); /* set handlers for termination */
- Signal(SIGINT, HandSIGTERM);
- Signal(SIGQUIT, HandSIGTERM);
- if (n==0) usage(); /* if no pool depth exit printing usage info */
- i = FortuneParse(fortunefilename, fortune, n); /* parse phrases */
- /* Create the queue */
- key = ftok("./MQFortuneServer.c", 1);
- msgid = msgget(key, IPC_CREAT|0666);
- if (msgid < 0) {
- perror("Cannot create message queue");
- exit(1);
- }
- /* Main body: loop over requests */
- daemon(0, 0);
- while (1) {
- msgrcv(msgid, &msg_read, sizeof(int), 1, MSG_NOERROR);
- n = random() % i; /* select random value */
- strncpy(msg_write.mtext, fortune[n], MSGMAX);
- size = min(strlen(fortune[n])+1, MSGMAX);
- msg_write.mtype=msg_read.pid; /* use request pid as type */
- msgsnd(msgid, &msg_write, size, 0);
- }
-}
-/*
- * Signal Handler to manage termination
- */
-void HandSIGTERM(int signo) {
- msgctl(msgid, IPC_RMID, NULL); /* remove message queue */
- exit(0);
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/MQFortuneServer.c}
\end{minipage}
\normalsize
\caption{Sezione principale del codice del server di \textit{fortunes}
\label{fig:ipc_mq_fortune_server}
\end{figure}
-In \figref{fig:ipc_mq_fortune_server} si è riportato un estratto delle parti
+In fig.~\ref{fig:ipc_mq_fortune_server} si è riportato un estratto delle parti
principali del codice del nuovo server (il codice completo è nel file
\file{MQFortuneServer.c} nei sorgenti allegati). Il programma è basato su un
uso accorto della caratteristica di poter associate un ``tipo'' ai messaggi
\begin{figure}[!bht]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-int main(int argc, char *argv[])
-{
- ...
- key = ftok("./MQFortuneServer.c", 1);
- msgid = msgget(key, 0);
- if (msgid < 0) {
- perror("Cannot find message queue");
- exit(1);
- }
- /* Main body: do request and write result */
- msg_read.mtype = 1; /* type for request is always 1 */
- msg_read.pid = getpid(); /* use pid for communications */
- size = sizeof(msg_read.pid);
- msgsnd(msgid, &msg_read, size, 0); /* send request message */
- msgrcv(msgid, &msg_write, MSGMAX, msg_read.pid, MSG_NOERROR);
- printf("%s", msg_write.mtext);
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/MQFortuneClient.c}
\end{minipage}
\normalsize
\caption{Sezione principale del codice del client di \textit{fortunes}
\label{fig:ipc_mq_fortune_client}
\end{figure}
-In \figref{fig:ipc_mq_fortune_client} si è riportato un estratto il codice del
-programma client. Al solito il codice completo è con i sorgenti allegati, nel
-file \file{MQFortuneClient.c}. Come sempre si sono rimosse le parti relative
-alla gestione delle opzioni, ed in questo caso, anche la dichiarazione delle
-variabili, che, per la parte relative alle strutture usate per la
-comunicazione tramite le code, sono le stesse viste in
-\figref{fig:ipc_mq_fortune_server}.
+In fig.~\ref{fig:ipc_mq_fortune_client} si è riportato un estratto il codice
+del programma client. Al solito il codice completo è con i sorgenti allegati,
+nel file \file{MQFortuneClient.c}. Come sempre si sono rimosse le parti
+relative alla gestione delle opzioni, ed in questo caso, anche la
+dichiarazione delle variabili, che, per la parte relative alle strutture usate
+per la comunicazione tramite le code, sono le stesse viste in
+fig.~\ref{fig:ipc_mq_fortune_server}.
Il client in questo caso è molto semplice; la prima parte del programma
(\texttt{\small 4--9}) si occupa di accedere alla coda di messaggi, ed è
I semafori non sono meccanismi di intercomunicazione diretta come quelli
(pipe, fifo e code di messaggi) visti finora, e non consentono di scambiare
dati fra processi, ma servono piuttosto come meccanismi di sincronizzazione o
-di protezione per le \textsl{sezioni critiche}\index{sezioni critiche} del
-codice (si ricordi quanto detto in \secref{sec:proc_race_cond}).
+di protezione per le \textsl{sezioni critiche}\index{sezioni~critiche} del
+codice (si ricordi quanto detto in sez.~\ref{sec:proc_race_cond}).
Un semaforo è uno speciale contatore, mantenuto nel kernel, che permette, a
seconda del suo valore, di consentire o meno la prosecuzione dell'esecuzione
La funzione è del tutto analoga a \func{msgget}, solo che in questo caso
restituisce l'identificatore di un insieme di semafori, in particolare è
identico l'uso degli argomenti \param{key} e \param{flag}, per cui non
-ripeteremo quanto detto al proposito in \secref{sec:ipc_sysv_mq}. L'argomento
+ripeteremo quanto detto al proposito in sez.~\ref{sec:ipc_sysv_mq}. L'argomento
\param{nsems} permette di specificare quanti semafori deve contenere l'insieme
quando se ne richieda la creazione, e deve essere nullo quando si effettua una
richiesta dell'identificatore di un insieme già esistente.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}
-struct semid_ds
-{
- struct ipc_perm sem_perm; /* operation permission struct */
- time_t sem_otime; /* last semop() time */
- time_t sem_ctime; /* last time changed by semctl() */
- unsigned long int sem_nsems; /* number of semaphores in set */
-};
- \end{lstlisting}
+ \includestruct{listati/semid_ds.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{semid\_ds}, associata a ciascun insieme di
\end{figure}
A ciascun insieme di semafori è associata una struttura \struct{semid\_ds},
-riportata in \figref{fig:ipc_semid_ds}.\footnote{non si sono riportati i campi
- ad uso interno del kernel, che vedremo in \figref{fig:ipc_sem_schema}, che
- dipendono dall'implementazione.} Come nel caso delle code di messaggi quando
-si crea un nuovo insieme di semafori con \func{semget} questa struttura viene
-inizializzata, in particolare il campo \var{sem\_perm} viene inizializzato
-come illustrato in \secref{sec:ipc_sysv_access_control} (si ricordi che in
-questo caso il permesso di scrittura è in realtà permesso di alterare il
-semaforo), per quanto riguarda gli altri campi invece:
+riportata in fig.~\ref{fig:ipc_semid_ds}.\footnote{non si sono riportati i
+ campi ad uso interno del kernel, che vedremo in
+ fig.~\ref{fig:ipc_sem_schema}, che dipendono dall'implementazione.} Come nel
+caso delle code di messaggi quando si crea un nuovo insieme di semafori con
+\func{semget} questa struttura viene inizializzata, in particolare il campo
+\var{sem\_perm} viene inizializzato come illustrato in
+sez.~\ref{sec:ipc_sysv_access_control} (si ricordi che in questo caso il
+permesso di scrittura è in realtà permesso di alterare il semaforo), per
+quanto riguarda gli altri campi invece:
\begin{itemize*}
\item il campo \var{sem\_nsems}, che esprime il numero di semafori
nell'insieme, viene inizializzato al valore di \param{nsems}.
ormai è ridotta ai soli due primi membri, e gli altri vengono calcolati
dinamicamente. La si è utilizzata a scopo di esempio, perché indica tutti i
valori associati ad un semaforo, restituiti dalle funzioni di controllo, e
- citati dalle pagine di manuale.} è riportata in \figref{fig:ipc_sem}. Questa
-struttura, non è accessibile in user space, ma i valori in essa specificati
-possono essere letti in maniera indiretta, attraverso l'uso delle funzioni di
-controllo.
+ citati dalle pagine di manuale.} è riportata in fig.~\ref{fig:ipc_sem}.
+Questa struttura, non è accessibile in user space, ma i valori in essa
+specificati possono essere letti in maniera indiretta, attraverso l'uso delle
+funzioni di controllo.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}
-struct sem {
- short sempid; /* pid of last operation */
- ushort semval; /* current value */
- ushort semncnt; /* num procs awaiting increase in semval */
- ushort semzcnt; /* num procs awaiting semval = 0 */
-};
- \end{lstlisting}
+ \includestruct{listati/sem.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{sem}, che contiene i dati di un singolo
\label{fig:ipc_sem}
\end{figure}
-I dati mantenuti nella struttura, ed elencati in \figref{fig:ipc_sem},
+I dati mantenuti nella struttura, ed elencati in fig.~\ref{fig:ipc_sem},
indicano rispettivamente:
\begin{description*}
\item[\var{semval}] il valore numerico del semaforo.
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 \tabref{tab:ipc_sem_limits}. Alcuni di questi limiti sono al
-solito accessibili e modificabili attraverso \func{sysctl} o scrivendo
+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}.
La funzione che permette di effettuare le varie operazioni di controllo sui
}
\end{functions}
-La funzione può avere tre o quattro parametri, a seconda dell'operazione
+La funzione può avere tre o quattro argomenti, a seconda dell'operazione
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]{15cm}
- \begin{lstlisting}[labelstep=0]{}
-union semun {
- int val; /* value for SETVAL */
- struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
- unsigned short *array; /* array for GETALL, SETALL */
- /* Linux specific part: */
- struct seminfo *__buf; /* buffer for IPC_INFO */
-};
- \end{lstlisting}
+ \includestruct{listati/semun.h}
\end{minipage}
\normalsize
\caption{La definizione dei possibili valori di una \direct{union}
generico, che conterrà un dato diverso a seconda dell'azione richiesta; per
unificare l'argomento esso deve essere passato come una \struct{semun}, la cui
definizione, con i possibili valori che può assumere, è riportata in
-\figref{fig:ipc_semun}.
+fig.~\ref{fig:ipc_semun}.
Come già accennato sia il comportamento della funzione che il numero di
-parametri con cui deve essere invocata, dipendono dal valore dell'argomento
+argomenti con cui deve essere invocata dipendono dal valore dell'argomento
\param{cmd}, che specifica l'azione da intraprendere; i valori validi (che
cioè non causano un errore di \errcode{EINVAL}) per questo argomento sono i
seguenti:
Il valore di ritorno della funzione in caso di successo dipende
dall'operazione richiesta; per tutte le operazioni che richiedono quattro
argomenti esso è sempre nullo, per le altre operazioni, elencate in
-\tabref{tab:ipc_semctl_returns} viene invece restituito il valore richiesto,
+tab.~\ref{tab:ipc_semctl_returns} viene invece restituito il valore richiesto,
corrispondente al campo della struttura \struct{sem} indicato nella seconda
colonna della tabella.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}
-struct sembuf
-{
- unsigned short int sem_num; /* semaphore number */
- short int sem_op; /* semaphore operation */
- short int sem_flg; /* operation flag */
-};
- \end{lstlisting}
+ \includestruct{listati/sembuf.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{sembuf}, usata per le operazioni sui
Il contenuto di ciascuna operazione deve essere specificato attraverso una
opportuna struttura \struct{sembuf} (la cui definizione è riportata in
-\figref{fig:ipc_sembuf}) che il programma chiamante deve avere cura di
+fig.~\ref{fig:ipc_sembuf}) che il programma chiamante deve avere cura di
allocare in un opportuno vettore. La struttura permette di indicare il
semaforo su cui operare, il tipo di operazione, ed un flag di controllo.
Il campo \var{sem\_num} serve per indicare a quale semaforo dell'insieme fa
Tutto questo però ha un problema di fondo. Per capire di cosa si tratta
occorre fare riferimento all'implementazione usata in Linux, che è riportata
-in maniera semplificata nello schema di \figref{fig:ipc_sem_schema}. Si è
+in maniera semplificata nello schema di fig.~\ref{fig:ipc_sem_schema}. Si è
presa come riferimento l'architettura usata fino al kernel 2.2.x che è più
semplice (ed illustrata in dettaglio in \cite{tlk}); nel kernel 2.4.x la
struttura del \textit{SysV IPC} è stata modificata, ma le definizioni relative
vecchie versioni delle librerie del C, come le libc5.}
\begin{figure}[htb]
- \centering \includegraphics[width=15cm]{img/semtruct}
+ \centering \includegraphics[width=13cm]{img/semtruct}
\caption{Schema della struttura di un insieme di semafori.}
\label{fig:ipc_sem_schema}
\end{figure}
kernel crea una struttura \struct{sem\_queue} che viene aggiunta in fondo alla
coda di attesa associata a ciascun insieme di semafori\footnote{che viene
referenziata tramite i campi \var{sem\_pending} e \var{sem\_pending\_last}
- di \struct{semid\_ds}.}. Nella struttura viene memorizzato il riferimento
-alle operazioni richieste (nel campo \var{sops}, che è un puntatore ad una
-struttura \struct{sembuf}) e al processo corrente (nel campo \var{sleeper}) poi
-quest'ultimo viene messo stato di attesa e viene invocato lo
-scheduler\index{scheduler} per passare all'esecuzione di un altro processo.
+ di \struct{semid\_ds}.}.
+
+Nella struttura viene memorizzato il riferimento alle operazioni richieste
+(nel campo \var{sops}, che è un puntatore ad una struttura \struct{sembuf}) e
+al processo corrente (nel campo \var{sleeper}) poi quest'ultimo viene messo
+stato di attesa e viene invocato lo scheduler\index{\textit{scheduler}} per
+passare all'esecuzione di un altro processo.
Se invece tutte le operazioni possono avere successo queste vengono eseguite
immediatamente, dopo di che il kernel esegue una scansione della coda di
struttura \struct{sem\_queue} viene rimossa e lo stato del processo associato
all'operazione (\var{sleeper}) viene riportato a \textit{running}; il tutto
viene ripetuto fin quando non ci sono più operazioni eseguibili o si è
-svuotata la coda.
-
-Per gestire il meccanismo del ripristino tutte le volte che per un'operazione
-si è specificato il flag \const{SEM\_UNDO} viene mantenuta per ciascun insieme
-di semafori una apposita struttura \struct{sem\_undo} che contiene (nel vettore
-puntato dal campo \var{semadj}) un valore di aggiustamento per ogni semaforo
-cui viene sommato l'opposto del valore usato per l'operazione.
+svuotata la coda. Per gestire il meccanismo del ripristino tutte le volte che
+per un'operazione si è specificato il flag \const{SEM\_UNDO} viene mantenuta
+per ciascun insieme di semafori una apposita struttura \struct{sem\_undo} che
+contiene (nel vettore puntato dal campo \var{semadj}) un valore di
+aggiustamento per ogni semaforo cui viene sommato l'opposto del valore usato
+per l'operazione.
Queste strutture sono mantenute in due liste,\footnote{rispettivamente
attraverso i due campi \var{id\_next} e \var{proc\_next}.} una associata
l'operazione;\footnote{attraverso il campo \var{semundo} di
\struct{task\_struct}, come mostrato in \ref{fig:ipc_sem_schema}.} quando un
processo termina, la lista ad esso associata viene scandita e le operazioni
-applicate al semaforo.
-
-Siccome un processo può accumulare delle richieste di ripristino per semafori
-differenti chiamate attraverso diverse chiamate a \func{semop}, si pone il
-problema di come eseguire il ripristino dei semafori all'uscita del processo,
-ed in particolare se questo può essere fatto atomicamente. Il punto è cosa
-succede quando una delle operazioni previste per il ripristino non può essere
-eseguita immediatamente perché ad esempio il semaforo è occupato; in tal caso
-infatti, se si pone il processo in stato di \textit{sleep} aspettando la
-disponibilità del semaforo (come faceva l'implementazione originaria) si perde
-l'atomicità dell'operazione. La scelta fatta dal kernel è pertanto quella di
-effettuare subito le operazioni che non prevedono un blocco del processo e di
-ignorare silenziosamente le altre; questo però comporta il fatto che il
-ripristino non è comunque garantito in tutte le occasioni.
+applicate al semaforo. Siccome un processo può accumulare delle richieste di
+ripristino per semafori differenti chiamate attraverso diverse chiamate a
+\func{semop}, si pone il problema di come eseguire il ripristino dei semafori
+all'uscita del processo, ed in particolare se questo può essere fatto
+atomicamente.
+
+Il punto è cosa succede quando una delle operazioni previste per il ripristino
+non può essere eseguita immediatamente perché ad esempio il semaforo è
+occupato; in tal caso infatti, se si pone il processo in stato di
+\textit{sleep} aspettando la disponibilità del semaforo (come faceva
+l'implementazione originaria) si perde l'atomicità dell'operazione. La scelta
+fatta dal kernel è pertanto quella di effettuare subito le operazioni che non
+prevedono un blocco del processo e di ignorare silenziosamente le altre;
+questo però comporta il fatto che il ripristino non è comunque garantito in
+tutte le occasioni.
Come esempio di uso dell'interfaccia dei semafori vediamo come implementare
con essa dei semplici \textit{mutex} (cioè semafori binari), tutto il codice
in questione, contenuto nel file \file{Mutex.c} allegato ai sorgenti, è
-riportato in \figref{fig:ipc_mutex_create}. Utilizzeremo l'interfaccia per
+riportato in fig.~\ref{fig:ipc_mutex_create}. Utilizzeremo l'interfaccia per
creare un insieme contenente un singolo semaforo, per il quale poi useremo un
valore unitario per segnalare la disponibilità della risorsa, ed un valore
nullo per segnalarne l'indisponibilità.
\begin{figure}[!bht]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-/* Function MutexCreate: create a mutex/semaphore */
-int MutexCreate(key_t ipc_key)
-{
- const union semun semunion={1}; /* semaphore union structure */
- int sem_id, ret;
- sem_id = semget(ipc_key, 1, IPC_CREAT|0666); /* get semaphore ID */
- if (sem_id == -1) { /* if error return code */
- return sem_id;
- }
- ret = semctl(sem_id, 0, SETVAL, semunion); /* init semaphore */
- if (ret == -1) {
- return ret;
- }
- return sem_id;
-}
-/* Function MutexFind: get the semaphore/mutex Id given the IPC key value */
-int MutexFind(key_t ipc_key)
-{
- return semget(ipc_key,1,0);
-}
-/* Function MutexRead: read the current value of the mutex/semaphore */
-int MutexRead(int sem_id)
-{
- return semctl(sem_id, 0, GETVAL);
-}
-/* Define sembuf structures to lock and unlock the semaphore */
-struct sembuf sem_lock={ /* to lock semaphore */
- 0, /* semaphore number (only one so 0) */
- -1, /* operation (-1 to use resource) */
- SEM_UNDO}; /* flag (set for undo at exit) */
-struct sembuf sem_ulock={ /* to unlock semaphore */
- 0, /* semaphore number (only one so 0) */
- 1, /* operation (1 to release resource) */
- SEM_UNDO}; /* flag (in this case 0) */
-/* Function MutexLock: to lock a mutex/semaphore */
-int MutexLock(int sem_id)
-{
- return semop(sem_id, &sem_lock, 1);
-}
-/* Function MutexUnlock: to unlock a mutex/semaphore */
-int MutexUnlock(int sem_id)
-{
- return semop(sem_id, &sem_ulock, 1);
-}
-/* Function MutexRemove: remove a mutex/semaphore */
-int MutexRemove(int sem_id)
-{
- return semctl(sem_id, 0, IPC_RMID);
-}
- \end{lstlisting}
+ \includecodesample{listati/Mutex.c}
\end{minipage}
\normalsize
\caption{Il codice delle funzioni che permettono di creare o recuperare
considerata libera). Infine si tenga presente che usare \func{MutexRead} per
controllare il valore dei mutex prima di proseguire in una operazione di
sblocco non servirebbe comunque, dato che l'operazione non sarebbe atomica.
-Vedremo in \secref{sec:ipc_lock_file} come sia possibile ottenere
+Vedremo in sez.~\ref{sec:ipc_lock_file} come sia possibile ottenere
un'interfaccia analoga a quella appena illustrata, senza incorrere in questi
problemi, usando il file locking\index{file!locking}.
La funzione, come \func{semget}, è del tutto analoga a \func{msgget}, ed
identico è l'uso degli argomenti \param{key} e \param{flag} per cui non
-ripeteremo quanto detto al proposito in \secref{sec:ipc_sysv_mq}. L'argomento
+ripeteremo quanto detto al proposito in sez.~\ref{sec:ipc_sysv_mq}. L'argomento
\param{size} specifica invece la dimensione, in byte, del segmento, che viene
comunque arrotondata al multiplo superiore di \const{PAGE\_SIZE}.
\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{15cm}
- \begin{lstlisting}[labelstep=0]{}
-struct shmid_ds {
- struct ipc_perm shm_perm; /* operation perms */
- int shm_segsz; /* size of segment (bytes) */
- time_t shm_atime; /* last attach time */
- time_t shm_dtime; /* last detach time */
- time_t shm_ctime; /* last change time */
- unsigned short shm_cpid; /* pid of creator */
- unsigned short shm_lpid; /* pid of last operator */
- short shm_nattch; /* no. of current attaches */
-};
- \end{lstlisting}
+ \includestruct{listati/shmid_ds.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{shmid\_ds}, associata a ciascun segmento di
\end{figure}
A ciascun segmento di memoria condivisa è associata una struttura
-\struct{shmid\_ds}, riportata in \figref{fig:ipc_shmid_ds}. Come nel caso
+\struct{shmid\_ds}, riportata in fig.~\ref{fig:ipc_shmid_ds}. Come nel caso
delle code di messaggi quando si crea un nuovo segmento di memoria condivisa
con \func{shmget} questa struttura viene inizializzata, in particolare il
campo \var{shm\_perm} viene inizializzato come illustrato in
-\secref{sec:ipc_sysv_access_control}, e valgono le considerazioni ivi fatte
+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
creato il segmento, viene inizializzato al \acr{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
\func{sysctl} o scrivendo direttamente nei rispettivi file di
\file{/proc/sys/kernel/}.
-In \tabref{tab:ipc_shm_limits} si sono riportate le
+In tab.~\ref{tab:ipc_shm_limits} si sono riportate le
costanti simboliche associate a ciascuno di essi, il loro significato, i
valori preimpostati, e, quando presente, il file in \file{/proc/sys/kernel/}
che permettono di cambiarne il valore.
\var{shm\_perm.uid} e \var{shm\_perm.gid} occorre essere il proprietario o
il creatore del segmento, oppure l'amministratore. Compiuta l'operazione
aggiorna anche il valore del campo \var{shm\_ctime}.
-\item[\const{SHM\_LOCK}] Abilita il
- \textit{memory locking}\index{memory locking}\footnote{impedisce cioè che la
+\item[\const{SHM\_LOCK}] Abilita il \textit{memory
+ locking}\index{\textit{memory~locking}}\footnote{impedisce cioè che la
memoria usata per il segmento venga salvata su disco dal meccanismo della
- memoria virtuale\index{memoria virtuale}; si ricordi quanto trattato in
- \secref{sec:proc_mem_lock}.} sul segmento di memoria condivisa. Solo
+ memoria virtuale\index{memoria~virtuale}; si ricordi quanto trattato in
+ sez.~\ref{sec:proc_mem_lock}.} sul segmento di memoria condivisa. Solo
l'amministratore può utilizzare questo comando.
-\item[\const{SHM\_UNLOCK}] Disabilita il \textit{memory locking} sul segmento
- di memoria condivisa. Solo l'amministratore può utilizzare questo comando.
+\item[\const{SHM\_UNLOCK}] Disabilita il \textit{memory
+ locking}\index{\textit{memory~locking}} sul segmento di memoria condivisa.
+ Solo l'amministratore può utilizzare questo comando.
\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 memoria virtuale\index{memoria virtuale} per il segmento.
+della memoria virtuale\index{memoria~virtuale} per il segmento.
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
La funzione inserisce un segmento di memoria condivisa all'interno dello
spazio di indirizzi del processo, in modo che questo possa accedervi
direttamente, la situazione dopo l'esecuzione di \func{shmat} è illustrata in
-\figref{fig:ipc_shmem_layout} (per la comprensione del resto dello schema si
-ricordi quanto illustrato al proposito in \secref{sec:proc_mem_layout}). In
+fig.~\ref{fig:ipc_shmem_layout} (per la comprensione del resto dello schema si
+ricordi quanto illustrato al proposito in sez.~\ref{sec:proc_mem_layout}). In
particolare l'indirizzo finale del segmento dati (quello impostato da
-\func{brk}, vedi \secref{sec:proc_mem_sbrk}) non viene influenzato. Si tenga
+\func{brk}, vedi sez.~\ref{sec:proc_mem_sbrk}) non viene influenzato. Si tenga
presente infine che la funzione ha successo anche se il segmento è stato
marcato per la cancellazione.
aumentato di uno.
\end{itemize*}
-Come accennato in \secref{sec:proc_fork} un segmento di memoria condivisa
+Come accennato in sez.~\ref{sec:proc_fork} un segmento di memoria condivisa
agganciato ad un processo viene ereditato da un figlio attraverso una
\func{fork}, dato che quest'ultimo riceve una copia dello spazio degli
indirizzi del padre. Invece, dato che attraverso una \func{exec} viene
inoltre la regione di indirizzi usata per il segmento di memoria condivisa
viene tolta dallo spazio di indirizzi del processo.
+\begin{figure}[!bht]
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/SharedMem.c}
+ \end{minipage}
+ \normalsize
+ \caption{Il codice delle funzioni che permettono di creare, trovare e
+ rimuovere un segmento di memoria condivisa.}
+ \label{fig:ipc_sysv_shm_func}
+\end{figure}
+
+Come esempio di uso di queste funzioni vediamo come implementare una serie di
+funzioni di libreria che ne semplifichino l'uso, automatizzando le operazioni
+più comuni; il codice, contenuto nel file \file{SharedMem.c}, è riportato in
+fig.~\ref{fig:ipc_sysv_shm_func}.
+
+La prima funzione (\texttt{\small 3--16}) è \func{ShmCreate} che, data una
+chiave, crea il segmento di memoria condivisa restituendo il puntatore allo
+stesso. La funzione comincia (\texttt{\small 6}) con il chiamare
+\func{shmget}, usando il flag \const{IPC\_CREATE} per creare il segmento
+qualora non esista, ed assegnandogli i privilegi specificati dall'argomento
+\var{perm} e la dimensione specificata dall'argomento \var{shm\_size}. In
+caso di errore (\texttt{\small 7--9}) si ritorna immediatamente un puntatore
+nullo, altrimenti (\texttt{\small 10}) si prosegue agganciando il segmento di
+memoria condivisa al processo con \func{shmat}. In caso di errore
+(\texttt{\small 11--13}) si restituisce di nuovo un puntatore nullo, infine
+(\texttt{\small 14}) si inizializza con \func{memset} il contenuto del
+segmento al valore costante specificato dall'argomento \var{fill}, e poi si
+ritorna il puntatore al segmento stesso.
+
+La seconda funzione (\texttt{\small 17--31}) è \func{ShmFind}, che, data una
+chiave, restituisce l'indirizzo del segmento ad essa associato. Anzitutto
+(\texttt{\small 22}) si richiede l'identificatore del segmento con
+\func{shmget}, ritornando (\texttt{\small 23--25}) un puntatore nullo in caso
+di errore. Poi si prosegue (\texttt{\small 26}) agganciando il segmento al
+processo con \func{shmat}, restituendo (\texttt{\small 27--29}) di nuovo un
+puntatore nullo in caso di errore, se invece non ci sono errori si restituisce
+il puntatore ottenuto da \func{shmat}.
+
+La terza funzione (\texttt{\small 32--51}) è \func{ShmRemove} che, data la
+chiave ed il puntatore associati al segmento di memoria condivisa, prima lo
+sgancia dal processo e poi lo rimuove. Il primo passo (\texttt{\small 37}) è
+la chiamata a \func{shmdt} per sganciare il segmento, restituendo
+(\texttt{\small 38--39}) un valore -1 in caso di errore. Il passo successivo
+(\texttt{\small 41}) è utilizzare \func{shmget} per ottenre l'identificatore
+associato al segmento data la chiave \var{key}. Al solito si restituisce un
+valore di -1 (\texttt{\small 42--45}) in caso di errore, mentre se tutto va
+bene si conclude restituendo un valore nullo.
+
Benché la memoria condivisa costituisca il meccanismo di intercomunicazione
fra processi più veloce, essa non è sempre il più appropriato, dato che, come
abbiamo visto, si avrà comunque la necessità di una sincronizzazione degli
sequenziale, altri meccanismi come le pipe, le fifo o i socket, che non
necessitano di sincronizzazione esplicita, sono da preferire. Essa diventa
l'unico meccanismo possibile quando la comunicazione non è
-sequenziale\footnote{come accennato in \secref{sec:ipc_sysv_mq} per la
+sequenziale\footnote{come accennato in sez.~\ref{sec:ipc_sysv_mq} per la
comunicazione non sequenziale si possono usare le code di messaggi,
attraverso l'uso del campo \var{mtype}, ma solo se quest'ultima può essere
effettuata in forma di messaggio.} o quando non può avvenire secondo una
segmento di memoria condivisa cui altri processi potranno accedere per
ricavare la parte di informazione che interessa.
-In \figref{fig:ipc_dirmonitor_main} si è riportata la sezione principale del
+In fig.~\ref{fig:ipc_dirmonitor_main} si è riportata la sezione principale del
corpo del programma server, insieme alle definizioni delle altre funzioni
usate nel programma e delle variabili globali, omettendo tutto quello che
riguarda la gestione delle opzioni e la stampa delle istruzioni di uso a
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-/* global variables for shared memory segment */
-struct DirProp {
- int tot_size;
- int tot_files;
- int tot_regular;
- int tot_fifo;
- int tot_link;
- int tot_dir;
- int tot_block;
- int tot_char;
- int tot_sock;
-} *shmptr;
-int shmid;
-int mutex;
-/* main body */
-int main(int argc, char *argv[])
-{
- int i;
- key_t key;
- ...
- if ((argc - optind) != 1) { /* There must be remaing parameters */
- printf("Wrong number of arguments %d\n", argc - optind);
- usage();
- }
- if (chdir(argv[1])) { /* chdir to be sure dir exist */
- perror("Cannot find directory to monitor");
- }
- Signal(SIGTERM, HandSIGTERM); /* set handlers for termination */
- Signal(SIGINT, HandSIGTERM);
- Signal(SIGQUIT, HandSIGTERM);
- key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */
- shmid = shmget(key, 4096, IPC_CREAT|0666); /* get a shared memory */
- if (shmid < 0) {
- perror("Cannot create shared memory");
- exit(1);
- }
- if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */
- perror("Cannot attach segment");
- exit(1);
- }
- if ((mutex = MutexCreate(key)) == -1) { /* get a Mutex */
- perror("Cannot create mutex");
- exit(1);
- }
- /* main loop, monitor directory properties each 10 sec */
- daemon(1, 0); /* demonize process, staying in monitored dir */
- while (1) {
- MutexLock(mutex); /* lock shared memory */
- memset(shmptr, 0, sizeof(struct DirProp)); /* erase previous data */
- DirScan(argv[1], ComputeValues); /* execute scan */
- MutexUnlock(mutex); /* unlock shared memory */
- sleep(pause); /* sleep until next watch */
- }
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/DirMonitor.c}
\end{minipage}
\normalsize
\caption{Codice della funzione principale del programma \file{DirMonitor.c}.}
riga di comando (che si limitano alla eventuale stampa di un messaggio di
aiuto a video ed all'impostazione della durata dell'intervallo con cui viene
ripetuto il calcolo delle proprietà della directory) controlla (\texttt{\small
- 21--24}) che sia stato specificato il parametro necessario contenente il
-nome della directory da tenere sotto controllo, senza il quale esce
-immediatamente con un messaggio di errore.
+ 20--23}) che sia stato specificato l'argoemnto necessario contenente il nome
+della directory da tenere sotto controllo, senza il quale esce immediatamente
+con un messaggio di errore.
-Poi, per verificare che il parametro specifichi effettivamente una directory,
-si esegue (\texttt{\small 25--27}) su di esso una \func{chdir}, uscendo
+Poi, per verificare che l'argomento specifichi effettivamente una directory,
+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 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 \secref{sec:sess_daemon}, per il
+\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 28--30}) si installano
+ 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 31--44}) è quello di creare gli oggetti di
-intercomunicazione necessari. Si inizia costruendo (\texttt{\small 31}) la
+Il passo successivo (\texttt{\small 30--39}) è quello di creare gli oggetti di
+intercomunicazione necessari. Si inizia costruendo (\texttt{\small 30}) la
chiave da usare come riferimento con il nome del programma,\footnote{si è
usato un riferimento relativo alla home dell'utente, supposto che i sorgenti
di GaPiL siano stati installati direttamente in essa. Qualora si effettui
una installazione diversa si dovrà correggere il programma.} dopo di che si
-richiede (\texttt{\small 32}) la creazione di un segmento di memoria condivisa
-con \func{shmget} (una pagina di memoria è sufficiente per i dati che
-useremo), uscendo (\texttt{\small 33--36}) qualora la creazione non abbia
-successo.
-
-Una volta ottenutone l'identificatore in \var{shmid}, si può agganciare
-(\texttt{\small 37--40}) il segmento al processo con \func{shmat} anche in
-questo caso si esce qualora la funzione non abbia successo. Con l'indirizzo
-\var{shmptr} così ottenuto potremo poi accedere alla memoria condivisa, che,
-per come abbiamo lo abbiamo definito, sarà vista nella forma data da
-\struct{DirProp}. Infine (\texttt{\small 41--44}) utilizzando sempre la stessa
-chiave, si crea, tramite le funzioni di interfaccia già descritte in
-\secref{sec:ipc_sysv_sem}, anche un mutex, che utilizzeremo per regolare
-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 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.
-
-Una volta che il programma è andato in background l'esecuzione prosegue
-(\texttt{\small 47--53}) all'interno di un ciclo infinito: si inizia
-(\texttt{\small 48}) 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 49}) si cancellano i valori
-precedentemente immagazzinati nella memoria condivisa con \func{memset}, e si
-esegue (\texttt{\small 50}) un nuovo calcolo degli stessi utilizzando la
-funzione \func{DirScan}; infine (\texttt{\small 51}) si sblocca il mutex con
-\func{MutexUnlock}, e si attende (\texttt{\small 52}) per il periodo di tempo
-specificato a riga di comando con l'opzione \code{-p} con una \func{sleep}.
+richiede (\texttt{\small 31}) la creazione di un segmento di memoria condivisa
+con usando la funzione \func{ShmCreate} illustrata in precedenza (una pagina
+di memoria è sufficiente per i dati che useremo), uscendo (\texttt{\small
+ 32--35}) qualora la creazione ed il successivo agganciamento al processo non
+abbia successo. Con l'indirizzo \var{shmptr} così ottenuto potremo poi
+accedere alla memoria condivisa, che, per come abbiamo lo abbiamo definito,
+sarà vista nella forma data da \struct{DirProp}. Infine (\texttt{\small
+ 36--39}) utilizzando sempre la stessa chiave, si crea, tramite le funzioni
+di interfaccia già descritte in sez.~\ref{sec:ipc_sysv_sem}, anche un mutex,
+che utilizzeremo per regolare l'accesso alla memoria condivisa.
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-/* Routine to compute directory properties inside DirScan */
-int ComputeValues(struct dirent * direntry)
-{
- struct stat data;
- stat(direntry->d_name, &data); /* get stat data */
- shmptr->tot_size += data.st_size;
- shmptr->tot_files++;
- if (S_ISREG(data.st_mode)) shmptr->tot_regular++;
- if (S_ISFIFO(data.st_mode)) shmptr->tot_fifo++;
- if (S_ISLNK(data.st_mode)) shmptr->tot_link++;
- if (S_ISDIR(data.st_mode)) shmptr->tot_dir++;
- if (S_ISBLK(data.st_mode)) shmptr->tot_block++;
- if (S_ISCHR(data.st_mode)) shmptr->tot_char++;
- if (S_ISSOCK(data.st_mode)) shmptr->tot_sock++;
- return 0;
-}
-/* Signal Handler to manage termination */
-void HandSIGTERM(int signo) {
- MutexLock(mutex);
- if (shmdt(shmptr)) {
- perror("Error detaching shared memory");
- exit(1);
- }
- if (shmctl(shmid, IPC_RMID, NULL)) {
- perror("Cannot remove shared memory segment");
- exit(1);
- }
- MutexRemove(mutex);
- exit(0);
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/ComputeValues.c}
\end{minipage}
\normalsize
- \caption{Codice delle funzione ausiliarie usate da \file{DirMonitor.c}.}
+ \caption{Codice delle funzioni ausiliarie usate da \file{DirMonitor.c}.}
\label{fig:ipc_dirmonitor_sub}
\end{figure}
+Completata l'inizializzazione e la creazione degli oggetti di
+intercomunicazione il programma entra nel ciclo principale (\texttt{\small
+ 40--49}) dove vengono eseguite indefinitamente le attività di monitoraggio.
+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
+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 \func{DirScan}; 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}.
+
Si noti come per il calcolo dei valori da mantenere nella memoria condivisa si
sia usata ancora una volta la funzione \func{DirScan}, già utilizzata (e
-descritta in dettaglio) in \secref{sec:file_dir_read}, che ci permette di
+descritta in dettaglio) in sez.~\ref{sec:file_dir_read}, che ci permette di
effettuare la scansione delle voci della directory, chiamando per ciascuna di
esse la funzione \func{ComputeValues}, che esegue tutti i calcoli necessari.
-Il codice di quest'ultima è riportato in \figref{fig:ipc_dirmonitor_sub}. Come
-si vede la funzione (\texttt{\small 2--16}) è molto semplice e si limita a
-chiamare (\texttt{\small 5}) la funzione \func{stat} sul file indicato da
+Il codice di quest'ultima è riportato in fig.~\ref{fig:ipc_dirmonitor_sub}.
+Come si vede la funzione (\texttt{\small 2--16}) è molto semplice e si limita
+a chiamare (\texttt{\small 5}) la funzione \func{stat} sul file indicato da
ciascuna voce, per ottenerne i dati, che poi utilizza per incrementare i vari
contatori nella memoria condivisa, cui accede grazie alla variabile globale
\var{shmptr}.
condivisa usando \var{shmptr} per riempire i campi della struttura
\struct{DirProp}; così prima (\texttt{\small 6--7}) si sommano le dimensioni
dei file ed il loro numero, poi, utilizzando le macro di
-\tabref{tab:file_type_macro}, si contano (\texttt{\small 8--14}) quanti ce ne
-sono per ciascun tipo.
-
-In \figref{fig:ipc_dirmonitor_sub} è riportato anche (\texttt{\small 17--30})
-il codice del gestore dei segnali di terminazione, usato per chiudere il
-programma. Esso, oltre a provocare l'uscita del programma, si incarica anche
-di cancellare tutti gli oggetti di intercomunicazione non più necessari. Per
-questo anzitutto (\texttt{\small 19}) acquisisce il mutex con
+tab.~\ref{tab:file_type_macro}, si contano (\texttt{\small 8--14}) quanti ce
+ne sono per ciascun tipo.
+
+In fig.~\ref{fig:ipc_dirmonitor_sub} è riportato anche il codice
+(\texttt{\small 17--23}) del gestore dei segnali di terminazione, usato per
+chiudere il programma. Esso, oltre a provocare l'uscita del programma, si
+incarica anche di cancellare tutti gli oggetti di intercomunicazione non più
+necessari. Per questo anzitutto (\texttt{\small 19}) acquisisce il mutex con
\func{MutexLock}, per evitare di operare mentre un client sta ancora leggendo
-i dati, dopo di che (\texttt{\small 20--23}) prima distacca il segmento di
-memoria condivisa con \func{shmad} e poi (\texttt{\small 24--27}) lo cancella
-con \func{shctl}. Infine (\texttt{\small 28}) rimuove il mutex con
-\func{MutexRemove} ed esce.
+i dati, dopo di che (\texttt{\small 20}) distacca e rimuove il segmento di
+memoria condivisa usando \func{ShmRemove}. Infine (\texttt{\small 21})
+rimuove il mutex con \func{MutexRemove} ed esce (\texttt{\small 22}).
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-int main(int argc, char *argv[])
-{
- int i;
- key_t key;
- ...
- /* create needed IPC objects */
- key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */
- shmid = shmget(key, 4096, 0); /* get the shared memory ID */
- if (shmid < 0) {
- perror("Cannot find shared memory");
- exit(1);
- }
- if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */
- perror("Cannot attach segment");
- exit(1);
- }
- if ((mutex = MutexFind(key)) == -1) { /* get the Mutex */
- perror("Cannot find mutex");
- exit(1);
- }
- /* main loop */
- MutexLock(mutex); /* lock shared memory */
- printf("Ci sono %d file dati\n", shmptr->tot_regular);
- printf("Ci sono %d directory\n", shmptr->tot_dir);
- printf("Ci sono %d link\n", shmptr->tot_link);
- printf("Ci sono %d fifo\n", shmptr->tot_fifo);
- printf("Ci sono %d socket\n", shmptr->tot_sock);
- printf("Ci sono %d device a caratteri\n", shmptr->tot_char);
- printf("Ci sono %d device a blocchi\n", shmptr->tot_block);
- printf("Totale %d file, per %d byte\n",
- shmptr->tot_files, shmptr->tot_size);
- MutexUnlock(mutex); /* unlock shared memory */
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6 cm}
+ \includecodesample{listati/ReadMonitor.c}
\end{minipage}
\normalsize
\caption{Codice del programma client del monitor delle proprietà di una
\label{fig:ipc_dirmonitor_client}
\end{figure}
-Il codice del client, che permette di leggere le informazioni mantenute nella
-memoria condivisa, è riportato in \figref{fig:ipc_dirmonitor_client}. Al
-solito si è omessa la sezione di gestione delle opzioni e la funzione che
-stampa a video le istruzioni; il codice completo è nei sorgenti allegati, nel
-file \file{ReadMonitor.c}.
+Il codice del client usato per leggere le informazioni mantenute nella memoria
+condivisa è riportato in fig.~\ref{fig:ipc_dirmonitor_client}. Al solito si è
+omessa la sezione di gestione delle opzioni e la funzione che stampa a video
+le istruzioni; il codice completo è nei sorgenti allegati, nel file
+\file{ReadMonitor.c}.
-Una volta completata la gestione delle opzioni a riga di comando il programma
+Una volta conclusa la gestione delle opzioni a riga di comando il programma
rigenera (\texttt{\small 7}) con \func{ftok} la stessa chiave usata dal server
per identificare il segmento di memoria condivisa ed il mutex, poi
-(\texttt{\small 8}) si richiede con \func{semget} l'identificatore della
-memoria condivisa, ma in questo caso si vuole che esso esista di già; al
-solito (\texttt{\small 9--12}) si esce in caso di errore. Una volta ottenuto
-l'identificatore in \var{shmid} si può (\texttt{\small 13--16}) agganciare il
-segmento al processo all'indirizzo \func{shmptr}; anche in questo caso si
-chiude immediatamente il programma se qualcosa non funziona. Infine
+(\texttt{\small 8}) richiede con \func{ShmFind} l'indirizzo della memoria
+condivisa agganciando al contempo il segmento al processo, Infine
(\texttt{\small 17--20}) con \func{MutexFind} si richiede l'identificatore del
-mutex.
-
-Una volta completata l'inizializzazione ed ottenuti i riferimenti agli oggetti
+mutex. Completata l'inizializzazione ed ottenuti i riferimenti agli oggetti
di intercomunicazione necessari viene eseguito il corpo principale del
programma (\texttt{\small 21--33}); si comincia (\texttt{\small 22})
acquisendo il mutex con \func{MutexLock}; qui avviene il blocco del processo
se la memoria condivisa non è disponibile. Poi (\texttt{\small 23--31}) si
stampano i vari valori mantenuti nella memoria condivisa attraverso l'uso di
-\var{shmptr}. Infine (\texttt{\small 41}) con \func{MutexUnlock} si rilascia
+\var{shmptr}. Infine (\texttt{\small 41}) con \func{MutexUnlock} si rilascia
il mutex, prima di uscire.
Verifichiamo allora il funzionamento dei nostri programmi; al solito, usando
\end{verbatim}%$
Se a questo punto aggiungiamo un file, ad esempio con \code{touch prova},
-potremo verificare (passati nel peggiore dei casi almeno 10 secondi, cioè
-l'intervallo scelto per la rilettura dei dati), che:
+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
Ci sono 69 file dati
Totale 72 file, per 489887 byte
\end{verbatim}%$
-Infine potremo terminare il server con il comando \code{killall dirmonitor},
-nel qual caso, ripetendo la lettura otterremo che:
+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}
[piccardi@gont sources]$ ./readmon
Cannot find shared memory: No such file or directory
\end{verbatim}%$
-e potremo verificare che anche gli oggetti di intercomunicazione sono stati
-cancellati:
+e inoltre potremo anche verificare che anche gli oggetti di intercomunicazione
+visti in precedenza sono stati regolarmente cancellati:
\begin{verbatim}
[piccardi@gont sources]$ ipcs
------ Shared Memory Segments --------
%% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta
%% riferimento alle strutture con cui il kernel implementa i segmenti di memoria
%% condivisa; uno schema semplificato della struttura è illustrato in
-%% \figref{fig:ipc_shm_struct}.
+%% fig.~\ref{fig:ipc_shm_struct}.
%% \begin{figure}[htb]
%% \centering
\section{Tecniche alternative}
\label{sec:ipc_alternatives}
-Come abbiamo detto in \secref{sec:ipc_sysv_generic}, e ripreso nella
+Come abbiamo detto in sez.~\ref{sec:ipc_sysv_generic}, e ripreso nella
descrizione dei singoli oggetti che ne fan parte, il \textit{SysV IPC}
presenta numerosi problemi; in \cite{APUE}\footnote{in particolare nel
capitolo 14.} Stevens ne effettua una accurata analisi (alcuni dei concetti
Le code di messaggi sono probabilmente il meno usato degli oggetti del
\textit{SysV IPC}; esse infatti nacquero principalmente come meccanismo di
comunicazione bidirezionale quando ancora le pipe erano unidirezionali; con la
-disponibilità di \func{socketpair} (vedi \secref{sec:ipc_socketpair}) o
+disponibilità di \func{socketpair} (vedi sez.~\ref{sec:ipc_socketpair}) o
utilizzando una coppia di pipe, si può ottenere questo risultato senza
incorrere nelle complicazioni introdotte dal \textit{SysV IPC}.
\label{sec:ipc_file_lock}
\index{file!di lock|(}
-Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC}
+Come illustrato in sez.~\ref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC}
presentano una interfaccia inutilmente complessa e con alcuni difetti
strutturali, per questo quando si ha una semplice esigenza di sincronizzazione
per la quale basterebbe un semaforo binario (quello che abbiamo definito come
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
-\secref{sec:file_open}) che prevede\footnote{questo è quanto dettato dallo
+sez.~\ref{sec:file_open}) 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 race
- condition\index{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}.
+ è comunque soggetti alla possibilità di una race
+ condition\index{\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 \figref{fig:ipc_file_lock}
+\func{LockFile} ed \func{UnlockFile} riportate in fig.~\ref{fig:ipc_file_lock}
(sono contenute in \file{LockFile.c}, un'altro dei sorgenti allegati alla
guida) che permettono rispettivamente di creare e rimuovere un \textsl{file di
lock}. Come si può notare entrambe le funzioni sono elementari; la prima
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h> /* unix standard functions */
-/*
- * Function LockFile:
- */
-int LockFile(const char* path_name)
-{
- return open(path_name, O_EXCL|O_CREAT);
-}
-/*
- * Function UnlockFile:
- */
-int UnlockFile(const char* path_name)
-{
- return unlink(path_name);
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/LockFile.c}
\end{minipage}
\normalsize
\caption{Il codice delle funzioni \func{LockFile} e \func{UnlockFile} che
\end{figure}
Uno dei limiti di questa tecnica è che, come abbiamo già accennato in
-\secref{sec:file_open}, questo comportamento di \func{open} può non funzionare
-(la funzione viene eseguita, ma non è garantita l'atomicità dell'operazione)
-se il filesystem su cui si va ad operare è su NFS; in tal caso si può adottare
-una tecnica alternativa che prevede l'uso della \func{link} per creare come
-\textsl{file di lock} un hard link ad un file esistente; se il link esiste già
-e la funzione fallisce, significa che la risorsa è bloccata e potrà essere
-sbloccata solo con un \func{unlink}, altrimenti il link è creato ed il lock
-acquisito; il controllo e l'eventuale acquisizione sono atomici; la soluzione
-funziona anche su NFS, ma ha un'altro difetto è che è quello di poterla usare
-solo se si opera all'interno di uno stesso filesystem.
+sez.~\ref{sec:file_open}, questo comportamento di \func{open} può non
+funzionare (la funzione viene eseguita, ma non è garantita l'atomicità
+dell'operazione) se il filesystem su cui si va ad operare è su NFS; in tal
+caso si può adottare una tecnica alternativa che prevede l'uso della
+\func{link} per creare come \textsl{file di lock} un hard link ad un file
+esistente; se il link esiste già e la funzione fallisce, significa che la
+risorsa è bloccata e potrà essere sbloccata solo con un \func{unlink},
+altrimenti il link è creato ed il lock acquisito; il controllo e l'eventuale
+acquisizione sono atomici; la soluzione funziona anche su NFS, ma ha un'altro
+difetto è che è quello di poterla usare solo se si opera all'interno di uno
+stesso filesystem.
Un generale comunque l'uso di un \textsl{file di lock} presenta parecchi
problemi, che non lo rendono una alternativa praticabile per la
sincronizzazione: anzitutto in caso di terminazione imprevista del processo,
si lascia allocata la risorsa (il \textsl{file di lock}) e questa deve essere
sempre cancellata esplicitamente. Inoltre il controllo della disponibilità
-può essere eseguito solo con una tecnica di \textit{polling}\index{polling},
-ed è quindi molto inefficiente.
+può essere eseguito solo con una tecnica di
+\textit{polling}\index{\textit{polling}}, ed è quindi molto inefficiente.
La tecnica dei file di lock ha comunque una sua utilità, e può essere usata
con successo quando l'esigenza è solo quella di segnalare l'occupazione di una
Dato che i file di lock\index{file!di lock} presentano gli inconvenienti
illustrati in precedenza, la tecnica alternativa di sincronizzazione più
comune è quella di fare ricorso al \textit{file locking}\index{file!locking}
-(trattato in \secref{sec:file_locking}) usando \func{fcntl} su un file creato
-per l'occasione per ottenere un write lock. In questo modo potremo usare il
-lock come un \textit{mutex}: per bloccare la risorsa basterà acquisire il
-lock, per sbloccarla basterà rilasciare il lock. Una richiesta fatta con un
-write lock metterà automaticamente il processo in stato di attesa, senza
-necessità di ricorrere al \textit{polling}\index{polling} per determinare la
-disponibilità della risorsa, e al rilascio della stessa da parte del processo
-che la occupava si otterrà il nuovo lock atomicamente.
+(trattato in sez.~\ref{sec:file_locking}) usando \func{fcntl} su un file
+creato per l'occasione per ottenere un write lock. In questo modo potremo
+usare il lock come un \textit{mutex}: per bloccare la risorsa basterà
+acquisire il lock, per sbloccarla basterà rilasciare il lock. Una richiesta
+fatta con un write lock metterà automaticamente il processo in stato di
+attesa, senza necessità di ricorrere al
+\textit{polling}\index{\textit{polling}} per determinare la disponibilità
+della risorsa, e al rilascio della stessa da parte del processo che la
+occupava si otterrà il nuovo lock atomicamente.
Questo approccio presenta il notevole vantaggio che alla terminazione di un
processo tutti i lock acquisiti vengono rilasciati automaticamente (alla
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{15cm}
- \begin{lstlisting}{}
-/* Function CreateMutex: Create a mutex using file locking. */
-int CreateMutex(const char *path_name)
-{
- return open(path_name, O_EXCL|O_CREAT);
-}
-/* Function UnlockMutex: unlock a file. */
-int FindMutex(const char *path_name)
-{
- return open(path_name, O_RDWR);
-}
-/* Function LockMutex: lock mutex using file locking. */
-int LockMutex(int fd)
-{
- struct flock lock; /* file lock structure */
- /* 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 */
- lock.l_start = 0; /* set the start of the locked region */
- lock.l_len = 0; /* set the length of the locked region */
- /* do locking */
- return fcntl(fd, F_SETLKW, &lock);
-}
-/* Function UnlockMutex: unlock a file. */
-int UnlockMutex(int fd)
-{
- struct flock lock; /* file lock structure */
- /* set flock structure */
- lock.l_type = F_UNLCK; /* set type: unlock */
- lock.l_whence = SEEK_SET; /* start from the beginning of the file */
- lock.l_start = 0; /* set the start of the locked region */
- lock.l_len = 0; /* set the length of the locked region */
- /* do locking */
- return fcntl(fd, F_SETLK, &lock);
-}
-/* Function RemoveMutex: remove a mutex (unlinking the lock file). */
-int RemoveMutex(const char *path_name)
-{
- return unlink(path_name);
-}
-/* Function ReadMutex: read a mutex status. */
-int ReadMutex(int fd)
-{
- int res;
- struct flock lock; /* file lock structure */
- /* set flock structure */
- lock.l_type = F_WRLCK; /* set type: unlock */
- lock.l_whence = SEEK_SET; /* start from the beginning of the file */
- lock.l_start = 0; /* set the start of the locked region */
- lock.l_len = 0; /* set the length of the locked region */
- /* do locking */
- if ( (res = fcntl(fd, F_GETLK, &lock)) ) {
- return res;
- }
- return lock.l_type;
-}
- \end{lstlisting}
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/MutexLocking.c}
\end{minipage}
\normalsize
\caption{Il codice delle funzioni che permettono per la gestione dei
\end{figure}
Il codice delle varie funzioni usate per implementare un mutex utilizzando il
-file locking\index{file!locking} è riportato in \figref{fig:ipc_flock_mutex};
+file locking\index{file!locking} è riportato in fig.~\ref{fig:ipc_flock_mutex};
si è mantenuta volutamente una struttura analoga alle precedenti funzioni che
usano i semafori, anche se le due interfacce non possono essere completamente
equivalenti, specie per quanto riguarda la rimozione del mutex.
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
+riveda quanto detto sez.~\ref{sec:file_posix_lock}) solo il processo che ha
precedentemente eseguito il lock può sbloccare il mutex.
La quinta funzione (\texttt{\small 36--39}) è \func{RemoveMutex} e serve a
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
+sez.~\ref{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 si cumulano e non danno perciò
nessun inconveniente.
evitare l'uso di una memoria condivisa facendo ricorso al cosiddetto
\textit{memory mapping} anonimo.
-In \secref{sec:file_memory_map} abbiamo visto come sia possibile mappare il
+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
restano in memoria e possono essere riletti secondo le stesse modalità usate
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.
+sez.~\ref{sec:ipc_sysv_shm} che possa restituisca i risultati via rete.
-\section{La comunicazione fra processi di POSIX}
+\section{Il sistema di comunicazione fra processi di POSIX}
\label{sec:ipc_posix}
Per superare i numerosi problemi del \textit{SysV IPC}, evidenziati per i suoi
-aspetti generali in coda a \secref{sec:ipc_sysv_generic} e per i singoli
+aspetti generali in coda a sez.~\ref{sec:ipc_sysv_generic} e per i singoli
oggetti nei paragrafi successivi, lo standard POSIX.1b ha introdotto dei nuovi
meccanismi di comunicazione, che vanno sotto il nome di POSIX IPC, definendo
una interfaccia completamente nuova, che tratteremo in questa sezione.
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:
+\textit{Posix IPC names}\index{\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}
\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.
+ \index{\textit{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.
è 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}.
+ consentire implementazioni non standardizzabili.} Nel caso di Linux, sia per
+quanto riguarda la memoria condivisa, 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} e
+sez.~\ref{sec:ipc_posix_mq}) ed i nomi specificati nelle relative funzioni
+sono considerati come un \index{\textit{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 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.
+comandi di accesso ai file,\footnote{questo è ancora più 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 esse sono
+ realizzate con dei file in speciali 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
+semantica (quella illustrata in sez.~\ref{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
+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
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
+trovati su \href{http://www.mat.uni.torun.pl/~wrona/posix_ipc}
+{\textsf{http://www.mat.uni.torun.pl/\tild{}wrona/posix\_ipc}}, questi sono
+stati inseriti nel kernel ufficiale a partire dalla versione 2.6.6-rc1.}. 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
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.}
+ \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} a partire dalla
+ versione 2.3.4.} 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
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
+tab.~\ref{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
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}.
+fig.~\ref{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}
+ \includestruct{listati/mq_attr.h}
\end{minipage}
\normalsize
\caption{La struttura \structd{mq\_attr}, contenente gli attributi di una
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
+\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 \secref{sec:file_asyncronous_io} a proposito dell'uso
+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.
Attraverso questa struttura si possono impostare le modalità con cui viene
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
+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 \secref{sec:sig_real_time} a proposito dei segnali
+ 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
-\secref{sec:sig_sigaction}.
+sez.~\ref{sec:sig_sigaction}.
La funzione registra il processo chiamante per la notifica se
\param{notification} punta ad una struttura \struct{sigevent} opportunamente
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
+fig.~\ref{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}.}
+ proposito in sez.~\ref{sec:sig_sigaction} e sez.~\ref{sec:sig_real_time}.}
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
+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{faxt user mode mutex}.}, con
+\textit{futex}\footnote{la sigla sta per \textit{fast 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
\label{sec:ipc_posix_shm}
La memoria condivisa è l'unico degli oggetti di IPC POSIX già presente nel
-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:
+kernel ufficiale; in realtà 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 \texttt{CONFIG\_TMPFS} in fase di compilazione del kernel.
+
+
+Per potere utilizzare l'interfaccia POSIX per le code di messaggi 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:
\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}.}
+ad \file{/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=10G,nr_inodes=10k,mode=700 tmpfs /mytmpfs
+mount -t tmpfs -o size=128M,nr_inodes=10k,mode=700 tmpfs /mytmpfs
\end{verbatim}
+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} che in
+questo caso è l'unità di allocazione elementare.
+
+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)}
+
+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}
+
+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 intepretati 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 routine
+ di libereria, 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
+maschera binaria comprendente almeno uno dei due valori \const{O\_RDONLY} e
+\const{O\_RDWR}; i valori possibili per i vari bit sono quelli visti in
+tab.~\ref{tab:file_open_flags} dei quali però \func{shm\_open} riconosce solo
+i seguenti:
+\begin{basedescript}{\desclabelwidth{2.0cm}\desclabelstyle{\nextlinelabel}}
+\item[\const{O\_RDONLY}] Apre il file descriptor associato al segmento di
+ memoria condivisa per l'accesso in sola lettura.
+\item[\const{O\_RDWR}] Apre il file descriptor associato al segmento di
+ memoria condivisa per l'accesso in lettura e scrittura.
+\item[\const{O\_CREAT}] Necessario qualora si debba creare il segmento di
+ memoria condivisa se esso non esiste; in questo caso viene usato il valore
+ di \param{mode} per impostare i permessi, che devono essere compatibili con
+ le modalità con cui si è aperto il file.
+\item[\const{O\_EXCL}] Se usato insieme a \const{O\_CREAT} fa fallire la
+ chiamata a \func{shm\_open} se il segmento esiste già, altrimenti esegue la
+ creazione atomicamente.
+\item[\const{O\_TRUNC}] Se il segmento di memoria condivisa esiste già, ne
+ tronca le dimensioni a 0 byte.
+\end{basedescript}
+
+In caso di successo la funzione restituisce un file descriptor associato al
+segmento di memoria condiviso con le stesse modalità di
+\func{open}\footnote{in realtà, come accennato, \func{shm\_open} è un semplice
+ wrapper per \func{open}, usare direttamente quest'ultima avrebbe lo stesso
+ effetto.} viste in sez.~\ref{sec:file_open}; in particolare viene impostato
+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 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.
+
+Quando il nome non esiste il segmento può essere creato specificando
+\const{O\_CREAT}; in tal caso il segmento avrà (così come i nuovi file)
+lunghezza nulla. Dato che un segmento di lunghezza nulla è di scarsa utilità,
+per impostarne la dimensione si deve usare \func{ftruncate} (vedi
+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}
+{int shm\_unlink(const char *name)}
+
+Rimuove un segmento di memoria condivisa.
+
+\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}
+
+La funzione è del tutto analoga ad \func{unlink}, e si limita a cancellare il
+nome del segmento da \file{/dev/shm}, senza nessun effetto né sui file
+descriptor precedentemente aperti con \func{shm\_open}, né sui segmenti già
+mappati in memoria; questi verranno cancellati automaticamente dal sistema
+solo con le rispettive chiamate a \func{close} e \func{munmap}. Una volta
+eseguita questa funzione però, qualora si richieda l'apertura di un segmento
+con lo stesso nome, la chiamata a \func{shm\_open} fallirà, a meno di non aver
+usato \const{O\_CREAT}, in quest'ultimo caso comunque si otterrà un file
+descriptor che fa riferimento ad un segmento distinto da eventuali precedenti.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15.6cm}
+ \includecodesample{listati/MemShared.c}
+ \end{minipage}
+ \normalsize
+ \caption{Il codice delle funzioni di gestione dei segmenti di memoria
+ condivisa POSIX.}
+ \label{fig:ipc_posix_shmmem}
+\end{figure}
+
+Come esempio per l'uso di queste funzioni vediamo come è possibile riscrivere
+una interfaccia semplificata analoga a quella vista in
+fig.~\ref{fig:ipc_sysv_shm_func} per la memoria condivisa in stile SysV. Il
+codice, riportato in fig.~\ref{fig:ipc_posix_shmmem}, è sempre contenuto nel
+file \file{SharedMem.c} dei sorgenti allegati.
+
+La prima funzione (\texttt{\small 1--24}) è \func{CreateShm} che, dato un nome
+nell'argomento \var{name} crea un nuovo segmento di memoria condivisa,
+accessibile in lettura e scrittura, e ne restituisce l'indirizzo. Anzitutto si
+definiscono (\texttt{\small 8}) i flag per la successiva (\texttt{\small 9})
+chiamata a \func{shm\_open}, che apre il segmento in lettura e scrittura
+(creandolo se non esiste, ed uscendo in caso contrario) assegnandogli sul
+filesystem i permessi specificati dall'argomento \var{perm}. In caso di errore
+(\texttt{\small 10--12}) si restituisce un puntatore nullo, altrimenti si
+prosegue impostando (\texttt{\small 14}) la dimensione del segmento con
+\func{ftruncate}. Di nuovo (\texttt{\small 15--16}) si esce immediatamente
+restituendo un puntatore nullo in caso di errore. Poi si passa (\texttt{\small
+ 18}) a mappare in memoria il segmento con \func{mmap} specificando dei
+diritti di accesso corrispondenti alla modalità di apertura. Di nuovo si
+restituisce (\texttt{\small 19--21}) un puntatore nullo in caso di errore,
+altrimenti si inizializza (\texttt{\small 22}) il contenuto del segmento al
+valore specificato dall'argomento \var{fill} con \func{memset}, e se ne
+restituisce (\texttt{\small 23}) l'indirizzo.
+
+La seconda funzione (\texttt{\small 25--40}) è \func{FindShm} che trova un
+segmento di memoria condiviso già esistente, restituendone l'indirizzo. In
+questo caso si apre (\texttt{\small 31}) il segmento con \func{shm\_open}
+richiedendo che il segmento sia già esistente, in caso di errore
+(\texttt{\small 31--33}) si ritorna immediatamente un puntatore nullo.
+Ottenuto il file descriptor del segmento lo si mappa (\texttt{\small 35}) in
+memoria con \func{mmap}, restituendo (\texttt{\small 36--38}) un puntatore
+nullo in caso di errore, o l'indirizzo (\texttt{\small 39}) dello stesso in
+caso di successo.
+
+La terza funzione (\texttt{\small 40--45}) è \func{RemoveShm}, e serve a
+cancellare un segmento di memoria condivisa. Dato che al contrario di quanto
+avveniva con i segmenti del SysV IPC gli oggetti allocati nel kernel vengono
+rilasciati automaticamente quando nessuna li usa più, tutto quello che c'è da
+fare (\texttt{\small 44}) in questo caso è chiamare \func{shm\_unlink},
+retituendo al chiamante il valore di ritorno.
- la memoria
-condivisa è trattata come un filesystem separato, con tutte le caratteristiche
-di un qualunque filesystem,
%%% Local Variables: