X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=ipc.tex;h=abe229a50538d4ffb8bd31449eba60c6f15f43a2;hb=8e03e6dab3729b15c924991f2f4a59079d7e53d7;hp=261de58ac9e57599f89f96c98bc551b1af6d8141;hpb=1c9b13f0f2292729be45593d013bbd38f0b9de95;p=gapil.git diff --git a/ipc.tex b/ipc.tex index 261de58..abe229a 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,234 +1,256 @@ -\chapter{La comunicazione fra processi} +%% ipc.tex +%% +%% Copyright (C) 2000-2015 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 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 neanche 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 introdotto nei sistemi Unix, -è quello delle cosiddette \textit{pipe}; esse costituiscono una delle +è 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. +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 una una coppia di file descriptor\footnote{si tenga presente che - le pipe sono oggetti creati dal kernel e non risiedono su disco.} connessi -fra di loro in modo che se quanto scrive su di uno si può rileggere -dall'altro. Si viene così a costituire un canale di comunicazione tramite i -due file descriptor, nella forma di un \textsl{tubo} (da cui il nome) -attraverso cui fluiscono i dati. - -La funzione che permette di creare questa speciale coppia di file descriptor -associati ad una \textit{pipe} è appunto \func{pipe}, ed il suo prototipo è: -\begin{prototype}{unistd.h} -{int pipe(int filedes[2])} - -Crea una coppia di file descriptor associati ad una \textit{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} - -La funzione restituisce la coppia di file descriptor nell'array -\param{filedes}; il primo è aperto in lettura ed il secondo in scrittura. Come -accennato concetto di funzionamento di una pipe è semplice: quello che si -scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale -nel file descriptor aperto in lettura. I file descriptor infatti non sono -connessi a nessun file reale, ma ad un buffer nel kernel, la cui dimensione è -specificata 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. - -\begin{figure}[htb] +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 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 singolo 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}. -\begin{figure}[htb] +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] \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 proprietà -ordinarie dei file, ma ci mostra anche qual'è il principale\footnote{Stevens - in \cite{APUE} riporta come limite anche il fatto che la comunicazione è - unidirezionale, ma in realtà questo è un limite facilmente superabile usando - una coppia di pipe.} limite nell'uso delle pipe. È necessario infatti che i -processi possano condividere i file descriptor della 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 -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 delle pipe faremo un esempio di quello che -è il loro uso più comune, analogo a quello effettuato della shell, e che -consiste nell'inviare l'output di un processo (lo standard output) sull'input -di un'altro. Realizzeremo il programma di esempio nella forma di un -\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un - 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{Example} +http://www.sito.it/cgi-bin/programma?argomento +\end{Example} 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 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 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. +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] +\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. + +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 +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. - +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, pipein and pipeout, to handle communication */ - if ( (retval = pipe(pipein)) ) { - WriteMess("input pipe creation error"); - exit(0); - } - if ( (retval = pipe(pipeout)) ) { - WriteMess("output pipe creation error"); - exit(0); - } - /* First fork: use child to run barcode program */ - if ( (pid = fork()) == -1) { /* on error exit */ - WriteMess("child creation error"); - exit(0); - } - /* if child */ - if (pid == 0) { - close(pipein[1]); /* close pipe write end */ - dup2(pipein[0], STDIN_FILENO); /* remap stdin to pipe read end */ - close(pipeout[0]); - dup2(pipeout[1], STDOUT_FILENO); /* remap stdout in pipe output */ - execlp("barcode", "barcode", size, NULL); - } - close(pipein[0]); /* close input side of input pipe */ - write(pipein[1], argv[1], strlen(argv[1])); /* write parameter to pipe */ - close(pipein[1]); /* closing write end */ - waitpid(pid, NULL, 0); /* wait child completion */ - /* Second fork: use child to run ghostscript */ - if ( (pid = fork()) == -1) { - WriteMess("child creation error"); - exit(0); - } - /* second child, convert PS to JPEG */ - if (pid == 0) { - close(pipeout[1]); /* close write end */ - dup2(pipeout[0], STDIN_FILENO); /* remap read end to stdin */ - /* send mime type */ - write(STDOUT_FILENO, content, strlen(content)); - execlp("gs", "gs", "-q", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL); - } - /* still parent */ - close(pipeout[1]); - waitpid(pid, NULL, 0); - exit(0); -} - \end{lstlisting} + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/BarCodePage.c} \end{minipage} \normalsize \caption{Sezione principale del codice del \textit{CGI} @@ -236,673 +258,4832 @@ 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} - -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}. +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 è: + + +\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 +processo 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} 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 di dimensioni corrette. -Questo approccio però non funziona, per via di una delle caratteristiche -principali delle 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 eseguela conversione con \cmd{gs} su -un file regolare non ci sono problemi, una pipe però è rigidamente -sequenziale, e l'uso di \func{lseek} su di essa fallisce sempre con un errore -di \macro{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 +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, è +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.} -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 + 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 è -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. +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 \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 è possibile aprire le fifo anche in lettura/scrittura,\footnote{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 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 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 \macro{PIPE\_BUF} (si ricordi quanto detto in -\secref{sec:ipc_pipes}). - -A parte il caso precedente, che resta probabilmente il più comune, Stevens -riporta in \cite{APUE} altre due casistiche principali per l'uso delle fifo: -\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}). - -\item Come canale di comunicazione fra client ed server (il modello - \textit{client-server} è illustrato in \secref{sec:net_cliserv}). -\end{itemize} - -Nel primo caso quello che si fa è creare tante fifo, da usare come standard -input, quanti sono i processi a cui i vogliono inviare i dati, questi ultimi -saranno stati posti in esecuzione ridirigendo lo standard input dalle fifo, si -potrà poi eseguire il processo che fornisce l'output replicando quest'ultimo, -con il comando \cmd{tee}, sulle varie fifo. - -Il secondo caso è relativamente semplice qualora si debba comunicare con un -processo alla volta (nel qual caso basta usare due fifo, 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 meccanismi di -comunicazione diversi, come quelli che esamineremo in seguito. - - - -\section{La comunicazione fra processi di System V} + 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 (\texttt{\small 23}) si può +chiamare 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 pipe e le 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 pipe. +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, che fossero in grado di garantire una maggiore flessibilità. -In questa sezione esamineremo come Linux supporta quello che viene ormai -chiamato il \textsl{Sistema di comunicazione inter-processo} di System V, o -\textit{System V IPC (Inter-Process Comunication)}. +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 sistema di IPC di System V è 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 che, al contrario di quanto avviene per pipe e fifo, -la memoria allocata per questi oggetti non viene rilasciata automaticamente, -ed essi devono essere cancellati esplicitamente, altrimenti resteranno attivi -fino al riavvio del sistema. - -Gli oggetti usati nel System V IPC vengono creati direttamente dal kernel, e -sono accessibili solo specificando il relativo \textsl{identificatore}. Questo -è il numero progressivo che il kernel assengna a ciascuno di essi quanto -vengono creati (il prodedimento è simile a quello con cui si assegna il -\acr{pid} ai processi). 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à, ne utilizzare un qualche valore statico, si pone perciò -il problema di come processi diversi possono accedere allo stesso oggetto. - -Per risolvere il problema il kernel associa a ciascun oggetto una struttura -\var{ipc\_perm}; questa contiene una \textsl{chiave}, identificata da una -variabile del tipo primitivo \type{key\_t}, che viene specificata in fase di -creazione e tramite la quale è possibile ricavare l'identificatore. La -struttura, la cui definizione è riportata in \figref{fig:ipc_ipc_perm}, -contiene anche le varie proprietà associate all'oggetto. +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]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm ]{} -struct ipc_perm -{ - key_t key; /* Key. */ - uid_t uid; /* Owner's user ID. */ - gid_t gid; /* Owner's group ID. */ - uid_t cuid; /* Creator's user ID. */ - gid_t cgid; /* Creator's group ID. */ - unsigned short int mode; /* Read/write permission. */ - unsigned short int seq; /* Sequence number. */ -}; - \end{lstlisting} + \begin{minipage}[c]{0.80\textwidth} + \includestruct{listati/ipc_perm.h} \end{minipage} \normalsize - \caption{La struttura \var{ipc\_perm}, come definita in \file{sys/ipc.h}.} + \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 è +associato ad un oggetto ed accedervi. Il problema che sorge a questo punto è come devono fare per accordarsi sull'uso di una stessa chiave. Se i processi -sono \textsl{parenti} la soluzione è relativamente semplice, in tal caso -infatti si può usare il valore speciale \texttt{IPC\_PRIVATE} per creare un -nuovo oggetto nel processo padre, l'idenficatore così ottenuto sarà -disponibile in tutti i figli, e potrà essere passato come parametro attraverso +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{parenti} (come capita tutte le volte -che si ha a che fare con un sistema client-server) tutto questo non è -possibile; si potebbe comunque salvare l'identificatore su un file noto, ma +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 +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, -\func{ftok}, che permette di ottenere una chiave specificando il nome di un -file ed un numero di versione; il suo prototipo è: -\begin{functions} - \headdecl{sys/types.h} - \headdecl{sys/ipc.h} - - \funcdecl{key\_t ftok(const char *pathname, int proj\_id)} - - Restituisce una chiave per identificare un oggetto del System V IPC. - - \bodydesc{La funzione restituisce la chiave in caso di successo e -1 - altrimenti, nel qual caso \var{errno} viene settata ad uno dei possibili - codici di errore di \func{stat}.} -\end{functions} +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 pathname di un file effettivamente esistente e di un -numero di progetto \param{proj\_id)}, che di norma viene specificato come -carattere, dato che ne vengono utilizzati solo gli 8 bit meno -significativi.\footnote{nelle libc4 e libc5, come avviene in SunOS, - l'argomento \param{proj\_id)} è dichiarato tipo \ctyp{char}, le \acr{glibc} - han modificato il prototipo, 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 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 device su cui è il file. Diventa -perciò relativamente facile ottenere delle collisioni, specie se i file sono -su dispositivi con lo stesso \textit{minor number}, come \file{/dev/hda1} e +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 haeder, o uno dei programmi che devono usare -l'oggetto in questione), utilizzando il numero di progetto per ottere 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 +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 \var{ipc\_perm}, non esiste una modalità semplice per essere -sicuri della validità di una certa chiave. +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 devono essere -cancellati esplicitamente, il principale problema del sistema di IPC di System -V. 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 ne è stata effettuata una revisione -completa nello standard POSIX.1b, che tratteremo in \secref{sec:ipc_posix}. +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 meccanismi 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 che ad ogni oggetto sono associate in -\var{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, simile -a quello che si ha per i file (vedi \secref{sec:file_perm_overview}). - -Benché il controllo di accesso relativo agli oggetti di intercomunicazione sia -molto simile a quello dei file, restano delle importanti differenze. La prima -è che il permesso di esecuzione non esiste (e viene ignorato), per cui si può -parlare solo di permessi di lettura e scrittura (nel caso dei semafori poi -quest'ultimo è più propriamente il permesso di modificarne lo stato). I valori -di \var{mode} sono gli stessi ed hanno lo stesso significato di quelli -riportati in \secref{tab:file_mode_flags}\footnote{se però si vogliono usare - le costanti simboliche ivi definite occorrerà includere il file - \file{sys/stat.h}, alcuni sistemi definiscono le costanti \macro{MSG\_R} - (\texttt{0400}) e \macro{MSG\_W} (\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.} e come per i file -definiscono gli accessi per il proprietario, il suo gruppo e tutti gli altri. - -Si tenga presente che per gli oggetti di IPC han senso solo i permessi di -lettura e scrittura, quelli di esecuzione vengono ignorati. Quando l'oggetto -viene creato i campi \var{cuid} e \var{uid} di \var{ipc\_perm} ed i campi -\var{cgid} e \var{gid} vengono settati rispettivamente al valore dell'userid e -del groupid effettivo del processo che ha chiamato la funzione, ma mentre i -campi \var{uid} e \var{gid} possono essere cambiati, \var{cuid} e \var{cgid} -restano sempre gli stessi. - -Il controllo di accesso è effettuato a due livelli. Il primo è nelle funzioni -che richiedono l'identificatore di un oggetto data la chiave, che specificano -tutte un argomento \param{flag}. In tal caso quando viene effettuata la -ricerca di una chiave, se \param{flag} specifica dei permessi, questi vengono -controllati e l'identificatore viene restituito solo se essi corrispondono a -quelli dell'oggetto. Se sono presenti dei permessi non presenti in \var{mode} -l'accesso sarà invece negato. Questo però è di utilità indicativa, dato che è -sempre possibile specificare un valore nullo per \param{flag}, nel qual caso -il controllo avrà sempre successo. - -Il secondo livello è quello delle varie funzioni che accedono (in lettura o -scrittura) all'oggetto. In tal caso lo schema dei controlli è simile a quello -dei file, ed avviene secondo questa sequenza: -\begin{enumerate} -\item se il processo ha i privilegi di amministatore l'accesso è sempre - consentito. -\item se l'userid effettivo del processo corrisponde o al valore del campo +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à \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 è - settato il permesso di scrittura per le operazioni di scrittura e quello - di lettura per le operazioni di lettura.} l'accesso è consentito. -\item se il groupid effettivo del processo corrisponde o al + 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 -\end{enumerate} + 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 IPC (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{System V IPC} è quello delle code di -messaggi. Le code di messaggi sono oggetti analoghi alle pipe o alle fifo, -anche se la loro struttura è diversa. La funzione che permette di ottenerne -una è \func{msgget} ed il suo prototipo è: -\begin{functions} - \headdecl{sys/types.h} - \headdecl{sys/ipc.h} - \headdecl{sys/msg.h} - - \funcdecl{int msgget(key\_t key, int flag)} - - Restituisce l'identificatore di una cosa di messaggi. - - \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 - in caso di errore, nel qual caso \var{errno} viene settato ad uno dei - valori: +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[\macro{EACCES}] Il processo chiamante non ha i provilegi per accedere - alla coda richiesta. - \item[\macro{EEXIST}] Si è richiesta la creazione di una coda che già - esiste, ma erano specificati sia \macro{IPC\_CREAT} che \macro{IPC\_EXCL}. - \item[\macro{EIDRM}] La coda richiesta è marcata per essere cancellata. - \item[\macro{ENOENT}] Si è cercato di ottenere l'identificatore di una coda - di messaggi specificando una chiave che non esiste e \macro{IPC\_CREAT} + \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[\macro{ENOSPC}] Si è cercato di creare una coda di messaggi quando è - stato il limite massimo del sistema. - \end{errlist} - ed inoltre \macro{ENOMEM}. -} -\end{functions} + \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 +nuova. L'argomento \param{key} specifica la chiave che è associata all'oggetto, eccetto il caso in cui si specifichi il valore -\macro{IPC\_PRIVATE}, nel qual caso la coda è creata ex-novo e non vi è -associata alcuna chiave, il processo (ed i suoi eventuali figli) potranno -farvi riferimento solo attraverso l'identificatore. +\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 \macro{IPC\_PRIVATE}\footnote{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 +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 \macro{ENOENT} se non -esiste o di \macro{EACCESS} se si sono specificati dei permessi non validi. +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ò +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 \macro{IPC\_CREAT}. In questo caso i nove bit meno +corrispondente al valore \const{IPC\_CREAT}. In questo caso i nove bit meno significativi di \param{flag} saranno usati come permessi per il nuovo -oggetto, secondo quanto illustrato in \secref{sec:ipc_sysv_access_control}. -Se si imposta anche il bit corrispondente a \macro{IPC\_EXCL} la funzione avrà -successo solo se l'oggetto non esiste già, fallendo con un errore di -\macro{EEXIST} altrimenti. +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. 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 (e non esiste una struttura \struct{msqid\_ds} +nel kernel), ma abbiamo mantenuto lo schema precedente dato che illustra in +maniera più che adeguata i principi di funzionamento delle code di messaggi. -Una coda di messaggi è costituita da una \textit{linked list} in cui nuovi -messaggi vengono inseriti in coda e letti dalla cima, con una struttura del -tipo di quella illustrata in +\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} ed a cui si accede +includendo \headfile{sys/msg.h}; +% +% INFO: sotto materiale obsoleto e non interessante +% In questa struttura il +% kernel mantiene le principali informazioni riguardo lo stato corrente della +% coda. 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 definiti in +%\headfile{sys/msg.h}; +si tenga presente che il campo \var{\_\_msg\_cbytes} non è previsto dallo +standard POSIX.1-2001 e che alcuni campi fino al kernel 2.2 erano definiti +come \ctyp{short}. +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{.90\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} -\subsection{Semafori} +Quando si crea una nuova coda con \func{msgget} questa struttura viene +inizializzata,\footnote{in realtà viene inizializzata una struttura interna al + kernel, ma i dati citati sono gli stessi.} in particolare il campo +\var{msg\_perm} che esprime i permessi di accesso viene inizializzato nella +modalità illustrata 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 ultima modifica 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 il campo \var{\_\_msg\_cbytes}, che esprime la dimensione in byte dei + messaggi presenti sulla coda, viene inizializzato a zero. +% 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 eseguire una operazione di controllo per la coda +specificata dall'identificatore \param{msqid}, utilizzando i valori della +struttura \struct{msqid\_ds}, mantenuta all'indirizzo \param{buf}. Il +comportamento della funzione dipende dal valore dell'argomento \param{cmd}, +che specifica il tipo di azione da eseguire. I valori possibili +per \param{cmd} sono: +\begin{basedescript}{\desclabelwidth{1.6cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge le informazioni riguardo la coda nella + struttura \struct{msqid\_ds} 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 e lo stesso vale per \var{msg\_qbytes}. Infine solo + l'amministratore (più precisamente un processo con la capacità + \const{CAP\_IPC\_RESOURCE}) ha la facoltà di incrementarne il valore a + limiti superiori a \const{MSGMNB}. Se eseguita con successo la funzione + aggiorna anche il campo \var{msg\_ctime}. +\end{basedescript} + +A questi tre valori, che sono quelli previsti dallo standard, su Linux se ne +affiancano altri tre (\const{IPC\_INFO}, \const{MSG\_STAT} e +\const{MSG\_INFO}) introdotti ad uso del programma \cmd{ipcs} per ottenere le +informazioni generali relative alle risorse usate dalle code di +messaggi. Questi potranno essere modificati o rimossi in favore dell'uso di +\texttt{/proc}, per cui non devono essere usati e non li tratteremo. + +Una volta che si abbia a disposizione l'identificatore, per inviare un +messaggio su una coda si utilizza la funzione di sistema \funcd{msgsnd}, il +cui prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/msg.h} +\fdecl{int msgsnd(int msqid, struct msgbuf *msgp, size\_t msgsz, int msgflg)} +\fdesc{Invia un messaggio 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}] non si hanno i privilegi di accesso sulla coda. + \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{EIDRM}] la coda è stata cancellata. + \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} e \errval{ENOMEM} nel loro + significato generico.} +\end{funcproto} + +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}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{0.8\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} + +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}). + +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. 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 di sistema che viene utilizzata per estrarre un messaggio da una +coda è \funcd{msgrcv}, ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/msg.h} +\fdecl{ssize\_t msgrcv(int msqid, struct msgbuf *msgp, size\_t msgsz, + long msgtyp, int msgflg)} +\fdesc{Legge un messaggio da una coda.} +} + +{La funzione ritorna il numero di byte letti in caso di successo e $-1$ per un + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{E2BIG}] il testo del messaggio è più lungo di \param{msgsz} e + non si è specificato \const{MSG\_NOERROR} in \param{msgflg}. + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \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} nel suo significato generico.} +\end{funcproto} + +La funzione legge un messaggio dalla coda specificata da \param{msqid}, +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*} + +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. 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 +\textit{SysV-IPC} che 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 è che 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. + +\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} vengono passate (\texttt{\small 8-11}) le richieste mentre +con \var{msgbuf\_write} vengono restituite (\texttt{\small 12-15}) le frasi. + +La gestione delle opzioni si è al solito omessa, essa si curerà di impostare +nella variabile \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 vengono +lette (\texttt{\small 23}) 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 +(\texttt{\small 12-13}) il messaggio di richiesta 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 rileggere la risposta (\texttt{\small 16}) dalla +coda 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 \textit{fifo}, potremo far partire il server con: +\begin{Console} +[piccardi@gont sources]$ \textbf{./mqfortuned -n10} +\end{Console} +%$ +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{Console} +[piccardi@gont sources]$ \textbf{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 +0x0102dc6a 0 piccardi 666 0 0 +\end{Console} +%$ +a questo punto potremo usare il client per ottenere le nostre frasi: +\begin{Console} +[piccardi@gont sources]$ \textbf{./mqfortune} +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +[piccardi@gont sources]$ \textbf{./mqfortune} +Let's call it an accidental feature. + --Larry Wall +\end{Console} +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 venga +rimossa. + +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} \textit{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{I semafori} \label{sec:ipc_sysv_sem} -Il secondo oggetto introdotto dal \textit{System V IPC} è quello dei semafori. -Un semaforo è uno speciale contatore che permette di controllare l'accesso a -risorse condivise. La funzione che permette di ottenere un insieme di semafori -è \func{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 semaforo. - - \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 - in caso di errore, nel qual caso \var{errno} viene settato agli stessi - valori visti per \func{msgget}.} -\end{functions} +I semafori non sono propriamente meccanismi di intercomunicazione come +\textit{pipe}, \textit{fifo} e code di messaggi, poiché 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 infatti non è altro che un +contatore mantenuto nel kernel che determina se consentire o meno la +prosecuzione dell'esecuzione di un programma. In questo modo si può +controllare l'accesso ad una risorsa condivisa da più processi, associandovi +un semaforo che assicuri che non possa essere usata da più di un processo alla +volta. -La funzione è del tutto analoga a \func{msgget} ed identico è l'uso degli -argomenti \param{key} e \param{flag}, per cui non ripeteremo quanto detto al -proposito in \secref{sec:ipc_sysv_mq}. L'argomento \param{nsems} permette di -specificare quanti semfori deve contenere l'insieme qualora se ne richieda la -creazione, e deve essere nullo quando si effettua una richiesta -dell'identificatore di un insieme già esistente. +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 condivisa 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 la risorsa viene +considerata non disponibile, ed il processo si bloccherà fin quando chi la sta +utilizzando non la rilascerà, incrementando il valore del semaforo. Non appena +il semaforo diventa positivo, indicando che la risorsa è tornata disponibile, +il processo bloccato in attesa riprenderà l'esecuzione, e potrà operare come +nel caso precedente (decremento del semaforo, accesso alla risorsa, incremento +del semaforo). +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. -\subsection{Memoria condivisa} -\label{sec:ipc_sysv_shm} +Il sistema di intercomunicazione di \textit{SysV-IPC} prevede anche una +implementazione dei semafori, ma gli oggetti utilizzati sono tuttavia non +semafori singoli, ma gruppi (più propriamente \textsl{insiemi}) di semafori +detti ``\textit{semaphore set}''. La funzione di sistema che permette di +creare o ottenere l'identificatore di un insieme di semafori è \funcd{semget}, +ed il suo prototipo è: -Il terzo oggetto introdotto dal \textit{System V IPC} è quello della memoria -condivisa. La funzione che permette di ottenerne uno è \func{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} viene settato agli stessi - valori visti per \func{msgget}.} -\end{functions} +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/sem.h} +\fdecl{int semget(key\_t key, int nsems, int flag)} +\fdesc{Restituisce l'identificatore di un insieme di semafori.} +} -La funzione, come per \func{semget}, è del tutto analoga a \func{msgget}, ed -identico è l'uso degli argomenti \param{key} e \param{flag}. L'argomento +{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{ENOSPC}] si è superato il limite di sistema per il numero + totale di semafori (\const{SEMMNS}) o di insiemi (\const{SEMMNI}). + \item[\errcode{EINVAL}] \param{nsems} è minore di zero o maggiore del limite + sul numero di semafori di un 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{EEXIST}, \errval{EIDRM} e + \errval{ENOENT} con lo stesso significato che hanno per \func{msgget}.} +\end{funcproto} +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 dell'interfaccia, il problema è che i semafori forniti +dal \textit{SysV-IPC} soffrono di altri due difetti progettuali molto più +gravi. -\section{La comunicazione fra processi di POSIX} -\label{sec:ipc_posix} +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. Eventuali +accessi che possono avvenire fra la creazione e l'inizializzazione potranno +avere effetti imprevisti. + +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ù. In questo caso il problema è più grave +perché ci si 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, e la gestione diventa più complicata. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{.80\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{anche in questo caso in + realtà il kernel usa una sua specifica struttura interna, ma i dati + significativi sono sempre quelli citati.} 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}, che +esprime i permessi di accesso, 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 ultimo cambiamento + 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*} + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{.80\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} + +Ciascun semaforo dell'insieme è realizzato come una struttura di tipo +\struct{sem} che ne contiene i dati essenziali, la cui definizione è riportata +in fig.~\ref{fig:ipc_sem}.\footnote{in realtà in fig~\ref{fig:ipc_sem} si è + riportata la definizione originaria del kernel 1.0, che contiene la prima + realizzazione del \textit{SysV-IPC} in Linux; ormai questa struttura è + ridotta ai soli due primi membri, e gli altri vengono calcolati + dinamicamente, la si è usata solo a scopo di esempio, perché indica tutti i + valori associati ad un semaforo, restituiti dalle funzioni di controllo, e + citati dalle pagine di manuale.} Questa struttura non è accessibile +direttamente dallo \textit{user space}, ma i valori in essa specificati +possono essere letti in maniera indiretta, attraverso l'uso delle opportune +funzioni di controllo. I dati mantenuti nella struttura, ed elencati in +fig.~\ref{fig:ipc_sem}, indicano rispettivamente: +\begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}} +\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{basedescript} + +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}. + +\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} + + +La funzione di sistema 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{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/sem.h} +\fdecl{int semctl(int semid, int semnum, int cmd)} +\fdecl{int semctl(int semid, int semnum, int cmd, union semun arg)} +\fdesc{Esegue le operazioni di controllo su un semaforo o un insieme di + semafori.} +} + +{La funzione ritorna in caso di successo un valore positivo quanto usata con + tre argomenti ed un valore nullo quando usata con quattro e $-1$ per un + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] i permessi assegnati al semaforo non consentono + l'operazione di lettura o scrittura richiesta e non si hanno i privilegi + di amministratore. + \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 è né il creatore né il proprietario del semaforo e + non ha i privilegi di amministratore. + \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} nel loro significato + generico.} +\end{funcproto} + +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]{0.80\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 detto argomento esso deve essere passato come una unione +\struct{semun}, la cui definizione, con i possibili valori che può assumere, è +riportata in fig.~\ref{fig:ipc_semun}. + +Nelle versioni più vecchie delle \acr{glibc} questa unione veniva definita in +\file{sys/sem.h}, ma nelle versioni più recenti questo non avviene più in +quanto lo standard POSIX.1-2001 richiede che sia sempre definita a cura del +chiamante. In questa seconda evenienza le \acr{glibc} definiscono però la +macro \macro{\_SEM\_SEMUN\_UNDEFINED} che può essere usata per controllare la +situazione. + +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. Per questo argomento i +valori validi, quelli cioè che non causano un errore di \errcode{EINVAL}, sono +i seguenti: +\begin{basedescript}{\desclabelwidth{1.6cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge i dati dell'insieme di semafori, copiandone i + valori nella struttura \struct{semid\_ds} posta 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 bloccati in attesa + 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}. La funziona aggiorna anche il campo + \var{sem\_ctime}. 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. 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. +\end{basedescript} + +Come per \func{msgctl} esistono tre ulteriori valori, \const{IPC\_INFO}, +\const{SEM\_STAT} e \const{SEM\_INFO}, specifici di Linux e fuori da ogni +standard, creati specificamente ad uso del comando \cmd{ipcs}. Dato che anche +questi potranno essere modificati o rimossi, non devono essere utilizzati e +pertanto non li tratteremo. + +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 di sistema \funcd{semop}, il cui prototipo +è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/sem.h} +\fdecl{int semop(int semid, struct sembuf *sops, unsigned nsops)} +\fdesc{Esegue operazioni ordinarie su un semaforo o un insieme di semafori.} +} + +{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{E2BIG}] l'argomento \param{nsops} è maggiore del numero + massimo di operazioni \const{SEMOPM}. + \item[\errcode{EACCES}] il processo non ha i permessi per eseguire + l'operazione richiesta e non ha i privilegi di amministratore. + \item[\errcode{EAGAIN}] un'operazione comporterebbe il blocco del processo, + ma si è specificato \const{IPC\_NOWAIT} in \var{sem\_flg}. + \item[\errcode{EFBIG}] il valore del campo \var{sem\_num} è negativo o + maggiore o uguale al numero di semafori dell'insieme. + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{EINTR}] la funzione, bloccata in attesa dell'esecuzione + dell'operazione, viene interrotta da un segnale. + \item[\errcode{ENOMEM}] si è richiesto un \const{SEM\_UNDO} ma il sistema + non ha le risorse per allocare la struttura di ripristino. + \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} nel loro significato + generico.} +\end{funcproto} + +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, +ed in tal caso vengono eseguite nella sequenza passata nel +vettore \param{sops}. + +Con lo standard POSIX.1-2001 è stata introdotta una variante di \func{semop} +che consente di specificare anche un tempo massimo di attesa. La nuova +funzione di sistema, disponibile a partire dal kernel 2.4.22 e dalle +\acr{glibc} 2.3.3, ed utilizzabile solo dopo aver definito la macro +\macro{\_GNU\_SOURCE}, è \funcd{semtimedop}, ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/sem.h} +\fdecl{int semtimedop(int semid, struct sembuf *sops, unsigned nsops, + struct timespec *timeout)} +\fdesc{Esegue operazioni ordinarie su un semaforo o un insieme di semafori.} +} + +{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{EAGAIN}] l'operazione comporterebbe il blocco del processo, + ma si è specificato \const{IPC\_NOWAIT} in \var{sem\_flg} oppure si è + atteso oltre quanto indicato da \param{timeout}. + \end{errlist} + e gli altri valori già visti per \func{semop}, con lo stesso significato.} +\end{funcproto} + +Rispetto a \func{semop} la funzione consente di specificare un tempo massimo +di attesa, indicato con una struttura \struct{timespec} (vedi +fig.~\ref{fig:sys_timespec_struct}), per le operazioni che verrebbero +bloccate. Alla scadenza di detto tempo la funzione ritorna comunque con un +errore di \errval{EAGAIN} senza che nessuna delle operazioni richieste venga +eseguita. + +Si tenga presente che la precisione della temporizzazione è comunque limitata +dalla risoluzione dell'orologio di sistema, per cui il tempo di attesa verrà +arrotondato per eccesso. In caso si passi un valore \val{NULL} +per \param{timeout} il comportamento di \func{semtimedop} è identico a quello +di \func{semop}. + + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{.80\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} + +Come indicato il contenuto di ciascuna operazione deve essere specificato +attraverso una 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 gli +elementi di un vettore, per cui il primo semaforo di un insieme 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 in tutti quei +casi in cui l'esecuzione di una operazione richiederebbe di porre il processo +vada nello stato di \textit{sleep}, invece di bloccarsi \func{semop} ritorni +immediatamente (abortendo così le eventuali operazioni restanti) con un errore +di \errcode{EAGAIN}. Impostando \const{SEM\_UNDO} si richiede invece che +l'operazione in questione venga registrata, in modo che il valore del semaforo +possa essere ripristinato all'uscita del processo. + +Infine \var{sem\_op} è il campo che controlla qual'è l'operazione che viene +eseguita e determina in generale il comportamento della chiamata a +\func{semop}. I casi possibili per il valore di questo campo sono tre: +\begin{basedescript}{\desclabelwidth{1.8cm}} +\item[\var{sem\_op} $>0$] In questo caso il valore viene aggiunto al valore + corrente di \var{semval} per il semaforo indicato. Questa operazione non + causa mai un blocco del processo, ed eventualmente \func{semop} ritorna + immediatamente con un errore di \errcode{ERANGE} qualora si sia superato il + limite \const{SEMVMX}. Se l'operazione ha successo si passa immediatamente + alla successiva. 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'operazione ha + successo immediato, e o si passa alla successiva o \func{semop} ritorna con + successo se questa era l'ultima. Se \var{semval} è diverso da zero il + comportamento è controllato da \var{sem\_flg}, se è stato impostato + \const{IPC\_NOWAIT} \func{semop} ritorna immediatamente abortendo tutte le + operazioni con un errore di \errcode{EAGAIN}, altrimenti viene incrementato + \var{semzcnt} di uno ed il processo viene bloccato fintanto che non si + verifica una delle condizioni seguenti: + \begin{itemize*} + \item \var{semval} diventa zero, nel qual caso \var{semzcnt} viene + decrementato di uno, l'operazione ha successo e si passa alla successiva, + oppure \func{semop} ritorna con successo se questa era l'ultima. + \item l'insieme di semafori viene rimosso, nel qual caso \func{semop} + ritorna abortendo tutte le operazioni con 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 abortendo tutte le + operazioni con un errore di \errcode{EINTR}. + \end{itemize*} + Al processo chiamante è richiesto soltanto 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 l'operazione ha successo e si + passa alla successiva, oppure \func{semop} ritorna con successo se questa + era l'ultima. 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} \func{semop} ritorna + immediatamente abortendo tutte le operazioni 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 abortendo tutte le operazioni con 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 abortendo tutte le + operazioni con un errore di \errcode{EINTR}. + \end{itemize*} + Al processo chiamante è richiesto il privilegio di alterazione (scrittura) + sull'insieme di semafori. +\end{basedescript} + +Qualora si sia usato \func{semtimedop} alle condizioni di errore precedenti si +aggiunge anche quella di scadenza del tempo di attesa indicato +con \param{timeout} che farà abortire la funzione, qualora resti bloccata +troppo a lungo nell'esecuzione delle operazioni richieste, con un errore di +\errcode{EAGAIN}. + +In caso di successo (sia per \func{semop} che per \func{semtimedop}) per ogni +semaforo modificato verrà aggiornato il campo \var{sempid} al valore del +\ids{PID} del processo chiamante; inoltre verranno 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} (e +\func{semtimedop}) 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à (in particolare con le vecchie +versioni delle librerie del C, come le \acr{libc5}). + +\begin{figure}[!htb] + \centering \includegraphics[width=12cm]{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, 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 +\textit{scheduler} per passare all'esecuzione di un altro processo. + +Se invece tutte le operazioni possono avere successo queste vengono eseguite +immediatamente, dopo di che il kernel esegue una scansione della coda di +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. + +Queste strutture sono mantenute in due liste (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, 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 +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 di sistema che permette di ottenerne uno è +\funcd{shmget}, ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/ipc.h} +\fhead{sys/shm.h} +\fdecl{int shmget(key\_t key, int size, int flag)} +\fdesc{Ottiene o crea una memoria condivisa.} +} + +{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{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. + \item[\errcode{ENOMEM}] si è specificato \const{IPC\_HUGETLB} ma non si + hanno i privilegi di amministratore. + \end{errlist} + ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, + \errval{EIDRM}, con lo stesso significato che hanno per \func{msgget}.} +\end{funcproto} + + +La funzione, come \func{semget}, è 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}. A partire dal kernel 2.6 +però sono stati introdotti degli ulteriori bit di controllo per +l'argomento \param{flag}, specifici di \func{shmget}, attinenti alle modalità +di gestione del segmento di memoria condivisa in relazione al sistema della +memoria virtuale. + +Il primo dei due flag è \const{SHM\_HUGETLB} che consente di richiedere la +creazione del segmento usando una \itindex{huge~page} \textit{huge page}, le +pagine di memoria di grandi dimensioni introdotte con il kernel 2.6 per +ottimizzare le prestazioni nei sistemi più recenti che hanno grandi quantità +di memoria. L'operazione è privilegiata e richiede che il processo abbia la +\textit{capability} \const{CAP\_IPC\_LOCK}. Questa funzionalità è specifica di +Linux e non è portabile. + +Il secondo flag aggiuntivo, introdotto a partire dal kernel 2.6.15, è +\const{SHM\_NORESERVE}, ed ha lo stesso scopo del flag \const{MAP\_NORESERVE} +di \func{mmap} (vedi sez.~\ref{sec:file_memory_map}): non vengono riservate +delle pagine di swap ad uso del meccanismo del \textit{copy on write} +\itindex{copy~on~write} per mantenere le modifiche fatte sul segmento. Questo +significa che caso di scrittura sul segmento quando non c'è più memoria +disponibile, si avrà l'emissione di un \signal{SIGSEGV}. + +Infine l'argomento \param{size} specifica la dimensione del segmento di +memoria condivisa; il valore deve essere specificato in byte, ma verrà +comunque arrotondato al multiplo superiore di \const{PAGE\_SIZE}. Il valore +deve essere specificato quando si crea un nuovo segmento di memoria con +\const{IPC\_CREAT} o \const{IPC\_PRIVATE}, se invece si accede ad un segmento +di memoria condivisa esistente non può essere maggiore del valore con cui esso +è stato creato. + +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]{0.80\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 (l'implementazione non + prevede l'esistenza di questo + limite).\\ + + + \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 di sistema che permette di effettuare le operazioni di +controllo su un segmento di memoria condivisa è \funcd{shmctl}; il suo +prototipo è: + +\begin{funcproto}{ +\fhead{sys/ipc.h} +\fhead{sys/shm.h} +\fdecl{int shmctl(int shmid, int cmd, struct shmid\_ds *buf)} + +\fdesc{Esegue le operazioni di controllo su un segmento di memoria condivisa.} +} + +{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 i permessi non + consentono l'accesso in lettura al segmento. + \item[\errcode{EFAULT}] l'indirizzo specificato con \param{buf} non è + valido. + \item[\errcode{EIDRM}] l'argomento \param{shmid} fa riferimento ad un + segmento che è stato cancellato. + \item[\errcode{EINVAL}] o \param{shmid} non è un identificatore valido o + \param{cmd} non è un comando valido. + \item[\errcode{ENOMEM}] si è richiesto un \textit{memory lock} di + dimensioni superiori al massimo consentito. + \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{EPERM}] si è specificato un comando con \const{IPC\_SET} o + \const{IPC\_RMID} senza i permessi necessari. + \end{errlist} +} +\end{funcproto} + +Il comando specificato attraverso l'argomento \param{cmd} determina i diversi +effetti della funzione. Nello standard POSIX.1-2001 i 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}. +\end{basedescript} + +Oltre ai precedenti su Linux sono definiti anche degli ulteriori comandi, che +consentono di estendere le funzionalità, ovviamente non devono essere usati se +si ha a cuore la portabilità. Questi comandi aggiuntivi sono: + +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{SHM\_LOCK}] Abilita il \itindex{memory~locking} \textit{memory + locking} sul segmento di memoria condivisa, impedendo che la memoria usata + per il segmento venga salvata su disco dal meccanismo della memoria + virtuale. Come illustrato in sez.~\ref{sec:proc_mem_lock} fino al kernel + 2.6.9 solo l'amministratore poteva utilizzare questa capacità,\footnote{che + richiedeva la \textit{capability} \const{CAP\_IPC\_LOCK}.} a partire dal + dal kernel 2.6.10 anche gli utenti normali possono farlo fino al limite + massimo determinato da \const{RLIMIT\_MEMLOCK} (vedi + sez.~\ref{sec:sys_resource_limit}). +\item[\const{SHM\_UNLOCK}] Disabilita il \itindex{memory~locking} + \textit{memory locking} sul segmento di memoria condivisa. Fino al kernel + 2.6.9 solo l'amministratore poteva utilizzare questo comando in + corrispondenza di un segmento da lui bloccato. +\end{basedescript} + +A questi due, come per \func{msgctl} e \func{semctl}, si aggiungono tre +ulteriori valori, \const{IPC\_INFO}, \const{MSG\_STAT} e \const{MSG\_INFO}, +introdotti ad uso del programma \cmd{ipcs} per ottenere le informazioni +generali relative alle risorse usate dai segmenti di memoria condivisa. Dato +che potranno essere modificati o rimossi in favore dell'uso di \texttt{/proc}, +non devono essere usati e non li tratteremo. + +L'argomento \param{buf} viene utilizzato solo con i comandi \const{IPC\_STAT} +e \const{IPC\_SET} nel qual caso esso dovrà puntare ad una struttura +\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{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/shm.h} +\fdecl{void *shmat(int shmid, const void *shmaddr, int shmflg)} + +\fdesc{Aggancia un segmento di memoria condivisa al processo chiamante.} +} + +{La funzione ritorna l'indirizzo del segmento in caso di successo e $-1$ (in + un cast a \type{void *}) per un errore, nel qual caso \var{errno} assumerà + uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al + segmento nella modalità richiesta. + \item[\errcode{EINVAL}] si è specificato un identificatore invalido per + \param{shmid}, o un indirizzo non allineato sul confine di una pagina + per \param{shmaddr} o il valore \val{NULL} indicando \const{SHM\_REMAP}. + \end{errlist} + ed inoltre \errval{ENOMEM} nel suo significato generico. +} +\end{funcproto} + +La funzione inserisce un segmento di memoria condivisa all'interno dello +spazio di indirizzi del processo, in modo che questo possa accedervi +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 *} seguendo POSIX.1-2001.} deve essere associato il +segmento, se il valore specificato è \val{NULL} è il sistema a scegliere +opportunamente un'area di memoria libera (questo è il modo più portabile e +sicuro di usare la funzione). Altrimenti il kernel aggancia il segmento +all'indirizzo specificato da \param{shmaddr}; questo però può avvenire solo se +l'indirizzo coincide con il limite di una pagina, cioè se è un multiplo esatto +del parametro di sistema \const{SHMLBA}, che in Linux è sempre uguale +\const{PAGE\_SIZE}. + +Si tenga presente però che quando si usa \val{NULL} come valore di +\param{shmaddr}, l'indirizzo restituito da \func{shmat} può cambiare da +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 al +momento sono sono tre e sono identificati dalle costanti \const{SHM\_RND}, +\const{SHM\_RDONLY} e \const{SHM\_REMAP} che vanno combinate con un OR +aritmetico. + +Specificando \const{SHM\_RND} si evita che \func{shmat} ritorni un errore +quando \param{shmaddr} non è allineato ai confini di una pagina. Si può quindi +usare un valore qualunque per \param{shmaddr}, e il segmento verrà comunque +agganciato, ma al più vicino multiplo di \const{SHMLBA}; il nome della +costante sta infatti per \textit{rounded}, e serve per specificare un +indirizzo come arrotondamento. + +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. + +Infine \const{SHM\_REMAP} è una estensione specifica di Linux (quindi non +portabile) che indica che la mappatura del segmento deve rimpiazzare ogni +precedente mappatura esistente nell'intervallo iniziante +all'indirizzo \param{shmaddr} e di dimensione pari alla lunghezza del +segmento. In condizioni normali questo tipo di richiesta fallirebbe con un +errore di \errval{EINVAL}. Ovviamente usando \const{SHM\_REMAP} +l'argomento \param{shmaddr} non può essere nullo. + +In caso di successo la funzione \func{shmat} aggiorna anche i seguenti campi +della struttura \struct{shmid\_ds}: +\begin{itemize*} +\item il tempo \var{shm\_atime} dell'ultima operazione di aggancio viene + impostato al tempo corrente. +\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{funcproto}{ +\fhead{sys/types.h} +\fhead{sys/shm.h} +\fdecl{int shmdt(const void *shmaddr)} + +\fdesc{Sgancia dal processo un segmento di memoria condivisa.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, la funzione + fallisce solo quando non c'è un segmento agganciato + all'indirizzo \param{shmaddr}, con \var{errno} che assume il valore + \errval{EINVAL}. +} +\end{funcproto} + +La funzione sgancia dallo spazio degli indirizzi del processo un segmento di +memoria condivisa; questo viene identificato con l'indirizzo \param{shmaddr} +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 1-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}. 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. + +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 all'interno di un ciclo +infinito (\texttt{\small 42-48}). + +Si inizia (\texttt{\small 43}) bloccando il mutex con \func{MutexLock} per +poter accedere alla memoria condivisa (la funzione si bloccherà +automaticamente se qualche client sta leggendo), poi (\texttt{\small 44}) si +cancellano i valori precedentemente immagazzinati nella memoria condivisa con +\func{memset}, e si esegue (\texttt{\small 45}) un nuovo calcolo degli stessi +utilizzando la funzione \myfunc{dir\_scan}; infine (\texttt{\small 46}) si +sblocca il mutex con \func{MutexUnlock}, e si attende (\texttt{\small 47}) per +il periodo di tempo specificato a riga di comando con l'opzione \code{-p} +usando una \func{sleep}. + +Si noti come per il calcolo dei valori da mantenere nella memoria condivisa si +sia usata ancora una volta la funzione \myfunc{dir\_scan}, già utilizzata (e +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. + +\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} + + +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{Console} +[piccardi@gont sources]$ \textbf{./dirmonitor ./} +\end{Console} +%$ +ed avendo usato \func{daemon} il comando ritornerà immediatamente. Una volta +che il server è in esecuzione, possiamo passare ad invocare il client per +verificarne i risultati, in tal caso otterremo: +\begin{Console} +[piccardi@gont sources]$ \textbf{./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{Console} +%$ +ed un rapido calcolo (ad esempio con \code{ls -a | wc} per contare i file) ci +permette di verificare che il totale dei file è giusto. Un controllo con +\cmd{ipcs} ci permette inoltre di verificare la presenza di un segmento di +memoria condivisa e di un semaforo: +\begin{Console} +[piccardi@gont sources]$ \textbf{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{Console} +%$ + +Se a questo punto aggiungiamo un file, ad esempio con \code{touch prova}, +potremo verificare che, passati nel peggiore dei casi almeno 10 secondi (o +l'eventuale altro intervallo impostato per la rilettura dei dati) avremo: +\begin{Console} +[piccardi@gont sources]$ \textbf{./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{Console} +%$ + +A questo punto possiamo far uscire il server inviandogli un segnale di +\signal{SIGTERM} con il comando \code{killall dirmonitor}, a questo punto +ripetendo la lettura, otterremo un errore: +\begin{Console} +[piccardi@gont sources]$ \textbf{./readmon} +Cannot find shared memory: No such file or directory +\end{Console} +%$ +e inoltre potremo anche verificare che anche gli oggetti di intercomunicazione +visti in precedenza sono stati regolarmente cancellati: +\begin{Console} +[piccardi@gont sources]$ \textbf{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{Console} +%$ + + +%% 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 si trova posto migliore, copy_from_process e +% copy_to_process, introdotte con il kernel 3.2. Vedi +% http://lwn.net/Articles/405346/ e +% http://ozlabs.org/~cyeoh/cma/process_vm_readv.txt + + +\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 è stata anche riservata una opportuna +directory, \file{/var/lock}, nella standardizzazione del \textit{Filesystem + Hierarchy 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}, 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 + +% TODO: con il kernel 3.17 è stata introdotta una fuunzionalità di +% sigillatura dei file mappati in memoria e la system call memfd +% (capire se va messo qui o altrove) vedi: http://lwn.net/Articles/593918/ + + +\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, 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 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}). I nomi +specificati nelle relative funzioni devono essere nella forma di un +\textit{pathname} assoluto (devono cioè iniziare con ``\texttt{/}'') e +corrisponderanno ad altrettanti file creati all'interno di queste directory; +per questo motivo detti nomi non possono contenere altre ``\texttt{/}'' oltre +quella iniziale. + +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, che funzionano come su dei file normali. Questo +però è vero nel caso di Linux, che usa una implementazione che lo consente, +non è detto che altrettanto valga per altri kernel. In particolare, come si +può facilmente verificare con il comando \cmd{strace}, sia per la memoria +condivisa che per le code di messaggi varie \textit{system call} utilizzate da +Linux corrispondono in realtà a quelle ordinarie dei file, essendo detti +oggetti realizzati come tali usando degli specifici filesystem. + +In particolare i permessi associati agli oggetti di IPC POSIX sono identici ai +permessi dei file, ed il controllo di accesso segue esattamente la stessa +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 Posix} +\label{sec:ipc_posix_mq} + +Le code di messaggi POSIX sono supportate da Linux a partire dalla versione +2.6.6 del kernel. 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 occorre utilizzare la libreria \file{librt} che contiene le +funzioni dell'interfaccia POSIX ed i programmi che usano le code di messaggi +devono essere compilati aggiungendo l'opzione \code{-lrt} al comando +\cmd{gcc}. In corrispondenza all'inclusione del supporto nel kernel ufficiale +le funzioni di libreria sono state inserite nella \acr{glibc}, e sono +disponibili a partire dalla versione 2.3.4 delle medesime. + +La libreria inoltre richiede la presenza dell'apposito filesystem di tipo +\texttt{mqueue} montato sulla directory \file{/dev/mqueue}; questo può essere +fatto aggiungendo ad \conffile{/etc/fstab} una riga come: +\begin{FileExample}[label=/etc/fstab] +mqueue /dev/mqueue mqueue defaults 0 0 +\end{FileExample} +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 di sistema che permette di aprire (e crearla se non esiste ancora) +una coda di messaggi POSIX è \funcd{mq\_open}, ed il suo prototipo è: + +\begin{funcproto}{ +\fhead{fcntl.h} +\fhead{sys/stat.h} +\fhead{mqueue.h} +\fdecl{mqd\_t mq\_open(const char *name, int oflag)} +\fdecl{mqd\_t mq\_open(const char *name, int oflag, unsigned long mode, + struct mq\_attr *attr)} + +\fdesc{Apre una coda di messaggi POSIX impostandone le caratteristiche.} +} + +{La funzione ritorna il descrittore associato alla coda in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere alla + coda secondo quanto specificato da \param{oflag} oppure \const{name} + contiene più di una ``\texttt{/}''. + \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 dei campi \var{mq\_maxmsg} e \var{mq\_msgsize}; questi valori + devono essere positivi ed inferiori ai limiti di sistema se il processo + non ha privilegi amministrativi, inoltre \var{mq\_maxmsg} non può comunque + superare \const{HARD\_MAX}. + \item[\errcode{ENOENT}] non si è specificato \const{O\_CREAT} ma la coda non + esiste o si è usato il nome ``\texttt{/}''. + \item[\errcode{ENOSPC}] lo spazio è insufficiente, probabilmente per aver + superato il limite di \texttt{queues\_max}. + \end{errlist} + ed inoltre \errval{EMFILE}, \errval{ENAMETOOLONG}, \errval{ENFILE}, + \errval{ENOMEM} ed nel loro significato generico. } +\end{funcproto} + +La funzione apre la coda di messaggi identificata dall'argomento \param{name} +restituendo il descrittore ad essa associato, del tutto analogo ad un file +descriptor, con l'unica differenza che lo standard prevede un apposito tipo +\type{mqd\_t}. 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 il nome indicato fa riferimento ad una coda di messaggi già esistente, il +descrittore ottenuto farà riferimento allo stesso oggetto, pertanto tutti i +processi che hanno usato \func{mq\_open} su quel nome otterranno un +riferimento alla stessa coda. Diventa così immediato costruire un canale di +comunicazione fra detti processi. + +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} (per questo occorre includere \texttt{fcntl.h}) +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} (per questo occorre includere \texttt{sys/stat.h}), 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]{0.90\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 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. + +I suddetti limiti di sistema sono impostati attraverso altrettanti file in +\texttt{/proc/sys/fs/mqueue}, in particolare i file che controllano i valori +dei limiti sono: +\begin{basedescript}{\desclabelwidth{1.5cm}\desclabelstyle{\nextlinelabel}} +\item[\sysctlfile{fs/mqueue/msg\_max}] Indica il valore massimo del numero di + messaggi in una coda e agisce come limite superiore per il valore di + \var{attr->mq\_maxmsg} in \func{mq\_open}. Il suo valore di default è 10. Il + valore massimo è \const{HARD\_MAX} che vale \code{(131072/sizeof(void *))}, + ed il valore minimo 1 (ma era 10 per i kernel precedenti il 2.6.28). Questo + limite viene ignorato per i processi con privilegi amministrativi (più + precisamente con la \textit{capability} \const{CAP\_SYS\_RESOURCE}) ma + \const{HARD\_MAX} resta comunque non superabile. + +\item[\sysctlfile{fs/mqueue/msgsize\_max}] Indica il valore massimo della + dimensione in byte di un messaggio sulla coda ed agisce come limite + superiore per il valore di \var{attr->mq\_msgsize} in \func{mq\_open}. Il + suo valore di default è 8192. Il valore massimo è 1048576 ed il valore + minimo 128 (ma per i kernel precedenti il 2.6.28 detti limiti erano + rispettivamente \const{INT\_MAX} e 8192). Questo limite viene ignorato dai + processi con privilegi amministrativi (con la \textit{capability} + \const{CAP\_SYS\_RESOURCE}). + +\item[\sysctlfile{fs/mqueue/queues\_max}] Indica il numero massimo di code di + messaggi creabili in totale sul sistema, il valore di default è 256 ma si + può usare un valore qualunque fra $0$ e \const{INT\_MAX}. Il limite non + viene applicato ai processi con privilegi amministrativi (cioè con la + \textit{capability} \const{CAP\_SYS\_RESOURCE}). + +\end{basedescript} + +Infine sulle code di messaggi si applica il limite imposto sulla risorsa +\const{RLIMIT\_MSGQUEUE} (vedi sez.~\ref{sec:sys_resource_limit}) che indica +lo spazio massimo (in byte) occupabile dall'insieme di tutte le code di +messaggi appartenenti ai processi di uno stesso utente, che viene identificato +in base al \textit{real user ID} degli stessi. + +Quando l'accesso alla coda non è più necessario si può chiudere il relativo +descrittore con la funzione \funcd{mq\_close}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_close(mqd\_t mqdes)} + +\fdesc{Chiude una coda di messaggi.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori \errval{EBADF} o \errval{EINTR} nel + loro significato generico. +} +\end{funcproto} + +La funzione è analoga a \func{close},\footnote{su Linux, dove le code sono + implementate come file su un filesystem dedicato, è esattamente la stessa + funzione, per cui non esiste una \textit{system call} autonoma e la funzione + viene rimappata su \func{close} dalle \acr{glibc}.} 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 di sistema \funcd{mq\_unlink}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_unlink(const char *name)} + +\fdesc{Rimuove una coda di messaggi.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà gli uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i permessi per cancellare la coda. + \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. + \item[\errcode{ENOENT}] non esiste una coda con il nome indicato. + \end{errlist} +} +\end{funcproto} + +Anche in questo caso il comportamento della funzione è analogo a quello di +\func{unlink} per i file, la funzione rimuove la coda \param{name} (ed il +relativo file sotto \texttt{/dev/mqueue}), così che una successiva chiamata a +\func{mq\_open} fallisce o crea una coda diversa. + +% TODO, verificare se mq_unlink è davvero una system call indipendente. + +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{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_getattr(mqd\_t mqdes, struct mq\_attr *mqstat)} +\fdesc{Legge gli attributi di una coda di messaggi POSIX.} +\fdecl{int mq\_setattr(mqd\_t mqdes, const struct mq\_attr *mqstat, + struct mq\_attr *omqstat)} +\fdesc{Modifica gli attributi di una coda di messaggi POSIX.} +} +{ +Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, + nel qual caso \var{errno} assumerà i valori \errval{EBADF} + o \errval{EINVAL} nel loro significato generico. +} +\end{funcproto} + +La funzione \func{mq\_getattr} legge i valori correnti degli attributi della +coda \param{mqdes} nella struttura \struct{mq\_attr} 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 impostate in fase di +apertura. + +La funzione \func{mq\_setattr} permette di modificare gli attributi di una +coda (indicata da \param{mqdes}) tramite i valori contenuti nella struttura +\struct{mq\_attr} puntata da \param{mqstat}, ma può essere modificato solo il +campo \var{mq\_flags}, gli altri campi vengono comunque 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 di sistema, +\funcd{mq\_send} e \funcd{mq\_timedsend}. In realtà su Linux la \textit{system + call} è soltanto \func{mq\_timedsend}, mentre \func{mq\_send} viene +implementata come funzione di libreria che si appoggia alla +precedente. Inoltre \func{mq\_timedsend} richiede che sia definita la macro +\macro{\_XOPEN\_SOURCE} ad un valore pari ad almeno \texttt{600} o la macro +\macro{\_POSIX\_C\_SOURCE} ad un valore uguale o maggiore di \texttt{200112L}. +I rispettivi prototipi sono: + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_send(mqd\_t mqdes, const char *msg\_ptr, size\_t msg\_len, + unsigned int msg\_prio)} +\fdesc{Esegue l'inserimento di un messaggio su una coda.} +\fhead{mqueue.h} +\fhead{time.h} +\fdecl{int mq\_timedsend(mqd\_t mqdes, const char *msg\_ptr, size\_t + msg\_len, \\ +\phantom{int mq\_timedsend(}unsigned int msg\_prio, const struct timespec *abs\_timeout)} +\fdesc{Esegue l'inserimento di un messaggio su una coda entro un tempo + specificato} +} + +{Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, + nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la + coda è piena. + \item[\errcode{EBADF}] si specificato un file descriptor non valido. + \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{EMSGSIZE}] la lunghezza del messaggio \param{msg\_len} + eccede il limite impostato per la coda. + \item[\errcode{ETIMEDOUT}] l'inserimento del messaggio non è stato + effettuato entro il tempo stabilito (solo \func{mq\_timedsend}). + \end{errlist} + ed inoltre \errval{EBADF} e \errval{EINTR} nel loro significato generico. +} +\end{funcproto} + +Entrambe le funzioni richiedono un puntatore ad un buffer in memoria +contenente il testo del messaggio da inserire nella coda \param{mqdes} +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, che, essendo +definito come \ctyp{unsigned int}, è sempre un intero positivo. 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 che hanno la stessa priorità +che quindi saranno letti con la politica di una \textit{fifo}. Il valore della +priorità non può eccedere il limite di sistema \const{MQ\_PRIO\_MAX}, che al +momento è pari a 32768. + +Qualora la coda sia piena, entrambe le funzioni si bloccano, a meno che non +sia stata selezionata in fase di apertura della stessa la modalità non +bloccante o non si sia impostato il flag \const{O\_NONBLOCK} sul file +descriptor della coda, nel qual caso entrambe ritornano con un codice di +errore di \errcode{EAGAIN}. + +La sola differenza fra le due funzioni è che \func{mq\_timedsend}, passato il +tempo massimo impostato con l'argomento \param{abs\_timeout}, ritorna con un +errore di \errcode{ETIMEDOUT}, se invece il tempo è già scaduto al momento +della chiamata e la coda è piena la funzione ritorna immediatamente. Il valore +di \param{abs\_timeout} deve essere specificato come 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. + +Come per l'inserimento, anche per l'estrazione dei messaggi da una coda sono +previste due funzioni di sistema, \funcd{mq\_receive} e +\funcd{mq\_timedreceive}. Anche in questo caso su Linux soltanto +\func{mq\_timedreceive} è effettivamente, una \textit{system call} e per +usarla devono essere definite le opportune macro come per +\func{mq\_timedsend}. I rispettivi prototipi sono: + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{ssize\_t mq\_receive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio)} +\fdesc{Effettua la ricezione di un messaggio da una coda.} +\fhead{mqueue.h} +\fhead{time.h} +\fdecl{ssize\_t mq\_timedreceive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len,\\ +\phantom{ssize\_t mq\_timedreceive(}unsigned int *msg\_prio, const struct timespec +*abs\_timeout)} +\fdesc{Riceve un messaggio da una coda entro un limite di tempo.} +} +{Entrambe le funzioni ritornano il numero di byte del messaggio in caso di + successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei + valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la + coda è vuota. + \item[\errcode{EINVAL}] si è specificato un valore nullo per + \param{msg\_ptr}, o un valore non valido per \param{abs\_timeout}. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio sulla coda eccede il + valore \param{msg\_len} specificato per la ricezione. + \item[\errcode{ETIMEDOUT}] la ricezione del messaggio non è stata + effettuata entro il tempo stabilito. + \end{errlist} + ed inoltre \errval{EBADF} o \errval{EINTR} nel loro significato generico. } +\end{funcproto} + +La funzione estrae dalla coda \param{mqdes} 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; si tenga presente che 0 è una dimensione valida e che la +condizione di errore è indicata soltanto da un valore di +$-1$.\footnote{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}. + +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{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_notify(mqd\_t mqdes, const struct sigevent *notification)} + +\fdesc{Attiva il meccanismo di notifica per 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{EBADF}] il descrittore non fa riferimento ad una coda di + messaggi. + \item[\errcode{EBUSY}] c'è già un processo registrato per la notifica. + \item[\errcode{EINVAL}] si è richiesto un meccanismo di notifica invalido + o specificato nella notifica con i segnali il valore di un segnale non + esistente. + \end{errlist} + ed inoltre \errval{ENOMEM} nel suo significato generico. +} +\end{funcproto} + +Il meccanismo di notifica permette di segnalare in maniera asincrona ad un +processo la presenza di dati sulla coda indicata da \param{mqdes}, 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 dai valori passati con +l'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 +su di essa si può rivedere 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}; fra questi la pagina di +manuale riporta soltanto i primi tre, ed 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. 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 (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'\textsl{user-ID} 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, 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} (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{FileExample}[label=/etc/fstab] +tmpfs /dev/shm tmpfs defaults 0 0 +\end{FileExample} +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{Example} +mount -t tmpfs -o size=128M,nr_inodes=10k,mode=700 tmpfs /mytmpfs +\end{Example} + +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{funcproto}{ +\fhead{sys/mman.h} +\fhead{sys/stat.h} +\fhead{fcntl.h} +\fdecl{int shm\_open(const char *name, int oflag, mode\_t mode)} + +\fdesc{Apre un segmento di memoria condivisa.} +} + +{La funzione ritorna un file descriptor in caso di successo e $-1$ per un + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i permessi di aprire il segmento nella + modalità scelta o si richiesto \const{O\_TRUNC} per un segmento su cui non + si ha il permesso di scrittura. + \item[\errcode{EINVAL}] si è utilizzato un nome non valido. + \end{errlist} + ed inoltre \errval{EEXIST}, \errval{EMFILE}, \errval{ENAMETOOLONG}, + \errval{ENFILE} e \errval{ENOENT} nello stesso significato che hanno per + \func{open}. +} +\end{funcproto} + + +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 +``\texttt{/}'' e senza ulteriori ``\texttt{/}''. 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} viste in +sez.~\ref{sec:file_open_close}. Inoltre sul file descriptor viene sempre +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 ordinari, 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 si può creare un nuovo segmento specificando +\const{O\_CREAT}; in tal caso il segmento avrà (così come i nuovi file) +lunghezza nulla. Il nuovo segmento verrà creato con i permessi indicati +da \param{mode} (di cui vengono usati solo i 9 bit meno significativi, non si +applicano pertanto i permessi speciali di sez.~\ref{sec:file_special_perm}) +filtrati dal valore dell'\textit{umask} del processo. Come gruppo ed utente +proprietario del segmento saranno presi quelli facenti parte del gruppo +\textit{effective} del processo chiamante. + +Dato che un segmento di lunghezza nulla è di scarsa utilità, una vola che lo +si è creato per impostarne la dimensione si dovrà poi 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 ad esso associato (semplicemente con +\func{close}), senza che la mappatura ne risenta, e che questa può essere +rimossa usando \func{munmap}. + +Come per i file, quando si vuole rimuovere completamente un segmento di +memoria condivisa occorre usare la funzione \funcd{shm\_unlink}, il cui +prototipo è: + +\begin{funcproto}{ +\fhead{sys/mman.h} +\fdecl{int shm\_unlink(const char *name)} + +\fdesc{Rimuove un segmento di memoria condivisa.} +} + +{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}] non si è proprietari del segmento. + \end{errlist} + ed inoltre \errval{ENAMETOOLONG} e \errval{ENOENT}, nel loro significato + generico. +} +\end{funcproto} + +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. + +Dato che i segmenti di memoria condivisa sono trattati come file del +filesystem \texttt{tmpfs}, si possono usare su di essi, con lo stesso +significato che assumono sui file ordinari, anche funzioni come quelle delle +famiglie \func{fstat}, \func{fchown} e \func{fchmod}. Inoltre a partire dal +kernel 2.6.19 per i permessi sono supportate anche le ACL illustrate in +sez.~\ref{sec:file_ACL}. + +Come esempio dell'uso delle funzioni attinenti ai segmenti di memoria +condivisa POSIX, 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 completo, di cui si sono riportate +le parti essenziali in fig.~\ref{fig:ipc_posix_shmmem}, è contenuto nel file +\file{SharedMem.c} dei sorgenti allegati. + +\begin{figure}[!htb] + \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} + +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 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} (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} (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 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} o con +\texttt{-lpthread} se si usano questi ultimi. + +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{funcproto}{ +\fhead{semaphore.h} +\fhead{sys/stat.h} +\fhead{fcntl.h} +\fdecl{sem\_t *sem\_open(const char *name, int oflag)} +\fdecl{sem\_t *sem\_open(const char *name, int oflag, mode\_t mode, + unsigned int value)} + +\fdesc{Crea un semaforo o ne apre uno esistente.} +} +{La funzione ritorna l'indirizzo del semaforo in caso di successo e + \const{SEM\_FAILED} per un errore, nel qual caso \var{errno} assumerà uno + dei 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} o il nome è solo ``\texttt{/}''. + \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{EMFILE}, \errval{ENFILE} ed \errval{ENOMEM} nel loro + significato generico. + +} +\end{funcproto} + +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 nella stessa forma utilizzata +per i segmenti di memoria condivisa, con un nome che inizia con ``\texttt{/}'' +e senza ulteriori ``\texttt{/}'', vale a dire nella forma +\texttt{/nome-semaforo}. + +Con Linux i file associati ai semafori sono mantenuti nel filesystem virtuale +\texttt{/dev/shm}, e gli viene assegnato automaticamente un nome nella forma +\texttt{sem.nome-semaforo}, si ha cioè una corrispondenza per cui +\texttt{/nome-semaforo} viene rimappato, nella creazione tramite +\func{sem\_open}, su \texttt{/dev/shm/sem.nome-semaforo}. Per questo motivo la +dimensione massima per il nome di un semaforo, a differenza di quanto avviene +per i segmenti di memoria condivisa, è pari a \const{NAME\_MAX}$ - 4$. + +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}; se il semaforo esiste già questi saranno semplicemente +ignorati. Usando il flag \const{O\_EXCL} si richiede invece la verifica che il +semaforo non esista, ed usandolo insieme ad \const{O\_CREAT} la funzione +fallisce qualora un semaforo con lo stesso nome sia già presente. + +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. + +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, e non sarà +più necessario fare riferimento al nome, che potrebbe anche essere rimosso con +\func{sem\_unlink}. + +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{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_wait(sem\_t *sem)} + +\fdesc{Blocca un semaforo.} +} + +{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{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{funcproto} + +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 consentendo la +prosecuzione del processo. + +Se invece il valore è nullo la funzione si blocca (fermando l'esecuzione del +processo) fintanto che il valore del semaforo non ritorna positivo (cosa che a +questo punto può avvenire solo per opera di altro processo che rilascia il +semaforo con una chiamata a \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}; inoltre questo avverrà +comunque, anche qualora si fosse richiesta la gestione con la semantica BSD, +installando il gestore del suddetto segnale con l'opzione \const{SA\_RESTART} +(vedi sez.~\ref{sec:sig_sigaction}) per riavviare le \textit{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{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_trywait(sem\_t *sem)} + +\fdesc{Tenta di bloccare un semaforo.} +} +{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{EAGAIN}] il semaforo non può essere acquisito senza + bloccarsi. + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} + +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 almeno 600 o la macro \macro{\_POSIX\_C\_SOURCE} ad un valore +uguale o maggiore di \texttt{200112L} prima di includere +\headfile{semaphore.h}, la funzione è \funcd{sem\_timedwait}, ed il suo +prototipo è: + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_timedwait(sem\_t *sem, const struct timespec + *abs\_timeout)} + +\fdesc{Blocca un semaforo.} +} + +{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{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] l'argomento \param{sem} non indica un semaforo + valido. + \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. + \end{errlist} +} +\end{funcproto} + +Anche in questo caso il comportamento della funzione è identico a quello di +\func{sem\_wait}, ma è possibile impostare un tempo limite per l'attesa +tramite la struttura \struct{timespec} (vedi +fig.~\ref{fig:sys_timespec_struct}) puntata +dall'argomento \param{abs\_timeout}, indicato in secondi e nanosecondi a +partire dalla cosiddetta \textit{Epoch} (00:00:00, 1 January 1970 +UTC). Scaduto il limite la funzione ritorna anche se non è possibile acquisire +il semaforo fallendo con un errore di \errval{ETIMEDOUT}. + +La seconda funzione principale utilizzata per l'uso dei semafori è quella 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 (si +ricordi che in generale un semaforo viene usato come indicatore di un numero +di risorse disponibili). Detta funzione è \funcd{sem\_post} ed il suo +prototipo è: + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_post(sem\_t *sem)} + +\fdesc{Rilascia un semaforo.} +} + +{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}] l'argomento \param{sem} non indica un semaforo + valido. + \item[\errcode{EOVERFLOW}] si superato il massimo valore di un semaforo. + \end{errlist} +} +\end{funcproto} + +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 possa 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 volesse semplicemente leggere il +valore, si potrà usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_getvalue(sem\_t *sem, int *sval)} + +\fdesc{Richiede il valore di un semaforo.} +} +{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}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} + +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{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_close(sem\_t *sem)} + +\fdesc{Chiude un semaforo.} +} + +{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}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} + +La funzione chiude il semaforo indicato dall'argomento \param{sem}, che non +potrà più essere utilizzato nelle altre funzioni. La chiusura comporta anche +che tutte le risorse che il sistema poteva avere assegnato al processo +nell'uso del semaforo vengono immediatamente rilasciate. Questo significa che +un eventuale altro processo bloccato sul semaforo a causa della acquisizione +dello stesso da parte del processo che chiama \func{sem\_close} potrà essere +immediatamente riavviato. + +Si tenga presente poi che come avviene per i file, all'uscita di un processo +anche 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 infine 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{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_unlink(const char *name)} + +\fdesc{Rimuove un semaforo.} +} + +{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}] 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{funcproto} + +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{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} +\fdesc{Inizializza un semaforo anonimo.} +} + +{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{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{funcproto} + +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}) a cui essi poi potranno accedere (si ricordi +che i tratti di memoria condivisa vengono mantenuti nei processi figli +attraverso la funzione \func{fork}). + +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. + +Qualora 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{funcproto}{ +\fhead{semaphore.h} +\fdecl{int sem\_destroy(sem\_t *sem)} +\fdesc{Elimina un semaforo anonimo.} +} +{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}] l'argomento \param{sem} non indica un semaforo + valido. + \end{errlist} +} +\end{funcproto} + +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 12-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 22}) 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 24-27}) 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}[!htb] + \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 30-33}) 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 fermarlo con una +interruzione 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}[!htb] + \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 coda a -\secref{sec:ipc_sysv_generic}. +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 pipefd SZ +% 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 SETPIPE +% 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 fclose +% 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 fs +% LocalWords: Larry Wall Escape the Hell William ipctestid Identifier segment +% LocalWords: violation dell'I SIGINT setter Fri Dec Sleeping seconds ECHILD +% LocalWords: SysV capability short RESOURCE INFO UNDEFINED EFBIG semtimedop +% LocalWords: scan HUGETLB huge page NORESERVE copy RLIMIT MEMLOCK REMAP UTC +% LocalWords: readmon Hierarchy defaults queues MSGQUEUE effective fstat +% LocalWords: fchown fchmod Epoch January %%% Local Variables: