X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=ipc.tex;h=ba47d11cd3007013384d2da885d37c6d54038121;hb=6686b8c78363c1e91e069b0ff9932929b7948136;hp=709db831b78fb53d5411a3625538b86f9a5eb581;hpb=bf78d63598adf72d71aeea3c32277dd63484bb02;p=gapil.git diff --git a/ipc.tex b/ipc.tex index 709db83..87c35f8 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,238 +1,256 @@ -\chapter{La comunicazione fra processi} +%% ipc.tex +%% +%% Copyright (C) 2000-2013 Simone Piccardi. Permission is granted to +%% copy, distribute and/or modify this document under the terms of the GNU Free +%% Documentation License, Version 1.1 or any later version published by the +%% 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". +%% + +\chapter{L'intercomunicazione fra processi} \label{cha:IPC} -Uno degli aspetti fondamentali della programmazione in un sistema unix-like è +Uno degli aspetti fondamentali della programmazione in un sistema unix-like è la comunicazione fra processi. In questo capitolo affronteremo solo i -meccanismi più elementari che permettono di mettere in comunicazione processi +meccanismi più elementari che permettono di mettere in comunicazione processi diversi, come quelli tradizionali che coinvolgono \textit{pipe} e -\textit{fifo} e i meccanismi di intercomunicazione di System V. +\textit{fifo} e i meccanismi di intercomunicazione di System V e quelli POSIX. Tralasceremo invece tutte le problematiche relative alla comunicazione attraverso la rete (e le relative interfacce) che saranno affrontate in -dettaglio in un secondo tempo. Non affronteremo invece meccanismi più -complessi ed evoluti come le RPC (\textit{Remote Procedure Calls}) e CORBA -(\textit{Common Object Request Brocker Architecture}) che in genere sono -implementati con un ulteriore livello sopra i meccanismi elementari. - +dettaglio in un secondo tempo. Non affronteremo neanche meccanismi più +complessi ed evoluti come le RPC (\textit{Remote Procedure Calls}) che in +genere sono implementati da un ulteriore livello di librerie sopra i +meccanismi elementari. -\section{La comunicazione fra processi tradizionale} +\section{L'intercomunicazione fra processi tradizionale} \label{sec:ipc_unix} -Il primo meccanismo di comunicazione fra processi usato dai sistemi unix-like, -e quello che viene correntemente usato di più, è quello delle \textit{pipe}, -che sono una delle caratteristiche peculiari del sistema, in particolar modo -dell'interfaccia a linea di comando. In questa sezione descriveremo le sue -basi, le funzioni che ne gestiscono l'uso e le varie forme in cui si è -evoluto. +Il primo meccanismo di comunicazione fra processi introdotto nei sistemi Unix, +è quello delle cosiddette \textit{pipe}; esse costituiscono una delle +caratteristiche peculiari del sistema, in particolar modo dell'interfaccia a +linea di comando. In questa sezione descriveremo le sue basi, le funzioni che +ne gestiscono l'uso e le varie forme in cui si è evoluto. \subsection{Le \textit{pipe} standard} \label{sec:ipc_pipes} 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 uno speciale tipo di file descriptor, più precisamente una coppia -di file descriptor,\footnote{si tenga presente che le pipe sono oggetti creati - dal kernel e non risiedono su disco.} su cui da una parte si scrive e da -un'altra si legge. Si viene così a costituire un canale di comunicazione -tramite i due file descriptor, nella forma di un \textsl{tubo} (da cui il -nome) in cui in genere un processo immette dati che poi arriveranno ad un -altro. - -La funzione che permette di creare una pipe è appunto \func{pipe}; il suo -prototipo è: -\begin{prototype}{unistd.h} -{int pipe(int filedes[2])} - -Crea una coppia di file descriptor associati ad una pipe. - - \bodydesc{La funzione restituisce zero in caso di successo e -1 per un - errore, nel qual caso \var{errno} potrà assumere i valori \macro{EMFILE}, - \macro{ENFILE} e \macro{EFAULT}.} -\end{prototype} +uno dei più usati, meccanismi di comunicazione fra processi. Si tratta in +sostanza di una coppia di file descriptor connessi fra di loro in modo che +quanto scrive su di uno si può rileggere dall'altro. Si viene così a +costituire un canale di comunicazione realizzato tramite i due file +descriptor, che costituisce appunto una sorta di \textsl{tubo} (che appunto il +significato del termine inglese \textit{pipe}) attraverso cui si possono far +passare i dati. + +In pratica si tratta di un buffer circolare in memoria in cui il kernel +appoggia i dati immessi nel file descriptor su cui si scrive per farli poi +riemergere dal file descriptor da cui si legge. Si tenga ben presente che in +questo passaggio di dati non è previsto nessun tipo di accesso al disco e che +nonostante l'uso dei file descriptor le \textit{pipe} non han nulla a che fare +con i file di dati di cui si è parlato al cap.~\ref{cha:file_IO_interface}. + +La funzione di sistema che permette di creare questa speciale coppia di file +descriptor associati ad una \textit{pipe} è appunto \funcd{pipe}, ed il suo +prototipo è: + +\begin{funcproto}{ +\fhead{unistd.h} +\fdecl{int pipe(int filedes[2])} +\fdesc{Crea la coppia di file descriptor di una \textit{pipe}.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EFAULT}] \param{filedes} non è un indirizzo valido. + \end{errlist} + ed inoltre \errval{EMFILE} e \errval{ENFILE} nel loro significato generico.} +\end{funcproto} -La funzione restituisce una coppia di file descriptor nell'array -\param{filedes}; il primo aperto in lettura ed il secondo in scrittura. Il -concetto di funzionamento di una pipe è relativamente semplice, quello che si -scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale -nel file descriptor aperto in lettura, da cui può essere riletto. - -I file descriptor infatti non sono connessi a nessun file reale, ma ad un -buffer nel kernel, la cui dimensione è specificata dalla costante -\macro{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 capi della pipe, associati a ciascun file -descriptor, con le frecce che indicano la direzione del flusso dei dati -attraverso la pipe. - -\begin{figure}[htb] +La funzione restituisce la coppia di file descriptor nel vettore +\param{filedes}, il primo è aperto in lettura ed il secondo in scrittura. Come +accennato concetto di funzionamento di una \textit{pipe} è semplice: quello +che si scrive nel file descriptor aperto in scrittura viene ripresentato tale +e quale nel file descriptor aperto in lettura. + +I file descriptor infatti non sono connessi a nessun file reale, ma, come +accennato, ad un buffer nel kernel la cui dimensione è specificata dal +parametro di sistema \const{PIPE\_BUF}, (vedi +sez.~\ref{sec:sys_file_limits}). Lo schema di funzionamento di una +\textit{pipe} è illustrato in fig.~\ref{fig:ipc_pipe_singular}, in cui sono +indicati i due capi della \textit{pipe}, associati a ciascun file descriptor, +con le frecce che indicano la direzione del flusso dei dati. + +\begin{figure}[!htb] \centering \includegraphics[height=5cm]{img/pipe} - \caption{Schema della struttura di una pipe.} + \caption{Schema della struttura di una \textit{pipe}.} \label{fig:ipc_pipe_singular} \end{figure} -Chiaramente creare una pipe all'interno di un processo non serve a niente; se -però ricordiamo quanto esposto in \secref{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 -capo della pipe, l'altro può leggere. +Della funzione di sistema esiste una seconda versione, \funcd{pipe2}, +introdotta con il kernel 2.6.27 e le \acr{glibc} 2.9 e specifica di Linux +(utilizzabile solo definendo la macro \macro{\_GNU\_SOURCE}), che consente di +impostare atomicamente le caratteristiche dei file descriptor restituiti, il +suo prototipo è: + +\begin{funcproto}{ +\fhead{unistd.h} +\fhead{fcntl.h} +\fdecl{int pipe2(int pipefd[2], int flags)} +\fdesc{Crea la coppia di file descriptor di una \textit{pipe}.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{flags} non valido. + \end{errlist} + e gli altri già visti per \func{pipe} con lo stesso significato.} +\end{funcproto} + +Utilizzando un valore nullo per \param{flags} la funzione è identica a +\func{pipe}, si può però passare come valore l'OR aritmetico di uno qualunque +fra \const{O\_NONBLOCK} o \const{O\_CLOEXEC} che hanno l'effetto di impostare +su entrambi i file descriptor restituiti dalla funzione i relativi flag, già +descritti per \func{open} in tab.~\ref{tab:open_operation_flag}, che attivano +rispettivamente la modalità di accesso \textsl{non-bloccante} ed il +\textit{close-on-exec} \itindex{close-on-exec}. + +Chiaramente creare una \textit{pipe} all'interno di un singolo processo non +serve a niente; se però ricordiamo quanto esposto in +sez.~\ref{sec:file_shared_access} riguardo al comportamento dei file +descriptor nei processi figli, è immediato capire come una \textit{pipe} possa +diventare un meccanismo di intercomunicazione. Un processo figlio infatti +condivide gli stessi file descriptor del padre, compresi quelli associati ad +una \textit{pipe} (secondo la situazione illustrata in +fig.~\ref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un +capo della \textit{pipe}, l'altro può leggere. -\begin{figure}[htb] +\begin{figure}[!htb] \centering \includegraphics[height=5cm]{img/pipefork} - \caption{Schema dei collegamenti ad una pipe, condivisi fra processo padre e - figlio dopo l'esecuzione \func{fork}.} + \caption{Schema dei collegamenti ad una \textit{pipe}, condivisi fra + processo padre e figlio dopo l'esecuzione \func{fork}.} \label{fig:ipc_pipe_fork} \end{figure} -Tutto ciò ci mostra come sia immediato realizzare un meccanismo di -comunicazione fra processi attraverso una pipe, utilizzando le ordinarie -proprietà dei file, ma ci mostra anche qual'è il principale\footnote{Stevens - in \cite{APUE} riporta come limite anche il fatto che la comunicazione è - unidirezionale, in realtà questo è un limite facilmente superabile usando - una coppia di pipe.} limite nell'uso delle pipe. È necessario infatti che i -processi possano condividere i file descriptor della pipe, e per questo essi -devono comunque derivare da uno stesso processo padre che ha aperto la pipe, -o, più comunemente, essere nella relazione padre/figlio. - -A differenza di quanto avviene con i file normali la lettura da una pipe può -essere bloccante (qualora non siano presenti dati), inoltre se si legge da una -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 \macro{EPIPE}, e la funzione di scrittura -restituirà un errore di \macro{EPIPE} (al ritorno del manipolatore, o qualora -il segnale sia ignorato o bloccato). - -La dimensione del buffer della pipe (\macro{PIPE\_BUF}) ci dà inoltre un'altra -importante informazione riguardo il comportamento delle operazioni di lettura -e scrittura su di una pipe; esse infatti sono atomiche fintanto che la -quantità di dati da scrivere non supera questa dimensione. Qualora ad esempio -si effettui una scrittura di una quantità di dati superiore l'operazione verrà -effettuata in più riprese, consentendo l'intromissione di scritture effettuate -da altri processi. - - -\subsection{Un esempio dell'uso delle pipe} +Tutto ciò ci mostra come sia immediato realizzare un meccanismo di +comunicazione fra processi attraverso una \textit{pipe}, utilizzando le +proprietà ordinarie dei file, ma ci mostra anche qual è il principale limite +nell'uso delle \textit{pipe}.\footnote{Stevens in \cite{APUE} riporta come + limite anche il fatto che la comunicazione è unidirezionale, ma in realtà + questo è un limite superabile usando una coppia di \textit{pipe}, anche se + al costo di una maggiore complessità di gestione.} È necessario infatti che +i processi possano condividere i file descriptor della \textit{pipe}, e per +questo essi devono comunque essere \textsl{parenti} (dall'inglese +\textit{siblings}), cioè o derivare da uno stesso processo padre in cui è +avvenuta la creazione della \textit{pipe}, o, più comunemente, essere nella +relazione padre/figlio. + +A differenza di quanto avviene con i file normali, la lettura da una +\textit{pipe} può essere bloccante (qualora non siano presenti dati), inoltre +se si legge da una \textit{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 \textit{pipe} il cui +capo in lettura non è aperto il processo riceverà il segnale \signal{SIGPIPE}, +e la funzione di scrittura restituirà un errore di \errcode{EPIPE} (al ritorno +del gestore, o qualora il segnale sia ignorato o bloccato). + +La dimensione del buffer della \textit{pipe} (\const{PIPE\_BUF}) ci dà inoltre +un'altra importante informazione riguardo il comportamento delle operazioni di +lettura e scrittura su di una \textit{pipe}; esse infatti sono atomiche +fintanto che la quantità di dati da scrivere non supera questa +dimensione. Qualora ad esempio si effettui una scrittura di una quantità di +dati superiore l'operazione verrà effettuata in più riprese, consentendo +l'intromissione di scritture effettuate da altri processi. + +La dimensione originale del buffer era di 4096 byte (uguale ad una pagina di +memoria) fino al kernel 2.6.11, ed è stata portata in seguito a 64kb; ma a +partire dal kernel 2.6.35 è stata resa disponibile l'operazione di controllo +\const{F\_SETPIPE\_SZ} (vedi sez.~\ref{sec:ipc_pipes}) che consente di +modificarne la dimensione. + + + +\subsection{Un esempio dell'uso delle \textit{pipe}} \label{sec:ipc_pipe_use} -Per capire meglio il funzionamento di una pipe faremo un esempio di quello che -è il loro uso più comune, analogo a quello effettuato della shell, e che -consiste nell'inviare l'output di un processo (lo standard output) sull'input -di un'altro. Realizzeremo il programma nella forma di un -\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un programma - che permette la creazione dinamica di un oggetto da inserire all'interno di - una pagina HTML.} per apache, che genera una immagine JPEG di un codice a -barre, specificato come parametro di input. +Per capire meglio il funzionamento delle \textit{pipe} faremo un esempio di +quello che è il loro uso più comune, analogo a quello effettuato della shell, +e che consiste nell'inviare l'output di un processo (lo standard output) +sull'input di un altro. Realizzeremo il programma di esempio nella forma di un +\textit{CGI}\footnote{quella dei CGI (\textit{Common Gateway Interface}) è una + interfaccia che consente ad un server web di eseguire un programma il cui + output (che deve essere opportunamente formattato seguendo le specifiche + dell'interfaccia) può essere presentato come risposta ad una richiesta HTTP + al posto del contenuto di un file, e che ha costituito probabilmente la + prima modalità con cui sono state create pagine HTML dinamiche.} che genera +una immagine JPEG 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 -\end{verbatim} +\begin{Verbatim} +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 -possa reinviarlo al browser che ha effettuato la richiesta, che in questo modo -è in grado di visualizzarlo opportunamente. - -Per fare questo useremo in sequenza i programmi \cmd{barcode} e \cmd{gs}, il -primo infatti è in grado di generare immagini postscript di 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 direzione del flusso -dei dati è data dalle frecce continue. - -\begin{figure}[htb] +che ne descrive il \textit{mime-type}) sullo \textit{standard output}, in modo +che il server web possa reinviarlo al browser che ha effettuato la richiesta, +che in questo modo è in grado di visualizzarlo opportunamente. + +\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.} + \caption{Schema dell'uso di una \textit{pipe} come mezzo di comunicazione fra + 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 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. +Per realizzare quanto voluto useremo in sequenza i programmi \cmd{barcode} e +\cmd{gs}, il primo infatti è in grado di generare immagini PostScript di +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 \textit{pipe} potremo inviare l'output del primo sull'input del +secondo, secondo lo schema mostrato in fig.~\ref{fig:ipc_pipe_use}, in cui la +direzione del flusso dei dati è data dalle frecce continue. -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 -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. +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} può essere eseguito più volte in contemporanea, e si avrebbe una +evidente \itindex{race~condition} \textit{race condition} in caso di accesso +simultaneo a detto file da istanze diverse. Il problema potrebbe essere +superato utilizzando un sempre diverso per il file temporaneo, che verrebbe +creato all'avvio di ogni istanza, utilizzato dai sottoprocessi, e cancellato +alla fine della sua esecuzione; ma a questo punto le cose non sarebbero più +tanto semplici. L'uso di una \textit{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 +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 tab.~\ref{tab:file_std_files} e +sez.~\ref{sec:file_stream}) sulla \textit{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 to handle process 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); //"-o", "-", 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) { /* on error exit */ - 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} + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/BarCodePage.c} \end{minipage} \normalsize \caption{Sezione principale del codice del \textit{CGI} @@ -240,388 +258,4521 @@ int main(int argc, char *argv[], char *envp[]) \label{fig:ipc_barcodepage_code} \end{figure} -La prima operazione del programma (\texttt{\small 4--12}) è quella di creare -le due pipe che serviranno per la comunicazione fra i due comandi utilizzati -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 - 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.} +La prima operazione del programma (\texttt{\small 4--12}) è quella di creare +le due \textit{pipe} che serviranno per la comunicazione fra i due comandi +utilizzati 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. La funzione \func{WriteMess} non è riportata in +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. -Una volta create le pipe, il programma può creare (\texttt{\small 13-17}) il -primo processo figlio, che si incaricherà (\texttt{\small 19--25}) di eseguire -\cmd{barcode}. Quest'ultimo legge dallo standard input una stringa di -caratteri, la converte nell'immagine postscript del codice a barre ad essa -corrispondente, e poi scrive il risultato direttamente sullo standard output. +Una volta create le \textit{pipe}, il programma può creare (\texttt{\small + 13-17}) il primo processo figlio, che si incaricherà (\texttt{\small + 19--25}) di eseguire \cmd{barcode}. Quest'ultimo legge dallo standard input +una stringa di caratteri, la converte nell'immagine PostScript del codice a +barre ad essa corrispondente, e poi scrive il risultato direttamente sullo +standard output. Per poter utilizzare queste caratteristiche prima di eseguire \cmd{barcode} si -chiude (\texttt{\small 20}) il capo aperto in scrittura della prima pipe, e se -ne collega (\texttt{\small 21}) il capo in lettura allo standard input, usando -\func{dup2}. Si ricordi che invocando \func{dup2} il secondo file, qualora -risulti aperto, viene, come nel caso corrente, chiuso prima di effettuare la -duplicazione. Allo stesso modo, dato che \cmd{barcode} scrive l'immagine -postscript del codice a barre sullo standard output, per poter effettuare una -ulteriore redirezione il capo in lettura della seconda pipe viene chiuso -(\texttt{\small 22}) mentre il capo in scrittura viene collegato allo standard -output (\texttt{\small 23}). +chiude (\texttt{\small 20}) il capo aperto in scrittura della prima +\textit{pipe}, e se ne collega (\texttt{\small 21}) il capo in lettura allo +\textit{standard input} usando \func{dup2}. Si ricordi che invocando +\func{dup2} il secondo file, qualora risulti aperto, viene, come nel caso +corrente, chiuso prima di effettuare la duplicazione. Allo stesso modo, dato +che \cmd{barcode} scrive l'immagine PostScript del codice a barre sullo +standard output, per poter effettuare una ulteriore redirezione il capo in +lettura della seconda \textit{pipe} viene chiuso (\texttt{\small 22}) mentre +il capo in scrittura viene collegato allo standard output (\texttt{\small + 23}). In questo modo all'esecuzione (\texttt{\small 25}) di \cmd{barcode} (cui si passa in \var{size} la dimensione della pagina per l'immagine) quest'ultimo -leggerà dalla prima pipe la stringa da codificare che gli sarà inviata dal -padre, e scriverà l'immagine postscript del codice a barre sulla seconda. +leggerà dalla prima \textit{pipe} la stringa da codificare che gli sarà +inviata dal padre, e scriverà l'immagine PostScript del codice a barre sulla +seconda. Al contempo una volta lanciato il primo figlio, il processo padre prima chiude -(\texttt{\small 26}) il capo inutilizzato della prima pipe (quello in input) e -poi scrive (\texttt{\small 27}) la stringa da convertire sul capo in output, -così che \cmd{barcode} possa riceverla dallo standard input. A questo punto -l'uso della prima pipe da parte del padre è finito ed essa può essere -definitivamente chiusa (\texttt{\small 28}), si attende poi (\texttt{\small - 29}) che l'esecuzione di \cmd{barcode} sia completata. - -Alla conclusione della sua esecuzione \cmd{barcode} avrà inviato l'immagine -postscript del codice a barre sul capo in scrittura della seconda pipe; a -questo punto si può eseguire la seconda conversione, da PS a JPEG, usando il -programma \cmd{gs}. Per questo si crea (\texttt{\small 30--34}) un secondo -processo figlio, che poi (\texttt{\small 35--42}) eseguirà questo programma -leggendo l'immagine postscript creata da \cmd{barcode} dallo standard input, -per convertirla in JPEG. - -Per fare tutto ciò anzitutto si chiude (\texttt{\small 37}) il capo in -scrittura della seconda pipe, e se ne collega (\texttt{\small 38}) il capo in -lettura allo standard input. Per poter formattare l'output del programma in -maniera utilizzabile da un browser, si provvede anche \texttt{\small 40}) alla -scrittura dell'apposita stringa di identificazione del mime-type in testa allo -standard output. A questo punto si può invocare \texttt{\small 41}) \cmd{gs}, -provvedendo gli appositi switch che consentono di leggere il file da -convertire dallo standard input e di inviare la conversione sullo standard -output. +(\texttt{\small 26}) il capo inutilizzato della prima \textit{pipe} (quello in +ingresso) e poi scrive (\texttt{\small 27}) la stringa da convertire sul capo +in uscita, così che \cmd{barcode} possa riceverla dallo \textit{standard + input}. A questo punto l'uso della prima \textit{pipe} da parte del padre è +finito ed essa può essere definitivamente chiusa (\texttt{\small 28}), si +attende poi (\texttt{\small 29}) che l'esecuzione di \cmd{barcode} sia +completata. + +Alla conclusione della sua esecuzione \cmd{barcode} avrà inviato l'immagine +PostScript del codice a barre sul capo in scrittura della seconda +\textit{pipe}; a questo punto si può eseguire la seconda conversione, da PS a +JPEG, usando il programma \cmd{gs}. Per questo si crea (\texttt{\small + 30--34}) un secondo processo figlio, che poi (\texttt{\small 35--42}) +eseguirà questo programma leggendo l'immagine PostScript creata da +\cmd{barcode} dallo \textit{standard input}, per convertirla in JPEG. + +Per fare tutto ciò anzitutto si chiude (\texttt{\small 37}) il capo in +scrittura della seconda \textit{pipe}, e se ne collega (\texttt{\small 38}) il +capo in lettura allo \textit{standard input}. Per poter formattare l'output +del programma in maniera utilizzabile da un browser, si provvede anche +\texttt{\small 40}) alla scrittura dell'apposita stringa di identificazione +del \textit{mime-type} in testa allo \textit{standard output}. A questo punto +si può invocare \texttt{\small 41}) \cmd{gs}, provvedendo le opportune opzioni +del comando che consentono di leggere il file da convertire dallo +\textit{standard input} e di inviare la conversione sullo \textit{standard + output}. Per completare le operazioni il processo padre chiude (\texttt{\small 44}) il -capo in scrittura della seconda pipe, e attende la conclusione del figlio -(\texttt{\small 45}); a questo punto può (\texttt{\small 46}) uscire. Si tenga -conto che l'operazione di chiudere il capo in scrittura della seconda pipe è -necessaria, infatti, se non venisse chiusa, \cmd{gs}, che legge il suo -standard input da detta pipe, resterebbe bloccato in attesa di ulteriori dati -in ingresso (l'unico modo che un programma ha per sapere che l'input è -terminato è rilevare che lo standard input è stato chiuso), e la \func{wait} -non ritornerebbe. +capo in scrittura della seconda \textit{pipe}, e attende la conclusione del +figlio (\texttt{\small 45}); a questo punto può (\texttt{\small 46}) +uscire. Si tenga conto che l'operazione di chiudere il capo in scrittura della +seconda \textit{pipe} è necessaria, infatti, se non venisse chiusa, \cmd{gs}, +che legge il suo \textit{standard input} da detta \textit{pipe}, resterebbe +bloccato in attesa di ulteriori dati in ingresso (l'unico modo che un +programma ha per sapere che i dati in ingresso sono terminati è rilevare che +lo \textit{standard input} è stato chiuso), e la \func{wait} non ritornerebbe. \subsection{Le funzioni \func{popen} e \func{pclose}} \label{sec:ipc_popen} -Come si è visto la modalità più comune di utilizzo di una pipe è quella di -utilizzarla per fare da tramite fra output ed input di due programmi invocati -in sequenza; per questo motivo lo standard POSIX.2 ha introdotto due funzioni -che permettono di sintetizzare queste operazioni. La prima di esse si chiama -\func{popen} ed il suo prototipo è: -\begin{prototype}{stdio.h} -{FILE *popen(const char *command, const char *type)} - -Esegue il programma \param{command}, di cui, a seconda di \param{type}, -restituisce, lo standard input o lo standard output nella pipe collegata allo -stream restituito come valore di ritorno. - -\bodydesc{La funzione restituisce l'indirizzo dello stream associato alla pipe - in caso di successo e \macro{NULL} per un errore, nel qual caso \var{errno} - potrà assumere i valori relativi alle sottostanti invocazioni di \func{pipe} - e \func{fork} o \macro{EINVAL} se \param{type} non è valido.} -\end{prototype} +Come si è visto la modalità più comune di utilizzo di una \textit{pipe} è +quella di utilizzarla per fare da tramite fra output ed input di due programmi +invocati in sequenza; per questo motivo lo standard POSIX.2 ha introdotto due +funzioni che permettono di sintetizzare queste operazioni. La prima di esse si +chiama \funcd{popen} ed il suo prototipo è: -La funzione crea una pipe, esegue una \func{fork}, ed invoca il programma -\param{command} attraverso la shell (in sostanza esegue \file{/bin/sh} con il -flag \code{-c}); l'argomento \param{type} deve essere una delle due stringhe -\verb|"w"| o \verb|"r"|, per indicare se la pipe sarà collegata allo standard -input o allo standard output del comando invocato. - -La funzione restituisce il puntatore allo stream associato alla pipe creata, -che sarà aperto in sola lettura (e quindi associato allo standard output del -programma indicato) in caso si sia indicato \code{"r"}, o in sola scrittura (e -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 \secref{cha:files_std_interface}, anche se è collegato ad una -pipe e non ad un inode, 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, \func{pclose}, il cui prototipo è: -\begin{prototype}{stdio.h} -{int pclose(FILE *stream)} - -Chiude il file \param{stream}, restituito da una precedente \func{popen} -attendendo la terminazione del processo ad essa associato. - -\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore; nel quel caso il valore di \func{errno} deriva dalle sottostanti - chiamate.} -\end{prototype} -\noindent che oltre alla chiusura dello stream si incarica anche di attendere -(tramite \func{wait4}) la conclusione del processo creato dalla precedente -\func{popen}. + +\begin{funcproto}{ +\fhead{stdio.h} +\fdecl{FILE *popen(const char *command, const char *type)} +\fdesc{Esegue un programma dirottando l'uscita su una \textit{pipe}.} +} + +{La funzione ritorna l'indirizzo dello stream associato alla \textit{pipe} in + caso di successo e \val{NULL} per un errore, nel qual caso \var{errno} potrà + assumere i valori relativi alle sottostanti invocazioni di \func{pipe} e + \func{fork} o \errcode{EINVAL} se \param{type} non è valido.} +\end{funcproto} + +La funzione crea una \textit{pipe}, esegue una \func{fork} creando un nuovo +processe nel quale invoca il programma \param{command} attraverso la shell (in +sostanza esegue \file{/bin/sh} con il flag \code{-c}). +L'argomento \param{type} deve essere una delle due stringhe \verb|"w"| o +\verb|"r"|, per richiedere che la \textit{pipe} restituita come valore di +ritorno sia collegata allo \textit{standard input} o allo \textit{standard + output} del comando invocato. + +La funzione restituisce il puntatore ad uno stream associato alla +\textit{pipe} creata, che sarà aperto in sola lettura (e quindi associato allo +\textit{standard output} del programma indicato) in caso si sia indicato +\code{r}, o in sola scrittura (e quindi associato allo \textit{standard + input}) in caso di \code{w}. A partire dalla versione 2.9 delle \acr{glibc} +(questa è una estensione specifica di Linux) all'argomento \param{type} può +essere aggiunta la lettera ``\texttt{e}'' per impostare automaticamente il +flag di \textit{close-on-exec} \itindex{close-on-exec} sul file descriptor +sottostante (si ricordi quanto spiegato in sez.~\ref{sec:file_open_close}). + +Lo \textit{stream} restituito da \func{popen} è identico a tutti gli effetti +ai \textit{file stream} visti in sez.~\ref{sec:files_std_interface}, anche se +è collegato ad una \textit{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{funcproto}{ +\fhead{stdio.h} +\fdecl{int pclose(FILE *stream)} +\fdesc{Chiude una \textit{pipe} creata con \func{popen}.} +} + +{La funzione ritorna lo stato del processo creato da \func{popen} in caso di + successo e $-1$ per un errore, nel qual caso \var{errno} assumerà i valori + derivanti dalle sottostanti funzioni \func{fclose} e \func{wait4}.} +\end{funcproto} + +La funzione chiude il file \param{stream} associato ad una \textit{pipe} +creato da una precedente \func{popen}, ed oltre alla chiusura dello stream si +incarica anche di attendere (tramite \func{wait4}) la conclusione del processo +creato dalla precedente \func{popen}. Se lo stato di uscita non può essere +letto la funzione restituirà per \var{errno} un errore di \errval{ECHILD}. Per illustrare l'uso di queste due funzioni riprendiamo il problema -precedente: il programma mostrato in \figref{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 -riconoscere correttamente l'encapsulated postscript, per cui deve essere usato -il postscript e tutte le volte viene generata una pagina intera, invece che -una immagine delle dimensioni corrispondenti al codice a barre. +precedente: il programma mostrato in fig.~\ref{fig:ipc_barcodepage_code} per +quanto funzionante, è volutamente codificato in maniera piuttosto complessa, +inoltre doveva scontare un problema di \cmd{gs} che non era in grado di +riconoscere correttamente l'Encapsulated PostScript,\footnote{si fa + riferimento alla versione di GNU Ghostscript 6.53 (2002-02-13), usata quando + l'esempio venne scritto per la prima volta.} per cui si era utilizzato il +PostScript semplice, generando una pagina intera invece che una immagine delle +dimensioni corrispondenti al codice a barre. Se si vuole generare una immagine di dimensioni appropriate si deve usare un -approccio diverso. Una possibilità sarebbe quella di ricorrere ad ulteriore -programma, \cmd{epstopsf}, per convertire in PDF un file EPS (che può essere -generato da \cmd{barcode} utilizzando lo switch \cmd{-E}). Utilizzando un PDF +approccio diverso. Una possibilità sarebbe quella di ricorrere ad ulteriore +programma, \cmd{epstopsf}, per convertire in PDF un file EPS (che può essere +generato da \cmd{barcode} utilizzando l'opzione \cmd{-E}). Utilizzando un PDF al posto di un EPS \cmd{gs} esegue la conversione rispettando le dimensioni -originarie del codice a barre e produce un JPEG delle dimensioni adeguate. - -Questo però ci porta a scontrarci con una caratteristica peculiare delle pipe, -che a prima vista non è evidente. Per poter effettuare la conversione di un -PDF infatti è necessario, per la struttura del formato, dover eseguire delle -\func{lseek} sul file da convertire; se si esegue \cmd{gs} su un file normale -non ci sono problemi, ma una pipe però è rigidamente sequenziale, ed il -tentativo di eseguire detta operazione su una pipe comporta l'immediato -fallimento con un errore di \macro{ESPIPE}. Questo ci dice che in generale la -concatenazione di vari programmi funzionerà soltanto quando tutti prevedono -una lettura sequenziale del loro input. - -Per questo motivo si è dovuto utilizzare una strada diversa, che prevede la -conversione attraverso \cmd{gs} del PS in un altro formato intermedio, il -PPM,\footnote{il \textit{Portable PixMap file format} è un formato usato - spesso come formato intermedio per effettuare conversioni, è estremamente - inefficiente, ma molto facile da manipolare dato che usa caratteri ASCII per - memorizzare le immagini.} dal quale poi si può ottenere un'immagine di -dimensioni corrette attraverso vari programmi di manipolazione (\cmd{pnmcrop}, -\cmd{pnmmargin}) che può essere infine trasformata in PNG (con \cmd{pnm2png}). - -In questo caso però occorre eseguire in sequenza ben quattro comandi diversi, -inviando l'output di ciascuno all'input del successivo, per poi ottenere il -risultato finale sullo standard output: un caso classico di utilizzazione -delle pipe, in cui l'uso di \func{popen} e \func{pclose} permette di -semplificare notevolmente la stesura del codice. - -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 +originarie del codice a barre e produce un JPEG di dimensioni corrette. + +Questo approccio però non può funzionare per via di una delle caratteristiche +principali delle \textit{pipe}. Per poter effettuare la conversione di un PDF +infatti è necessario, per la struttura del formato, potersi spostare (con +\func{lseek}) all'interno del file da convertire. Se si esegue la conversione +con \cmd{gs} su un file regolare non ci sono problemi, una \textit{pipe} però +è rigidamente sequenziale, e l'uso di \func{lseek} su di essa fallisce sempre +con un errore di \errcode{ESPIPE}, rendendo impossibile la conversione. +Questo ci dice che in generale la concatenazione di vari programmi funzionerà +soltanto quando tutti prevedono una lettura sequenziale del loro input. + +Per questo motivo si è dovuto utilizzare un procedimento diverso, eseguendo +prima la conversione (sempre con \cmd{gs}) del PS in un altro formato +intermedio, il PPM,\footnote{il \textit{Portable PixMap file format} è un + formato usato spesso come formato intermedio per effettuare conversioni, è + infatti molto facile da manipolare, dato che usa caratteri ASCII per + memorizzare le immagini, anche se per questo è estremamente inefficiente + come formato di archiviazione.} dal quale poi si può ottenere un'immagine +di dimensioni corrette attraverso vari programmi di manipolazione +(\cmd{pnmcrop}, \cmd{pnmmargin}) che può essere infine trasformata in PNG (con +\cmd{pnm2png}). + +In questo caso però occorre eseguire in sequenza ben quattro comandi diversi, +inviando l'uscita di ciascuno all'ingresso del successivo, per poi ottenere il +risultato finale sullo \textit{standard output}: un caso classico di +utilizzazione delle \textit{pipe}, in cui l'uso di \func{popen} e \func{pclose} +permette di semplificare notevolmente la stesura del codice. + +Nel nostro caso, dato che ciascun processo deve scrivere la sua uscita sullo +\textit{standard input} del successivo, occorrerà usare \func{popen} aprendo +la \textit{pipe} in scrittura. Il codice del nuovo programma è riportato in +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. +lettura su una \textit{pipe} è bloccante, per cui un processo, anche se +lanciato per primo, se non ottiene i dati che gli servono si bloccherà in +attesa sullo \textit{standard input} finché non otterrà il risultato +dell'elaborazione del processo che li deve creare, che pur essendo logicamente +precedente, viene lanciato dopo di lui. \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 stout */ - 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} + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/BarCode.c} \end{minipage} \normalsize \caption{Codice completo del \textit{CGI} \file{BarCode.c}.} \label{fig:ipc_barcode_code} \end{figure} -Nel nostro caso il primo passo (\texttt{\small 14}) è scrivere il mime-type -sullo standard output; a questo punto il processo padre non necessita più di -eseguire ulteriori operazioni sullo standard output e può tranquillamente -provvedere alla redirezione. +Nel nostro caso il primo passo (\texttt{\small 14}) è scrivere il +\textit{mime-type} sullo \textit{standard output}; a questo punto il processo +padre non necessita più di eseguire ulteriori operazioni sullo +\textit{standard output} e può tranquillamente provvedere alla redirezione. -Dato che i vari programmi devono essere lanciati in successione, si è +Dato che i vari programmi devono essere lanciati in successione, si è approntato un ciclo (\texttt{\small 15--19}) che esegue le operazioni in -sequenza: prima crea una pipe (\texttt{\small 17}) per la scrittura eseguendo -il programma con \func{popen}, in modo che essa sia collegata allo standard -input, e poi redirige (\texttt{\small 18}) lo standard output su detta pipe. +sequenza: prima crea una \textit{pipe} (\texttt{\small 17}) per la scrittura +eseguendo il programma con \func{popen}, in modo che essa sia collegata allo +\textit{standard input}, e poi redirige (\texttt{\small 18}) lo +\textit{standard output} su detta \textit{pipe}. -In questo modo il primo processo ad essere invocato (che è l'ultimo della -catena) scriverà ancora sullo standard output del processo padre, ma i -successivi, a causa di questa redirezione, scriveranno sulla pipe associata -allo standard input del processo invocato nel ciclo precedente. +In questo modo il primo processo ad essere invocato (che è l'ultimo della +catena) scriverà ancora sullo \textit{standard output} del processo padre, ma +i successivi, a causa di questa redirezione, scriveranno sulla \textit{pipe} +associata allo \textit{standard input} del processo invocato nel ciclo +precedente. -Alla fine tutto quello che resta da fare è lanciare (\texttt{\small 21}) il -primo processo della catena, che nel caso è \cmd{barcode}, e scrivere -(\texttt{\small 23}) la stringa del codice a barre sulla pipe, che è collegata -al suo standard input, infine si può eseguire (\texttt{\small 24--27}) un -ciclo, che chiuda, nell'ordine inverso rispetto a quello in cui le si sono -create, tutte le pipe create con \func{pclose}. +Alla fine tutto quello che resta da fare è lanciare (\texttt{\small 21}) il +primo processo della catena, che nel caso è \cmd{barcode}, e scrivere +(\texttt{\small 23}) la stringa del codice a barre sulla \textit{pipe}, che è +collegata al suo \textit{standard input}, infine si può eseguire +(\texttt{\small 24--27}) un ciclo che chiuda con \func{pclose}, nell'ordine +inverso rispetto a quello in cui le si sono create, tutte le \textit{pipe} +create in precedenza. \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 -caratteristiche delle pipe, ma che invece di essere strutture interne del -kernel, visibili solo attraverso un file descriptor, sono accessibili -attraverso un inode che risiede sul filesystem, così che i processi le possono -usare senza dovere per forza essere in una relazione di \textsl{parentela}. - -Utilizzando una \textit{fifo} tutti i dati passeranno, come per le pipe, -attraverso un apposito buffer nel kernel, senza transitare dal filesystem; -l'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}. - -Abbiamo già visto in \secref{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à -leggere, nel secondo al capo di ingresso, e dovrà scrivere. - -Il kernel crea una singola pipe per ciascuna fifo che sia stata aperta, che può -essere acceduta contemporaneamente da più processi, sia in lettura che in -scrittura. Dato che per funzionare deve essere aperta in entrambe le -direzioni, per una fifo di norma la funzione \func{open} si blocca se viene -eseguita quando l'altro capo non è aperto. - -Le fifo però possono essere anche aperte in modalità \textsl{non-bloccante}, -nel qual caso l'apertura del capo in lettura avrà successo solo quando anche -l'altro capo è aperto, mentre l'apertura del capo in scrittura restituirà -l'errore di \macro{ENXIO} fintanto che non verrà aperto il capo in lettura. - -In Linux\footnote{lo standard POSIX lascia indefinito questo comportamento.} è -possibile aprire le fifo anche in lettura/scrittura, operazione che avrà -sempre successo immediato qualunque sia la modalità di apertura (bloccante e -non bloccante); questo può essere utilizzato per aprire comunque una fifo in -scrittura anche se non ci sono ancora processi il lettura; è possibile anche -usare la fifo all'interno di un solo processo, nel qual caso però occorre -stare molto attenti alla possibili deadlock.\footnote{se si cerca di leggere - da una fifo che non contiene dati si avrà un 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 -situazioni un processo deve ricevere informazioni dagli altri. In questo caso -è 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 \macro{PIPE\_BUF} (si ricordi quanto -detto in \secref{sec:ipc_pipes}). - -A parte il precedente, che resta probabilmente il più comune, Stevens riporta -in \cite{APUE} altre due casistiche principali per l'uso delle fifo: -\begin{itemize} +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 introdotto le \textit{fifo}, che hanno le stesse caratteristiche +delle \textit{pipe}, ma che invece di essere visibili solo attraverso un file +descriptor creato all'interno di un processo da una \textit{system call} +apposita, costituiscono un oggetto che risiede sul filesystem (si rammenti +quanto detto in sez.~\ref{sec:file_file_types}) che può essere aperto come un +qualunque file, così che i processi le possono usare senza dovere per forza +essere in una relazione di \textsl{parentela}. + +Utilizzando una \textit{fifo} tutti i dati passeranno, come per le +\textit{pipe}, attraverso un buffer nel kernel, senza transitare dal +filesystem. Il fatto che siano associate ad un \itindex{inode} +\textit{inode} presente sul filesystem serve infatti solo a fornire un punto +di accesso per i processi, che permetta a questi ultimi di accedere alla +stessa \textit{fifo} senza avere nessuna relazione, con una semplice +\func{open}. Il comportamento delle funzioni di lettura e scrittura è identico +a quello illustrato per le \textit{pipe} in sez.~\ref{sec:ipc_pipes}. + +Abbiamo già trattato in sez.~\ref{sec:file_mknod} le funzioni \func{mknod} e +\func{mkfifo} che permettono di creare una \textit{fifo}. Per utilizzarne una +un processo non avrà che da aprire il relativo \index{file!speciali} file +speciale o in lettura o scrittura; nel primo caso il processo sarà collegato +al capo di uscita della \textit{fifo}, e dovrà leggere, nel secondo al capo di +ingresso, e dovrà scrivere. + +Il kernel alloca un singolo buffer per ciascuna \textit{fifo} che sia stata +aperta, e questa potrà essere acceduta contemporaneamente da più processi, sia +in lettura che in scrittura. Dato che per funzionare deve essere aperta in +entrambe le direzioni, per una \textit{fifo} la funzione \func{open} di norma +si blocca se viene eseguita quando l'altro capo non è aperto. + +Le \textit{fifo} però possono essere anche aperte in modalità +\textsl{non-bloccante}, nel qual caso l'apertura del capo in lettura avrà +successo solo quando anche l'altro capo è aperto, mentre l'apertura del capo +in scrittura restituirà l'errore di \errcode{ENXIO} fintanto che non verrà +aperto il capo in lettura. + +In Linux è possibile aprire le \textit{fifo} anche in lettura/scrittura (lo +standard POSIX lascia indefinito il comportamento in questo caso) operazione +che avrà sempre successo immediato qualunque sia la modalità di apertura, +bloccante e non bloccante. Questo può essere utilizzato per aprire comunque +una \textit{fifo} in scrittura anche se non ci sono ancora processi il +lettura. Infine è possibile anche usare la \textit{fifo} all'interno di un +solo processo, nel qual caso però occorre stare molto attenti alla possibili +situazioni di stallo: se si cerca di leggere da una \textit{fifo} che non +contiene dati si avrà infatti un \itindex{deadlock} \textit{deadlock} +immediato, dato che il processo si blocca e quindi non potrà mai eseguire le +funzioni di scrittura. + +Per la loro caratteristica di essere accessibili attraverso il filesystem, è +piuttosto frequente l'utilizzo di una \textit{fifo} come canale di +comunicazione nelle situazioni un processo deve ricevere informazioni da +altri. In questo caso è 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 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 +\textit{fifo}: +\begin{itemize*} \item Da parte dei comandi di shell, per evitare la creazione di file temporanei quando si devono inviare i dati di uscita di un processo - sull'input di parecchi altri (attraverso l'uso del comando \cmd{tee}). + sull'input di parecchi altri (attraverso l'uso del comando \cmd{tee}). +\item Come canale di comunicazione fra un client ed un + server (il modello \textit{client-server} è illustrato in + sez.~\ref{sec:net_cliserv}). +\end{itemize*} + +Nel primo caso quello che si fa è creare tante \textit{fifo} da usare come +\textit{standard input} quanti sono i processi a cui i vogliono inviare i +dati; questi ultimi saranno stati posti in esecuzione ridirigendo lo +\textit{standard input} dalle \textit{fifo}, si potrà poi eseguire il processo +che fornisce l'output replicando quest'ultimo, con il comando \cmd{tee}, sulle +varie \textit{fifo}. + +Il secondo caso è relativamente semplice qualora si debba comunicare con un +processo alla volta, nel qual caso basta usare due \textit{fifo}, una per +leggere ed una per scrivere. Le cose diventano invece molto più complesse +quando si vuole effettuare una comunicazione fra un server ed un numero +imprecisato di client. Se il primo infatti può ricevere le richieste +attraverso una \textit{fifo} ``\textsl{nota}'', per le risposte non si può +fare altrettanto, dato che, per la struttura sequenziale delle \textit{fifo}, +i client dovrebbero sapere prima di leggerli quando i dati inviati sono +destinati a loro. + +Per risolvere questo problema, si può usare un'architettura come quella +illustrata in fig.~\ref{fig:ipc_fifo_server_arch} in cui i client +inviano le richieste al server su una \textit{fifo} nota mentre le +risposte vengono reinviate dal server a ciascuno di essi su una +\textit{fifo} temporanea creata per l'occasione. + +\begin{figure}[!htb] + \centering + \includegraphics[height=9cm]{img/fifoserver} + \caption{Schema dell'utilizzo delle \textit{fifo} nella realizzazione di una + architettura di comunicazione client/server.} + \label{fig:ipc_fifo_server_arch} +\end{figure} + +Come esempio di uso questa architettura e dell'uso delle \textit{fifo}, +abbiamo scritto un server di \textit{fortunes}, che restituisce, alle +richieste di un client, 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 impostabili da riga di comando. Il corpo principale del +server è riportato in fig.~\ref{fig:ipc_fifo_server}, dove si è +tralasciata la parte che tratta la gestione delle opzioni a riga di comando, +che effettua l'impostazione 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 diverso da quelli preimpostati. Il codice +completo è nel file \file{FortuneServer.c}. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/FortuneServer.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del server di \textit{fortunes} + basato sulle \textit{fifo}.} + \label{fig:ipc_fifo_server} +\end{figure} + +Il server richiede (\texttt{\small 12}) che sia stata impostata una dimensione +dell'insieme delle frasi non nulla, dato che l'inizializzazione del vettore +\var{fortune} avviene solo quando questa dimensione viene specificata, la +presenza di un valore nullo provoca l'uscita dal programma attraverso la +funzione (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 fig.~\ref{fig:ipc_fifo_server}) +che si limita a rimuovere dal filesystem la \textit{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 +\var{fortunefilename} le prime \var{n} frasi e le memorizza (allocando +dinamicamente la memoria necessaria) nel vettore di puntatori \var{fortune}. +Anche il codice della funzione non è riportato, in quanto non direttamente +attinente allo scopo dell'esempio. + +Il passo successivo (\texttt{\small 17--22}) è quello di creare con +\func{mkfifo} la \textit{fifo} nota sulla quale il server ascolterà le +richieste, qualora si riscontri un errore il server uscirà (escludendo +ovviamente il caso in cui la funzione \func{mkfifo} fallisce per la precedente +esistenza della \textit{fifo}). + +Una volta che si è certi che la \textit{fifo} di ascolto esiste la procedura +di inizializzazione è completata. A questo punto si può chiamare +(\texttt{\small 23}) la funzione \func{daemon} per far proseguire l'esecuzione +del programma in background come demone. Si può quindi procedere +(\texttt{\small 24--33}) alla apertura della \textit{fifo}: si noti che questo +viene fatto due volte, prima in lettura e poi in scrittura, per evitare di +dover gestire all'interno del ciclo principale il caso in cui il server è in +ascolto ma non ci sono client che effettuano richieste. Si ricordi infatti +che quando una \textit{fifo} è aperta solo dal capo in lettura, l'esecuzione di +\func{read} ritorna con zero byte (si ha cioè una condizione di end-of-file). + +Nel nostro caso la prima apertura si bloccherà fintanto che un qualunque +client non apre a sua volta la \textit{fifo} nota in scrittura per effettuare la sua +richiesta. Pertanto all'inizio non ci sono problemi, il client però, una volta +ricevuta la risposta, uscirà, chiudendo tutti i file aperti, compresa la +\textit{fifo}. A questo punto il server resta (se non ci sono altri client +che stanno effettuando richieste) con la \textit{fifo} chiusa sul lato in +lettura, ed in questo stato la funzione \func{read} non si bloccherà in attesa +di dati in ingresso, ma ritornerà in continuazione, restituendo una condizione +di \textit{end-of-file}. + +Si è usata questa tecnica per compatibilità, Linux infatti supporta l'apertura +delle \textit{fifo} in lettura/scrittura, per cui si sarebbe potuto effettuare +una singola apertura con \const{O\_RDWR}; la doppia apertura comunque ha il +vantaggio che non si può scrivere per errore sul capo aperto in sola lettura. + +Per questo motivo, dopo aver eseguito l'apertura in lettura (\texttt{\small + 24--28}),\footnote{di solito si effettua l'apertura del capo in lettura di + una \textit{fifo} in modalità non bloccante, per evitare il rischio di uno + stallo: se infatti nessuno apre la \textit{fifo} in scrittura il processo + non ritornerà mai dalla \func{open}. Nel nostro caso questo rischio non + esiste, mentre è necessario potersi bloccare in lettura in attesa di una + richiesta.} si esegue una seconda apertura in scrittura (\texttt{\small + 29--32}), scartando il relativo file descriptor, che non sarà mai usato, in +questo modo però la \textit{fifo} resta comunque aperta anche in scrittura, +cosicché le successive chiamate a \func{read} possono bloccarsi. + +A questo punto si può entrare nel ciclo principale del programma che fornisce +le risposte ai client (\texttt{\small 34--50}); questo viene eseguito +indefinitamente (l'uscita del server viene effettuata inviando un segnale, in +modo da passare attraverso la funzione di chiusura che cancella la +\textit{fifo}). + +Il server è progettato per accettare come richieste dai client delle stringhe +che contengono il nome della \textit{fifo} sulla quale deve essere inviata la +risposta. Per cui prima (\texttt{\small 35--39}) si esegue la lettura dalla +stringa di richiesta dalla \textit{fifo} nota (che a questo punto si bloccherà +tutte le volte che non ci sono richieste). Dopo di che, una volta terminata la +stringa (\texttt{\small 40}) e selezionato (\texttt{\small 41}) un numero +casuale per ricavare la frase da inviare, si procederà (\texttt{\small + 42--46}) all'apertura della \textit{fifo} per la risposta, che poi +\texttt{\small 47--48}) vi sarà scritta. Infine (\texttt{\small 49}) si chiude +la \textit{fifo} di risposta che non serve più. + +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}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/FortuneClient.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del client di \textit{fortunes} + basato sulle \textit{fifo}.} + \label{fig:ipc_fifo_client} +\end{figure} + +La prima istruzione (\texttt{\small 12}) compone il nome della \textit{fifo} +che dovrà essere utilizzata per ricevere la risposta dal server. Si usa il +\ids{PID} del processo per essere sicuri di avere un nome univoco; dopo di che +(\texttt{\small 13-18}) si procede alla creazione del relativo file, uscendo +in caso di errore (a meno che il file non sia già presente sul filesystem). + +A questo punto il client può effettuare l'interrogazione del server, per +questo prima si apre la \textit{fifo} nota (\texttt{\small 19--23}), e poi ci +si scrive (\texttt{\small 24}) la stringa composta in precedenza, che contiene +il nome della \textit{fifo} da utilizzare per la risposta. Infine si richiude +la \textit{fifo} del server che a questo punto non serve più (\texttt{\small + 25}). + +Inoltrata la richiesta si può passare alla lettura della risposta; anzitutto +si apre (\texttt{\small 26--30}) la \textit{fifo} appena creata, da cui si +deve riceverla, dopo di che si effettua una lettura (\texttt{\small 31}) +nell'apposito buffer; si è supposto, come è ragionevole, che le frasi inviate +dal server siano sempre di dimensioni inferiori a \const{PIPE\_BUF}, +tralasciamo la gestione del caso in cui questo non è vero. Infine si stampa +(\texttt{\small 32}) a video la risposta, si chiude (\texttt{\small 33}) la +\textit{fifo} e si cancella (\texttt{\small 34}) il relativo file. Si noti +come la \textit{fifo} per la risposta sia stata aperta solo dopo aver inviato +la richiesta, se non si fosse fatto così si avrebbe avuto uno stallo, in +quanto senza la richiesta, il server non avrebbe potuto aprirne il capo in +scrittura e l'apertura si sarebbe bloccata indefinitamente. + +Verifichiamo allora il comportamento dei nostri programmi, in questo, come in +altri esempi precedenti, si fa uso delle varie funzioni di servizio, che sono +state raccolte nella libreria \file{libgapil.so}, per poter usare quest'ultima +occorrerà definire la variabile di ambiente \envvar{LD\_LIBRARY\_PATH} in modo +che il linker dinamico possa accedervi. + +In generale questa variabile indica il \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 LD\_LIBRARY\_PATH=./}; a questo punto potremo lanciare il server, +facendogli leggere una decina di frasi, con: +\begin{Console} +[piccardi@gont sources]$ \textbf{./fortuned -n10} +\end{Console} +%$ + +Avendo usato \func{daemon} per eseguire il server in background il comando +ritornerà immediatamente, ma potremo verificare con \cmd{ps} che in effetti il +programma resta un esecuzione in background, e senza avere associato un +terminale di controllo (si ricordi quanto detto in sez.~\ref{sec:sess_daemon}): +\begin{Console} +[piccardi@gont sources]$ \textbf{ps aux} +... +piccardi 27489 0.0 0.0 1204 356 ? S 01:06 0:00 ./fortuned -n10 +piccardi 27492 3.0 0.1 2492 764 pts/2 R 01:08 0:00 ps aux +\end{Console} +%$ +e si potrà verificare anche che in \file{/tmp} è stata creata la \textit{fifo} +di ascolto \file{fortune.fifo}. A questo punto potremo interrogare il server +con il programma client; otterremo così: +\begin{Console} +[piccardi@gont sources]$ \textbf{./fortune} +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +[piccardi@gont sources]$ \textbf{./fortune} +Let's call it an accidental feature. + --Larry Wall +[piccardi@gont sources]$ \textbf{./fortune} +......... Escape the 'Gates' of Hell + `:::' ....... ...... + ::: * `::. ::' + ::: .:: .:.::. .:: .:: `::. :' + ::: :: :: :: :: :: :::. + ::: .::. .:: ::. `::::. .:' ::. +...:::.....................::' .::::.. + -- William E. Roadcap +[piccardi@gont sources]$ ./fortune +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +\end{Console} +%$ +e ripetendo varie volte il comando otterremo, in ordine casuale, le dieci +frasi tenute in memoria dal server. + +Infine per chiudere il server basterà inviargli un segnale di terminazione (ad +esempio con \cmd{killall fortuned}) e potremo verificare che il gestore del +segnale ha anche correttamente cancellato la \textit{fifo} di ascolto da +\file{/tmp}. + +Benché il nostro sistema client-server funzioni, la sua struttura è piuttosto +complessa e continua ad avere vari inconvenienti\footnote{lo stesso Stevens, + che esamina questa architettura in \cite{APUE}, nota come sia impossibile + per il server sapere se un client è andato in crash, con la possibilità di + far restare le \textit{fifo} temporanee sul filesystem, di come sia + necessario intercettare \signal{SIGPIPE} dato che un client può terminare + dopo aver fatto una richiesta, ma prima che la risposta sia inviata (cosa + che nel nostro esempio non è stata fatta).}; in generale infatti +l'interfaccia delle \textit{fifo} non è adatta a risolvere questo tipo di +problemi, che possono essere affrontati in maniera più semplice ed efficace o +usando i socket (che tratteremo in dettaglio a partire da +cap.~\ref{cha:socket_intro}) o ricorrendo a meccanismi di comunicazione +diversi, come quelli che esamineremo in seguito. + + + +\subsection{La funzione \func{socketpair}} +\label{sec:ipc_socketpair} + +Un meccanismo di comunicazione molto simile alle \textit{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 +in generale i socket in cap.~\ref{cha:socket_intro}, nell'ambito +dell'interfaccia che essi forniscono per la programmazione di rete, e vedremo +anche (in~sez.~\ref{sec:sock_sa_local}) come si possono utilizzare i +\index{file!speciali} file speciali di tipo socket, analoghi a quelli +associati alle \textit{fifo} (si rammenti sez.~\ref{sec:file_file_types}) cui +si accede però attraverso quella medesima interfaccia; vale però la pena +esaminare qui una modalità di uso dei socket locali che li rende +sostanzialmente identici ad una \textit{pipe} bidirezionale. + +La funzione di sistema \funcd{socketpair}, introdotta da BSD ma supportata in +genere da qualunque sistema che fornisca l'interfaccia dei socket ed inclusa +in POSIX.1-2001, consente infatti di creare una coppia di file descriptor +connessi fra loro (tramite un socket, appunto) senza dover ricorrere ad un +\index{file!speciali} file speciale sul filesystem. I descrittori sono del +tutto analoghi a quelli che si avrebbero con una chiamata a \func{pipe}, con +la sola differenza è che in questo caso il flusso dei dati può essere +effettuato in entrambe le direzioni. Il prototipo della funzione è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/socket.h} +\fdecl{int socketpair(int domain, int type, int protocol, int sv[2])} +\fdesc{Crea una coppia di socket connessi fra loro.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EAFNOSUPPORT}] i socket locali non sono supportati. + \item[\errcode{EOPNOTSUPP}] il protocollo specificato non supporta la + creazione di coppie di socket. + \item[\errcode{EPROTONOSUPPORT}] il protocollo specificato non è supportato. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{EMFILE} e \errval{ENFILE} nel loro + significato generico.} +\end{funcproto} + +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. Gli argomenti \param{domain}, \param{type} e +\param{protocol} derivano dall'interfaccia dei 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}. + +A partire dal kernel 2.6.27 la funzione supporta anche l'uso dei flag +\const{SOCK\_NONBLOCK} e \const{SOCK\_CLOEXEC} (trattati in +sez.~\ref{sec:sock_type}) nell'indicazione del tipo di socket, con effetto +identico agli analoghi \const{O\_CLOEXEC} e \const{O\_NONBLOCK} di una +\func{open} (vedi tab.~\ref{tab:open_operation_flag}). + +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 socket +locali in generale) permette di trasmettere attraverso le 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 sez.~\ref{sec:sock_fd_passing}). + + +\section{L'intercomunicazione fra processi di System V} +\label{sec:ipc_sysv} + +Benché le \textit{pipe} e le \textit{fifo} siano ancora ampiamente usate, esse +scontano il limite fondamentale che il meccanismo di comunicazione che +forniscono è rigidamente sequenziale: una situazione in cui un processo scrive +qualcosa che molti altri devono poter leggere non può essere implementata con +una \textit{pipe}. + +Per questo nello sviluppo di System V vennero introdotti una serie di nuovi +oggetti per la comunicazione fra processi ed una nuova interfaccia di +programmazione, poi inclusa anche in POSIX.1-2001, 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 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}). + + + +\subsection{Considerazioni generali} +\label{sec:ipc_sysv_generic} + +La principale caratteristica del \textit{SysV-IPC} è quella di essere basato +su oggetti permanenti che risiedono nel kernel. Questi, a differenza di quanto +avviene per i file descriptor, non mantengono un contatore dei riferimenti, e +non vengono cancellati dal sistema una volta che non sono più in uso. Questo +comporta due problemi: il primo è che, al contrario di quanto avviene per +\textit{pipe} e \textit{fifo}, la memoria allocata per questi oggetti non +viene rilasciata automaticamente quando non c'è più nessuno che li utilizzi ed +essi devono essere cancellati esplicitamente, se non si vuole che restino +attivi fino al riavvio del sistema. Il secondo problema è, dato che non c'è +come per i file un contatore del numero di riferimenti che ne indichi l'essere +in uso, che essi possono essere cancellati anche se ci sono dei processi che +li stanno utilizzando, con tutte le conseguenze (ovviamente assai sgradevoli) +del caso. + +Un'ulteriore caratteristica negativa è che gli oggetti usati nel +\textit{SysV-IPC} vengono creati direttamente dal kernel, e sono accessibili +solo specificando il relativo \textsl{identificatore}. Questo è un numero +progressivo (un po' come il \ids{PID} dei processi) che il kernel assegna a +ciascuno di essi quanto vengono creati (sul procedimento di assegnazione +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 pone perciò il problema di come processi diversi possono +accedere allo stesso oggetto. + +Per risolvere il problema nella struttura \struct{ipc\_perm} che il kernel +associa a ciascun oggetto, viene mantenuto anche un campo apposito che +contiene anche una \textsl{chiave}, identificata da una variabile del tipo +primitivo \type{key\_t}, da specificare in fase di creazione dell'oggetto, e +tramite la quale è possibile ricavare l'identificatore.\footnote{in sostanza + 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 fig.~\ref{fig:ipc_ipc_perm}, +mantiene varie proprietà ed informazioni associate all'oggetto. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{0.80\textwidth} + \includestruct{listati/ipc_perm.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{ipc\_perm}, come definita in + \headfile{sys/ipc.h}.} + \label{fig:ipc_ipc_perm} +\end{figure} + +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{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 argomento attraverso +una \func{exec}. + +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 +la chiave (che ad esempio può essere dichiarato in un header comune), ma c'è +sempre il rischio che questa chiave possa essere stata già utilizzata da +qualcun altro. Dato che non esiste una convenzione su come assegnare queste +chiavi in maniera univoca l'interfaccia mette a disposizione una funzione +apposita, \funcd{ftok}, che permette di ottenere una chiave specificando il +nome di un file ed un numero di versione; il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fdecl{key\_t ftok(const char *pathname, int proj\_id)} +\fdesc{Restituisce una chiave per identificare un oggetto del \textit{SysV + IPC}.}} + +{La funzione ritorna la chiave in caso di successo e $-1$ per un errore, nel + qual caso \var{errno} assumerà uno dei possibili codici di errore di + \func{stat}.} +\end{funcproto} + +La funzione determina un valore della chiave sulla base di \param{pathname}, +che deve specificare il \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. Nelle \acr{libc4} e \acr{libc5}, come avviene in SunOS, +l'argomento \param{proj\_id} è dichiarato tipo \ctyp{char}, la \acr{glibc} usa +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)} +con i 16 bit meno significativi \itindex{inode} dell'inode del file +\param{pathname} (che vengono ottenuti attraverso \func{stat}, da cui derivano +i possibili errori), e gli 8 bit meno significativi del numero del dispositivo +su cui è il file. Diventa perciò relativamente facile ottenere delle +collisioni, specie se i file sono su dispositivi con lo stesso +\itindex{minor~number} \textit{minor number}, come \file{/dev/hda1} e +\file{/dev/sda1}. + +In genere quello che si fa è utilizzare un file comune usato dai programmi che +devono comunicare (ad esempio un header comune, o uno dei programmi che devono +usare l'oggetto in questione), utilizzando il numero di progetto per ottenere +le chiavi che interessano. In ogni caso occorre sempre controllare, prima di +creare un oggetto, che la chiave non sia già stata utilizzata. Se questo va +bene in fase di creazione, le cose possono complicarsi per i programmi che +devono solo accedere, in quanto, a parte gli eventuali controlli sugli altri +attributi di \struct{ipc\_perm}, non esiste una modalità semplice per essere +sicuri che l'oggetto associato ad una certa chiave sia stato effettivamente +creato da chi ci si aspetta. + +Questo è, insieme al fatto che gli oggetti sono permanenti e non mantengono un +contatore di riferimenti per la cancellazione automatica, il principale +problema del \textit{SysV-IPC}. Non esiste infatti una modalità chiara per +identificare un oggetto, come sarebbe stato se lo si fosse associato ad in +file, e tutta l'interfaccia è inutilmente complessa. Per questo se ne +sconsiglia assolutamente l'uso nei nuovi programmi, considerato che è ormai +disponibile una revisione completa dei meccamismi di IPC fatta secondo quanto +indicato dallo standard POSIX.1b, che presenta una realizzazione più sicura ed +una interfaccia più semplice, che tratteremo in sez.~\ref{sec:ipc_posix}. + + +\subsection{Il controllo di accesso} +\label{sec:ipc_sysv_access_control} + +Oltre alle chiavi, abbiamo visto in fig.~\ref{fig:ipc_ipc_perm} che ad ogni +oggetto sono associate in \struct{ipc\_perm} ulteriori informazioni, come gli +identificatori del creatore (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 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 +esiste (e se specificato viene ignorato), per cui si può parlare solo di +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 +tab.~\ref{tab:file_mode_flags} e come per i file definiscono gli accessi per +il proprietario, il suo gruppo e tutti gli altri. + +Se però si vogliono usare le costanti simboliche di +tab.~\ref{tab:file_mode_flags} occorrerà includere anche il file +\headfile{sys/stat.h}; alcuni sistemi definiscono le costanti \const{MSG\_R} +(il valore ottale \texttt{0400}) e \const{MSG\_W} (il valore ottale +\texttt{0200}) per indicare i permessi base di lettura e scrittura per il +proprietario, da utilizzare, con gli opportuni shift, pure per il gruppo e gli +altri. In Linux, visto la loro scarsa utilità, queste costanti non sono +definite. + +Quando l'oggetto viene creato i campi \var{cuid} e \var{uid} di +\struct{ipc\_perm} ed i campi \var{cgid} e \var{gid} vengono impostati +rispettivamente al valore dell'\ids{UID} e del \ids{GID} effettivo del processo +che ha chiamato la funzione, ma, mentre i campi \var{uid} e \var{gid} possono +essere cambiati, i campi \var{cuid} e \var{cgid} restano sempre gli stessi. + +Il controllo di accesso è effettuato a due livelli. Il primo livello è nelle +funzioni che richiedono l'identificatore di un oggetto data la chiave. Queste +specificano tutte un argomento \param{flag}, in tal caso quando viene +effettuata la ricerca di una chiave, qualora \param{flag} specifichi dei +permessi, questi vengono controllati e l'identificatore viene restituito solo +se corrispondono a quelli dell'oggetto. Se ci sono dei permessi non presenti +in \var{mode} l'accesso sarà negato. Questo controllo però è di utilità +indicativa, dato che è sempre possibile specificare per \param{flag} un valore +nullo, nel qual caso l'identificatore sarà restituito comunque. + +Il secondo livello di controllo è quello delle varie funzioni che accedono +direttamente (in lettura o scrittura) all'oggetto. In tal caso lo schema dei +controlli è simile a quello dei file, ed avviene secondo questa sequenza: +\begin{itemize*} +\item se il processo ha i privilegi di amministratore (più precisamente la + capacità \itindex{capability} \const{CAP\_IPC\_OWNER}) l'accesso è sempre + consentito. +\item se l'\ids{UID} effettivo del processo corrisponde o al valore del campo + \var{cuid} o a quello del campo \var{uid} ed il permesso per il proprietario + in \var{mode} è appropriato\footnote{per appropriato si intende che è + impostato il permesso di scrittura per le operazioni di scrittura e quello + di lettura per le operazioni di lettura.} l'accesso è consentito. +\item se il \ids{GID} effettivo del processo corrisponde o al + valore del campo \var{cgid} o a quello del campo \var{gid} ed il permesso + per il gruppo in \var{mode} è appropriato l'accesso è consentito. +\item se il permesso per gli altri è appropriato l'accesso è consentito. +\end{itemize*} +solo se tutti i controlli elencati falliscono l'accesso è negato. Si noti che +a differenza di quanto avviene per i permessi dei file, fallire in uno dei +passi elencati non comporta il fallimento dell'accesso. Un'ulteriore +differenza rispetto a quanto avviene per i file è che per gli oggetti di IPC +il valore di \itindex{umask} \textit{umask} (si ricordi quanto esposto in +sez.~\ref{sec:file_perm_management}) 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 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. + +Quando il sistema si avvia, alla creazione di ogni nuovo oggetto di IPC viene +assegnato un numero progressivo, pari al numero di oggetti di quel tipo +esistenti. Se il comportamento fosse sempre questo sarebbe identico a quello +usato nell'assegnazione dei file descriptor nei processi, ed i valori degli +identificatori tenderebbero ad essere riutilizzati spesso e restare di piccole +dimensioni (inferiori al numero massimo di oggetti disponibili). + +Questo va benissimo nel caso dei file descriptor, che sono locali ad un +processo, ma qui il comportamento varrebbe per tutto il sistema, e per +processi del tutto scorrelati fra loro. Così si potrebbero avere situazioni +come quella in cui un server esce e cancella le sue code di messaggi, ed il +relativo identificatore viene immediatamente assegnato a quelle di un altro +server partito subito dopo, con la possibilità che i client del primo non +facciano in tempo ad accorgersi dell'avvenuto, e finiscano con l'interagire +con gli oggetti del secondo, con conseguenze imprevedibili. + +Proprio per evitare questo tipo di situazioni il sistema usa il valore di +\var{seq} per provvedere un meccanismo che porti gli identificatori ad +assumere tutti i valori possibili, rendendo molto più lungo il periodo in cui +un identificatore può venire riutilizzato. + +Il sistema dispone sempre di un numero fisso di oggetti di IPC, fino al kernel +2.2.x questi erano definiti dalle costanti \const{MSGMNI}, \const{SEMMNI} e +\const{SHMMNI}, e potevano essere cambiati (come tutti gli altri limiti +relativi al \textit{SysV-IPC}) solo con una ricompilazione del kernel. A +partire dal kernel 2.4.x è possibile cambiare questi valori a sistema attivo +scrivendo sui file \sysctlrelfile{kernel}{shmmni}, +\sysctlrelfile{kernel}{msgmni} e \sysctlrelfile{kernel}{sem} di +\file{/proc/sys/kernel} o con l'uso di \func{sysctl}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/IPCTestId.c} + \end{minipage} + \normalsize + \caption{Sezione principale del programma di test per l'assegnazione degli + identificatori degli oggetti di IPC \file{IPCTestId.c}.} + \label{fig:ipc_sysv_idtest} +\end{figure} + +Per ciascun tipo di oggetto di IPC viene mantenuto in \var{seq} un numero di +sequenza progressivo che viene incrementato di uno ogni volta che l'oggetto +viene cancellato. Quando l'oggetto viene creato usando uno spazio che era già +stato utilizzato in precedenza, per restituire il nuovo identificatore al +numero di oggetti presenti viene sommato il valore corrente del campo +\var{seq}, moltiplicato per il numero massimo di oggetti di quel tipo. + +Questo in realtà è quanto avveniva fino ai kernel della serie 2.2, dalla serie +2.4 viene usato lo stesso fattore di moltiplicazione per qualunque tipo di +oggetto, utilizzando il valore dalla costante \const{IPCMNI} (definita in +\file{include/linux/ipc.h}), che indica il limite massimo complessivo per il +numero di tutti gli oggetti presenti nel \textit{SysV-IPC}, ed il cui default +è 32768. Si evita così il riutilizzo degli stessi numeri, e si fa sì che +l'identificatore assuma tutti i valori possibili. + +In fig.~\ref{fig:ipc_sysv_idtest} è riportato il codice di un semplice +programma di test che si limita a creare un oggetto di ICP (specificato con +una opzione a riga di comando), stamparne il numero di identificatore, e +cancellarlo, il tutto un numero di volte specificato tramite una seconda +opzione. 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 \var{n} al numero di volte che si vuole effettuare il ciclo +di creazione, stampa, cancellazione. + +I valori di default sono per l'uso delle code di messaggi e per 5 ripetizioni +del ciclo. Per questo motivo se non si utilizzano opzioni verrà eseguito per +cinque volte il ciclo (\texttt{\small 7-11}), in cui si crea una coda di +messaggi (\texttt{\small 8}), se ne stampa l'identificativo (\texttt{\small + 9}) e la si rimuove (\texttt{\small 10}). Non stiamo ad approfondire adesso +il significato delle funzioni utilizzate, che verranno esaminate nelle +prossime sezioni. + +Quello che ci interessa infatti è verificare l'allocazione degli +identificativi associati agli oggetti; lanciando il comando si otterrà +pertanto qualcosa del tipo: +\begin{Console} +piccardi@gont sources]$ \textbf{./ipctestid} +Identifier Value 0 +Identifier Value 32768 +Identifier Value 65536 +Identifier Value 98304 +Identifier Value 131072 +\end{Console} +%$ +il che ci mostra che stiamo lavorando con un kernel posteriore alla serie 2.2 +nel quale non avevamo ancora usato nessuna coda di messaggi (il valore nullo +del primo identificativo indica che il campo \var{seq} era zero). Ripetendo il +comando, e quindi eseguendolo in un processo diverso, in cui non può esistere +nessuna traccia di quanto avvenuto in precedenza, otterremo come nuovo +risultato: +\begin{Console} +[piccardi@gont sources]$ \textbf{./ipctestid} +Identifier Value 163840 +Identifier Value 196608 +Identifier Value 229376 +Identifier Value 262144 +Identifier Value 294912 +\end{Console} +%$ +in cui la sequenza numerica prosegue, cosa che ci mostra come il valore di +\var{seq} continui ad essere incrementato e costituisca in effetti una +quantità mantenuta all'interno del sistema ed indipendente dai processi. + + +\subsection{Code di messaggi} +\label{sec:ipc_sysv_mq} + +Il primo oggetto introdotto dal \textit{SysV-IPC} è quello delle code di +messaggi. Le code di messaggi sono oggetti analoghi alle \textit{pipe} o alle +\textit{fifo} ed il loro scopo principale è quello di fornire a processi +diversi un meccanismo con cui scambiarsi dei dati in forma di messaggio. Dato +che le \textit{pipe} e le \textit{fifo} costituiscono una ottima alternativa, +ed in genere sono molto più semplici da usare, le code di messaggi sono il +meno utilizzato degli oggetti introdotti dal \textit{SysV-IPC}. + +La funzione di sistema che permette di ottenere l'identificativo di una coda +di messaggi esistente per potervi accedere, oppure di creare una nuova coda +qualora quella indicata non esista ancora, è \funcd{msgget}, e il suo +prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/msg.h} +\fdecl{int msgget(key\_t key, int flag)} +\fdesc{Ottiene o crea una coda di messaggi.} +} + +{La funzione ritorna l'identificatore (un intero positivo) in caso di successo + e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo chiamante non ha i privilegi per + accedere alla coda richiesta. + \item[\errcode{EEXIST}] si è richiesta la creazione di una coda che già + esiste, ma erano specificati sia \const{IPC\_CREAT} che \const{IPC\_EXCL}. + \item[\errcode{EIDRM}] la coda richiesta è marcata per essere cancellata + (solo fino al kernel 2.3.20). + \item[\errcode{ENOENT}] si è cercato di ottenere l'identificatore di una coda + di messaggi specificando una chiave che non esiste e \const{IPC\_CREAT} + non era specificato. + \item[\errcode{ENOSPC}] si è cercato di creare una coda di messaggi quando è + stato superato il limite massimo di code (\const{MSGMNI}). + \end{errlist} + ed inoltre \errval{ENOMEM} nel suo significato generico.} +\end{funcproto} + +Le funzione (come le analoghe che si usano per gli altri oggetti) serve sia a +ottenere l'identificatore di una coda di messaggi esistente, che a crearne una +nuova. L'argomento \param{key} specifica la chiave che è associata +all'oggetto, eccetto il caso in cui si specifichi il valore +\const{IPC\_PRIVATE}, nel qual caso la coda è creata ex-novo e non vi è +associata alcuna chiave (per questo viene detta \textsl{privata}), ed il +processo e i suoi eventuali figli potranno farvi riferimento solo attraverso +l'identificatore. + +Se invece si specifica un valore diverso da \const{IPC\_PRIVATE} (in Linux +questo significa un valore diverso da zero) l'effetto della funzione dipende +dal valore di \param{flag}, se questo è nullo la funzione si limita ad +effettuare una ricerca sugli oggetti esistenti, restituendo l'identificatore +se trova una corrispondenza, o fallendo con un errore di \errcode{ENOENT} se +non esiste o di \errcode{EACCES} se si sono specificati dei permessi non +validi. + +Se invece si vuole creare una nuova coda di messaggi \param{flag} non può +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 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. + +Si tenga conto che l'uso di \const{IPC\_PRIVATE} non impedisce ad altri +processi di accedere alla coda, se hanno privilegi sufficienti, una volta che +questi possano indovinare o ricavare, ad esempio per tentativi, +l'identificatore ad essa associato. Per come sono implementati gli oggetti di +IPC infatti non esiste alcun modo in cui si possa garantire l'accesso +esclusivo ad una coda di messaggi. Usare \const{IPC\_PRIVATE} o +\const{IPC\_CREAT} e \const{IPC\_EXCL} per \param{flag} comporta solo la +creazione di una nuova coda. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|r|l|l|} + \hline + \textbf{Costante} & \textbf{Valore} & \textbf{File in \file{/proc}} + & \textbf{Significato} \\ + \hline + \hline + \const{MSGMNI}& 16& \file{msgmni} & Numero massimo di code di + messaggi.\\ + \const{MSGMAX}& 8192& \file{msgmax} & Dimensione massima di un singolo + messaggio.\\ + \const{MSGMNB}&16384& \file{msgmnb} & Dimensione massima del contenuto di + una coda.\\ + \hline + \end{tabular} + \caption{Valori delle costanti associate ai limiti delle code di messaggi.} + \label{tab:ipc_msg_limits} +\end{table} + +Le code di messaggi sono caratterizzate da tre limiti fondamentali, un tempo +definiti staticamente e corrispondenti alle prime tre costanti riportate in +tab.~\ref{tab:ipc_msg_limits}. Come accennato però con tutte le versioni più +recenti del kernel con Linux è possibile modificare questi limiti attraverso +l'uso di \func{sysctl} o scrivendo nei file \sysctlrelfile{kernel}{msgmax}, +\sysctlrelfile{kernel}{msgmnb} e \sysctlrelfile{kernel}{msgmni} di +\file{/proc/sys/kernel/}. + +Una coda di messaggi è costituita da una \itindex{linked~list} \textit{linked + list}.\footnote{una \itindex{linked~list} \textit{linked list} è una tipica + struttura di dati, organizzati in una lista in cui ciascun elemento contiene + un puntatore al successivo. In questo modo la struttura è veloce + nell'estrazione ed immissione dei dati dalle estremità dalla lista (basta + aggiungere un elemento in testa o in coda ed 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 fig.~\ref{fig:ipc_mq_schema} si è riportato uno schema +semplificato con cui queste strutture vengono mantenute dal +kernel.\footnote{lo schema illustrato è in realtà una semplificazione di + quello usato fino ai kernel della serie 2.2, a partire della serie 2.4 la + gestione delle code di messaggi è effettuata in maniera diversa, ma abbiamo + mantenuto lo schema precedente dato che illustra in maniera più che adeguata + i principi di funzionamento delle code di messaggi.} + +\begin{figure}[!htb] + \centering \includegraphics[width=13cm]{img/mqstruct} + \caption{Schema della struttura di una coda messaggi.} + \label{fig:ipc_mq_schema} +\end{figure} + + +A ciascuna coda è associata una struttura \struct{msqid\_ds} la cui +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, essa + viene usata nei kernel della serie 2.4 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 fig.~\ref{fig:ipc_mq_schema} + che sono presi dalla definizione di \file{include/linux/msg.h}, e fanno + riferimento alla definizione della omonima struttura usata nel kernel.} In +fig.~\ref{fig:ipc_msqid_ds} sono elencati i campi significativi definiti in +\headfile{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. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\textwidth} + \includestruct{listati/msqid_ds.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{msqid\_ds}, associata a ciascuna coda di + messaggi.} + \label{fig:ipc_msqid_ds} +\end{figure} + +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 sez.~\ref{sec:ipc_sysv_access_control}, per quanto riguarda +gli altri campi invece: +\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 + rispettivamente il \ids{PID} dell'ultimo processo che ha inviato o ricevuto + un messaggio sulla coda, sono inizializzati a 0. +\item i campi \var{msg\_stime} e \var{msg\_rtime}, che esprimono + rispettivamente il tempo in cui è stato inviato o ricevuto l'ultimo + messaggio sulla coda, sono inizializzati a 0. +\item il campo \var{msg\_ctime}, che esprime il tempo di creazione della coda, + viene inizializzato al tempo corrente. +\item il campo \var{msg\_qbytes} che esprime la dimensione massima del + contenuto della coda (in byte) viene inizializzato al valore preimpostato + del sistema (\const{MSGMNB}). +\item i campi \var{msg\_first} e \var{msg\_last} che esprimono l'indirizzo del + primo e ultimo messaggio sono inizializzati a \val{NULL} e + \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*} + +Una volta creata una coda di messaggi le operazioni di controllo vengono +effettuate con la funzione di sistema \funcd{msgctl}, che, come le analoghe +\func{semctl} e \func{shmctl}, fa le veci di quello che \func{ioctl} è per i +file; il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/msg.h} +\fdecl{int msgctl(int msqid, int cmd, struct msqid\_ds *buf)} +\fdesc{Esegue una operazione su una coda.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] si è richiesto \const{IPC\_STAT} ma processo + chiamante non ha i privilegi di lettura sulla coda. + \item[\errcode{EIDRM}] la coda richiesta è stata cancellata. + \item[\errcode{EPERM}] si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} ma + il processo non ha i privilegi, o si è richiesto di aumentare il valore di + \var{msg\_qbytes} oltre il limite \const{MSGMNB} senza essere + amministratore. + \end{errlist} + ed inoltre \errval{EFAULT} ed \errval{EINVAL} nel loro significato + generico.} +\end{funcproto} + + +La funzione permette di accedere ai valori della struttura \struct{msqid\_ds}, +mantenuta all'indirizzo \param{buf}, per la coda specificata +dall'identificatore \param{msqid}. Il comportamento della funzione dipende dal +valore dell'argomento \param{cmd}, che specifica il tipo di azione da +eseguire; i valori possibili sono: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge le informazioni riguardo la coda nella + struttura indicata da \param{buf}. Occorre avere il permesso di lettura + sulla coda. +\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 lettura o di scrittura sulla coda saranno svegliati ricevendo + il medesimo errore. Questo comando può essere eseguito solo da un processo + con \ids{UID} effettivo corrispondente al creatore o al proprietario della + coda, o all'amministratore. +\item[\const{IPC\_SET}] Permette di modificare i permessi ed il proprietario + della coda, ed il limite massimo sulle dimensioni del totale dei messaggi in + essa contenuti (\var{msg\_qbytes}). I valori devono essere passati in una + struttura \struct{msqid\_ds} puntata da \param{buf}. Per modificare i valori + di \var{msg\_perm.mode}, \var{msg\_perm.uid} e \var{msg\_perm.gid} occorre + essere il proprietario o il creatore della coda, oppure l'amministratore; lo + stesso vale per \var{msg\_qbytes}, ma l'amministratore ha la facoltà di + incrementarne il valore a limiti superiori a \const{MSGMNB}. +\end{basedescript} + + +Una volta che si abbia a disposizione l'identificatore, per inviare un +messaggio su una coda si utilizza la funzione \funcd{msgsnd}; il suo prototipo +è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{int msgsnd(int msqid, struct msgbuf *msgp, size\_t msgsz, int + msgflg)} + + Invia un messaggio sulla coda \param{msqid}. + + \bodydesc{La funzione restituisce 0, e $-1$ in caso di errore, nel qual caso + \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \item[\errcode{EAGAIN}] il messaggio non può essere inviato perché si è + superato il limite \var{msg\_qbytes} sul numero massimo di byte presenti + sulla coda, e si è richiesto \const{IPC\_NOWAIT} in \param{flag}. + \item[\errcode{EINVAL}] si è specificato un \param{msgid} invalido, o un + valore non positivo per \param{mtype}, o un valore di \param{msgsz} + maggiore di \const{MSGMAX}. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{EINTR} ed \errval{ENOMEM}. } +\end{functions} + +La funzione inserisce il messaggio sulla coda specificata da \param{msqid}; il +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 +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 fig.~\ref{fig:ipc_msbuf} è comunque solo un modello, tanto che +la definizione contenuta in \headfile{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 \var{mtype} come nell'esempio; esso infatti serve ad identificare il +tipo di messaggio e deve essere sempre specificato come intero positivo di +tipo \ctyp{long}. Il campo \var{mtext} invece può essere di qualsiasi tipo e +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 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 +indica il tipo. + +Si tenga presente che la lunghezza che deve essere indicata in questo +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 fig.~\ref{fig:ipc_msbuf}, \param{msgsz} +dovrà essere pari a \const{LENGTH}). + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\textwidth} + \includestruct{listati/msgbuf.h} + \end{minipage} + \normalsize + \caption{Schema della struttura \structd{msgbuf}, da utilizzare come + argomento per inviare/ricevere messaggi.} + \label{fig:ipc_msbuf} +\end{figure} + +Per capire meglio il funzionamento della funzione riprendiamo in +considerazione la struttura della coda illustrata in +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 +messaggio; il valore di \var{mtype} verrà mantenuto in \var{msg\_type} ed il +valore di \param{msgsz} in \var{msg\_ts}; il testo del messaggio sarà copiato +all'indirizzo specificato da \var{msg\_spot}. + +Il valore dell'argomento \param{flag} permette di specificare il comportamento +della funzione. Di norma, quando si specifica un valore nullo, la funzione +ritorna immediatamente a meno che si sia ecceduto il valore di +\var{msg\_qbytes}, o il limite di sistema sul numero di messaggi, nel qual +caso si blocca mandando il processo in stato di \textit{sleep}. Se si +specifica per \param{flag} il valore \const{IPC\_NOWAIT} la funzione opera in +modalità non bloccante, ed in questi casi ritorna immediatamente con un errore +di \errcode{EAGAIN}. + +Se non si specifica \const{IPC\_NOWAIT} la funzione resterà bloccata fintanto +che non si liberano risorse sufficienti per poter inserire nella coda il +messaggio, nel qual caso ritornerà normalmente. La funzione può ritornare, con +una condizione di errore anche in due altri casi: quando la coda viene rimossa +(nel qual caso si ha un errore di \errcode{EIDRM}) o quando la funzione viene +interrotta da un segnale (nel qual caso si ha un errore di \errcode{EINTR}). + +Una volta completato con successo l'invio del messaggio sulla coda, la +funzione aggiorna i dati mantenuti in \struct{msqid\_ds}, in particolare +vengono modificati: +\begin{itemize*} +\item Il valore di \var{msg\_lspid}, che viene impostato al \ids{PID} del + processo chiamante. +\item Il valore di \var{msg\_qnum}, che viene incrementato di uno. +\item Il valore \var{msg\_stime}, che viene impostato al tempo corrente. +\end{itemize*} + +La funzione che viene utilizzata per estrarre un messaggio da una coda è +\funcd{msgrcv}; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{ssize\_t msgrcv(int msqid, struct msgbuf *msgp, size\_t msgsz, + long msgtyp, int msgflg)} -\item Come canale di comunicazione fra un client ed un server (il modello - \textit{client-server} è illustrato in \secref{sec:net_cliserv}). + Legge un messaggio dalla coda \param{msqid}. + + \bodydesc{La funzione restituisce il numero di byte letti in caso di + successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà uno + dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \item[\errcode{E2BIG}] il testo del messaggio è più lungo di \param{msgsz} e + non si è specificato \const{MSG\_NOERROR} in \param{msgflg}. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale mentre + era in attesa di ricevere un messaggio. + \item[\errcode{EINVAL}] si è specificato un \param{msgid} invalido o un + valore di \param{msgsz} negativo. + \end{errlist} + ed inoltre \errval{EFAULT}. +} +\end{functions} + +La funzione legge un messaggio dalla coda specificata, scrivendolo sulla +struttura puntata da \param{msgp}, che dovrà avere un formato analogo a quello +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 +\const{MSG\_NOERROR}, il messaggio viene troncato e la parte in eccesso viene +perduta, altrimenti il messaggio non viene estratto e la funzione ritorna con +un errore di \errcode{E2BIG}. + +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 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 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}. +\item se \param{msgtyp} è negativo viene estratto il primo fra i messaggi con + il valore più basso del tipo, fra tutti quelli il cui tipo ha un valore + inferiore al valore assoluto di \param{msgtyp}. \end{itemize} -Nel primo caso quello che si fa è creare tante pipe quanti sono i processi a -cui i vogliono inviare i dati, da usare come standard input per gli stessi; una -volta che li si saranno posti in esecuzione ridirigendo lo standard input si -potrà eseguire il processo iniziale replicandone, con il comando \cmd{tee}, -l'output sulle pipe. - -Il secondo caso è relativamente semplice qualora si debba comunicare con un -processo alla volta (nel qual caso basta usare due pipe, una per leggere ed -una per scrivere), le cose diventano invece molto più complesse quando si -vuole effettuare una comunicazione fra il server ed un numero imprecisato di -client; se il primo infatti può ricevere le richieste attraverso una fifo -``nota'', per le risposte non si può fare altrettanto, dato che per la -struttura sequenziale delle fifo, i client dovrebbero sapere, prima di -leggerli, quando i dati inviati sono destinati a loro. - -Per risolvere questo problema, si può usare un'architettura come quella -illustrata da Stevens in \cite{APUE}, in cui le risposte vengono inviate su -fifo temporanee identificate dal \acr{pid} dei client, ma in ogni caso il -sistema è macchinoso e continua ad avere vari inconvenienti\footnote{lo stesso - Stevens nota come sia impossibile per il server sapere se un client è andato - in crash, con la possibilità di far restare le fifo temporanee sul - filesystem, come sia necessario intercettare \macro{SIGPIPE} dato che un - client può terminare dopo aver fatto una richiesta, ma prima che la risposta - sia inviata, e come occorra gestire il caso in cui non ci sono client attivi - (e la lettura dalla fifo nota restituisca al serve un end-of-file.}; in -generale infatti l'interfaccia delle 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 diversi meccanismi di -comunicazione, come quelli che esamineremo in \secref{sec:ipc_sysv}. - - - -\section{La comunicazione fra processi di System V} -\label{sec:ipc_sysv} +Il valore di \param{msgflg} permette di controllare il comportamento della +funzione, esso può essere nullo o una maschera binaria composta da uno o più +valori. Oltre al precedente \const{MSG\_NOERROR}, sono possibili altri due +valori: \const{MSG\_EXCEPT}, che permette, quando \param{msgtyp} è positivo, +di leggere il primo messaggio nella coda con tipo diverso da \param{msgtyp}, e +\const{IPC\_NOWAIT} che causa il ritorno immediato della funzione quando non +ci sono messaggi sulla coda. + +Il comportamento usuale della funzione infatti, se non ci sono messaggi +disponibili per la lettura, è di bloccare il processo in stato di +\textit{sleep}. Nel caso però si sia specificato \const{IPC\_NOWAIT} la +funzione ritorna immediatamente con un errore \errcode{ENOMSG}. Altrimenti la +funzione ritorna normalmente non appena viene inserito un messaggio del tipo +desiderato, oppure ritorna con errore qualora la coda sia rimossa (con +\var{errno} impostata a \errcode{EIDRM}) o se il processo viene interrotto da +un segnale (con \var{errno} impostata a \errcode{EINTR}). + +Una volta completata con successo l'estrazione del messaggio dalla coda, la +funzione aggiorna i dati mantenuti in \struct{msqid\_ds}, in particolare +vengono modificati: +\begin{itemize*} +\item Il valore di \var{msg\_lrpid}, che viene impostato al \ids{PID} del + processo chiamante. +\item Il valore di \var{msg\_qnum}, che viene decrementato di uno. +\item Il valore \var{msg\_rtime}, che viene impostato al tempo corrente. +\end{itemize*} + +Le code di messaggi presentano il solito problema di tutti gli oggetti del +SysV-IPC; essendo questi permanenti restano nel sistema occupando risorse +anche quando un processo è terminato, al contrario delle \textit{pipe} per le +quali tutte le risorse occupate vengono rilasciate quanto l'ultimo processo +che le 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 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 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 \itindex{polling} \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 \textit{fifo}. In +questo caso useremo una sola coda di messaggi, usando il tipo di messaggio per +comunicare in maniera indipendente con client diversi. -Benché le pipe (e le fifo) siano ancora ampiamente usate, esse presentano -numerosi limiti, il principale dei quali è che il meccanismo di comunicazione -è rigidamente sequenziale; per cui una situazione in cui un processo scrive -qualcosa che molti altri devono poter leggere non può essere implementata in -maniera semplice con una pipe. - -Per superarne questi limiti nello sviluppo di System V vennero introdotti una -serie di nuovi oggetti di comunicazione e relative interfacce di -programmazione che garantissero una maggiore flessibilità; in questa sezione -esamineremo quello che viene ormai chiamato il \textsl{Sistema di - comunicazione inter-processo} di System V , più comunemente noto come -\textit{System V IPC (Inter-Process Comunication)}. +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/MQFortuneServer.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del server di \textit{fortunes} + basato sulle \textit{message queue}.} + \label{fig:ipc_mq_fortune_server} +\end{figure} + +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 +per permettere una comunicazione indipendente fra il server ed i vari client, +usando il \ids{PID} di questi ultimi come identificativo. Questo è possibile +in quanto, al contrario di una \textit{fifo}, la lettura di una coda di +messaggi può non essere sequenziale, proprio grazie alla classificazione dei +messaggi sulla base del loro tipo. + +Il programma, oltre alle solite variabili per il nome del file da cui leggere +le \textit{fortunes} e per il vettore di stringhe che contiene le frasi, +definisce due strutture appositamente per la comunicazione; con +\var{msgbuf\_read} (\texttt{\small 8--11}) vengono passate le richieste mentre +con \var{msgbuf\_write} (\texttt{\small 12--15}) vengono restituite le frasi. + +La gestione delle opzioni si è al solito omessa, essa si curerà di impostare +in \var{n} il numero di frasi da leggere specificato a linea di comando ed in +\var{fortunefilename} il file da cui leggerle; dopo aver installato +(\texttt{\small 19--21}) i gestori dei segnali per trattare l'uscita dal +server, viene prima controllato (\texttt{\small 22}) il numero di frasi +richieste abbia senso (cioè sia maggiore di zero), le quali poi +(\texttt{\small 23}) vengono lette nel vettore in memoria con la stessa +funzione \code{FortuneParse} usata anche per il server basato sulle +\textit{fifo}. + +Una volta inizializzato il vettore di stringhe coi messaggi presi dal file +delle \textit{fortune} si procede (\texttt{\small 25}) con la generazione di +una chiave per identificare la coda di messaggi (si usa il nome del file dei +sorgenti del server) con la quale poi si esegue (\texttt{\small 26}) la +creazione della stessa (si noti come si sia chiamata \func{msgget} con un +valore opportuno per l'argomento \param{flag}), avendo cura di abortire il +programma (\texttt{\small 27--29}) in caso di errore. + +Finita la fase di inizializzazione il server prima (\texttt{\small 32}) chiama +la funzione \func{daemon} per andare in background e poi esegue in permanenza +il ciclo principale (\texttt{\small 33--40}). Questo inizia (\texttt{\small + 34}) con il porsi in attesa di un messaggio di richiesta da parte di un +client; si noti infatti come \func{msgrcv} richieda un messaggio con +\var{mtype} uguale a 1: questo è il valore usato per le richieste dato che +corrisponde al \ids{PID} di \cmd{init}, che non può essere un client. L'uso +del flag \const{MSG\_NOERROR} è solo per sicurezza, dato che i messaggi di +richiesta sono di dimensione fissa (e contengono solo il \ids{PID} del +client). + +Se non sono presenti messaggi di richiesta \func{msgrcv} si bloccherà, +ritornando soltanto in corrispondenza dell'arrivo sulla coda di un messaggio +di richiesta da parte di un client, in tal caso il ciclo prosegue +(\texttt{\small 35}) selezionando una frase a caso, copiandola (\texttt{\small + 36}) nella struttura \var{msgbuf\_write} usata per la risposta e +calcolandone (\texttt{\small 37}) la dimensione. + +Per poter permettere a ciascun client di ricevere solo la risposta indirizzata +a lui il tipo del messaggio in uscita viene inizializzato (\texttt{\small 38}) +al valore del \ids{PID} del client ricevuto nel messaggio di richiesta. +L'ultimo passo del ciclo (\texttt{\small 39}) è inviare sulla coda il +messaggio di risposta. Si tenga conto che se la coda è piena anche questa +funzione potrà bloccarsi fintanto che non venga liberato dello spazio. + +Si noti che il programma può terminare solo grazie ad una interruzione da +parte di un segnale; in tal caso verrà eseguito (\texttt{\small 45--48}) il +gestore \code{HandSIGTERM}, che semplicemente si limita a cancellare la coda +(\texttt{\small 46}) ed ad uscire (\texttt{\small 47}). + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/MQFortuneClient.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del client di \textit{fortunes} + basato sulle \textit{message queue}.} + \label{fig:ipc_mq_fortune_client} +\end{figure} + +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 è +identica a quanto visto per il server, solo che in questo caso \func{msgget} +non viene chiamata con il flag di creazione in quanto la coda deve essere +preesistente. In caso di errore (ad esempio se il server non è stato avviato) +il programma termina immediatamente. + +Una volta acquisito l'identificatore della coda il client compone il +messaggio di richiesta (\texttt{\small 12--13}) in \var{msg\_read}, usando 1 +per il tipo ed inserendo il proprio \ids{PID} come dato da passare al server. +Calcolata (\texttt{\small 14}) la dimensione, provvede (\texttt{\small 15}) ad +immettere la richiesta sulla coda. + +A questo punto non resta che (\texttt{\small 16}) rileggere dalla coda la +risposta del server richiedendo a \func{msgrcv} di selezionare i messaggi di +tipo corrispondente al valore del \ids{PID} inviato nella richiesta. L'ultimo +passo (\texttt{\small 17}) prima di uscire è quello di stampare a video il +messaggio ricevuto. +Proviamo allora il nostro nuovo sistema, al solito occorre definire +\code{LD\_LIBRARY\_PATH} per accedere alla libreria \file{libgapil.so}, dopo di +che, in maniera del tutto analoga a quanto fatto con il programma che usa le +fifo, potremo far partire il server con: +\begin{verbatim} +[piccardi@gont sources]$ ./mqfortuned -n10 +\end{verbatim}%$ +come nel caso precedente, avendo eseguito il server in background, il comando +ritornerà immediatamente; potremo però verificare con \cmd{ps} che il +programma è effettivamente in esecuzione, e che ha creato una coda di +messaggi: +\begin{verbatim} +[piccardi@gont sources]$ ipcs -\subsection{Code di messaggi} -\label{sec:ipc_messque} +------ Shared Memory Segments -------- +key shmid owner perms bytes nattch status -Il primo oggetto introdotto dal \textit{System V IPC} è quello delle code di -messaggi. +------ Semaphore Arrays -------- +key semid owner perms nsems -\subsection{Semafori} -\label{sec:ipc_semaph} +------ Message Queues -------- +key msqid owner perms used-bytes messages +0x0102dc6a 0 piccardi 666 0 0 +\end{verbatim} +a questo punto potremo usare il client per ottenere le nostre frasi: +\begin{verbatim} +[piccardi@gont sources]$ ./mqfortune +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +[piccardi@gont sources]$ ./mqfortune +Let's call it an accidental feature. + --Larry Wall +\end{verbatim} +con un risultato del tutto equivalente al precedente. Infine potremo chiudere +il server inviando il segnale di terminazione con il comando \code{killall + mqfortuned} verificando che effettivamente la coda di messaggi viene rimossa. -Il secondo oggetto introdotto dal \textit{System V IPC} è quello dei semafori. +Benché funzionante questa architettura risente dello stesso inconveniente +visto anche nel caso del precedente server basato sulle \textit{fifo}; se il +client viene interrotto dopo l'invio del messaggio di richiesta e prima della +lettura della risposta, quest'ultima resta nella coda (così come per le +\textit{fifo} si aveva il problema delle \textit{fifo} che restavano nel +filesystem). In questo caso però il problemi sono maggiori, sia perché è molto +più facile esaurire la memoria dedicata ad una coda di messaggi che gli +\itindex{inode} inode di un filesystem, sia perché, con il riutilizzo dei +\ids{PID} da parte dei processi, un client eseguito in un momento successivo +potrebbe ricevere un messaggio non indirizzato a lui. -\subsection{Memoria condivisa} -\label{sec:ipc_shar_mem} -Il terzo oggetto introdotto dal \textit{System V IPC} è quello della memoria -condivisa. +\subsection{Semafori} +\label{sec:ipc_sysv_sem} + +I semafori non sono meccanismi di intercomunicazione diretta come quelli +(\textit{pipe}, \textit{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 \index{sezione~critica} +\textsl{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 +di un programma. In questo modo l'accesso ad una risorsa condivisa da più +processi può essere controllato, associando ad essa un semaforo che consente +di assicurare che non più di un processo alla volta possa usarla. +Il concetto di semaforo è uno dei concetti base nella programmazione ed è +assolutamente generico, così come del tutto generali sono modalità con cui lo +si utilizza. Un processo che deve accedere ad una risorsa eseguirà un +controllo del semaforo: se questo è positivo il suo valore sarà decrementato, +indicando che si è consumato una unità della risorsa, ed il processo potrà +proseguire nell'utilizzo di quest'ultima, provvedendo a rilasciarla, una volta +completate le operazioni volute, reincrementando il semaforo. +Se al momento del controllo il valore del semaforo è nullo, siamo invece in +una situazione in cui la risorsa non è disponibile, ed il processo si +bloccherà in stato di \textit{sleep} fin quando chi la sta utilizzando non la +rilascerà, incrementando il valore del semaforo. Non appena il semaforo torna +positivo, indicando che la risorsa è disponibile, il processo sarà svegliato, +e si potrà operare come nel caso precedente (decremento del semaforo, accesso +alla risorsa, incremento del semaforo). -\section{La comunicazione fra processi di POSIX} -\label{sec:ipc_posix} +Per poter implementare questo tipo di logica le operazioni di controllo e +decremento del contatore associato al semaforo devono essere atomiche, +pertanto una realizzazione di un oggetto di questo tipo è necessariamente +demandata al kernel. La forma più semplice di semaforo è quella del +\textsl{semaforo binario}, o \textit{mutex}, in cui un valore diverso da zero +(normalmente 1) indica la libertà di accesso, e un valore nullo l'occupazione +della risorsa. In generale però si possono usare semafori con valori interi, +utilizzando il valore del contatore come indicatore del ``numero di risorse'' +ancora disponibili. + +Il sistema di comunicazione inter-processo di \textit{SysV-IPC} prevede anche i +semafori, ma gli oggetti utilizzati non sono semafori singoli, ma gruppi di +semafori detti \textsl{insiemi} (o \textit{semaphore set}); la funzione che +permette di creare o ottenere l'identificatore di un insieme di semafori è +\funcd{semget}, ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semget(key\_t key, int nsems, int flag)} + + Restituisce l'identificatore di un insieme di semafori. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ENOSPC}] si è cercato di creare una insieme di semafori + quando è stato superato o il limite per il numero totale di semafori + (\const{SEMMNS}) o quello per il numero totale degli insiemi + (\const{SEMMNI}) nel sistema. + \item[\errcode{EINVAL}] l'argomento \param{nsems} è minore di zero o + maggiore del limite sul numero di semafori per ciascun insieme + (\const{SEMMSL}), o se l'insieme già esiste, maggiore del numero di + semafori che contiene. + \item[\errcode{ENOMEM}] il sistema non ha abbastanza memoria per poter + contenere le strutture per un nuovo insieme di semafori. + \end{errlist} + ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, + \errval{EIDRM}, con lo stesso significato che hanno per \func{msgget}.} +\end{functions} + +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 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. + +Purtroppo questa implementazione complica inutilmente lo schema elementare che +abbiamo descritto, dato che non è possibile definire un singolo semaforo, ma +se ne deve creare per forza un insieme. Ma questa in definitiva è solo una +complicazione inutile, il problema è che i semafori del \textit{SysV-IPC} +soffrono di altri due, ben più gravi, difetti. + +Il primo difetto è che non esiste una funzione che permetta di creare ed +inizializzare un semaforo in un'unica chiamata; occorre prima creare l'insieme +dei semafori con \func{semget} e poi inizializzarlo con \func{semctl}, si +perde così ogni possibilità di eseguire l'operazione atomicamente. + +Il secondo difetto deriva dalla caratteristica generale degli oggetti del +\textit{SysV-IPC} di essere risorse globali di sistema, che non vengono +cancellate quando nessuno le usa più; ci si così a trova a dover affrontare +esplicitamente il caso in cui un processo termina per un qualche errore, +lasciando un semaforo occupato, che resterà tale fino al successivo riavvio +del sistema. Come vedremo esistono delle modalità per evitare tutto ciò, ma +diventa necessario indicare esplicitamente che si vuole il ripristino del +semaforo all'uscita del processo. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\textwidth} + \includestruct{listati/semid_ds.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{semid\_ds}, associata a ciascun insieme di + semafori.} + \label{fig:ipc_semid_ds} +\end{figure} + +A ciascun insieme di semafori è associata una struttura \struct{semid\_ds}, +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}. +\item il campo \var{sem\_ctime}, che esprime il tempo di creazione + dell'insieme, viene inizializzato al tempo corrente. +\item il campo \var{sem\_otime}, che esprime il tempo dell'ultima operazione + effettuata, viene inizializzato a zero. +\end{itemize*} + +Ciascun semaforo dell'insieme è realizzato come una struttura di tipo +\struct{sem} che ne contiene i dati essenziali, la sua definizione\footnote{si + è riportata la definizione originaria del kernel 1.0, che contiene la prima + realizzazione del \textit{SysV-IPC} in Linux. In realtà questa struttura + 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 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]{\textwidth} + \includestruct{listati/sem.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{sem}, che contiene i dati di un singolo + semaforo.} + \label{fig:ipc_sem} +\end{figure} + +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. +\item[\var{sempid}] il \ids{PID} dell'ultimo processo che ha eseguito una + operazione sul semaforo. +\item[\var{semncnt}] il numero di processi in attesa che esso venga + incrementato. +\item[\var{semzcnt}] il numero di processi in attesa che esso si annulli. +\end{description*} + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|r|p{8cm}|} + \hline + \textbf{Costante} & \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \const{SEMMNI}& 128 & Numero massimo di insiemi di semafori.\\ + \const{SEMMSL}& 250 & Numero massimo di semafori per insieme.\\ + \const{SEMMNS}&\const{SEMMNI}*\const{SEMMSL}& Numero massimo di semafori + nel sistema.\\ + \const{SEMVMX}& 32767 & Massimo valore per un semaforo.\\ + \const{SEMOPM}& 32 & Massimo numero di operazioni per chiamata a + \func{semop}. \\ + \const{SEMMNU}&\const{SEMMNS}& Massimo numero di strutture di ripristino.\\ + \const{SEMUME}&\const{SEMOPM}& Massimo numero di voci di ripristino.\\ + \const{SEMAEM}&\const{SEMVMX}& Valore massimo per l'aggiustamento + all'uscita. \\ + \hline + \end{tabular} + \caption{Valori delle costanti associate ai limiti degli insiemi di + semafori, definite in \file{linux/sem.h}.} + \label{tab:ipc_sem_limits} +\end{table} + +Come per le code di messaggi anche per gli insiemi di semafori esistono una +serie di limiti, i cui valori sono associati ad altrettante costanti, che si +sono riportate in tab.~\ref{tab:ipc_sem_limits}. Alcuni di questi limiti sono +al solito accessibili e modificabili attraverso \func{sysctl} o scrivendo +direttamente nel file \sysctlfile{kernel/sem}. + +La funzione che permette di effettuare le varie operazioni di controllo sui +semafori (fra le quali, come accennato, è impropriamente compresa anche la +loro inizializzazione) è \funcd{semctl}; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semctl(int semid, int semnum, int cmd)} + \funcdecl{int semctl(int semid, int semnum, int cmd, union semun arg)} + + Esegue le operazioni di controllo su un semaforo o un insieme di semafori. + + \bodydesc{La funzione restituisce in caso di successo un valore positivo + quanto usata con tre argomenti ed un valore nullo quando usata con + quattro. In caso di errore restituisce -1, ed \var{errno} assumerà uno dei + valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per eseguire + l'operazione richiesta. + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{EPERM}] si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} + ma il processo non ha privilegi sufficienti ad eseguire l'operazione. + \item[\errcode{ERANGE}] si è richiesto \const{SETALL} \const{SETVAL} ma il + valore a cui si vuole impostare il semaforo è minore di zero o maggiore + di \const{SEMVMX}. + \end{errlist} + ed inoltre \errval{EFAULT} ed \errval{EINVAL}. +} +\end{functions} + +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]{\textwidth} + \includestruct{listati/semun.h} + \end{minipage} + \normalsize + \caption{La definizione dei possibili valori di una \direct{union} + \structd{semun}, usata come quarto argomento della funzione + \func{semctl}.} + \label{fig:ipc_semun} +\end{figure} + +Qualora la funzione operi con quattro argomenti \param{arg} è un argomento +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 +fig.~\ref{fig:ipc_semun}. + +Come già accennato sia il comportamento della funzione che il numero di +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: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge i dati dell'insieme di semafori, copiando il + contenuto della relativa struttura \struct{semid\_ds} all'indirizzo + specificato con \var{arg.buf}. Occorre avere il permesso di lettura. + L'argomento \param{semnum} viene ignorato. +\item[\const{IPC\_RMID}] Rimuove l'insieme di semafori e le relative strutture + dati, con effetto immediato. Tutti i processi che erano stato di + \textit{sleep} vengono svegliati, ritornando con un errore di + \errcode{EIDRM}. L'\ids{UID} effettivo del processo deve corrispondere o al + creatore o al proprietario dell'insieme, o all'amministratore. L'argomento + \param{semnum} viene ignorato. +\item[\const{IPC\_SET}] Permette di modificare i permessi ed il proprietario + dell'insieme. I valori devono essere passati in una struttura + \struct{semid\_ds} puntata da \param{arg.buf} di cui saranno usati soltanto i + campi \var{sem\_perm.uid}, \var{sem\_perm.gid} e i nove bit meno + significativi di \var{sem\_perm.mode}. L'\ids{UID} effettivo del processo deve + corrispondere o al creatore o al proprietario dell'insieme, o + all'amministratore. L'argomento \param{semnum} viene ignorato. +\item[\const{GETALL}] Restituisce il valore corrente di ciascun semaforo + dell'insieme (corrispondente al campo \var{semval} di \struct{sem}) nel + vettore indicato da \param{arg.array}. Occorre avere il permesso di lettura. + L'argomento \param{semnum} viene ignorato. +\item[\const{GETNCNT}] Restituisce come valore di ritorno della funzione il + numero di processi in attesa che il semaforo \param{semnum} dell'insieme + \param{semid} venga incrementato (corrispondente al campo \var{semncnt} di + \struct{sem}); va invocata con tre argomenti. Occorre avere il permesso di + lettura. +\item[\const{GETPID}] Restituisce come valore di ritorno della funzione il + \ids{PID} dell'ultimo processo che ha compiuto una operazione sul semaforo + \param{semnum} dell'insieme \param{semid} (corrispondente al campo + \var{sempid} di \struct{sem}); va invocata con tre argomenti. Occorre avere + il permesso di lettura. +\item[\const{GETVAL}] Restituisce come valore di ritorno della funzione il il + valore corrente del semaforo \param{semnum} dell'insieme \param{semid} + (corrispondente al campo \var{semval} di \struct{sem}); va invocata con tre + argomenti. Occorre avere il permesso di lettura. +\item[\const{GETZCNT}] Restituisce come valore di ritorno della funzione il + numero di processi in attesa che il valore del semaforo \param{semnum} + dell'insieme \param{semid} diventi nullo (corrispondente al campo + \var{semncnt} di \struct{sem}); va invocata con tre argomenti. Occorre avere + il permesso di lettura. +\item[\const{SETALL}] Inizializza il valore di tutti i semafori dell'insieme, + aggiornando il campo \var{sem\_ctime} di \struct{semid\_ds}. I valori devono + essere passati nel vettore indicato da \param{arg.array}. Si devono avere i + privilegi di scrittura sul semaforo. L'argomento \param{semnum} viene + ignorato. +\item[\const{SETVAL}] Inizializza il semaforo \param{semnum} al valore passato + dall'argomento \param{arg.val}, aggiornando il campo \var{sem\_ctime} di + \struct{semid\_ds}. Si devono avere i privilegi di scrittura sul semaforo. +\end{basedescript} + +Quando si imposta il valore di un semaforo (sia che lo si faccia per tutto +l'insieme con \const{SETALL}, che per un solo semaforo con \const{SETVAL}), i +processi in attesa su di esso reagiscono di conseguenza al cambiamento di +valore. Inoltre la coda delle operazioni di ripristino viene cancellata per +tutti i semafori il cui valore viene modificato. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|l|} + \hline + \textbf{Operazione} & \textbf{Valore restituito} \\ + \hline + \hline + \const{GETNCNT}& Valore di \var{semncnt}.\\ + \const{GETPID} & Valore di \var{sempid}.\\ + \const{GETVAL} & Valore di \var{semval}.\\ + \const{GETZCNT}& Valore di \var{semzcnt}.\\ + \hline + \end{tabular} + \caption{Valori di ritorno della funzione \func{semctl}.} + \label{tab:ipc_semctl_returns} +\end{table} + +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 +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. + +Le operazioni ordinarie sui semafori, come l'acquisizione o il rilascio degli +stessi (in sostanza tutte quelle non comprese nell'uso di \func{semctl}) +vengono effettuate con la funzione \funcd{semop}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semop(int semid, struct sembuf *sops, unsigned nsops)} + + Esegue le operazioni ordinarie su un semaforo o un insieme di semafori. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per eseguire + l'operazione richiesta. + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{ENOMEM}] si è richiesto un \const{SEM\_UNDO} ma il sistema + non ha le risorse per allocare la struttura di ripristino. + \item[\errcode{EAGAIN}] un'operazione comporterebbe il blocco del processo, + ma si è specificato \const{IPC\_NOWAIT} in \var{sem\_flg}. + \item[\errcode{EINTR}] la funzione, bloccata in attesa dell'esecuzione + dell'operazione, viene interrotta da un segnale. + \item[\errcode{E2BIG}] l'argomento \param{nsops} è maggiore del numero + massimo di operazioni \const{SEMOPM}. + \item[\errcode{ERANGE}] per alcune operazioni il valore risultante del + semaforo viene a superare il limite massimo \const{SEMVMX}. + \end{errlist} + ed inoltre \errval{EFAULT} ed \errval{EINVAL}. +} +\end{functions} + + +%TODO manca semtimedop, trattare qui, referenziata in +%sez.~\ref{sec:sig_gen_beha}. + +La funzione permette di eseguire operazioni multiple sui singoli semafori di +un insieme. La funzione richiede come primo argomento l'identificatore +\param{semid} dell'insieme su cui si vuole operare. Il numero di operazioni da +effettuare viene specificato con l'argomento \param{nsop}, mentre il loro +contenuto viene passato con un puntatore ad un vettore di strutture +\struct{sembuf} nell'argomento \param{sops}. Le operazioni richieste vengono +effettivamente eseguite se e soltanto se è possibile effettuarle tutte quante. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\textwidth} + \includestruct{listati/sembuf.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{sembuf}, usata per le operazioni sui + semafori.} + \label{fig:ipc_sembuf} +\end{figure} + +Il contenuto di ciascuna operazione deve essere specificato attraverso una +opportuna struttura \struct{sembuf} (la cui definizione è riportata in +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 +riferimento l'operazione; si ricordi che i semafori sono numerati come in un +vettore, per cui il primo semaforo corrisponde ad un valore nullo di +\var{sem\_num}. + +Il campo \var{sem\_flg} è un flag, mantenuto come maschera binaria, per il +quale possono essere impostati i due valori \const{IPC\_NOWAIT} e +\const{SEM\_UNDO}. Impostando \const{IPC\_NOWAIT} si fa si che, invece di +bloccarsi (in tutti quei casi in cui l'esecuzione di una operazione richiede +che il processo vada in stato di \textit{sleep}), \func{semop} ritorni +immediatamente con un errore di \errcode{EAGAIN}. Impostando \const{SEM\_UNDO} +si richiede invece che l'operazione venga registrata in modo che il valore del +semaforo possa essere ripristinato all'uscita del processo. + +Infine \var{sem\_op} è il campo che controlla l'operazione che viene eseguita +e determina il comportamento della chiamata a \func{semop}; tre sono i casi +possibili: +\begin{basedescript}{\desclabelwidth{2.0cm}} +\item[\var{sem\_op}$>0$] In questo caso il valore di \var{sem\_op} viene + aggiunto al valore corrente di \var{semval}. La funzione ritorna + immediatamente (con un errore di \errcode{ERANGE} qualora si sia superato il + limite \const{SEMVMX}) ed il processo non viene bloccato in nessun caso. + Specificando \const{SEM\_UNDO} si aggiorna il contatore per il ripristino + del valore del semaforo. Al processo chiamante è richiesto il privilegio di + alterazione (scrittura) sull'insieme di semafori. + +\item[\var{sem\_op}$=0$] Nel caso \var{semval} sia zero l'esecuzione procede + immediatamente. Se \var{semval} è diverso da zero il comportamento è + controllato da \var{sem\_flg}, se è stato impostato \const{IPC\_NOWAIT} la + funzione ritorna con un errore di \errcode{EAGAIN}, altrimenti viene + incrementato \var{semzcnt} di uno ed il processo resta in stato di + \textit{sleep} fintanto che non si ha una delle condizioni seguenti: + \begin{itemize*} + \item \var{semval} diventa zero, nel qual caso \var{semzcnt} viene + decrementato di uno. + \item l'insieme di semafori viene rimosso, nel qual caso \func{semop} ritorna + un errore di \errcode{EIDRM}. + \item il processo chiamante riceve un segnale, nel qual caso \var{semzcnt} + viene decrementato di uno e \func{semop} ritorna un errore di + \errcode{EINTR}. + \end{itemize*} + Al processo chiamante è richiesto il privilegio di lettura dell'insieme dei + semafori. + +\item[\var{sem\_op}$<0$] Nel caso in cui \var{semval} è maggiore o uguale del + valore assoluto di \var{sem\_op} (se cioè la somma dei due valori resta + positiva o nulla) i valori vengono sommati e la funzione ritorna + immediatamente; qualora si sia impostato \const{SEM\_UNDO} viene anche + aggiornato il contatore per il ripristino del valore del semaforo. In caso + contrario (quando cioè la somma darebbe luogo ad un valore di \var{semval} + negativo) se si è impostato \const{IPC\_NOWAIT} la funzione ritorna con un + errore di \errcode{EAGAIN}, altrimenti viene incrementato di uno + \var{semncnt} ed il processo resta in stato di \textit{sleep} fintanto che + non si ha una delle condizioni seguenti: + \begin{itemize*} + \item \var{semval} diventa maggiore o uguale del valore assoluto di + \var{sem\_op}, nel qual caso \var{semncnt} viene decrementato di uno, il + valore di \var{sem\_op} viene sommato a \var{semval}, e se era stato + impostato \const{SEM\_UNDO} viene aggiornato il contatore per il + ripristino del valore del semaforo. + \item l'insieme di semafori viene rimosso, nel qual caso \func{semop} + ritorna un errore di \errcode{EIDRM}. + \item il processo chiamante riceve un segnale, nel qual caso \var{semncnt} + viene decrementato di uno e \func{semop} ritorna un errore di + \errcode{EINTR}. + \end{itemize*} + Al processo chiamante è richiesto il privilegio di alterazione (scrittura) + sull'insieme di semafori. +\end{basedescript} + +In caso di successo della funzione viene aggiornato il campo \var{sempid} per +ogni semaforo modificato al valore del \ids{PID} del processo chiamante; +inoltre vengono pure aggiornati al tempo corrente i campi \var{sem\_otime} e +\var{sem\_ctime}. + +Dato che, come già accennato in precedenza, in caso di uscita inaspettata i +semafori possono restare occupati, abbiamo visto come \func{semop} permetta di +attivare un meccanismo di ripristino attraverso l'uso del flag +\const{SEM\_UNDO}. Il meccanismo è implementato tramite una apposita struttura +\kstruct{sem\_undo}, associata ad ogni processo per ciascun semaforo che esso +ha modificato; all'uscita i semafori modificati vengono ripristinati, e le +strutture disallocate. Per mantenere coerente il comportamento queste +strutture non vengono ereditate attraverso una \func{fork} (altrimenti si +avrebbe un doppio ripristino), mentre passano inalterate nell'esecuzione di +una \func{exec} (altrimenti non si avrebbe ripristino). + +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 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 +a queste strutture restano per compatibilità.\footnote{in particolare con le + vecchie versioni delle librerie del C, come le libc5.} + +\begin{figure}[!htb] + \centering \includegraphics[width=13cm]{img/semtruct} + \caption{Schema della struttura di un insieme di semafori.} + \label{fig:ipc_sem_schema} +\end{figure} + +Alla creazione di un nuovo insieme viene allocata una nuova strutture +\struct{semid\_ds} ed il relativo vettore di strutture \struct{sem}. Quando si +richiede una operazione viene anzitutto verificato che tutte le operazioni +possono avere successo; se una di esse comporta il blocco del processo il +kernel crea una struttura \kstruct{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 \itindex{scheduler} 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 +attesa (a partire da \var{sem\_pending}) per verificare se qualcuna delle +operazioni sospese in precedenza può essere eseguita, nel qual caso la +struttura \kstruct{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 \kstruct{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. + +%TODO verificare queste strutture \kstruct{sem\_queue} e \kstruct{sem\_undo} + +Queste strutture sono mantenute in due liste,\footnote{rispettivamente + attraverso i due campi \var{id\_next} e \var{proc\_next}.} una associata +all'insieme di cui fa parte il semaforo, che viene usata per invalidare le +strutture se questo viene cancellato o per azzerarle se si è eseguita una +operazione con \func{semctl}; l'altra associata al processo che ha eseguito +l'operazione;\footnote{attraverso il campo \var{semundo} di + \kstruct{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. + +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 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}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/Mutex.c} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni che permettono di creare o recuperare + l'identificatore di un semaforo da utilizzare come \textit{mutex}.} + \label{fig:ipc_mutex_create} +\end{figure} + +La prima funzione (\texttt{\small 2--15}) è \func{MutexCreate} che data una +chiave crea il semaforo usato per il mutex e lo inizializza, restituendone +l'identificatore. Il primo passo (\texttt{\small 6}) è chiamare \func{semget} +con \const{IPC\_CREATE} per creare il semaforo qualora non esista, +assegnandogli i privilegi di lettura e scrittura per tutti. In caso di errore +(\texttt{\small 7--9}) si ritorna subito il risultato di \func{semget}, +altrimenti (\texttt{\small 10}) si inizializza il semaforo chiamando +\func{semctl} con il comando \const{SETVAL}, utilizzando l'unione +\struct{semunion} dichiarata ed avvalorata in precedenza (\texttt{\small 4}) +ad 1 per significare che risorsa è libera. In caso di errore (\texttt{\small + 11--13}) si restituisce il valore di ritorno di \func{semctl}, altrimenti +(\texttt{\small 14}) si ritorna l'identificatore del semaforo. + +La seconda funzione (\texttt{\small 17--20}) è \func{MutexFind}, che, data una +chiave, restituisce l'identificatore del semaforo ad essa associato. La +comprensione del suo funzionamento è immediata in quanto essa è soltanto un +\textit{wrapper}\footnote{si chiama così una funzione usata per fare da + \textsl{involucro} alla chiamata di un altra, usata in genere per + semplificare un'interfaccia (come in questo caso) o per utilizzare con la + stessa funzione diversi substrati (librerie, ecc.) che possono fornire le + stesse funzionalità.} di una chiamata a \func{semget} per cercare +l'identificatore associato alla chiave, il valore di ritorno di quest'ultima +viene passato all'indietro al chiamante. + +La terza funzione (\texttt{\small 22--25}) è \func{MutexRead} che, dato un +identificatore, restituisce il valore del semaforo associato al mutex. Anche +in questo caso la funzione è un \textit{wrapper} per una chiamata a +\func{semctl} con il comando \const{GETVAL}, che permette di restituire il +valore del semaforo. + +La quarta e la quinta funzione (\texttt{\small 36--44}) sono \func{MutexLock}, +e \func{MutexUnlock}, che permettono rispettivamente di bloccare e sbloccare +il mutex. Entrambe fanno da wrapper per \func{semop}, utilizzando le due +strutture \var{sem\_lock} e \var{sem\_unlock} definite in precedenza +(\texttt{\small 27--34}). Si noti come per queste ultime si sia fatto uso +dell'opzione \const{SEM\_UNDO} per evitare che il semaforo resti bloccato in +caso di terminazione imprevista del processo. + +L'ultima funzione (\texttt{\small 46--49}) della serie, è \func{MutexRemove}, +che rimuove il mutex. Anche in questo caso si ha un wrapper per una chiamata a +\func{semctl} con il comando \const{IPC\_RMID}, che permette di cancellare il +semaforo; il valore di ritorno di quest'ultima viene passato all'indietro. + +Chiamare \func{MutexLock} decrementa il valore del semaforo: se questo è +libero (ha già valore 1) sarà bloccato (valore nullo), se è bloccato la +chiamata a \func{semop} si bloccherà fintanto che la risorsa non venga +rilasciata. Chiamando \func{MutexUnlock} il valore del semaforo sarà +incrementato di uno, sbloccandolo qualora fosse bloccato. + +Si noti che occorre eseguire sempre prima \func{MutexLock} e poi +\func{MutexUnlock}, perché se per un qualche errore si esegue più volte +quest'ultima il valore del semaforo crescerebbe oltre 1, e \func{MutexLock} +non avrebbe più l'effetto aspettato (bloccare la risorsa quando questa è +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 sez.~\ref{sec:ipc_lock_file} come sia possibile ottenere +un'interfaccia analoga a quella appena illustrata, senza incorrere in questi +problemi, usando il \itindex{file~locking} \textit{file locking}. + + +\subsection{Memoria condivisa} +\label{sec:ipc_sysv_shm} + +Il terzo oggetto introdotto dal \textit{SysV-IPC} è quello dei segmenti di +memoria condivisa. La funzione che permette di ottenerne uno è \funcd{shmget}, +ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmget(key\_t key, int size, int flag)} + + Restituisce l'identificatore di una memoria condivisa. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ENOSPC}] si è superato il limite (\const{SHMMNI}) sul numero + di segmenti di memoria nel sistema, o cercato di allocare un segmento le + cui dimensioni fanno superare il limite di sistema (\const{SHMALL}) per + la memoria ad essi riservata. + \item[\errcode{EINVAL}] si è richiesta una dimensione per un nuovo segmento + maggiore di \const{SHMMAX} o minore di \const{SHMMIN}, o se il segmento + già esiste \param{size} è maggiore delle sue dimensioni. + \item[\errcode{ENOMEM}] il sistema non ha abbastanza memoria per poter + contenere le strutture per un nuovo segmento di memoria condivisa. + \end{errlist} + ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, + \errval{EIDRM}, con lo stesso significato che hanno per \func{msgget}.} +\end{functions} + +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 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}. + +% TODO aggiungere l'uso di SHM_HUGETLB introdotto con il kernel 2.6.0 + +La memoria condivisa è la forma più veloce di comunicazione fra due processi, +in quanto permette agli stessi di vedere nel loro spazio di indirizzi una +stessa sezione di memoria. Pertanto non è necessaria nessuna operazione di +copia per trasmettere i dati da un processo all'altro, in quanto ciascuno può +accedervi direttamente con le normali operazioni di lettura e scrittura dei +dati in memoria. + +Ovviamente tutto questo ha un prezzo, ed il problema fondamentale della +memoria condivisa è la sincronizzazione degli accessi. È evidente infatti che +se un processo deve scambiare dei dati con un altro, si deve essere sicuri che +quest'ultimo non acceda al segmento di memoria condivisa prima che il primo +non abbia completato le operazioni di scrittura, inoltre nel corso di una +lettura si deve essere sicuri che i dati restano coerenti e non vengono +sovrascritti da un accesso in scrittura sullo stesso segmento da parte di un +altro processo. Per questo in genere la memoria condivisa viene sempre +utilizzata in abbinamento ad un meccanismo di sincronizzazione, il che, di +norma, significa insieme a dei semafori. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\textwidth} + \includestruct{listati/shmid_ds.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{shmid\_ds}, associata a ciascun segmento di + memoria condivisa.} + \label{fig:ipc_shmid_ds} +\end{figure} + +A ciascun segmento di memoria condivisa è associata una struttura +\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 +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} +\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 + segmento, viene inizializzato al tempo corrente. +\item i campi \var{shm\_atime} e \var{shm\_dtime}, che esprimono + rispettivamente il tempo dell'ultima volta che il segmento è stato + agganciato o sganciato da un processo, vengono inizializzati a zero. +\item il campo \var{shm\_lpid}, che esprime il \ids{PID} del processo che ha + eseguito l'ultima operazione, viene inizializzato a zero. +\item il campo \var{shm\_cpid}, che esprime il \ids{PID} del processo che ha + creato il segmento, viene inizializzato al \ids{PID} del processo chiamante. +\item il campo \var{shm\_nattac}, che esprime il numero di processi agganciati + al segmento viene inizializzato a zero. +\end{itemize} + +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 +di questi limiti sono al solito accessibili e modificabili attraverso +\func{sysctl} o scrivendo direttamente nei rispettivi file di +\file{/proc/sys/kernel/}. + +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. + + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|r|c|p{7cm}|} + \hline + \textbf{Costante} & \textbf{Valore} & \textbf{File in \texttt{proc}} + & \textbf{Significato} \\ + \hline + \hline + \const{SHMALL}& 0x200000&\sysctlrelfile{kernel}{shmall} + & Numero massimo di pagine che + possono essere usate per i segmenti di + memoria condivisa.\\ + \const{SHMMAX}&0x2000000&\sysctlrelfile{kernel}{shmmax} + & Dimensione massima di un segmento di memoria + condivisa.\\ + \const{SHMMNI}& 4096&\sysctlrelfile{kernel}{msgmni} + & Numero massimo di segmenti di memoria condivisa + presenti nel kernel.\\ + \const{SHMMIN}& 1& --- & Dimensione minima di un segmento di + memoria condivisa.\\ + \const{SHMLBA}&\const{PAGE\_SIZE}&--- & Limite inferiore per le dimensioni + minime di un segmento (deve essere + allineato alle dimensioni di una + pagina di memoria).\\ + \const{SHMSEG}& --- & --- & Numero massimo di segmenti di + memoria condivisa per ciascun + processo.\\ + + + \hline + \end{tabular} + \caption{Valori delle costanti associate ai limiti dei segmenti di memoria + condivisa, insieme al relativo file in \file{/proc/sys/kernel/} ed al + valore preimpostato presente nel sistema.} + \label{tab:ipc_shm_limits} +\end{table} + +Al solito la funzione che permette di effettuare le operazioni di controllo su +un segmento di memoria condivisa è \funcd{shmctl}; il suo prototipo è: +\begin{functions} + \headdecl{sys/ipc.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmctl(int shmid, int cmd, struct shmid\_ds *buf)} + + Esegue le operazioni di controllo su un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCES}] si è richiesto \const{IPC\_STAT} ma i permessi non + consentono l'accesso in lettura al segmento. + \item[\errcode{EINVAL}] o \param{shmid} non è un identificatore valido o + \param{cmd} non è un comando valido. + \item[\errcode{EIDRM}] l'argomento \param{shmid} fa riferimento ad un + segmento che è stato cancellato. + \item[\errcode{EPERM}] si è specificato un comando con \const{IPC\_SET} o + \const{IPC\_RMID} senza i permessi necessari. + \item[\errcode{EOVERFLOW}] si è tentato il comando \const{IPC\_STAT} ma il + valore del \ids{GID} o dell'\ids{UID} è troppo grande per essere + memorizzato nella struttura puntata da \param{buf}. + \item[\errcode{EFAULT}] l'indirizzo specificato con \param{buf} non è + valido. + \end{errlist} +} +\end{functions} + +Il comando specificato attraverso l'argomento \param{cmd} determina i diversi +effetti della funzione; i possibili valori che esso può assumere, ed il +corrispondente comportamento della funzione, sono i seguenti: + +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge le informazioni riguardo il segmento di memoria + condivisa nella struttura \struct{shmid\_ds} puntata da \param{buf}. Occorre + che il processo chiamante abbia il permesso di lettura sulla segmento. +\item[\const{IPC\_RMID}] Marca il segmento di memoria condivisa per la + rimozione, questo verrà cancellato effettivamente solo quando l'ultimo + processo ad esso agganciato si sarà staccato. Questo comando può essere + eseguito solo da un processo con \ids{UID} effettivo corrispondente o al + creatore del segmento, o al proprietario del segmento, o all'amministratore. +\item[\const{IPC\_SET}] Permette di modificare i permessi ed il proprietario + del segmento. Per modificare i valori di \var{shm\_perm.mode}, + \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 \itindex{memory~locking} \textit{memory + locking}\footnote{impedisce cioè che la memoria usata per il segmento + venga salvata su disco dal meccanismo della \index{memoria~virtuale} + 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 \itindex{memory~locking} + \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 \index{memoria~virtuale} 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 +\struct{shmid\_ds} precedentemente allocata, in cui nel primo caso saranno +scritti i dati del segmento di memoria restituiti dalla funzione e da cui, nel +secondo caso, verranno letti i dati da impostare sul segmento. + +Una volta che lo si è creato, per utilizzare un segmento di memoria condivisa +l'interfaccia prevede due funzioni, \funcd{shmat} e \func{shmdt}. La prima di +queste serve ad agganciare un segmento al processo chiamante, in modo che +quest'ultimo possa inserirlo nel suo spazio di indirizzi per potervi accedere; +il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/shm.h} + + \funcdecl{void *shmat(int shmid, const void *shmaddr, int shmflg)} + Aggancia al processo un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce l'indirizzo del segmento in caso di + successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà i + valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al + segmento nella modalità richiesta. + \item[\errcode{EINVAL}] si è specificato un identificatore invalido per + \param{shmid}, o un indirizzo non allineato sul confine di una pagina + per \param{shmaddr}. + \end{errlist} + ed inoltre \errval{ENOMEM}.} +\end{functions} + +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 +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 sez.~\ref{sec:proc_mem_alloc}) non viene influenzato. +Si tenga presente infine che la funzione ha successo anche se il segmento è +stato marcato per la cancellazione. + +\begin{figure}[!htb] + \centering \includegraphics[height=10cm]{img/sh_memory_layout} + \caption{Disposizione dei segmenti di memoria di un processo quando si è + agganciato un segmento di memoria condivisa.} + \label{fig:ipc_shmem_layout} +\end{figure} + +L'argomento \param{shmaddr} specifica a quale indirizzo\footnote{lo standard + SVID prevede che l'argomento \param{shmaddr} sia di tipo \ctyp{char *}, così + come il valore di ritorno della funzione; in Linux è stato così con le + \acr{libc4} e le \acr{libc5}, con il passaggio alla \acr{glibc} il tipo di + \param{shmaddr} è divenuto un \ctyp{const void *} e quello del valore di + ritorno un \ctyp{void *}.} deve essere associato il segmento, se il valore +specificato è \val{NULL} è il sistema a scegliere opportunamente un'area di +memoria libera (questo è il modo più portabile e sicuro di usare la funzione). +Altrimenti il kernel aggancia il segmento all'indirizzo specificato da +\param{shmaddr}; questo però può avvenire solo se l'indirizzo coincide con il +limite di una pagina, cioè se è un multiplo esatto del parametro di sistema +\const{SHMLBA}, che in Linux è sempre uguale \const{PAGE\_SIZE}. + +Si tenga presente però che quando si usa \val{NULL} come valore di +\param{shmaddr}, l'indirizzo restituito da \func{shmat} può cambiare da +processo a processo; pertanto se nell'area di memoria condivisa si salvano +anche degli indirizzi, si deve avere cura di usare valori relativi (in genere +riferiti all'indirizzo di partenza del segmento). + +L'argomento \param{shmflg} permette di cambiare il comportamento della +funzione; esso va specificato come maschera binaria, i bit utilizzati sono +solo due e sono identificati dalle costanti \const{SHM\_RND} e +\const{SHM\_RDONLY}, che vanno combinate con un OR aritmetico. Specificando +\const{SHM\_RND} si evita che \func{shmat} ritorni un errore quando +\param{shmaddr} non è allineato ai confini di una pagina. Si può quindi usare +un valore qualunque per \param{shmaddr}, e il segmento verrà comunque +agganciato, ma al più vicino multiplo di \const{SHMLBA} (il nome della +costante sta infatti per \textit{rounded}, e serve per specificare un +indirizzo come arrotondamento, in Linux è equivalente a \const{PAGE\_SIZE}). + +L'uso di \const{SHM\_RDONLY} permette di agganciare il segmento in sola +lettura (si ricordi che anche le pagine di memoria hanno dei permessi), in tal +caso un tentativo di scrivere sul segmento comporterà una +\itindex{segment~violation} violazione di accesso con l'emissione di un +segnale di \signal{SIGSEGV}. Il comportamento usuale di \func{shmat} è quello +di agganciare il segmento con l'accesso in lettura e scrittura (ed il processo +deve aver questi permessi in \var{shm\_perm}), non è prevista la possibilità +di agganciare un segmento in sola scrittura. + +In caso di successo la funzione aggiorna anche i seguenti campi di +\struct{shmid\_ds}: +\begin{itemize*} +\item il tempo \var{shm\_atime} dell'ultima operazione di aggancio viene + impostato al tempo corrente. +\item il \ids{PID} \var{shm\_lpid} dell'ultimo processo che ha operato sul + segmento viene impostato a quello del processo corrente. +\item il numero \var{shm\_nattch} di processi agganciati al segmento viene + aumentato di uno. +\end{itemize*} + +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 +eseguito un diverso programma con uno spazio di indirizzi completamente +diverso, tutti i segmenti agganciati al processo originario vengono +automaticamente sganciati. Lo stesso avviene all'uscita del processo +attraverso una \func{exit}. + +Una volta che un segmento di memoria condivisa non serve più, si può +sganciarlo esplicitamente dal processo usando l'altra funzione +dell'interfaccia, \funcd{shmdt}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmdt(const void *shmaddr)} + Sgancia dal processo un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di + errore, la funzione fallisce solo quando non c'è un segmento agganciato + all'indirizzo \param{shmaddr}, con \var{errno} che assume il valore + \errval{EINVAL}.} +\end{functions} + +La funzione sgancia dallo spazio degli indirizzi del processo un segmento di +memoria condivisa; questo viene identificato con l'indirizzo \param{shmaddr} +restituito dalla precedente chiamata a \func{shmat} con il quale era stato +agganciato al processo. + +In caso di successo la funzione aggiorna anche i seguenti campi di +\struct{shmid\_ds}: +\begin{itemize*} +\item il tempo \var{shm\_dtime} dell'ultima operazione di sganciamento viene + impostato al tempo corrente. +\item il \ids{PID} \var{shm\_lpid} dell'ultimo processo che ha operato sul + segmento viene impostato a quello del processo corrente. +\item il numero \var{shm\_nattch} di processi agganciati al segmento viene + decrementato di uno. +\end{itemize*} +inoltre la regione di indirizzi usata per il segmento di memoria condivisa +viene tolta dallo spazio di indirizzi del processo. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \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 ottenere 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 +accessi. Per questo motivo, quando la comunicazione fra processi è +sequenziale, altri meccanismi come le \textit{pipe}, le \textit{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 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 +modalità predefinita. + +Un esempio classico di uso della memoria condivisa è quello del +``\textit{monitor}'', in cui viene per scambiare informazioni fra un processo +server, che vi scrive dei dati di interesse generale che ha ottenuto, e i +processi client interessati agli stessi dati che così possono leggerli in +maniera completamente asincrona. Con questo schema di funzionamento da una +parte si evita che ciascun processo client debba compiere l'operazione, +potenzialmente onerosa, di ricavare e trattare i dati, e dall'altra si evita +al processo server di dover gestire l'invio a tutti i client di tutti i dati +(non potendo il server sapere quali di essi servono effettivamente al singolo +client). + +Nel nostro caso implementeremo un ``\textsl{monitor}'' di una directory: un +processo si incaricherà di tenere sotto controllo alcuni parametri relativi ad +una directory (il numero dei file contenuti, la dimensione totale, quante +directory, link simbolici, file normali, ecc.) che saranno salvati in un +segmento di memoria condivisa cui altri processi potranno accedere per +ricavare la parte di informazione che interessa. + +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 \index{variabili!globali} variabili globali, +omettendo tutto quello che riguarda la gestione delle opzioni e la stampa +delle istruzioni di uso a video; al solito il codice completo si trova con i +sorgenti allegati nel file \file{DirMonitor.c}. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/DirMonitor.c} + \end{minipage} + \normalsize + \caption{Codice della funzione principale del programma \file{DirMonitor.c}.} + \label{fig:ipc_dirmonitor_main} +\end{figure} + +Il programma usa delle \index{variabili!globali} variabili globali +(\texttt{\small 2--14}) per mantenere i valori relativi agli oggetti usati per +la comunicazione inter-processo; si è definita inoltre una apposita struttura +\struct{DirProp} che contiene i dati relativi alle proprietà che si vogliono +mantenere nella memoria condivisa, per l'accesso da parte dei client. + +Il programma, dopo la sezione, omessa, relativa alla gestione delle opzioni da +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 + 20--23}) che sia stato specificato l'argomento necessario contenente il nome +della directory da tenere sotto controllo, senza il quale esce immediatamente +con un messaggio di errore. + +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 \index{directory~di~lavoro} directory di lavoro del programma nella +directory da tenere sotto controllo, in vista del successivo uso della +funzione \func{daemon}.\footnote{si noti come si è potuta fare questa scelta, + nonostante le indicazioni illustrate in sez.~\ref{sec:sess_daemon}, per il + particolare scopo del programma, che necessita comunque di restare + all'interno di una directory.} Infine (\texttt{\small 27--29}) si installano +i gestori per i vari segnali di terminazione che, avendo a che fare con un +programma che deve essere eseguito come server, sono il solo strumento +disponibile per concluderne l'esecuzione. + +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 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}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/ComputeValues.c} + \end{minipage} + \normalsize + \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 +\index{directory~di~lavoro} directory di lavoro corrente. Una volta che il +programma è andato in background l'esecuzione prosegue (\texttt{\small + 42--48}) all'interno di un ciclo infinito: si inizia (\texttt{\small 43}) +bloccando il mutex con \func{MutexLock} per poter accedere alla memoria +condivisa (la funzione si bloccherà automaticamente se qualche client sta +leggendo), poi (\texttt{\small 44}) si cancellano i valori precedentemente +immagazzinati nella memoria condivisa con \func{memset}, e si esegue +(\texttt{\small 45}) un nuovo calcolo degli stessi utilizzando la funzione +\myfunc{dir\_scan}; infine (\texttt{\small 46}) si sblocca il mutex con +\func{MutexUnlock}, e si attende (\texttt{\small 47}) per il periodo di tempo +specificato a riga di comando con l'opzione \code{-p} con una \func{sleep}. + +Si noti come per il calcolo dei valori da mantenere nella memoria condivisa si +sia usata ancora una volta la funzione \myfunc{dir\_scan}, già utilizzata (e +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 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 +\index{variabili!globali} variabile globale \var{shmptr}. + +Dato che la funzione è chiamata da \myfunc{dir\_scan}, si è all'interno del +ciclo principale del programma, con un mutex acquisito, perciò non è +necessario effettuare nessun controllo e si può accedere direttamente alla +memoria 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 +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}) 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}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/ReadMonitor.c} + \end{minipage} + \normalsize + \caption{Codice del programma client del monitor delle proprietà di una + directory, \file{ReadMonitor.c}.} + \label{fig:ipc_dirmonitor_client} +\end{figure} + +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 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}) 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. 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 +il mutex, prima di uscire. + +Verifichiamo allora il funzionamento dei nostri programmi; al solito, usando +le funzioni di libreria occorre definire opportunamente +\code{LD\_LIBRARY\_PATH}; poi si potrà lanciare il server con: +\begin{Verbatim} +[piccardi@gont sources]$ ./dirmonitor ./ +\end{Verbatim} +%$ +ed avendo usato \func{daemon} il comando ritornerà immediatamente. Una volta +che il server è in esecuzione, possiamo passare ad invocare il client per +verificarne i risultati, in tal caso otterremo: +\begin{Verbatim} +[piccardi@gont sources]$ ./readmon +Ci sono 68 file dati +Ci sono 3 directory +Ci sono 0 link +Ci sono 0 fifo +Ci sono 0 socket +Ci sono 0 device a caratteri +Ci sono 0 device a blocchi +Totale 71 file, per 489831 byte +\end{Verbatim} +%$ +ed un rapido calcolo (ad esempio con \code{ls -a | wc} per contare i file) ci +permette di verificare che il totale dei file è giusto. Un controllo con +\cmd{ipcs} ci permette inoltre di verificare la presenza di un segmento di +memoria condivisa e di un semaforo: +\begin{Verbatim} +[piccardi@gont sources]$ ipcs +------ Shared Memory Segments -------- +key shmid owner perms bytes nattch status +0xffffffff 54067205 piccardi 666 4096 1 + +------ Semaphore Arrays -------- +key semid owner perms nsems +0xffffffff 229376 piccardi 666 1 + +------ Message Queues -------- +key msqid owner perms used-bytes messages +\end{Verbatim} +%$ + +Se a questo punto aggiungiamo un file, ad esempio con \code{touch prova}, +potremo verificare che, passati nel peggiore dei casi almeno 10 secondi (o +l'eventuale altro intervallo impostato per la rilettura dei dati) avremo: +\begin{Verbatim} +[piccardi@gont sources]$ ./readmon +Ci sono 69 file dati +Ci sono 3 directory +Ci sono 0 link +Ci sono 0 fifo +Ci sono 0 socket +Ci sono 0 device a caratteri +Ci sono 0 device a blocchi +Totale 72 file, per 489887 byte +\end{Verbatim} +%$ + +A questo punto possiamo far uscire il server inviandogli un segnale di +\signal{SIGTERM} con il comando \code{killall dirmonitor}, a questo punto +ripetendo la lettura, otterremo un errore: +\begin{Verbatim} +[piccardi@gont sources]$ ./readmon +Cannot find shared memory: No such file or directory +\end{Verbatim} +%$ +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 -------- +key shmid owner perms bytes nattch status + +------ Semaphore Arrays -------- +key semid owner perms nsems + +------ Message Queues -------- +key msqid owner perms used-bytes messages +\end{Verbatim} +%$ + + +%% 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 +%% fig.~\ref{fig:ipc_shm_struct}. + +%% \begin{figure}[!htb] +%% \centering +%% \includegraphics[width=10cm]{img/shmstruct} +%% \caption{Schema dell'implementazione dei segmenti di memoria condivisa in +%% Linux.} +%% \label{fig:ipc_shm_struct} +%% \end{figure} + + + + +\section{Tecniche alternative} +\label{sec:ipc_alternatives} + +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 +sono già stati accennati in precedenza) ed elenca alcune possibili tecniche +alternative, che vogliamo riprendere in questa sezione. + + +\subsection{Alternative alle code di messaggi} +\label{sec:ipc_mq_alternative} + +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 \textit{pipe} erano +unidirezionali; con la disponibilità di \func{socketpair} (vedi +sez.~\ref{sec:ipc_socketpair}) o utilizzando una coppia di \textit{pipe}, si +può ottenere questo risultato senza incorrere nelle complicazioni introdotte +dal \textit{SysV-IPC}. + +In realtà, grazie alla presenza del campo \var{mtype}, le code di messaggi +hanno delle caratteristiche ulteriori, consentendo una classificazione dei +messaggi ed un accesso non rigidamente sequenziale; due caratteristiche che +sono impossibili da ottenere con le \textit{pipe} e i socket di +\func{socketpair}. A queste esigenze però si può comunque ovviare in maniera +diversa con un uso combinato della memoria condivisa e dei meccanismi di +sincronizzazione, per cui alla fine l'uso delle code di messaggi classiche è +relativamente poco diffuso. + +% TODO: trattare qui, se non ssis trova posto migliore, copy_from_process e +% copy_to_process, introdotte con il kernel 3.2. Vedi +% http://lwn.net/Articles/405346/ e +% http://ozlabs.org/~cyeoh/cma/process_vm_readv.txt + + +\subsection{I \textsl{file di lock}} +\label{sec:ipc_file_lock} + +\index{file!di lock|(} + +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 +\textit{mutex}), per indicare la disponibilità o meno di una risorsa, senza la +necessità di un contatore come i semafori, si possono utilizzare metodi +alternativi. + +La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare +dei \textsl{file di lock} (per i quali esiste anche una opportuna directory, +\file{/var/lock}, nel filesystem standard). Per questo si usa la +caratteristica della funzione \func{open} (illustrata in +sez.~\ref{sec:file_open_close}) che prevede\footnote{questo è quanto dettato dallo + standard POSIX.1, ciò non toglie che in alcune implementazioni questa + tecnica possa non funzionare; in particolare per Linux, nel caso di NFS, si + è comunque soggetti alla possibilità di una \itindex{race~condition} + \textit{race condition}.} che essa ritorni un errore quando usata con i +flag di \const{O\_CREAT} e \const{O\_EXCL}. In tal modo la creazione di un +\textsl{file di lock} può essere eseguita atomicamente, il processo che crea +il file con successo si può considerare come titolare del lock (e della +risorsa ad esso associata) mentre il rilascio si può eseguire con una chiamata +ad \func{unlink}. + +Un esempio dell'uso di questa funzione è mostrato dalle funzioni +\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 +(\texttt{\small 4--10}) si limita ad aprire il file di lock (\texttt{\small + 9}) nella modalità descritta, mentre la seconda (\texttt{\small 11--17}) lo +cancella con \func{unlink}. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/LockFile.c} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni \func{LockFile} e \func{UnlockFile} che + permettono di creare e rimuovere un \textsl{file di lock}.} + \label{fig:ipc_file_lock} +\end{figure} + +Uno dei limiti di questa tecnica è che, come abbiamo già accennato in +sez.~\ref{sec:file_open_close}, 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. + +In 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 \itindex{polling} +\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 +risorsa, senza necessità di attendere che questa si liberi; ad esempio la si +usa spesso per evitare interferenze sull'uso delle porte seriali da parte di +più programmi: qualora si trovi un file di lock il programma che cerca di +accedere alla seriale si limita a segnalare che la risorsa non è disponibile. + +\index{file!di lock|)} + + +\subsection{La sincronizzazione con il \textit{file locking}} +\label{sec:ipc_lock_file} + +Dato che i \index{file!di lock} file di lock presentano gli inconvenienti +illustrati in precedenza, la tecnica alternativa di sincronizzazione più +comune è quella di fare ricorso al \itindex{file~locking} \textit{file + locking} (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 \itindex{polling} \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 +chiusura dei relativi file) e non ci si deve preoccupare di niente; inoltre +non consuma risorse permanentemente allocate nel sistema. Lo svantaggio è che, +dovendo fare ricorso a delle operazioni sul filesystem, esso è in genere +leggermente più lento. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/MutexLocking.c} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni che permettono per la gestione dei + \textit{mutex} con il \itindex{file~locking} \textit{file locking}.} + \label{fig:ipc_flock_mutex} +\end{figure} + +Il codice delle varie funzioni usate per implementare un mutex utilizzando il +\textit{file locking} \itindex{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. + +La prima funzione (\texttt{\small 1--5}) è \func{CreateMutex}, e serve a +creare il mutex; la funzione è estremamente semplice, e si limita +(\texttt{\small 4}) a creare, con una opportuna chiamata ad \func{open}, il +file che sarà usato per il successivo \textit{file locking}, assicurandosi che +non esista già (nel qual caso segnala un errore); poi restituisce il file +descriptor che sarà usato dalle altre funzioni per acquisire e rilasciare il +mutex. + +La seconda funzione (\texttt{\small 6--10}) è \func{FindMutex}, che, come la +precedente, è stata definita per mantenere una analogia con la corrispondente +funzione basata sui semafori. Anch'essa si limita (\texttt{\small 9}) ad +aprire il file da usare per il \itindex{file~locking} \textit{file locking}, +solo che in questo caso le opzioni di \func{open} sono tali che il file in +questione deve esistere di già. + +La terza funzione (\texttt{\small 11--22}) è \func{LockMutex} e serve per +acquisire il mutex. La funzione definisce (\texttt{\small 14}) e inizializza +(\texttt{\small 16--19}) la struttura \var{lock} da usare per acquisire un +write lock sul file, che poi (\texttt{\small 21}) viene richiesto con +\func{fcntl}, restituendo il valore di ritorno di quest'ultima. Se il file è +libero il lock viene acquisito e la funzione ritorna immediatamente; +altrimenti \func{fcntl} si bloccherà (si noti che la si è chiamata con +\const{F\_SETLKW}) fino al rilascio del lock. + +La quarta funzione (\texttt{\small 24--34}) è \func{UnlockMutex} e serve a +rilasciare il mutex. La funzione è analoga alla precedente, solo che in questo +caso si inizializza (\texttt{\small 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 \itindex{file~locking} \textit{file + locking} in semantica POSIX (si 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 +cancellare il mutex. Anche questa funzione è stata definita per mantenere una +analogia con le funzioni basate sui semafori, e si limita a cancellare +(\texttt{\small 38}) il file con una chiamata ad \func{unlink}. Si noti che in +questo caso la funzione non ha effetto sui mutex già ottenuti con precedenti +chiamate a \func{FindMutex} o \func{CreateMutex}, che continueranno ad essere +disponibili fintanto che i relativi file descriptor restano aperti. Pertanto +per rilasciare un mutex occorrerà prima chiamare \func{UnlockMutex} oppure +chiudere il file usato per il lock. + +La sesta funzione (\texttt{\small 41--55}) è \func{ReadMutex} e serve a +leggere lo stato del mutex. In questo caso si prepara (\texttt{\small 46--49}) +la solita struttura \var{lock} come l'acquisizione del lock, ma si effettua +(\texttt{\small 51}) la chiamata a \func{fcntl} usando il comando +\const{F\_GETLK} per ottenere lo stato del lock, e si restituisce +(\texttt{\small 52}) il valore di ritorno in caso di errore, ed il valore del +campo \var{l\_type} (che descrive lo stato del lock) altrimenti +(\texttt{\small 54}). Per questo motivo la funzione restituirà -1 in caso di +errore e uno dei due valori \const{F\_UNLCK} o \const{F\_WRLCK}\footnote{non + si dovrebbe mai avere il terzo valore possibile, \const{F\_RDLCK}, dato che + la nostra interfaccia usa solo i write lock. Però è sempre possibile che + siano richiesti altri lock sul file al di fuori dell'interfaccia, nel qual + caso si potranno avere, ovviamente, interferenze indesiderate.} in caso di +successo, ad indicare che il mutex è, rispettivamente, libero o occupato. + +Basandosi sulla semantica dei file lock POSIX valgono tutte le considerazioni +relative al comportamento di questi ultimi fatte in +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. + + +\subsection{Il \textit{memory mapping} anonimo} +\label{sec:ipc_mmap_anonymous} + +\itindbeg{memory~mapping} Abbiamo già visto che quando i processi sono +\textsl{correlati}\footnote{se cioè hanno almeno un progenitore comune.} l'uso +delle \textit{pipe} può costituire una valida alternativa alle code di +messaggi; nella stessa situazione si può evitare l'uso di una memoria +condivisa facendo ricorso al cosiddetto \textit{memory mapping} anonimo. + +In sez.~\ref{sec:file_memory_map} abbiamo visto come sia possibile mappare il +contenuto di un file nella memoria di un processo, e che, quando viene usato +il flag \const{MAP\_SHARED}, le modifiche effettuate al contenuto del file +vengono viste da tutti i processi che lo hanno mappato. Utilizzare questa +tecnica per creare una memoria condivisa fra processi diversi è estremamente +inefficiente, in quanto occorre passare attraverso il disco. Però abbiamo +visto anche che se si esegue la mappatura con il flag \const{MAP\_ANONYMOUS} +la regione mappata non viene associata a nessun file, anche se quanto scritto +rimane in memoria e può essere riletto; allora, dato che un processo figlio +mantiene nel suo spazio degli indirizzi anche le regioni mappate, esso sarà +anche in grado di accedere a quanto in esse è contenuto. + +In questo modo diventa possibile creare una memoria condivisa fra processi +diversi, purché questi abbiano almeno un progenitore comune che ha effettuato +il \textit{memory mapping} anonimo.\footnote{nei sistemi derivati da SysV una + funzionalità simile a questa viene implementata mappando il file speciale + \file{/dev/zero}. In tal caso i valori scritti nella regione mappata non + vengono ignorati (come accade qualora si scriva direttamente sul file), ma + restano in memoria e possono essere riletti secondo le stesse modalità usate + nel \textit{memory mapping} anonimo.} Vedremo come utilizzare questa tecnica +più avanti, quando realizzeremo una nuova versione del monitor visto in +sez.~\ref{sec:ipc_sysv_shm} che possa restituisca i risultati via rete. +\itindend{memory~mapping} + +% TODO: fare esempio di mmap anonima + +% TODO: con il kernel 3.2 è stata introdotta un nuovo meccanismo di +% intercomunicazione veloce chiamato Cross Memory Attach, da capire se e come +% trattarlo qui, vedi http://lwn.net/Articles/405346/ +% https://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=fcf634098c00dd9cd247447368495f0b79be12d1 + +\section{L'intercomunicazione 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 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. + + +\subsection{Considerazioni generali} +\label{sec:ipc_posix_generic} + +Oggi Linux supporta tutti gli oggetti definito nello standard POSIX per l'IPC, +ma a lungo non è stato così; la memoria condivisa è presente a partire dal +kernel 2.4.x, i semafori sono forniti dalla \acr{glibc} nella sezione che +implementa i \itindex{thread} \textit{thread} POSIX di nuova generazione che +richiedono il kernel 2.6, le code di messaggi sono supportate a partire dal +kernel 2.6.6. + +La caratteristica fondamentale dell'interfaccia POSIX è l'abbandono dell'uso +degli identificatori e delle chiavi visti nel \textit{SysV-IPC}, per passare ai +\itindex{POSIX~IPC~names} \textit{POSIX IPC names}, che sono sostanzialmente +equivalenti ai nomi dei file. Tutte le funzioni che creano un oggetto di IPC +POSIX prendono come primo argomento una stringa che indica uno di questi nomi; +lo standard è molto generico riguardo l'implementazione, ed i nomi stessi +possono avere o meno una corrispondenza sul filesystem; tutto quello che è +richiesto è che: +\begin{itemize*} +\item i nomi devono essere conformi alle regole che caratterizzano i + \textit{pathname}, in particolare non essere più lunghi di \const{PATH\_MAX} + byte e terminati da un carattere nullo. +\item se il nome inizia per una \texttt{/} chiamate differenti allo stesso + nome fanno riferimento allo stesso oggetto, altrimenti l'interpretazione del + nome dipende dall'implementazione. +\item l'interpretazione di ulteriori \texttt{/} presenti nel nome dipende + dall'implementazione. +\end{itemize*} + +Data la assoluta genericità delle specifiche, il comportamento delle funzioni +è subordinato in maniera quasi completa alla relativa +implementazione.\footnote{tanto che Stevens in \cite{UNP2} cita questo caso + come un esempio della maniera standard usata dallo standard POSIX per + consentire implementazioni non standardizzabili.} Nel caso di Linux, sia per +quanto riguarda la memoria condivisa ed i semafori, che per quanto riguarda le +code di messaggi, tutto viene creato usando come radici delle opportune +directory (rispettivamente \file{/dev/shm} e \file{/dev/mqueue}, per i +dettagli si faccia riferimento a sez.~\ref{sec:ipc_posix_shm}, +sez.~\ref{sec:ipc_posix_sem} e sez.~\ref{sec:ipc_posix_mq}) ed i nomi +specificati nelle relative funzioni sono considerati come un +\itindsub{pathname}{assoluto} \textit{pathname} assoluto (comprendente +eventuali sottodirectory) rispetto a queste radici. + +Il vantaggio degli oggetti di IPC POSIX è comunque che essi vengono inseriti +nell'albero dei file, e possono essere maneggiati con le usuali funzioni e +comandi di accesso ai file,\footnote{questo è vero nel caso di Linux, che usa + una implementazione che lo consente, non è detto che altrettanto valga per + altri kernel; in particolare, come si può facilmente verificare con uno + \cmd{strace}, sia per la memoria condivisa che per le code di messaggi le + system call utilizzate da Linux sono le stesse di quelle dei file, essendo + detti oggetti realizzati come tali in appositi filesystem.} che funzionano +come su dei file normali. + +In particolare i permessi associati agli oggetti di IPC POSIX sono identici ai +permessi dei file, ed il controllo di accesso segue esattamente la stessa +semantica (quella illustrata in sez.~\ref{sec:file_access_control}), e non +quella particolare (si ricordi quanto visto in +sez.~\ref{sec:ipc_sysv_access_control}) che viene usata per gli oggetti del +SysV-IPC. Per quanto riguarda l'attribuzione dell'utente e del gruppo +proprietari dell'oggetto alla creazione di quest'ultimo essa viene effettuata +secondo la semantica SysV: corrispondono cioè a \ids{UID} e \ids{GID} effettivi +del processo che esegue la creazione. + + +\subsection{Code di messaggi} +\label{sec:ipc_posix_mq} + +Le code di messaggi POSIX sono supportate da Linux a partire dalla versione +2.6.6-rc1 del kernel,\footnote{l'implementazione è dovuta a Michal Wronski e + Krzysztof Benedyczak, e le relative informazioni si possono trovare su + \url{http://www.geocities.com/wronski12/posix_ipc/index.html}.} In generale, +come le corrispettive del \textit{SysV-IPC}, le code di messaggi sono poco +usate, dato che i socket, nei casi in cui sono sufficienti, sono più comodi, e +che in casi più complessi la comunicazione può essere gestita direttamente con +mutex (o semafori) e memoria condivisa con tutta la flessibilità che occorre. + +Per poter utilizzare le code di messaggi, oltre ad utilizzare un kernel +superiore al 2.6.6 (o precedente, se sono stati opportunamente applicati i +relativi patch) occorre utilizzare la libreria \file{libmqueue}\footnote{i + programmi che usano le code di messaggi cioè devono essere compilati + aggiungendo l'opzione \code{-lmqueue} al comando \cmd{gcc}; in + corrispondenza all'inclusione del supporto nel kernel ufficiale anche + \file{libmqueue} è stata inserita nella \acr{glibc}, a partire dalla + versione 2.3.4 delle medesime.} che contiene le funzioni dell'interfaccia +POSIX.\footnote{in realtà l'implementazione è realizzata tramite delle + opportune chiamate ad \func{ioctl} sui file del filesystem speciale su cui + vengono mantenuti questi oggetti di IPC.} + +La libreria inoltre richiede la presenza dell'apposito filesystem di tipo +\texttt{mqueue} montato su \file{/dev/mqueue}; questo può essere fatto +aggiungendo ad \conffile{/etc/fstab} una riga come: +\begin{verbatim} +mqueue /dev/mqueue mqueue defaults 0 0 +\end{verbatim} +ed esso sarà utilizzato come radice sulla quale vengono risolti i nomi delle +code di messaggi che iniziano con una ``\texttt{/}''. Le opzioni di mount +accettate sono \texttt{uid}, \texttt{gid} e \texttt{mode} che permettono +rispettivamente di impostare l'utente, il gruppo ed i permessi associati al +filesystem. + + +La funzione che permette di aprire (e crearla se non esiste ancora) una coda +di messaggi POSIX è \funcd{mq\_open}, ed il suo prototipo è: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{mqd\_t mq\_open(const char *name, int oflag)} + + \funcdecl{mqd\_t mq\_open(const char *name, int oflag, unsigned long mode, + struct mq\_attr *attr)} + + Apre una coda di messaggi POSIX impostandone le caratteristiche. + + \bodydesc{La funzione restituisce il descrittore associato alla coda in caso + di successo e -1 per un errore; nel quel caso \var{errno} assumerà i + valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al + alla memoria secondo quanto specificato da \param{oflag}. + \item[\errcode{EEXIST}] si è specificato \const{O\_CREAT} e + \const{O\_EXCL} ma la coda già esiste. + \item[\errcode{EINVAL}] il file non supporta la funzione, o si è + specificato \const{O\_CREAT} con una valore non nullo di \param{attr} e + valori non validi di \var{mq\_maxmsg} e \var{mq\_msgsize}. + \item[\errcode{ENOENT}] non si è specificato \const{O\_CREAT} ma la coda + non esiste. + \end{errlist} + ed inoltre \errval{ENOMEM}, \errval{ENOSPC}, \errval{EFAULT}, + \errval{EMFILE}, \errval{EINTR} ed \errval{ENFILE}. +} +\end{functions} + +La funzione apre la coda di messaggi identificata dall'argomento \param{name} +restituendo il descrittore ad essa associato, del tutto analogo ad un file +descriptor, con l'unica differenza che lo standard prevede un apposito tipo +\type{mqd\_t}.\footnote{nel caso di Linux si tratta in effetti proprio di un + normale file descriptor; pertanto, anche se questo comportamento non è + portabile, lo si può tenere sotto osservazione con le funzioni dell'I/O + multiplexing (vedi sez.~\ref{sec:file_multiplexing}) come possibile + alternativa all'uso dell'interfaccia di notifica di \func{mq\_notify} (che + vedremo a breve).} Se la coda esiste già il descrittore farà riferimento +allo stesso oggetto, consentendo così la comunicazione fra due processi +diversi. + +La funzione è del tutto analoga ad \func{open} ed analoghi sono i valori che +possono essere specificati per \param{oflag}, che deve essere specificato come +maschera binaria; i valori possibili per i vari bit sono quelli visti in +sez.~\ref{sec:file_open_close} dei quali però \func{mq\_open} riconosce solo i +seguenti: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{O\_RDONLY}] Apre la coda solo per la ricezione di messaggi. Il + processo potrà usare il descrittore con \func{mq\_receive} ma non con + \func{mq\_send}. +\item[\const{O\_WRONLY}] Apre la coda solo per la trasmissione di messaggi. Il + processo potrà usare il descrittore con \func{mq\_send} ma non con + \func{mq\_receive}. +\item[\const{O\_RDWR}] Apre la coda solo sia per la trasmissione che per la + ricezione. +\item[\const{O\_CREAT}] Necessario qualora si debba creare la coda; la + presenza di questo bit richiede la presenza degli ulteriori argomenti + \param{mode} e \param{attr}. +\item[\const{O\_EXCL}] Se usato insieme a \const{O\_CREAT} fa fallire la + chiamata se la coda esiste già, altrimenti esegue la creazione atomicamente. +\item[\const{O\_NONBLOCK}] Imposta la coda in modalità non bloccante, le + funzioni di ricezione e trasmissione non si bloccano quando non ci sono le + risorse richieste, ma ritornano immediatamente con un errore di + \errcode{EAGAIN}. +\end{basedescript} + +I primi tre bit specificano la modalità di apertura della coda, e sono fra +loro esclusivi. Ma qualunque sia la modalità in cui si è aperta una coda, +questa potrà essere riaperta più volte in una modalità diversa, e vi si potrà +sempre accedere attraverso descrittori diversi, esattamente come si può fare +per i file normali. + +Se la coda non esiste e la si vuole creare si deve specificare +\const{O\_CREAT}, in tal caso occorre anche specificare i permessi di +creazione con l'argomento \param{mode};\footnote{fino al 2.6.14 per un bug i + valori della \textit{umask} del processo non venivano applicati a questi + permessi.} i valori di quest'ultimo sono identici a quelli usati per +\func{open}, anche se per le code di messaggi han senso solo i permessi di +lettura e scrittura. Oltre ai permessi di creazione possono essere specificati +anche gli attributi specifici della coda tramite l'argomento \param{attr}; +quest'ultimo è un puntatore ad una apposita struttura \struct{mq\_attr}, la +cui definizione è riportata in fig.~\ref{fig:ipc_mq_attr}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\textwidth} + \includestruct{listati/mq_attr.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{mq\_attr}, contenente gli attributi di una + coda di messaggi POSIX.} + \label{fig:ipc_mq_attr} +\end{figure} + +Per la creazione della coda i campi della struttura che devono essere +specificati sono \var{mq\_maxmsg} e \var{mq\_msgsize}, che indicano +rispettivamente il numero massimo di messaggi che può contenere e la +dimensione massima di un messaggio. Il valore dovrà essere positivo e minore +dei rispettivi limiti di sistema \const{MQ\_MAXMSG} e \const{MQ\_MSGSIZE}, +altrimenti la funzione fallirà con un errore di \errcode{EINVAL}. +Se \param{attr} è un puntatore nullo gli attributi della coda saranno +impostati ai valori predefiniti. + +Quando l'accesso alla coda non è più necessario si può chiudere il relativo +descrittore con la funzione \funcd{mq\_close}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_close(mqd\_t mqdes)} + +Chiude la coda \param{mqdes}. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore; + nel quel caso \var{errno} assumerà i valori \errval{EBADF} o + \errval{EINTR}.} +\end{prototype} + +La funzione è analoga a \func{close},\footnote{in Linux, dove le code sono + implementate come file su un filesystem dedicato, è esattamente la stessa + funzione.} dopo la sua esecuzione il processo non sarà più in grado di usare +il descrittore della coda, ma quest'ultima continuerà ad esistere nel sistema +e potrà essere acceduta con un'altra chiamata a \func{mq\_open}. All'uscita di +un processo tutte le code aperte, così come i file, vengono chiuse +automaticamente. Inoltre se il processo aveva agganciato una richiesta di +notifica sul descrittore che viene chiuso, questa sarà rilasciata e potrà +essere richiesta da qualche altro processo. + + +Quando si vuole effettivamente rimuovere una coda dal sistema occorre usare la +funzione \funcd{mq\_unlink}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_unlink(const char *name)} + +Rimuove una coda di messaggi. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori riportati da + \func{unlink}.} +\end{prototype} + +Anche in questo caso il comportamento della funzione è analogo a quello di +\func{unlink} per i file,\footnote{di nuovo l'implementazione di Linux usa + direttamente \func{unlink}.} la funzione rimuove la coda \param{name}, così +che una successiva chiamata a \func{mq\_open} fallisce o crea una coda +diversa. + +Come per i file ogni coda di messaggi ha un contatore di riferimenti, per cui +la coda non viene effettivamente rimossa dal sistema fin quando questo non si +annulla. Pertanto anche dopo aver eseguito con successo \func{mq\_unlink} la +coda resterà accessibile a tutti i processi che hanno un descrittore aperto su +di essa. Allo stesso modo una coda ed i suoi contenuti resteranno disponibili +all'interno del sistema anche quando quest'ultima non è aperta da nessun +processo (questa è una delle differenze più rilevanti nei confronti di +\textit{pipe} e \textit{fifo}). La sola differenza fra code di messaggi POSIX +e file normali è che, essendo il filesystem delle code di messaggi virtuale e +basato su oggetti interni al kernel, il suo contenuto viene perduto con il +riavvio del sistema. + +Come accennato ad ogni coda di messaggi è associata una struttura +\struct{mq\_attr}, che può essere letta e modificata attraverso le due +funzioni \funcd{mq\_getattr} e \funcd{mq\_setattr}, i cui prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{int mq\_getattr(mqd\_t mqdes, struct mq\_attr *mqstat)} + Legge gli attributi di una coda di messaggi POSIX. + + \funcdecl{int mq\_setattr(mqd\_t mqdes, const struct mq\_attr *mqstat, + struct mq\_attr *omqstat)} + Modifica gli attributi di una coda di messaggi POSIX. + + \bodydesc{Entrambe le funzioni restituiscono 0 in caso di successo e -1 in + caso di errore; nel quel caso \var{errno} assumerà i valori \errval{EBADF} + o \errval{EINVAL}.} +\end{functions} + +La funzione \func{mq\_getattr} legge i valori correnti degli attributi della +coda nella struttura puntata da \param{mqstat}; di questi l'unico relativo +allo stato corrente della coda è \var{mq\_curmsgs} che indica il numero di +messaggi da essa contenuti, gli altri indicano le caratteristiche generali +della stessa. + +La funzione \func{mq\_setattr} permette di modificare gli attributi di una +coda tramite i valori contenuti nella struttura puntata da \param{mqstat}, ma +può essere modificato solo il campo \var{mq\_flags}, gli altri campi vengono +ignorati. In particolare i valori di \var{mq\_maxmsg} e \var{mq\_msgsize} +possono essere specificati solo in fase ci creazione della coda. Inoltre i +soli valori possibili per \var{mq\_flags} sono 0 e \const{O\_NONBLOCK}, per +cui alla fine la funzione può essere utilizzata solo per abilitare o +disabilitare la modalità non bloccante. L'argomento \param{omqstat} viene +usato, quando diverso da \val{NULL}, per specificare l'indirizzo di una +struttura su cui salvare i valori degli attributi precedenti alla chiamata +della funzione. + +Per inserire messaggi su di una coda sono previste due funzioni, +\funcd{mq\_send} e \funcd{mq\_timedsend}, i cui prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{int mq\_send(mqd\_t mqdes, const char *msg\_ptr, size\_t msg\_len, + unsigned int msg\_prio)} + Esegue l'inserimento di un messaggio su una coda. + + \funcdecl{int mq\_timedsend(mqd\_t mqdes, const char *msg\_ptr, size\_t + msg\_len, unsigned msg\_prio, const struct timespec *abs\_timeout)} + Esegue l'inserimento di un messaggio su una coda entro il tempo + \param{abs\_timeout}. + + + \bodydesc{Le funzioni restituiscono 0 in caso di successo e $-1$ per un + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la + coda è piena. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio \param{msg\_len} + eccede il limite impostato per la coda. + \item[\errcode{EINVAL}] si è specificato un valore nullo per + \param{msg\_len}, o un valore di \param{msg\_prio} fuori dai limiti, o + un valore non valido per \param{abs\_timeout}. + \item[\errcode{ETIMEDOUT}] l'inserimento del messaggio non è stato + effettuato entro il tempo stabilito. + \end{errlist} + ed inoltre \errval{EBADF}, \errval{ENOMEM} ed \errval{EINTR}.} +\end{functions} + +Entrambe le funzioni richiedono un puntatore al testo del messaggio +nell'argomento \param{msg\_ptr} e la relativa lunghezza in \param{msg\_len}. +Se quest'ultima eccede la dimensione massima specificata da \var{mq\_msgsize} +le funzioni ritornano immediatamente con un errore di \errcode{EMSGSIZE}. + +L'argomento \param{msg\_prio} indica la priorità dell'argomento; i messaggi di +priorità maggiore vengono inseriti davanti a quelli di priorità inferiore (e +quindi saranno riletti per primi). A parità del valore della priorità il +messaggio sarà inserito in coda a tutti quelli con la stessa priorità. Il +valore della priorità non può eccedere il limite di sistema +\const{MQ\_PRIO\_MAX}, che nel caso è pari a 32768. + +Qualora la coda sia piena, entrambe le funzioni si bloccano, a meno che non +sia stata selezionata in fase di apertura la modalità non +bloccante,\footnote{o si sia impostato il flag \const{O\_NONBLOCK} sul file + descriptor della coda.} nel qual caso entrambe ritornano \errcode{EAGAIN}. +La sola differenza fra le due funzioni è che la seconda, passato il tempo +massimo impostato con l'argomento \param{abs\_timeout},\footnote{deve essere + specificato un tempo assoluto tramite una struttura \struct{timespec} (vedi + fig.~\ref{fig:sys_timespec_struct}) indicato in numero di secondi e + nanosecondi a partire dal 1 gennaio 1970.} ritorna comunque con un errore di +\errcode{ETIMEDOUT}, se invece il tempo è già scaduto al momento della +chiamata e la coda è vuota la funzione ritorna immediatamente. + +Come per l'inserimento, anche per l'estrazione dei messaggi da una coda sono +previste due funzioni, \funcd{mq\_receive} e \funcd{mq\_timedreceive}, i cui +prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{ssize\_t mq\_receive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio)} + Effettua la ricezione di un messaggio da una coda. + + \funcdecl{ssize\_t mq\_timedreceive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio, const struct timespec *abs\_timeout)} + Effettua la ricezione di un messaggio da una coda entro il tempo + \param{abs\_timeout}. + + \bodydesc{Le funzioni restituiscono il numero di byte del messaggio in caso + di successo e -1 in caso di errore; nel quel caso \var{errno} assumerà i + valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la + coda è vuota. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio sulla coda eccede il + valore \param{msg\_len} specificato per la ricezione. + \item[\errcode{EINVAL}] si è specificato un valore nullo per + \param{msg\_ptr}, o un valore non valido per \param{abs\_timeout}. + \item[\errcode{ETIMEDOUT}] la ricezione del messaggio non è stata + effettuata entro il tempo stabilito. + \end{errlist} + ed inoltre \errval{EBADF}, \errval{EINTR}, \errval{ENOMEM}, o + \errval{EINVAL}.} +\end{functions} + +La funzione estrae dalla coda il messaggio a priorità più alta, o il più +vecchio fra quelli della stessa priorità. Una volta ricevuto il messaggio +viene tolto dalla coda e la sua dimensione viene restituita come valore di +ritorno.\footnote{si tenga presente che 0 è una dimensione valida e che la + condizione di errore è restituita dal valore -1; Stevens in \cite{UNP2} fa + notare che questo è uno dei casi in cui vale ciò che lo standard + \textsl{non} dice, una dimensione nulla infatti, pur non essendo citata, non + viene proibita.} + +Se la dimensione specificata da \param{msg\_len} non è sufficiente a contenere +il messaggio, entrambe le funzioni, al contrario di quanto avveniva nelle code +di messaggi di SysV, ritornano un errore di \errcode{EMSGSIZE} senza estrarre +il messaggio. È pertanto opportuno eseguire sempre una chiamata a +\func{mq\_getattr} prima di eseguire una ricezione, in modo da ottenere la +dimensione massima dei messaggi sulla coda, per poter essere in grado di +allocare dei buffer sufficientemente ampi per la lettura. + +Se si specifica un puntatore per l'argomento \param{msg\_prio} il valore della +priorità del messaggio viene memorizzato all'indirizzo da esso indicato. +Qualora non interessi usare la priorità dei messaggi si può specificare +\var{NULL}, ed usare un valore nullo della priorità nelle chiamate a +\func{mq\_send}. + +Si noti che con le code di messaggi POSIX non si ha la possibilità di +selezionare quale messaggio estrarre con delle condizioni sulla priorità, a +differenza di quanto avveniva con le code di messaggi di SysV che permettono +invece la selezione in base al valore del campo \var{mtype}. + +% TODO inserire i dati di /proc/sys/fs/mqueue + +Qualora la coda sia vuota entrambe le funzioni si bloccano, a meno che non si +sia selezionata la modalità non bloccante; in tal caso entrambe ritornano +immediatamente con l'errore \errcode{EAGAIN}. Anche in questo caso la sola +differenza fra le due funzioni è che la seconda non attende indefinitamente e +passato il tempo massimo \param{abs\_timeout} ritorna comunque con un errore +di \errcode{ETIMEDOUT}. + +Uno dei problemi sottolineati da Stevens in \cite{UNP2}, comuni ad entrambe le +tipologie di code messaggi, è che non è possibile per chi riceve identificare +chi è che ha inviato il messaggio, in particolare non è possibile sapere da +quale utente esso provenga. Infatti, in mancanza di un meccanismo interno al +kernel, anche se si possono inserire delle informazioni nel messaggio, queste +non possono essere credute, essendo completamente dipendenti da chi lo invia. +Vedremo però come, attraverso l'uso del meccanismo di notifica, sia possibile +superare in parte questo problema. + +Una caratteristica specifica delle code di messaggi POSIX è la possibilità di +usufruire di un meccanismo di notifica asincrono; questo può essere attivato +usando la funzione \funcd{mq\_notify}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_notify(mqd\_t mqdes, const struct sigevent *notification)} + +Attiva il meccanismo di notifica per la coda \param{mqdes}. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EBUSY}] c'è già un processo registrato per la notifica. + \item[\errcode{EBADF}] il descrittore non fa riferimento ad una coda di + messaggi. + \end{errlist}} +\end{prototype} + +Il meccanismo di notifica permette di segnalare in maniera asincrona ad un +processo la presenza di dati sulla coda, in modo da evitare la necessità di +bloccarsi nell'attesa. Per far questo un processo deve registrarsi con la +funzione \func{mq\_notify}, ed il meccanismo è disponibile per un solo +processo alla volta per ciascuna coda. + +Il comportamento di \func{mq\_notify} dipende dal valore dell'argomento +\param{notification}, che è un puntatore ad una apposita struttura +\struct{sigevent}, (definita in fig.~\ref{fig:struct_sigevent}) introdotta +dallo standard POSIX.1b per gestire la notifica di eventi; per altri dettagli +si può vedere quanto detto in sez.~\ref{sec:sig_timer_adv} a proposito +dell'uso della stessa struttura per la notifica delle scadenze dei +\textit{timer}. + +Attraverso questa struttura si possono impostare le modalità con cui viene +effettuata la notifica nel campo \var{sigev\_notify}, che può assumere i +valori di tab.~\ref{tab:sigevent_sigev_notify}.\footnote{la pagina di manuale + riporta soltanto i primi tre (inizialmente era possibile solo + \const{SIGEV\_SIGNAL}).} Il metodo consigliato è quello di usare +\const{SIGEV\_SIGNAL} usando il campo \var{sigev\_signo} per indicare il quale +segnale deve essere inviato al processo. Inoltre il campo \var{sigev\_value} è +un puntatore ad una struttura \struct{sigval} (definita in +fig.~\ref{fig:sig_sigval}) che permette di restituire al gestore del segnale +un valore numerico o un indirizzo,\footnote{per il suo uso si riveda la + trattazione fatta in sez.~\ref{sec:sig_real_time} a proposito dei segnali + \textit{real-time}.} posto che questo sia installato nella forma estesa +vista in sez.~\ref{sec:sig_sigaction}. + +La funzione registra il processo chiamante per la notifica se +\param{notification} punta ad una struttura \struct{sigevent} opportunamente +inizializzata, o cancella una precedente registrazione se è \val{NULL}. Dato +che un solo processo alla volta può essere registrato, la funzione fallisce +con \errcode{EBUSY} se c'è un altro processo già registrato.\footnote{questo + significa anche che se si registra una notifica con \const{SIGEV\_NONE} il + processo non la riceverà, ma impedirà anche che altri possano registrarsi + per poterlo fare.} Si tenga presente inoltre che alla chiusura del +descrittore associato alla coda (e quindi anche all'uscita del processo) ogni +eventuale registrazione di notifica presente viene cancellata. + +La notifica del segnale avviene all'arrivo di un messaggio in una coda vuota +(cioè solo se sulla coda non ci sono messaggi) e se non c'è nessun processo +bloccato in una chiamata a \func{mq\_receive}, in questo caso infatti il +processo bloccato ha la precedenza ed il messaggio gli viene immediatamente +inviato, mentre per il meccanismo di notifica tutto funziona come se la coda +fosse rimasta vuota. + +Quando un messaggio arriva su una coda vuota al processo che si era registrato +viene inviato il segnale specificato da \code{notification->sigev\_signo}, e +la coda diventa disponibile per una ulteriore registrazione. Questo comporta +che se si vuole mantenere il meccanismo di notifica occorre ripetere la +registrazione chiamando nuovamente \func{mq\_notify} all'interno del gestore +del segnale di notifica. A differenza della situazione simile che si aveva con +i segnali non affidabili,\footnote{l'argomento è stato affrontato in + \ref{sec:sig_semantics}.} questa caratteristica non configura una +\itindex{race~condition} \textit{race condition} perché l'invio di un segnale +avviene solo se la coda è vuota; pertanto se si vuole evitare di correre il +rischio di perdere eventuali ulteriori segnali inviati nel lasso di tempo che +occorre per ripetere la richiesta di notifica basta avere cura di eseguire +questa operazione prima di estrarre i messaggi presenti dalla coda. + +L'invio del segnale di notifica avvalora alcuni campi di informazione +restituiti al gestore attraverso la struttura \struct{siginfo\_t} (definita in +fig.~\ref{fig:sig_siginfo_t}). In particolare \var{si\_pid} viene impostato al +valore del \ids{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 sez.~\ref{sec:sig_sigaction} e sez.~\ref{sec:sig_real_time}.} + + + +\subsection{Memoria condivisa} +\label{sec:ipc_posix_shm} + +La memoria condivisa è stato il primo degli oggetti di IPC POSIX inserito nel +kernel ufficiale; il supporto a questo tipo di oggetti è realizzato attraverso +il filesystem \texttt{tmpfs}, uno speciale filesystem che mantiene tutti i +suoi contenuti in memoria, che viene attivato abilitando l'opzione +\texttt{CONFIG\_TMPFS} in fase di compilazione del kernel. + +Per potere utilizzare l'interfaccia POSIX per la memoria condivisa la +\acr{glibc}\footnote{le funzioni sono state introdotte con la versione 2.2.} +richiede 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 fatto aggiungendo una riga del tipo di: +\begin{verbatim} +tmpfs /dev/shm tmpfs defaults 0 0 +\end{verbatim} +ad \conffile{/etc/fstab}. In realtà si può montare un filesystem \texttt{tmpfs} +dove si vuole, per usarlo come RAM disk, con un comando del tipo: +\begin{verbatim} +mount -t tmpfs -o size=128M,nr_inodes=10k,mode=700 tmpfs /mytmpfs +\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{functions} + \headdecl{sys/mman.h} + \headdecl{sys/stat.h} + \headdecl{fcntl.h} + + \funcdecl{int shm\_open(const char *name, int oflag, mode\_t mode)} + + Apre un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce un file descriptor positivo in caso di + successo e -1 in caso di errore; nel quel caso \var{errno} assumerà gli + stessi valori riportati da \func{open}.} +\end{functions} + +La funzione apre un segmento di memoria condivisa identificato dal nome +\param{name}. Come già spiegato in sez.~\ref{sec:ipc_posix_generic} questo +nome può essere specificato in forma standard solo facendolo iniziare per +``\file{/}'' e senza ulteriori ``\file{/}''. Linux supporta comunque nomi +generici, che verranno interpretati prendendo come radice +\file{/dev/shm}.\footnote{occorre pertanto evitare di specificare qualcosa del + tipo \file{/dev/shm/nome} all'interno di \param{name}, perché questo + comporta, da parte delle funzioni di libreria, il tentativo di accedere a + \file{/dev/shm/dev/shm/nome}.} + +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 +sez.~\ref{sec:file_open_close} 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_close}; 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 +\itindex{inode} inode). In questo modo è possibile effettuare una chiamata ad +\func{mmap} sul file descriptor restituito da \func{shm\_open} ed i processi +vedranno lo stesso segmento di memoria condivisa. + +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}{sys/mman.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}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \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 \textit{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}, +restituendo al chiamante il valore di ritorno. + + + + +\subsection{Semafori} +\label{sec:ipc_posix_sem} + +Fino alla serie 2.4.x del kernel esisteva solo una implementazione parziale +dei semafori POSIX che li realizzava solo a livello di \itindex{thread} +\textit{thread} e non di processi,\footnote{questo significava che i semafori + erano visibili solo all'interno dei \itindex{thread} \textit{thread} creati + da un singolo processo, e non potevano essere usati come meccanismo di + sincronizzazione fra processi diversi.} fornita attraverso la sezione delle +estensioni \textit{real-time} della \acr{glibc}.\footnote{quelle che si + accedono collegandosi alla libreria \texttt{librt}.} Esisteva inoltre una +libreria che realizzava (parzialmente) l'interfaccia POSIX usando le funzioni +dei semafori di \textit{SysV-IPC} (mantenendo così tutti i problemi +sottolineati in sez.~\ref{sec:ipc_sysv_sem}). + +A partire dal kernel 2.5.7 è stato introdotto un meccanismo di +sincronizzazione completamente nuovo, basato sui cosiddetti +\textit{futex},\footnote{la sigla sta per \textit{fast user mode mutex}.} con +il quale è stato possibile implementare una versione nativa dei semafori +POSIX. Grazie a questo con i kernel della serie 2.6 e le nuove versioni della +\acr{glibc} che usano questa nuova infrastruttura per quella che viene quella +che viene chiamata \textit{New Posix Thread Library}, sono state implementate +anche tutte le funzioni dell'interfaccia dei semafori POSIX. + +Anche in questo caso è necessario appoggiarsi alla libreria per le estensioni +\textit{real-time} \texttt{librt}, questo significa che se si vuole utilizzare +questa interfaccia, oltre ad utilizzare gli opportuni file di definizione, +occorrerà compilare i programmi con l'opzione \texttt{-lrt}. + +La funzione che permette di creare un nuovo semaforo POSIX, creando il +relativo file, o di accedere ad uno esistente, è \funcd{sem\_open}, questa +prevede due forme diverse a seconda che sia utilizzata per aprire un semaforo +esistente o per crearne uno nuovi, i relativi prototipi sono: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{sem\_t *sem\_open(const char *name, int oflag)} + + \funcdecl{sem\_t *sem\_open(const char *name, int oflag, mode\_t mode, + unsigned int value)} + + Crea un semaforo o ne apre uno esistente. + + \bodydesc{La funzione restituisce l'indirizzo del semaforo in caso di + successo e \const{SEM\_FAILED} in caso di errore; nel quel caso + \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCES}] il semaforo esiste ma non si hanno permessi + sufficienti per accedervi. + \item[\errcode{EEXIST}] si sono specificati \const{O\_CREAT} e + \const{O\_EXCL} ma il semaforo esiste. + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENAMETOOLONG}] si è utilizzato un nome troppo lungo. + \item[\errcode{ENOENT}] non si è usato \const{O\_CREAT} ed il nome + specificato non esiste. + \end{errlist} + ed inoltre \errval{ENFILE} ed \errval{ENOMEM}.} +\end{functions} + +L'argomento \param{name} definisce il nome del semaforo che si vuole +utilizzare, ed è quello che permette a processi diversi di accedere allo +stesso semaforo. Questo deve essere specificato con un \textit{pathname} nella +forma \texttt{/qualchenome}, che non ha una corrispondenza diretta con un +\textit{pathname} reale; con Linux infatti i file associati ai semafori sono +mantenuti nel filesystem virtuale \texttt{/dev/shm}, e gli viene assegnato +automaticamente un nome nella forma \texttt{sem.qualchenome}.\footnote{si ha + cioè una corrispondenza per cui \texttt{/qualchenome} viene rimappato, nella + creazione tramite \func{sem\_open}, su \texttt{/dev/shm/sem.qualchenome}.} + +L'argomento \param{oflag} è quello che controlla le modalità con cui opera la +funzione, ed è passato come maschera binaria; i bit corrispondono a quelli +utilizzati per l'analogo argomento di \func{open}, anche se dei possibili +valori visti in sez.~\ref{sec:file_open_close} sono utilizzati soltanto +\const{O\_CREAT} e \const{O\_EXCL}. + +Se si usa \const{O\_CREAT} si richiede la creazione del semaforo qualora +questo non esista, ed in tal caso occorre utilizzare la seconda forma della +funzione, in cui si devono specificare sia un valore iniziale con l'argomento +\param{value},\footnote{e si noti come così diventa possibile, differenza di + quanto avviene per i semafori del \textit{SysV-IPC}, effettuare in maniera + atomica creazione ed inizializzazione di un semaforo usando una unica + funzione.} che una maschera dei permessi con l'argomento +\param{mode};\footnote{anche questo argomento prende gli stessi valori + utilizzati per l'analogo di \func{open}, che si sono illustrati in dettaglio + sez.~\ref{sec:file_perm_overview}.} questi verranno assegnati al semaforo +appena creato. Se il semaforo esiste già i suddetti valori saranno invece +ignorati. Usando il flag \const{O\_EXCL} si richiede invece la verifica che il +semaforo non esiste, usandolo insieme ad \const{O\_CREAT} la funzione fallisce +qualora un semaforo con lo stesso nome sia già presente. + +La funzione restituisce in caso di successo un puntatore all'indirizzo del +semaforo con un valore di tipo \ctyp{sem\_t *}, è questo valore che dovrà +essere passato alle altre funzioni per operare sul semaforo stesso. Si tenga +presente che, come accennato in sez.~\ref{sec:ipc_posix_generic}, i semafori +usano la semantica standard dei file per quanto riguarda i controlli di +accesso. + +Questo significa che un nuovo semaforo viene sempre creato con l'\ids{UID} ed +il \ids{GID} effettivo del processo chiamante, e che i permessi indicati con +\param{mode} vengono filtrati dal valore della \itindex{umask} \textit{umask} +del processo. Inoltre per poter aprire un semaforo è necessario avere su di +esso sia il permesso di lettura che quello di scrittura. + +Una volta che si sia ottenuto l'indirizzo di un semaforo, sarà possibile +utilizzarlo; se si ricorda quanto detto all'inizio di +sez.~\ref{sec:ipc_sysv_sem}, dove si sono introdotti i concetti generali +relativi ai semafori, le operazioni principali sono due, quella che richiede +l'uso di una risorsa bloccando il semaforo e quella che rilascia la risorsa +liberando il semaforo. La prima operazione è effettuata dalla funzione +\funcd{sem\_wait}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_wait(sem\_t *sem)} + + Blocca il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione cerca di decrementare il valore del semaforo indicato dal +puntatore \param{sem}, se questo ha un valore positivo, cosa che significa che +la risorsa è disponibile, la funzione ha successo, il valore del semaforo +viene diminuito di 1 ed essa ritorna immediatamente; se il valore è nullo la +funzione si blocca fintanto che il valore del semaforo non torni +positivo\footnote{ovviamente per opera di altro processo che lo rilascia + chiamando \func{sem\_post}.} così che poi essa possa decrementarlo con +successo e proseguire. + +Si tenga presente che la funzione può sempre essere interrotta da un segnale +(nel qual caso si avrà un errore di \const{EINTR}) e che questo avverrà +comunque, anche se si è richiesta la semantica BSD installando il relativo +gestore con \const{SA\_RESTART} (vedi sez.~\ref{sec:sig_sigaction}) per +riavviare le system call interrotte. + +Della funzione \func{sem\_wait} esistono due varianti che consentono di +gestire diversamente le modalità di attesa in caso di risorsa occupata, la +prima di queste è \funcd{sem\_trywait}, che serve ad effettuare un tentativo +di acquisizione senza bloccarsi; il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_trywait(sem\_t *sem)} + + Tenta di bloccare il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori: + \begin{errlist} + \item[\errcode{EAGAIN}] il semaforo non può essere acquisito senza + bloccarsi. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione è identica a \func{sem\_wait} ed se la risorsa è libera ha lo +stesso effetto, vale a dire che in caso di semaforo diverso da zero la +funzione lo decrementa e ritorna immediatamente; la differenza è che nel caso +in cui il semaforo è occupato essa non si blocca e di nuovo ritorna +immediatamente, restituendo però un errore di \errval{EAGAIN}, così che il +programma possa proseguire. + +La seconda variante di \func{sem\_wait} è una estensione specifica che può +essere utilizzata soltanto se viene definita la macro \macro{\_XOPEN\_SOURCE} +ad un valore di 600 prima di includere \headfile{semaphore.h}, la funzione è +\funcd{sem\_timedwait}, ed il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_timedwait(sem\_t *sem, const struct timespec + *abs\_timeout)} + + Blocca il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori: + \begin{errlist} + \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \end{errlist} +} +\end{functions} + +Anche in questo caso il comportamento della funzione è identico a quello di +\func{sem\_wait}, la sola differenza consiste nel fatto che con questa +funzione è possibile impostare tramite l'argomento \param{abs\_timeout} un +tempo limite per l'attesa, scaduto il quale la funzione ritorna comunque, +anche se non è possibile acquisire il semaforo. In tal caso la funzione +fallirà, riportando un errore di \errval{ETIMEDOUT}. + +La seconda funzione principale utilizzata per l'uso dei semafori è +\funcd{sem\_post}, che viene usata per rilasciare un semaforo occupato o, in +generale, per aumentare di una unità il valore dello stesso anche qualora non +fosse occupato;\footnote{si ricordi che in generale un semaforo viene usato + come indicatore di un numero di risorse disponibili.} il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_post(sem\_t *sem)} + + Rilascia il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione incrementa di uno il valore corrente del semaforo indicato +dall'argomento \param{sem}, se questo era nullo la relativa risorsa risulterà +sbloccata, cosicché un altro processo (o \itindex{thread} \textit{thread}) +eventualmente bloccato in una \func{sem\_wait} sul semaforo potrà essere +svegliato e rimesso in esecuzione. Si tenga presente che la funzione è sicura +\index{funzioni!sicure} per l'uso all'interno di un gestore di segnali (si +ricordi quanto detto in sez.~\ref{sec:sig_signal_handler}). + +Se invece di operare su un semaforo se ne vuole solamente leggere il valore, +si può usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_getvalue(sem\_t *sem, int *sval)} + + Richiede il valore del semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione legge il valore del semaforo indicato dall'argomento \param{sem} e +lo restituisce nella variabile intera puntata dall'argomento +\param{sval}. Qualora ci siano uno o più processi bloccati in attesa sul +semaforo lo standard prevede che la funzione possa restituire un valore nullo +oppure il numero di processi bloccati in una \func{sem\_wait} sul suddetto +semaforo; nel caso di Linux vale la prima opzione. + +Questa funzione può essere utilizzata per avere un suggerimento sullo stato di +un semaforo, ovviamente non si può prendere il risultato riportato in +\param{sval} che come indicazione, il valore del semaforo infatti potrebbe +essere già stato modificato al ritorno della funzione. + +% TODO verificare comportamento sem_getvalue + +Una volta che non ci sia più la necessità di operare su un semaforo se ne può +terminare l'uso con la funzione \funcd{sem\_close}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_close(sem\_t *sem)} + + Chiude il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione chiude il semaforo indicato dall'argomento \param{sem}; questo +comporta che tutte le risorse che il sistema può avere assegnato al processo +nell'uso dello stesso vengono rilasciate. Questo significa che un altro +processo bloccato sul semaforo a causa della acquisizione da parte del +processo che chiama \func{sem\_close} potrà essere riavviato. + +Si tenga presente poi che come per i file all'uscita di un processo tutti i +semafori che questo aveva aperto vengono automaticamente chiusi; questo +comportamento risolve il problema che si aveva con i semafori del \textit{SysV + IPC} (di cui si è parlato in sez.~\ref{sec:ipc_sysv_sem}) per i quali le +risorse possono restare bloccate. Si tenga poi presente che, a differenza di +quanto avviene per i file, in caso di una chiamata ad \func{execve} tutti i +semafori vengono chiusi automaticamente. + +Come per i semafori del \textit{SysV-IPC} anche quelli POSIX hanno una +persistenza di sistema; questo significa che una volta che si è creato un +semaforo con \func{sem\_open} questo continuerà ad esistere fintanto che il +kernel resta attivo (vale a dire fino ad un successivo riavvio) a meno che non +lo si cancelli esplicitamente. Per far questo si può utilizzare la funzione +\funcd{sem\_unlink}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_unlink(const char *name)} + + Rimuove il semaforo \param{name}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i permessi necessari a cancellare il + semaforo. + \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. + \item[\errcode{ENOENT}] il semaforo \param{name} non esiste. + \end{errlist} +} +\end{functions} + +La funzione rimuove il semaforo indicato dall'argomento \param{name}, che +prende un valore identico a quello usato per creare il semaforo stesso con +\func{sem\_open}. Il semaforo viene rimosso dal filesystem immediatamente; ma +il semaforo viene effettivamente cancellato dal sistema soltanto quando tutti +i processi che lo avevano aperto lo chiudono. Si segue cioè la stessa +semantica usata con \func{unlink} per i file, trattata in dettaglio in +sez.~\ref{sec:link_symlink_rename}. + +Una delle caratteristiche peculiari dei semafori POSIX è che questi possono +anche essere utilizzati anche in forma anonima, senza necessità di fare +ricorso ad un nome sul filesystem o ad altri indicativi. In questo caso si +dovrà porre la variabile che contiene l'indirizzo del semaforo in un tratto di +memoria che sia accessibile a tutti i processi in gioco. La funzione che +consente di inizializzare un semaforo anonimo è \funcd{sem\_init}, il cui +prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} + + Inizializza il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENOSYS}] il valore di \param{pshared} non è nullo ed il + sistema non supporta i semafori per i processi. + \end{errlist} +} +\end{functions} + +La funzione inizializza un semaforo all'indirizzo puntato dall'argomento +\param{sem}, e come per \func{sem\_open} consente di impostare un valore +iniziale con \param{value}. L'argomento \param{pshared} serve ad indicare se +il semaforo deve essere utilizzato dai \itindex{thread} \textit{thread} di uno +stesso processo (con un valore nullo) o condiviso fra processi diversi (con un +valore non nullo). + +Qualora il semaforo debba essere condiviso dai \itindex{thread} +\textit{thread} di uno stesso processo (nel qual caso si parla di +\textit{thread-shared semaphore}), occorrerà che \param{sem} sia l'indirizzo +di una variabile visibile da tutti i \itindex{thread} \textit{thread}, si +dovrà usare cioè una \index{variabili!globali} variabile globale o una +variabile allocata dinamicamente nello \itindex{heap} \textit{heap}. + +Qualora il semaforo debba essere condiviso fra più processi (nel qual caso si +parla di \textit{process-shared semaphore}) la sola scelta possibile per +renderlo visibile a tutti è di porlo in un tratto di memoria condivisa. Questo +potrà essere ottenuto direttamente sia con \func{shmget} (vedi +sez.~\ref{sec:ipc_sysv_shm}) che con \func{shm\_open} (vedi +sez.~\ref{sec:ipc_posix_shm}), oppure, nel caso che tutti i processi in gioco +abbiano un genitore comune, con una mappatura anonima con \func{mmap} (vedi +sez.~\ref{sec:file_memory_map}),\footnote{si ricordi che i tratti di memoria + condivisa vengono mantenuti nei processi figli attraverso la funzione + \func{fork}.} a cui essi poi potranno accedere. + +Una volta inizializzato il semaforo anonimo con \func{sem\_init} lo si potrà +utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e +\func{sem\_post}. Si tenga presente però che inizializzare due volte lo stesso +semaforo può dar luogo ad un comportamento indefinito. + +Una volta che non si intenda più utilizzare un semaforo anonimo questo può +essere eliminato dal sistema; per far questo di deve utilizzare una apposita +funzione, \funcd{sem\_destroy}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_destroy(sem\_t *sem)} + + Elimina il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \end{errlist} +} +\end{functions} + +La funzione prende come unico argomento l'indirizzo di un semaforo che deve +essere stato inizializzato con \func{sem\_init}; non deve quindi essere +applicata a semafori creati con \func{sem\_open}. Inoltre si deve essere +sicuri che il semaforo sia effettivamente inutilizzato, la distruzione di un +semaforo su cui sono presenti processi (o \itindex{thread} \textit{thread}) in +attesa (cioè bloccati in una \func{sem\_wait}) provoca un comportamento +indefinito. + +Si tenga presente infine che utilizzare un semaforo che è stato distrutto con +\func{sem\_destroy} di nuovo può dare esito a comportamenti indefiniti. Nel +caso ci si trovi in una tale evenienza occorre reinizializzare il semaforo una +seconda volta con \func{sem\_init}. + +Come esempio di uso sia della memoria condivisa che dei semafori POSIX si sono +scritti due semplici programmi con i quali è possibile rispettivamente +monitorare il contenuto di un segmento di memoria condivisa e modificarne il +contenuto. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/message_getter.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del programma + \file{message\_getter.c}.} + \label{fig:ipc_posix_sem_shm_message_server} +\end{figure} + +Il corpo principale del primo dei due, il cui codice completo è nel file +\file{message\_getter.c} dei sorgenti allegati, è riportato in +fig.~\ref{fig:ipc_posix_sem_shm_message_server}; si è tralasciata la parte che +tratta la gestione delle opzioni a riga di comando (che consentono di +impostare un nome diverso per il semaforo e il segmento di memoria condivisa) +ed il controllo che al programma venga fornito almeno un argomento, contenente +la stringa iniziale da inserire nel segmento di memoria condivisa. + +Lo scopo del programma è quello di creare un segmento di memoria condivisa su +cui registrare una stringa, e tenerlo sotto osservazione stampando la stessa +una volta al secondo. Si utilizzerà un semaforo per proteggere l'accesso in +lettura alla stringa, in modo che questa non possa essere modificata +dall'altro programma prima di averla finita di stampare. + +La parte iniziale del programma contiene le definizioni (\texttt{\small 1--8}) +del gestore del segnale usato per liberare le risorse utilizzate, delle +\index{variabili!globali} variabili globali contenenti i nomi di default del +segmento di memoria condivisa e del semaforo (il default scelto è +\texttt{messages}), e delle altre variabili utilizzate dal programma. + +Come prima istruzione (\texttt{\small 10}) si è provveduto ad installare un +gestore di segnale che consentirà di effettuare le operazioni di pulizia +(usando la funzione \func{Signal} illustrata in +fig.~\ref{fig:sig_Signal_code}), dopo di che (\texttt{\small 10--16}) si è +creato il segmento di memoria condivisa con la funzione \func{CreateShm} che +abbiamo appena trattato in sez.~\ref{sec:ipc_posix_shm}, uscendo con un +messaggio in caso di errore. + +Si tenga presente che la funzione \func{CreateShm} richiede che il segmento +non sia già presente e fallirà qualora un'altra istanza, o un altro programma +abbia già allocato un segmento con quello stesso nome. Per semplicità di +gestione si è usata una dimensione fissa pari a 256 byte, definita tramite la +costante \texttt{MSGMAXSIZE}. + +Il passo successivo (\texttt{\small 17--21}) è quello della creazione del +semaforo che regola l'accesso al segmento di memoria condivisa con +\func{sem\_open}; anche in questo caso si gestisce l'uscita con stampa di un +messaggio in caso di errore. Anche per il semaforo, avendo specificato la +combinazione di flag \code{O\_CREAT|O\_EXCL} come secondo argomento, si esce +qualora fosse già esistente; altrimenti esso verrà creato con gli opportuni +permessi specificati dal terzo argomento, (indicante lettura e scrittura in +notazione ottale). Infine il semaforo verrà inizializzato ad un valore nullo +(il quarto argomento), corrispondete allo stato in cui risulta bloccato. + +A questo punto (\texttt{\small 23}) si potrà inizializzare il messaggio posto +nel segmento di memoria condivisa usando la stringa passata come argomento al +programma. Essendo il semaforo stato creato già bloccato non ci si dovrà +preoccupare di eventuali \itindex{race~condition} \textit{race condition} +qualora il programma di modifica del messaggio venisse lanciato proprio in +questo momento. Una volta inizializzato il messaggio occorrerà però +rilasciare il semaforo (\texttt{\small 25--28}) per consentirne l'uso; in +tutte queste operazioni si provvederà ad uscire dal programma con un opportuno +messaggio in caso di errore. + +Una volta completate le inizializzazioni il ciclo principale del programma +(\texttt{\small 29--47}) viene ripetuto indefinitamente (\texttt{\small 29}) +per stampare sia il contenuto del messaggio che una serie di informazioni di +controllo. Il primo passo (\texttt{\small 30--34}) è quello di acquisire (con +\func{sem\_getvalue}, con uscita in caso di errore) e stampare il valore del +semaforo ad inizio del ciclo; seguito (\texttt{\small 35--36}) dal tempo +corrente. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/HandSigInt.c} + \end{minipage} + \normalsize + \caption{Codice del gestore di segnale del programma + \file{message\_getter.c}.} + \label{fig:ipc_posix_sem_shm_message_server_handler} +\end{figure} + +Prima della stampa del messaggio invece si deve acquisire il semaforo +(\texttt{\small 31--34}) per evitare accessi concorrenti alla stringa da parte +del programma di modifica. Una volta eseguita la stampa (\texttt{\small 41}) +il semaforo dovrà essere rilasciato (\texttt{\small 42--45}). Il passo finale +(\texttt{\small 46}) è attendere per un secondo prima di eseguire da capo il +ciclo. + +Per uscire in maniera corretta dal programma sarà necessario interromperlo con +il break da tastiera (\texttt{C-c}), che corrisponde all'invio del segnale +\signal{SIGINT}, per il quale si è installato (\texttt{\small 10}) una +opportuna funzione di gestione, riportata in +fig.~\ref{fig:ipc_posix_sem_shm_message_server_handler}. La funzione è molto +semplice e richiama le funzioni di rimozione sia per il segmento di memoria +condivisa che per il semaforo, garantendo così che possa essere riaperto +ex-novo senza errori in un futuro riutilizzo del comando. + +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/message_setter.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del programma + \file{message\_setter.c}.} + \label{fig:ipc_posix_sem_shm_message_setter} +\end{figure} + +Il secondo programma di esempio è \file{message\_setter.c}, di cui si è +riportato il corpo principale in +fig.~\ref{fig:ipc_posix_sem_shm_message_setter},\footnote{al solito il codice + completo è nel file dei sorgenti allegati.} dove si è tralasciata, non +essendo significativa per quanto si sta trattando, la parte relativa alla +gestione delle opzioni a riga di comando e degli argomenti, che sono identici +a quelli usati da \file{message\_getter}, con l'unica aggiunta di un'opzione +``\texttt{-t}'' che consente di indicare un tempo di attesa (in secondi) in +cui il programma si ferma tenendo bloccato il semaforo. + +Una volta completata la gestione delle opzioni e degli argomenti (ne deve +essere presente uno solo, contenente la nuova stringa da usare come +messaggio), il programma procede (\texttt{\small 10--14}) con l'acquisizione +del segmento di memoria condivisa usando la funzione \func{FindShm} (trattata +in sez.~\ref{sec:ipc_posix_shm}) che stavolta deve già esistere. Il passo +successivo (\texttt{\small 16--19}) è quello di aprire il semaforo, e a +differenza di \file{message\_getter}, in questo caso si richiede a +\func{sem\_open} che questo esista, passando uno zero come secondo ed unico +argomento. + +Una volta completate con successo le precedenti inizializzazioni, il passo +seguente (\texttt{\small 21--24}) è quello di acquisire il semaforo, dopo di +che sarà possibile eseguire la sostituzione del messaggio (\texttt{\small 25}) +senza incorrere in possibili \itindex{race~condition} \textit{race condition} +con la stampa dello stesso da parte di \file{message\_getter}. + +Una volta effettuata la modifica viene stampato (\texttt{\small 26}) il tempo +di attesa impostato con l'opzione ``\texttt{-t}'' dopo di che (\texttt{\small + 27}) viene eseguita la stessa, senza rilasciare il semaforo che resterà +quindi bloccato (causando a questo punto una interruzione delle stampe +eseguite da \file{message\_getter}). Terminato il tempo di attesa si rilascerà +(\texttt{\small 29--32}) il semaforo per poi uscire. + +Per verificare il funzionamento dei programmi occorrerà lanciare per primo +\file{message\_getter}\footnote{lanciare per primo \file{message\_setter} darà + luogo ad un errore, non essendo stati creati il semaforo ed il segmento di + memoria condivisa.} che inizierà a stampare una volta al secondo il +contenuto del messaggio ed i suoi dati, con qualcosa del tipo: +\begin{Console} +piccardi@hain:~/gapil/sources$ \textbf{./message_getter messaggio} +sem=1, Fri Dec 31 14:12:41 2010 +message: messaggio +sem=1, Fri Dec 31 14:12:42 2010 +message: messaggio +... +\end{Console} +%$ +proseguendo indefinitamente fintanto che non si prema \texttt{C-c} per farlo +uscire. Si noti come il valore del semaforo risulti sempre pari ad 1 (in +quanto al momento esso sarà sempre libero). + +A questo punto si potrà lanciare \file{message\_setter} per cambiare il +messaggio, nel nostro caso per rendere evidente il funzionamento del blocco +richiederemo anche una attesa di 3 secondi, ed otterremo qualcosa del tipo: +\begin{Console} +piccardi@hain:~/gapil/sources$ \textbf{./message_setter -t 3 ciao} +Sleeping for 3 seconds +\end{Console} +%$ +dove il programma si fermerà per 3 secondi prima di rilasciare il semaforo e +terminare. + +L'effetto di questo programma si potrà però apprezzare meglio nell'uscita di +\file{message\_getter}, che verrà interrotta per questo stesso tempo, prima di +ricominciare con il nuovo testo: +\begin{Console} +... +sem=1, Fri Dec 31 14:16:27 2010 +message: messaggio +sem=1, Fri Dec 31 14:16:28 2010 +message: messaggio +sem=0, Fri Dec 31 14:16:29 2010 +message: ciao +sem=1, Fri Dec 31 14:16:32 2010 +message: ciao +sem=1, Fri Dec 31 14:16:33 2010 +message: ciao +... +\end{Console} +%$ -Lo standard POSIX.1b ha introdotto dei nuovi meccanismi di comunicazione, -rifacendosi a quelli di System V, introducendo una nuova interfaccia che -evitasse i principali problemi evidenziati in ... +E si noterà come nel momento in cui si è lanciato \file{message\_setter} le +stampe di \file{message\_getter} si bloccheranno, come corretto, dopo aver +registrato un valore nullo per il semaforo. Il programma infatti resterà +bloccato nella \func{sem\_wait} (quella di riga (\texttt{\small 37}) in +fig.~\ref{fig:ipc_posix_sem_shm_message_server}) fino alla scadenza +dell'attesa di \file{message\_setter} (con l'esecuzione della \func{sem\_post} +della riga (\texttt{\small 29}) di +fig.~\ref{fig:ipc_posix_sem_shm_message_setter}), e riprenderanno con il nuovo +testo alla terminazione di quest'ultimo. +% LocalWords: like fifo System POSIX RPC Calls Common Object Request Brocker +% LocalWords: Architecture descriptor kernel unistd int filedes errno EMFILE +% LocalWords: ENFILE EFAULT BUF sez fig fork Stevens siblings EOF read SIGPIPE +% LocalWords: EPIPE shell CGI Gateway Interface HTML JPEG URL mime type gs dup +% LocalWords: barcode PostScript race condition stream BarCodePage WriteMess +% LocalWords: size PS switch wait popen pclose stdio const char command NULL +% LocalWords: EINVAL cap fully buffered Ghostscript l'Encapsulated epstopsf of +% LocalWords: PDF EPS lseek ESPIPE PPM Portable PixMap format pnmcrop PNG pnm +% LocalWords: pnmmargin png BarCode inode filesystem l'inode mknod mkfifo RDWR +% LocalWords: ENXIO deadlock client reinviate fortunes fortunefilename daemon +% LocalWords: FortuneServer FortuneParse FortuneClient pid libgapil LD librt +% LocalWords: PATH linker pathname ps tmp killall fortuned crash socket domain +% LocalWords: socketpair BSD sys protocol sv EAFNOSUPPORT EPROTONOSUPPORT AF +% LocalWords: EOPNOTSUPP SOCK Process Comunication ipc perm key exec +% LocalWords: header ftok proj stat libc SunOS glibc XPG dell'inode number uid +% LocalWords: cuid cgid gid tab MSG shift group umask seq MSGMNI SEMMNI SHMMNI +% LocalWords: shmmni msgmni sem sysctl IPCMNI IPCTestId msgget EACCES EEXIST +% LocalWords: CREAT EXCL EIDRM ENOENT ENOSPC ENOMEM novo proc MSGMAX msgmax ds +% LocalWords: MSGMNB msgmnb linked list msqid msgid linux msg qnum lspid lrpid +% LocalWords: rtime ctime qbytes first last cbytes msgctl semctl shmctl ioctl +% LocalWords: cmd struct buf EPERM RMID msgsnd msgbuf msgp msgsz msgflg EAGAIN +% LocalWords: NOWAIT EINTR mtype mtext long message sizeof LENGTH ts sleep BIG +% LocalWords: msgrcv ssize msgtyp NOERROR EXCEPT ENOMSG multiplexing select ls +% LocalWords: poll polling queue MQFortuneServer write init HandSIGTERM l'IPC +% LocalWords: MQFortuneClient mqfortuned mutex risorse' inter semaphore semget +% LocalWords: nsems SEMMNS SEMMSL semid otime semval sempid semncnt semzcnt nr +% LocalWords: SEMVMX SEMOPM semop SEMMNU SEMUME SEMAEM semnum union semun arg +% LocalWords: ERANGE SETALL SETVAL GETALL array GETNCNT GETPID GETVAL GETZCNT +% LocalWords: sembuf sops unsigned nsops UNDO flg nsop num undo pending semadj +% LocalWords: sleeper scheduler running next semundo MutexCreate semunion lock +% LocalWords: MutexFind wrapper MutexRead MutexLock MutexUnlock unlock locking +% LocalWords: MutexRemove shmget SHMALL SHMMAX SHMMIN shmid shm segsz atime FD +% LocalWords: dtime lpid cpid nattac shmall shmmax SHMLBA SHMSEG EOVERFLOW brk +% LocalWords: memory shmat shmdt void shmaddr shmflg SVID RND RDONLY rounded +% LocalWords: SIGSEGV nattch exit SharedMem ShmCreate memset fill ShmFind home +% LocalWords: ShmRemove DirMonitor DirProp chdir GaPiL shmptr ipcs NFS +% LocalWords: ComputeValues ReadMonitor touch SIGTERM dirmonitor unlink fcntl +% LocalWords: LockFile UnlockFile CreateMutex FindMutex LockMutex SETLKW GETLK +% LocalWords: UnlockMutex RemoveMutex ReadMutex UNLCK WRLCK RDLCK mapping MAP +% LocalWords: SHARED ANONYMOUS thread patch names strace system call userid Di +% LocalWords: groupid Michal Wronski Krzysztof Benedyczak wrona posix mqueue +% LocalWords: lmqueue gcc mount mqd name oflag attr maxmsg msgsize receive ptr +% LocalWords: send WRONLY NONBLOCK close mqdes EBADF getattr setattr mqstat to +% LocalWords: omqstat curmsgs flags timedsend len prio timespec abs EMSGSIZE +% LocalWords: ETIMEDOUT timedreceive getaddr notify sigevent notification l'I +% LocalWords: EBUSY sigev SIGNAL signo value sigval siginfo all'userid MESGQ +% LocalWords: Konstantin Knizhnik futex tmpfs ramfs cache shared swap CONFIG +% LocalWords: lrt blocks PAGECACHE TRUNC CLOEXEC mmap ftruncate munmap FindShm +% LocalWords: CreateShm RemoveShm LIBRARY Library libmqueue FAILED has +% LocalWords: ENAMETOOLONG qualchenome RESTART trywait XOPEN SOURCE timedwait +% LocalWords: process getvalue sval execve pshared ENOSYS heap PAGE destroy it +% LocalWords: xffffffff Arrays owner perms Queues used bytes messages device +% LocalWords: Cannot find such Segments getter Signal MSGMAXSIZE been stable +% LocalWords: for now it's break Berlin sources Let's an accidental feature +% LocalWords: Larry Wall Escape the Hell William ipctestid Identifier segment +% LocalWords: violation dell'I SIGINT setter Fri Dec Sleeping seconds %%% Local Variables: