X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=da8d31719fb989b7878b2087d3db17360d19bd70;hp=7dd8c67907ad02b670c4e76a5a8482cee72705f8;hb=193d612d40c5f81f5559ea6e11e70f6b6e51fb39;hpb=c4ee73fb823eafce8bb5e4faa9caebc4105dbb17 diff --git a/ipc.tex b/ipc.tex index 7dd8c67..bb9e0b0 100644 --- a/ipc.tex +++ b/ipc.tex @@ -1,71 +1,78 @@ +%% ipc.tex +%% +%% Copyright (C) 2000-2011 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{La comunicazione fra processi} \label{cha:IPC} -Uno degli aspetti fondamentali della programmazione in un sistema unix-like è +Uno degli aspetti fondamentali della programmazione in un sistema unix-like è la comunicazione fra processi. In questo capitolo affronteremo solo i -meccanismi più elementari che permettono di mettere in comunicazione processi +meccanismi più elementari che permettono di mettere in comunicazione processi diversi, come quelli tradizionali che coinvolgono \textit{pipe} e -\textit{fifo} e i meccanismi di intercomunicazione di System V. +\textit{fifo} e i meccanismi di intercomunicazione di System V e quelli POSIX. Tralasceremo invece tutte le problematiche relative alla comunicazione attraverso la rete (e le relative interfacce) che saranno affrontate in -dettaglio in un secondo tempo. Non affronteremo invece meccanismi più +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. - \section{La comunicazione fra processi tradizionale} \label{sec:ipc_unix} -Il primo meccanismo di comunicazione fra processi usato dai sistemi unix-like, -e quello che viene correntemente usato di più, è quello delle \textit{pipe}, -che sono una delle caratteristiche peculiari del sistema, in particolar modo -dell'interfaccia a linea di comando. In questa sezione descriveremo le sue -basi, le funzioni che ne gestiscono l'uso e le varie forme in cui si è -evoluto. +Il primo meccanismo di comunicazione fra processi introdotto nei sistemi Unix, +è quello delle cosiddette \textit{pipe}; esse costituiscono una delle +caratteristiche peculiari del sistema, in particolar modo dell'interfaccia a +linea di comando. In questa sezione descriveremo le sue basi, le funzioni che +ne gestiscono l'uso e le varie forme in cui si è evoluto. \subsection{Le \textit{pipe} standard} \label{sec:ipc_pipes} Le \textit{pipe} nascono sostanzialmente con Unix, e sono il primo, e tuttora -uno dei più usati, meccanismi di comunicazione fra processi. Si tratta in -sostanza di uno speciale tipo di file descriptor, più precisamente una coppia -di file descriptor,\footnote{si tenga presente che le pipe sono oggetti creati - dal kernel e non risiedono su disco.} su cui da una parte si scrive e da -un'altra si legge. Si viene così a costituire un canale di comunicazione -tramite i due file descriptor, nella forma di un \textsl{tubo} (da cui il -nome) in cui in genere un processo immette dati che poi arriveranno ad un -altro. - -La funzione che permette di creare una pipe è appunto \func{pipe}; il suo -prototipo è: +uno dei più usati, meccanismi di comunicazione fra processi. Si tratta in +sostanza di una coppia di file descriptor\footnote{si tenga presente che + le pipe sono oggetti creati dal kernel e non risiedono su disco.} connessi +fra di loro in modo che se quanto scrive su di uno si può rileggere +dall'altro. Si viene così a costituire un canale di comunicazione tramite i +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 \funcd{pipe}, ed il suo prototipo è: \begin{prototype}{unistd.h} {int pipe(int filedes[2])} -Crea una coppia di file descriptor associati ad una pipe. +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}.} + errore, nel qual caso \var{errno} potrà assumere i valori \errval{EMFILE}, + \errval{ENFILE} e \errval{EFAULT}.} \end{prototype} -La funzione restituisce una coppia di file descriptor nell'array -\param{filedes}; il primo aperto in lettura ed il secondo in scrittura. Il -concetto di funzionamento di una pipe è relativamente semplice, quello che si +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 pipe è semplice: quello che si scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale -nel file descriptor aperto in lettura, da cui può essere riletto. - -I file descriptor infatti non sono connessi a nessun file reale, ma ad un -buffer nel kernel, la cui dimensione è specificata dalla costante -\macro{PIPE\_BUF}, (vedi \secref{sec:sys_file_limits}); lo schema di -funzionamento di una pipe è illustrato in \figref{fig:ipc_pipe_singular}, in -cui sono illustrati i due capi della pipe, associati a ciascun file -descriptor, con le frecce che indicano la direzione del flusso dei dati -attraverso la pipe. +nel file descriptor aperto in lettura. I file descriptor infatti non sono +connessi a nessun file reale, ma, come accennato in +sez.~\ref{sec:file_sendfile_splice}, 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 pipe è +illustrato in fig.~\ref{fig:ipc_pipe_singular}, in cui sono illustrati i due +capi della pipe, associati a ciascun file descriptor, con le frecce che +indicano la direzione del flusso dei dati. \begin{figure}[htb] \centering @@ -74,247 +81,4550 @@ attraverso la pipe. \label{fig:ipc_pipe_singular} \end{figure} -Chiaramente creare una pipe all'interno di un processo non serve a niente; se -però ricordiamo quanto esposto in \secref{sec:file_sharing} riguardo al -comportamento dei file descriptor nei processi figli, è immediato capire come -una pipe possa diventare un meccanismo di intercomunicazione. Un processo -figlio infatti condivide gli stessi file descriptor del padre, compresi quelli -associati ad una pipe (secondo la situazione illustrata in -\figref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un -capo della pipe, l'altro può leggere. +Chiaramente creare una pipe all'interno di un singolo processo non serve a +niente; se però ricordiamo quanto esposto in sez.~\ref{sec:file_sharing} +riguardo al comportamento dei file descriptor nei processi figli, è immediato +capire come una pipe possa diventare un meccanismo di intercomunicazione. Un +processo figlio infatti condivide gli stessi file descriptor del padre, +compresi quelli associati ad una pipe (secondo la situazione illustrata in +fig.~\ref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un +capo della pipe, l'altro può leggere. \begin{figure}[htb] \centering \includegraphics[height=5cm]{img/pipefork} - \caption{Schema dell'uso di una pipe come mezzo di comunicazione fra - processo attraverso una \func{fork}.} + \caption{Schema dei collegamenti ad una pipe, condivisi fra processo padre e + figlio dopo l'esecuzione \func{fork}.} \label{fig:ipc_pipe_fork} \end{figure} -Tutto ciò ci mostra come sia immediato realizzare un meccanismo di -comunicazione fra processi attraverso una pipe, utilizzando le ordinarie -proprietà dei file, ma ci mostra anche qual'è il principale\footnote{Stevens - riporta in APUE come limite anche il fatto che la comunicazione è - unidirezionale, in realtà questo è un limite facilmente risolvibile usando - una coppia di pipe.} limite nell'uso delle pipe. È necessario infatti che i +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 derivare da uno stesso processo padre che ha aperto la pipe, -o, più comunemente, essere nella relazione padre/figlio. +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 \const{SIGPIPE}, e la funzione di scrittura +restituirà un errore di \errcode{EPIPE} (al ritorno del gestore, o qualora il +segnale sia ignorato o bloccato). +La dimensione del buffer della pipe (\const{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} \label{sec:ipc_pipe_use} -Per capire meglio il funzionamento di una pipe faremo un esempio di quello che -è il loro uso più comune, analogo a quello effettuato della shell, e che +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. Realizzaremo il programma nella forma di un -\textit{cgi-bin}\footnote{breve descrizione, da fare, di cosa è un cgi-bin.} -per apache, che genera una immagine JPEG di un codice a barre, specificato -come parametro di input. - -Un programma che deve essere eseguito come \textit{cgi-bin} per apache 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: +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 argomento in ingresso. + +Un programma che deve essere eseguito come \textit{CGI} deve rispondere a +delle caratteristiche specifiche, esso infatti non viene lanciato da una +shell, ma dallo stesso web server, alla richiesta di una specifica URL, che di +solito ha la forma: \begin{verbatim} -http://www.sito.it/cgi-bin/programma?parametro + http://www.sito.it/cgi-bin/programma?argomento \end{verbatim} ed il risultato dell'elaborazione deve essere presentato (con una intestazione -che ne descrive il mime-type) sullo standard output, in modo che apache possa -reinviarlo al browser che ha effettuato la richiesta. - - -Per fare questo useremo in sequenza i programmi \cmd{barcode} e \cmd{gs}, il -primo infatti è in grado di generare immagini postscript di codici a barre -corrispondenti ad una qualunque stringa, mentre il secondo serve per poter -effettuare la conversione della stessa immagine in formato JPEG. - -Si potrebbe obiettare che sarebbe molto più semplice salvare il risultato -intermedio su un file temporaneo. Questo però non tiene conto del fatto che il -\textit{cgi-bin} deve poter gestire più richieste in concorrenza, e si avrebbe -una evidente race condition in caso di accesso simultaneo a detto -file.\footnote{la questione potrebbe essere evitata creando prima dei file - temporanei, da comunicare poi ai vari processi, e da cancellare alla fine - dell'esecuzione; ma a questo punto avremmo perso tutta la semplicità.} L'uso -di una pipe invece permette di risolvere il problema in maniera semplice ed -elegante. - -Il programma ci servirà anche come esempio dell'uso di alcune delle funzioni -di manipolazione dei file descriptor, come \func{dup} e \func{dup2}, viste in -\secref{sec:file_dup}; è attraverso queste funzioni 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. Le -sezioni significative del programma è riportato in -\figref{fig:ipc_barcode_code}, il codice è disponibile nel file -\file{BarCode.c} nella directory dei sorgenti. +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 fig.~\ref{fig:ipc_pipe_use}, in cui la +direzione del flusso dei dati è data dalle frecce continue. +\begin{figure}[htb] + \centering + \includegraphics[height=5cm]{img/pipeuse} + \caption{Schema dell'uso di una pipe come mezzo di comunicazione fra + due processi attraverso 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 \itindex{race~condition} \textit{race condition} in caso di accesso +simultaneo a detto file.\footnote{il problema potrebbe essere superato + determinando in anticipo un nome appropriato per il file temporaneo, che + verrebbe utilizzato dai vari sotto-processi, e cancellato alla fine della + loro esecuzione; ma a questo punto le cose non sarebbero più tanto + semplici.} L'uso di una pipe invece permette di risolvere il problema in +maniera semplice ed elegante, oltre ad essere molto più efficiente, dato che +non si deve scrivere su disco. + +Il programma ci servirà anche come esempio dell'uso delle funzioni di +duplicazione dei file descriptor che abbiamo trattato in +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 sez.~\ref{sec:file_std_descr} e +sez.~\ref{sec:file_std_stream}) sulla pipe. In +fig.~\ref{fig:ipc_barcodepage_code} abbiamo riportato il corpo del programma, +il cui codice completo è disponibile nel file \file{BarCodePage.c} che si +trova nella directory dei sorgenti. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}{} -int main(int argc, char *argv[], char *envp[]) -{ - ... - /* create two pipes to handle process communication */ - if ( (retval = pipe(pipein)) ) { - WriteMess("input pipe creation error"); - exit(0); - } - if ( (retval = pipe(pipeout)) ) { - WriteMess("output pipe creation error"); - exit(0); - } - /* fork child to run barcode program */ - pid = fork(); - if (pid == -1) { - WriteMess("child creation error"); - exit(0); - } - /* if child */ - if (pid == 0) { - close(pipein[1]); /* close output side of input pipe */ - dup2(0, pipein[0]); /* remap stdin in pipe input */ - close(pipeout[0]); /* close input side of output pipe */ - dup2(1, pipeout[1]); /* remap stdout in pipe output */ - execlp("barcode", "barcode", "-E", NULL); - } else { - /* first set the pipe */ - close(pipein[0]); /* close input side of input pipe */ - close(pipeout[1]); /* close output side of output pipe */ - write(pipein[1], argv[1], strlen(argv[1])); - close(pipein[1]); - waitpid(pid, NULL, 0); - pid = fork(); - if (pid == -1) { - WriteMess("child creation error") - exit(0); - } - if (pid == 0) { - /* send mime type */ - write(0,content, strlen(content)); - dup2(0, pipeout[0]); - execlp("gs", "gs", "-sDEVICE=jpeg", "-sOutputFile=-", "-", NULL); - } else { - close(pipeout[0]); - waitpid(pid, NULL, 0); - } - } - exit(0); -} - \end{lstlisting} + \includecodesample{listati/BarCodePage.c} \end{minipage} \normalsize - \caption{Codice del \textit{cgi-bin} \cmd{BarCode}.} - \label{fig:ipc_barcode_code} + \caption{Sezione principale del codice del \textit{CGI} + \file{BarCodePage.c}.} + \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 + 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.} -Il primo passo (\texttt{\small 4-12}) è quello di creare le due pipe, una per -l'input e l'altra per l'output, che servono per la comunicazione con i due -programmi che verranno utilizzati, inviando in caso di errore (attraverso una -apposita funzione \func{WriteMess}, non riportata in -\ref{fig:ipc_barcode_code}, che si incarica di formattare l'output in HTML -perché sia interpretabile da un browser) un messaggio invece dell'immagine -richiesta. +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 pipe il programma può creare (\texttt{\small 13-18}) il -primo processo figlio, che si incaricherà (\texttt{\small 19-25}) di eseguire -\cmd{barcode}: quest'ultimo funziona ricevendo dallo standard input la stringa -da convertire nell'immagine postscript del codice a barre che sarà scritta -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}). -Per questo il processo figlio prima chiude (\texttt{\small 21}) il capo aperto -in scrittura della prima pipe (che sarà usato dal padre per trasmettergli la -stringa da codificare), e poi collega (\texttt{\small 22}) il capo il lettura -allo standard input usando \func{dup2}. Analogamente il capo in lettura della -seconda pipe sarà chiuso mentre il capo in scrittura viene collegato allo -standard output (\texttt{\small 23-24}. In questo modo all'esecuzione -(\texttt{\small 25}) di \cmd{barcode} quest'ultimo leggerà la stringa da -codificare dalla prima pipe e scriverà l'immagine postscript nella seconda. +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. -Dall'altra parte il processo padre prima chiude (\texttt{\small 28-29}) i due -capi inutilizzati delle pipe (input della prima ed output della seconda), poi -scrive (\texttt{\small 30}) la stringa da convertire sull'output della prima -pipe così che \cmd{barcode} possa riceverla dallo standard input; a questo -punto l'uso della prima pipe è finito ed essa può essere definitivamente -chiusa (\texttt{\small 31}), si attenderà poi (\texttt{\small 32}) che pure -l'esecuzione di \cmd{barcode} venga completata. +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à effettuato inviato -l'immagine postscript del codice a barre sul capo in scrittura della seconda -pipe; +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. +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. \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 +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 comuni in una sola -chiamata. La prima di esse si chiama \func{popen} ed il suo prototipo è: +che permettono di sintetizzare queste operazioni. La prima di esse si chiama +\funcd{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 \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{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 cap.~\ref{cha:files_std_interface}, anche se è collegato ad +una pipe e non ad un file, e viene sempre aperto in modalità +\textit{fully-buffered} (vedi sez.~\ref{sec:file_buffering}); l'unica +differenza con gli usuali stream è che dovrà essere chiuso dalla seconda delle +due nuove funzioni, \funcd{pclose}, il cui prototipo è: +\begin{prototype}{stdio.h} +{int pclose(FILE *stream)} + +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 \var{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}. -L'esempio in \figref{fig:ipc_barcode_code} per quanto perfettamente -funzionante, è piuttosto complesso; 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 tutte le volte generata una pagina intera, invece che una semplice figura. -Se si vuole generare una immagine di dimensioni corrette si deve allora -ricorrere ad ulteriore programma, \cmd{epstopsf}, per convertire in PDF il -file EPS generato da \cmd{barcode}, che invece viene trattato correttamente. +Per illustrare l'uso di queste due funzioni riprendiamo il problema +precedente: il programma mostrato in fig.~\ref{fig:ipc_barcodepage_code} per +quanto funzionante, è (volutamente) codificato in maniera piuttosto complessa, +inoltre nella pratica sconta un problema di \cmd{gs} che non è in +grado\footnote{nella versione GNU Ghostscript 6.53 (2002-02-13).} di +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. + +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 +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 esegue la 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 \errcode{ESPIPE}, rendendo impossibile la conversione. Questo ci dice che +in generale la concatenazione di vari programmi funzionerà soltanto quando +tutti prevedono una lettura sequenziale del loro input. + +Per questo motivo si è dovuto utilizzare un procedimento diverso, eseguendo +prima la conversione (sempre con \cmd{gs}) del PS in un altro formato +intermedio, il PPM,\footnote{il \textit{Portable PixMap file format} è un + formato usato spesso come formato intermedio per effettuare conversioni, è + infatti molto facile da manipolare, dato che usa caratteri ASCII per + memorizzare le immagini, anche se per questo è estremamente inefficiente.} +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 +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. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \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. +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. +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. +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}. \subsection{Le \textit{pipe} con nome, o \textit{fifo}} \label{sec:ipc_named_pipe} -Per poter superare il problema delle \textit{pipe}, illustrato in -\secref{sec:ipc_pipes}, che ne consente l'uso solo fra processi con un -progenitore comune o nella relazione padre/figlio, lo standard POSIX.1 -definisce dei nuovi oggetti, le \textit{fifo}, che invece possono risiedere -sul filesystem, e che i processi possono usare per le comunicazioni senza -dovere per forza essere in relazione diretta. +Come accennato in sez.~\ref{sec:ipc_pipes} il problema delle \textit{pipe} è +che esse possono essere utilizzate solo da processi con un progenitore comune +o nella relazione padre/figlio; per superare questo problema lo standard +POSIX.1 ha definito dei nuovi oggetti, le \textit{fifo}, che hanno le stesse +caratteristiche delle pipe, ma che invece di essere strutture interne del +kernel, visibili solo attraverso un file descriptor, sono accessibili +attraverso un \index{inode} 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; +\index{inode} 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 sez.~\ref{sec:ipc_pipes}. + +Abbiamo già visto in sez.~\ref{sec:file_mknod} le funzioni \func{mknod} e +\func{mkfifo} che permettono di creare una fifo; per utilizzarne una un +processo non avrà che da aprire il relativo file speciale o in lettura o +scrittura; nel primo caso sarà collegato al capo di uscita della fifo, e dovrà +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 \errcode{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 situazioni di +stallo.\footnote{se si cerca di leggere da una fifo che non contiene dati si + avrà un \itindex{deadlock} 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 \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 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 sez.~\ref{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 +``\textsl{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 in fig.~\ref{fig:ipc_fifo_server_arch} in cui i client inviano le +richieste al server su una fifo nota mentre le risposte vengono reinviate dal +server a ciascuno di essi su una fifo temporanea creata per l'occasione. + +\begin{figure}[htb] + \centering + \includegraphics[height=9cm]{img/fifoserver} + \caption{Schema dell'utilizzo delle 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 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 importabili +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 il settaggio delle +variabili \var{fortunefilename}, che indica il file da cui leggere le frasi, +ed \var{n}, che indica il numero di frasi tenute in memoria, ad un valore +diverso da quelli preimpostati. Il codice completo è nel file +\file{FortuneServer.c}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/FortuneServer.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del server di \textit{fortunes} + basato sulle 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 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 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 +fifo). + +Una volta che si è certi che la fifo di ascolto esiste la procedura di +inizializzazione è completata. A questo punto si può chiamare (\texttt{\small + 23}) la funzione \func{daemon} per far proseguire l'esecuzione del programma +in background come demone. Si può quindi procedere (\texttt{\small 24--33}) +alla apertura della 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 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 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 fifo. +A questo punto il server resta (se non ci sono altri client che stanno +effettuando richieste) con la fifo chiusa sul lato in lettura, ed in questo +stato la funzione \func{read} non si bloccherà in attesa di input, ma +ritornerà in continuazione, restituendo un end-of-file.\footnote{si è usata + questa tecnica per compatibilità, Linux infatti supporta l'apertura delle + 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 fifo in modalità non bloccante, per evitare il rischio di uno stallo: se + infatti nessuno apre la 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 +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 fifo). + +Il server è progettato per accettare come richieste dai client delle stringhe +che contengono il nome della fifo sulla quale deve essere inviata la risposta. +Per cui prima (\texttt{\small 35--39}) si esegue la lettura dalla stringa di +richiesta dalla 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 fifo per la risposta, che poi \texttt{\small 47--48}) vi +sarà scritta. Infine (\texttt{\small 49}) si chiude la 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}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/FortuneClient.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del client di \textit{fortunes} + basato sulle fifo.} + \label{fig:ipc_fifo_client} +\end{figure} + +La prima istruzione (\texttt{\small 12}) compone il nome della fifo che dovrà +essere utilizzata per ricevere la risposta dal server. Si usa il \acr{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 fifo nota (\texttt{\small 19--23}), e poi ci si scrive +(\texttt{\small 24}) la stringa composta in precedenza, che contiene il nome +della fifo da utilizzare per la risposta. Infine si richiude la 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 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 +fifo e si cancella (\texttt{\small 34}) il relativo file. +Si noti come la 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 speciale variabile di ambiente \code{LD\_LIBRARY\_PATH} +in modo che il linker dinamico possa accedervi. + +In generale questa variabile indica il \itindex{pathname} \textit{pathname} +della directory contenente la libreria. Nell'ipotesi (che daremo sempre per +verificata) che si facciano le prove direttamente nella directory dei sorgenti +(dove di norma vengono creati sia i programmi che la libreria), il comando da +dare sarà \code{export LD\_LIBRARY\_PATH=./}; a questo punto potremo lanciare +il server, facendogli leggere una decina di frasi, con: +\begin{Verbatim} +[piccardi@gont sources]$ ./fortuned -n10 +\end{Verbatim} +%$ + +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{Verbatim} +[piccardi@gont sources]$ 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{Verbatim} +%$ +e si potrà verificare anche che in \file{/tmp} è stata creata la fifo di +ascolto \file{fortune.fifo}. A questo punto potremo interrogare il server con +il programma client; otterremo così: +\begin{Verbatim} +[piccardi@gont sources]$ ./fortune +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +[piccardi@gont sources]$ ./fortune +Let's call it an accidental feature. + --Larry Wall +[piccardi@gont sources]$ ./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{Verbatim} +%$ +e ripetendo varie volte il comando otterremo, in ordine casuale, le dieci +frasi tenute in memoria dal server. + +Infine per chiudere il server basterà inviare un segnale di terminazione con +\code{killall fortuned} e potremo verificare che il gestore del segnale ha +anche correttamente cancellato la 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 fifo temporanee sul filesystem, di come sia necessario + intercettare \const{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 +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 pipe, ma che non presenta il +problema della unidirezionalità del flusso dei dati, è quello dei cosiddetti +\textsl{socket locali} (o \textit{Unix domain socket}). Tratteremo l'argomento +dei socket in cap.~\ref{cha:socket_intro},\footnote{si tratta comunque di + oggetti di comunicazione che, come le pipe, sono utilizzati attraverso dei + file descriptor.} nell'ambito dell'interfaccia generale che essi forniscono +per la programmazione di rete; e vedremo anche +(in~sez.~\ref{sec:sock_sa_local}) come si possono definire dei file speciali +(di tipo socket, analoghi a quello associati alle fifo) cui si accede però +attraverso quella medesima interfaccia; vale però la pena esaminare qui una +modalità di uso dei socket locali\footnote{la funzione \func{socketpair} è + stata introdotta in BSD4.4, ma è supportata in genere da qualunque sistema + che fornisca l'interfaccia dei socket.} che li rende sostanzialmente +identici ad una pipe bidirezionale. + +La funzione \funcd{socketpair} infatti consente di creare una coppia di file +descriptor connessi fra di loro (tramite un socket, appunto), senza dover +ricorrere ad un 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{functions} + \headdecl{sys/types.h} + \headdecl{sys/socket.h} + + \funcdecl{int socketpair(int domain, int type, int protocol, int sv[2])} + + Crea una coppia di socket connessi fra loro. -\section{La comunicazione fra processi di System V} + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EAFNOSUPPORT}] i socket locali non sono supportati. + \item[\errcode{EPROTONOSUPPORT}] il protocollo specificato non è supportato. + \item[\errcode{EOPNOTSUPP}] il protocollo specificato non supporta la + creazione di coppie di socket. + \end{errlist} + ed inoltre \errval{EMFILE}, \errval{EFAULT}. +} +\end{functions} + +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}. + +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{Il sistema di comunicazione fra processi di System V} \label{sec:ipc_sysv} -Per ovviare ai vari limiti dei meccanismo tradizionale di comunicazione fra -processi visto in \secref{sec:ipc_unix}, nello sviluppo di System V vennero -introdotti una serie di nuovi oggetti e relative interfacce che garantissero -una maggiore flessibilità; in questa sezione esamineremo quello che viene -ormai chiamato il \textit{System V Inter-Process Comunication System}, più -comunemente noto come \textit{SystemV IPC}. - +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. + +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 chiamato il +\textsl{Sistema di comunicazione fra processi} di System V, cui da qui in +avanti faremo riferimento come \textit{SysV IPC} (dove IPC è la sigla di +\textit{Inter-Process Comunication}). + + + +\subsection{Considerazioni generali} +\label{sec:ipc_sysv_generic} + +La principale caratteristica del \textit{SysV IPC} è quella di essere basato +su oggetti permanenti che risiedono nel kernel. Questi, a differenza di quanto +avviene per i file descriptor, non mantengono un contatore dei riferimenti, e +non vengono cancellati dal sistema una volta che non sono più in uso. + +Questo comporta due problemi: il primo è che, al contrario di quanto avviene +per pipe e 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 è che, dato che non c'è, come per i +file, un contatore del numero di riferimenti che ne indichi l'essere in uso, +essi possono essere cancellati anche se ci sono dei processi che li stanno +utilizzando, con tutte le conseguenze (negative) 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 \acr{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} + \includestruct{listati/ipc_perm.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{ipc\_perm}, come definita in + \file{sys/ipc.h}.} + \label{fig:ipc_ipc_perm} +\end{figure} + +Usando la stessa chiave due processi diversi possono ricavare l'identificatore +associato ad un oggetto ed accedervi. Il problema che sorge a questo punto è +come devono fare per accordarsi sull'uso di una stessa chiave. Se i processi +sono \textsl{imparentati} la soluzione è relativamente semplice, in tal caso +infatti si può usare il valore speciale \texttt{IPC\_PRIVATE} per creare un +nuovo oggetto nel processo padre, l'identificatore così ottenuto sarà +disponibile in tutti i figli, e potrà essere passato come argomento attraverso +una \func{exec}. + +Però quando i processi non sono \textsl{imparentati} (come capita tutte le +volte che si ha a che fare con un sistema client-server) tutto questo non è +possibile; si potrebbe comunque salvare l'identificatore su un file noto, ma +questo ovviamente comporta lo svantaggio di doverselo andare a rileggere. Una +alternativa più efficace è quella che i programmi usino un valore comune per +la chiave (che ad esempio può essere dichiarato in un header comune), ma c'è +sempre il rischio che questa chiave possa essere stata già utilizzata da +qualcun altro. Dato che non esiste una convenzione su come assegnare queste +chiavi in maniera univoca l'interfaccia mette a disposizione una funzione +apposita, \funcd{ftok}, che permette di ottenere una chiave specificando il +nome di un file ed un numero di versione; il suo prototipo è: +\begin{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 \textit{SysV IPC}. + + \bodydesc{La funzione restituisce la chiave in caso di successo e -1 + altrimenti, nel qual caso \var{errno} sarà uno dei possibili codici di + errore di \func{stat}.} +\end{functions} + +La funzione determina un valore della chiave sulla base di \param{pathname}, +che deve specificare il \itindex{pathname} \textit{pathname} di un file +effettivamente esistente e di un numero di progetto \param{proj\_id)}, che di +norma viene specificato come carattere, dato che ne vengono utilizzati solo +gli 8 bit meno significativi.\footnote{nelle libc4 e libc5, come avviene in + SunOS, l'argomento \param{proj\_id} è dichiarato tipo \ctyp{char}, le + \acr{glibc} usano il prototipo specificato da XPG4, ma vengono lo stesso + utilizzati gli 8 bit meno significativi.} + +Il problema è che anche così non c'è la sicurezza che il valore della chiave +sia univoco, infatti esso è costruito combinando il byte di \param{proj\_id)} +con i 16 bit meno significativi \index{inode} dell'inode del file +\param{pathname} (che vengono ottenuti attraverso \func{stat}, da cui derivano +i possibili errori), e gli 8 bit meno significativi del numero del dispositivo +su cui è il file. Diventa perciò relativamente facile ottenere delle +collisioni, specie se i file sono su dispositivi con lo stesso +\itindex{minor~number} \textit{minor number}, come \file{/dev/hda1} e +\file{/dev/sda1}. + +In genere quello che si fa è utilizzare un file comune usato dai programmi che +devono comunicare (ad esempio un header comune, o uno dei programmi che devono +usare l'oggetto in questione), utilizzando il numero di progetto per ottenere +le chiavi che interessano. In ogni caso occorre sempre controllare, prima di +creare un oggetto, che la chiave non sia già stata utilizzata. Se questo va +bene in fase di creazione, le cose possono complicarsi per i programmi che +devono solo accedere, in quanto, a parte gli eventuali controlli sugli altri +attributi di \struct{ipc\_perm}, non esiste una modalità semplice per essere +sicuri che l'oggetto associato ad una certa chiave sia stato effettivamente +creato da chi ci si aspetta. + +Questo è, insieme al fatto che gli oggetti sono permanenti e non mantengono un +contatore di riferimenti per la cancellazione automatica, il principale +problema del \textit{SysV IPC}. Non esiste infatti una modalità chiara per +identificare un oggetto, come sarebbe stato se lo si fosse associato ad in +file, e tutta l'interfaccia è inutilmente complessa. Per questo ne è stata +effettuata una revisione completa nello standard POSIX.1b, 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 +\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}\footnote{se però si vogliono usare le costanti + simboliche ivi definite occorrerà includere il file \file{sys/stat.h}, + alcuni sistemi definiscono le costanti \const{MSG\_R} (\texttt{0400}) e + \const{MSG\_W} (\texttt{0200}) per indicare i permessi base di lettura e + 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. + +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'user-ID e del group-ID 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 l'accesso è sempre + consentito. +\item se l'user-ID effettivo del processo corrisponde o al valore del campo + \var{cuid} o a quello del campo \var{uid} ed il permesso per il proprietario + in \var{mode} è appropriato\footnote{per appropriato si intende che è + impostato il permesso di scrittura per le operazioni di scrittura e quello + di lettura per le operazioni di lettura.} l'accesso è consentito. +\item se il group-ID effettivo del processo corrisponde o al + valore del campo \var{cgid} o a quello del campo \var{gid} ed il permesso + per il gruppo in \var{mode} è appropriato l'accesso è consentito. +\item se il permesso per gli altri è appropriato l'accesso è consentito. +\end{itemize*} +solo se tutti i controlli elencati falliscono l'accesso è negato. Si noti che +a differenza di quanto avviene per i permessi dei file, fallire in uno dei +passi elencati non comporta il fallimento dell'accesso. Un'ulteriore +differenza rispetto a quanto avviene per i file è che per gli oggetti di IPC +il valore di \itindex{umask} \textit{umask} (si ricordi quanto esposto in +sez.~\ref{sec:file_perm_management}) non ha alcun significato. + + +\subsection{Gli identificatori ed il loro utilizzo} +\label{sec:ipc_sysv_id_use} + +L'unico campo di \struct{ipc\_perm} del quale non abbiamo ancora parlato è +\var{seq}, che in fig.~\ref{fig:ipc_ipc_perm} è qualificato con un criptico +``\textsl{numero di sequenza}'', ne parliamo adesso dato che esso è +strettamente attinente alle modalità con cui il kernel assegna gli +identificatori degli oggetti del sistema di IPC. + +Quando il sistema si avvia, alla creazione di ogni nuovo oggetto di IPC viene +assegnato un numero progressivo, pari al numero di oggetti di quel tipo +esistenti. Se il comportamento fosse sempre questo sarebbe identico a quello +usato nell'assegnazione dei file descriptor nei processi, ed i valori degli +identificatori tenderebbero ad essere riutilizzati spesso e restare di piccole +dimensioni (inferiori al numero massimo di oggetti disponibili). + +Questo va benissimo nel caso dei file descriptor, che sono locali ad un +processo, ma qui il comportamento varrebbe per tutto il sistema, e per +processi del tutto scorrelati fra loro. Così si potrebbero avere situazioni +come quella in cui un server esce e cancella le sue code di messaggi, ed il +relativo identificatore viene immediatamente assegnato a quelle di un altro +server partito subito dopo, con la possibilità che i client del primo non +facciano in tempo ad accorgersi dell'avvenuto, e finiscano con l'interagire +con gli oggetti del secondo, con conseguenze imprevedibili. + +Proprio per evitare questo tipo di situazioni il sistema usa il valore di +\var{seq} per provvedere un meccanismo che porti gli identificatori ad +assumere tutti i valori possibili, rendendo molto più lungo il periodo in cui +un identificatore può venire riutilizzato. + +Il sistema dispone sempre di un numero fisso di oggetti di IPC,\footnote{fino + al kernel 2.2.x questi valori, definiti dalle costanti \const{MSGMNI}, + \const{SEMMNI} e \const{SHMMNI}, potevano essere cambiati (come tutti gli + altri limiti relativi al \textit{SysV IPC}) solo con una ricompilazione del + kernel, andando a modificarne la definizione nei relativi header file. A + partire dal kernel 2.4.x è possibile cambiare questi valori a sistema attivo + scrivendo sui file \procrelfile{/proc/sys/kernel}{shmmni}, + \procrelfile{/proc/sys/kernel}{msgmni} e \procrelfile{/proc/sys/kernel}{sem} + di \file{/proc/sys/kernel} o con l'uso di \func{sysctl}.} e per ciascuno di +essi 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 l'identificatore al numero di oggetti presenti viene +sommato il valore di \var{seq} moltiplicato per il numero massimo di oggetti +di quel tipo,\footnote{questo vale fino ai kernel della serie 2.2.x, dalla + serie 2.4.x viene usato lo stesso fattore per tutti gli oggetti, esso è dato + dalla costante \const{IPCMNI}, definita in \file{include/linux/ipc.h}, che + indica il limite massimo per il numero di tutti oggetti di IPC, ed il cui + valore è 32768.} si evita così il riutilizzo degli stessi numeri, e si fa +sì che l'identificatore assuma tutti i valori possibili. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \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} + +In fig.~\ref{fig:ipc_sysv_idtest} è riportato il codice di un semplice +programma di test che si limita a creare un oggetto (specificato a riga di +comando), stamparne il numero di identificatore e cancellarlo per un numero +specificato di volte. Al solito non si è riportato il codice della gestione +delle opzioni a riga di comando, che permette di specificare quante volte +effettuare il ciclo \var{n}, e su quale tipo di oggetto eseguirlo. + +La figura non riporta il codice di selezione delle opzioni, che permette di +inizializzare i valori delle variabili \var{type} al tipo di oggetto voluto, e +\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 un ciclo di 5 volte. Se si lancia il comando si otterrà qualcosa +del tipo: +\begin{Verbatim} +piccardi@gont sources]$ ./ipctestid +Identifier Value 0 +Identifier Value 32768 +Identifier Value 65536 +Identifier Value 98304 +Identifier Value 131072 +\end{Verbatim} +%$ +il che ci mostra che abbiamo un kernel della serie 2.4.x nel quale non avevamo +ancora usato nessuna coda di messaggi. Se ripetiamo il comando otterremo +ancora: +\begin{Verbatim} +[piccardi@gont sources]$ ./ipctestid +Identifier Value 163840 +Identifier Value 196608 +Identifier Value 229376 +Identifier Value 262144 +Identifier Value 294912 +\end{Verbatim} +%$ +che ci mostra come il valore di \var{seq} sia in effetti una quantità +mantenuta staticamente all'interno del sistema. + \subsection{Code di messaggi} -\label{sec:ipc_messque} +\label{sec:ipc_sysv_mq} + +Il primo oggetto introdotto dal \textit{SysV IPC} è quello delle code di +messaggi. Le code di messaggi sono oggetti analoghi alle pipe o alle fifo, +anche se la loro struttura è diversa, ed il loro scopo principale è appunto +quello di permettere a processi diversi di scambiarsi dei dati. + +La funzione che permette di richiedere al sistema l'identificatore di una coda +di messaggi esistente (o di crearne una se questa non esiste) è +\funcd{msgget}; 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 coda di messaggi. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo chiamante non ha i privilegi per accedere + alla coda richiesta. + \item[\errcode{EEXIST}] si è richiesta la creazione di una coda che già + esiste, ma erano specificati sia \const{IPC\_CREAT} che \const{IPC\_EXCL}. + \item[\errcode{EIDRM}] la coda richiesta è marcata per essere cancellata. + \item[\errcode{ENOENT}] si è cercato di ottenere l'identificatore di una coda + di messaggi specificando una chiave che non esiste e \const{IPC\_CREAT} + non era specificato. + \item[\errcode{ENOSPC}] si è cercato di creare una coda di messaggi quando è + stato superato il limite massimo di code (\const{MSGMNI}). + \end{errlist} + ed inoltre \errval{ENOMEM}. +} +\end{functions} + +Le funzione (come le analoghe che si usano per gli altri oggetti) serve sia a +ottenere l'identificatore di una coda di messaggi esistente, che a crearne una +nuova. L'argomento \param{key} specifica la chiave che è associata +all'oggetto, eccetto il caso in cui si specifichi il valore +\const{IPC\_PRIVATE}, nel qual caso la coda è creata ex-novo e non vi è +associata alcuna chiave, il processo (ed i suoi eventuali figli) potranno +farvi riferimento solo attraverso l'identificatore. + +Se invece si specifica un valore diverso da \const{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 +effettuare una ricerca sugli oggetti esistenti, restituendo l'identificatore +se trova una corrispondenza, o fallendo con un errore di \errcode{ENOENT} se +non esiste o di \errcode{EACCES} se si sono specificati dei permessi non +validi. + +Se invece si vuole creare una nuova coda di messaggi \param{flag} non può +essere nullo e deve essere fornito come maschera binaria, impostando il bit +corrispondente al valore \const{IPC\_CREAT}. In questo caso i nove bit meno +significativi di \param{flag} saranno usati come permessi per il nuovo +oggetto, secondo quanto illustrato in sez.~\ref{sec:ipc_sysv_access_control}. +Se si imposta anche il bit corrispondente a \const{IPC\_EXCL} la funzione avrà +successo solo se l'oggetto non esiste già, fallendo con un errore di +\errcode{EEXIST} altrimenti. + +Si tenga conto che l'uso di \const{IPC\_PRIVATE} non impedisce ad altri +processi di accedere alla coda (se hanno privilegi sufficienti) una volta che +questi possano indovinare o ricavare (ad esempio per tentativi) +l'identificatore ad essa associato. Per come sono implementati gli oggetti di +IPC infatti non esiste una maniera che garantisca 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 \texttt{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, definiti +negli header e corrispondenti alle prime tre costanti riportate in +tab.~\ref{tab:ipc_msg_limits}, come accennato però in Linux è possibile +modificare questi limiti attraverso l'uso di \func{sysctl} o scrivendo nei +file \procrelfile{/proc/sys/kernel}{msgmax}, +\procrelfile{/proc/sys/kernel}{msgmnb} e +\procrelfile{/proc/sys/kernel}{msgmni} di \file{/proc/sys/kernel/}. + +\begin{figure}[htb] + \centering \includegraphics[width=13cm]{img/mqstruct} + \caption{Schema della struttura di una coda messaggi.} + \label{fig:ipc_mq_schema} +\end{figure} + + +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 lo schema con +cui queste strutture vengono mantenute dal kernel.\footnote{lo schema + illustrato in fig.~\ref{fig:ipc_mq_schema} è in realtà una semplificazione + di quello usato effettivamente fino ai kernel della serie 2.2.x, nei kernel + della serie 2.4.x la gestione delle code di messaggi è stata modificata ed è + effettuata in maniera diversa; abbiamo mantenuto lo schema precedente in + quanto illustra comunque in maniera più che adeguata i principi di + funzionamento delle code di messaggi.} + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \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} + +A ciascuna coda è associata una struttura \struct{msgid\_ds}, la cui +definizione, è riportata in fig.~\ref{fig:ipc_msqid_ds}. In questa struttura il +kernel mantiene le principali informazioni riguardo lo stato corrente della +coda.\footnote{come accennato questo vale fino ai kernel della serie 2.2.x, + essa viene usata nei kernel della serie 2.4.x solo per compatibilità in + quanto è quella restituita dalle funzioni dell'interfaccia. Si noti come ci + sia una differenza con i campi mostrati nello schema di + fig.~\ref{fig:ipc_mq_schema} che sono presi dalla definizione di + \file{linux/msg.h}, e fanno riferimento alla definizione della omonima + struttura usata nel kernel.} In fig.~\ref{fig:ipc_msqid_ds} sono elencati i +campi significativi definiti in \file{sys/msg.h}, a cui si sono aggiunti gli +ultimi tre campi che sono previsti dalla implementazione originale di System +V, ma non dallo standard Unix98. + +Quando si crea una nuova coda con \func{msgget} questa struttura viene +inizializzata, in particolare il campo \var{msg\_perm} viene inizializzato +come illustrato in 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 \acr{pid} dell'ultimo processo che ha inviato o ricevuto + un messaggio sulla coda, sono inizializzati a 0. +\item i campi \var{msg\_stime} e \var{msg\_rtime}, che esprimono + rispettivamente il tempo in cui è stato inviato o ricevuto l'ultimo + messaggio sulla coda, sono inizializzati a 0. +\item il campo \var{msg\_ctime}, che esprime il tempo di creazione della coda, + viene inizializzato al tempo corrente. +\item il campo \var{msg\_qbytes} che esprime la dimensione massima del + contenuto della coda (in byte) viene inizializzato al valore preimpostato + del sistema (\const{MSGMNB}). +\item i campi \var{msg\_first} e \var{msg\_last} che esprimono l'indirizzo del + primo e ultimo messaggio sono inizializzati a \val{NULL} e + \var{msg\_cbytes}, che esprime la dimensione in byte dei messaggi presenti è + inizializzato a zero. Questi campi sono ad uso interno dell'implementazione + e non devono essere utilizzati da programmi in user space). +\end{itemize*} + +Una volta creata una coda di messaggi le operazioni di controllo vengono +effettuate con la funzione \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{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{int msgctl(int msqid, int cmd, struct msqid\_ds *buf)} + + Esegue l'operazione specificata da \param{cmd} sulla coda \param{msqid}. + + \bodydesc{La funzione restituisce 0 in caso di successo o $-1$ in caso di + 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}. +} +\end{functions} + +La funzione permette di accedere ai valori della struttura \struct{msqid\_ds}, +mantenuta all'indirizzo \param{buf}, per la coda specificata +dall'identificatore \param{msqid}. Il comportamento della funzione dipende dal +valore dell'argomento \param{cmd}, che specifica il tipo di azione da +eseguire; i valori possibili sono: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge le informazioni riguardo la coda nella + struttura indicata da \param{buf}. Occorre avere il permesso di lettura + sulla coda. +\item[\const{IPC\_RMID}] Rimuove la coda, cancellando tutti i dati, con + effetto immediato. Tutti i processi che cercheranno di accedere alla coda + riceveranno un errore di \errcode{EIDRM}, e tutti processi in attesa su + funzioni di lettura o di scrittura sulla coda saranno svegliati ricevendo + il medesimo errore. Questo comando può essere eseguito solo da un processo + con user-ID effettivo corrispondente al creatore o al proprietario della + coda, o all'amministratore. +\item[\const{IPC\_SET}] Permette di modificare i permessi ed il proprietario + della coda, ed il limite massimo sulle dimensioni del totale dei messaggi in + essa contenuti (\var{msg\_qbytes}). I valori devono essere passati in una + struttura \struct{msqid\_ds} puntata da \param{buf}. Per modificare i valori + di \var{msg\_perm.mode}, \var{msg\_perm.uid} e \var{msg\_perm.gid} occorre + essere il proprietario o il creatore della coda, oppure l'amministratore; lo + stesso vale per \var{msg\_qbytes}, ma l'amministratore ha la facoltà di + incrementarne il valore a limiti superiori a \const{MSGMNB}. +\end{basedescript} + + +Una volta che si abbia a disposizione l'identificatore, per inviare un +messaggio su una coda si utilizza la funzione \funcd{msgsnd}; il suo prototipo +è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{int msgsnd(int msqid, struct msgbuf *msgp, size\_t msgsz, int + msgflg)} + + Invia un messaggio sulla coda \param{msqid}. + + \bodydesc{La funzione restituisce 0, e $-1$ in caso di errore, nel qual caso + \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \item[\errcode{EAGAIN}] il messaggio non può essere inviato perché si è + superato il limite \var{msg\_qbytes} sul numero massimo di byte presenti + sulla coda, e si è richiesto \const{IPC\_NOWAIT} in \param{flag}. + \item[\errcode{EINVAL}] si è specificato un \param{msgid} invalido, o un + valore non positivo per \param{mtype}, o un valore di \param{msgsz} + maggiore di \const{MSGMAX}. + \end{errlist} + ed inoltre \errval{EFAULT}, \errval{EINTR} ed \errval{ENOMEM}. } +\end{functions} + +La funzione inserisce il messaggio sulla coda specificata da \param{msqid}; il +messaggio ha lunghezza specificata da \param{msgsz} ed è passato attraverso il +l'argomento \param{msgp}. Quest'ultimo deve venire passato sempre come +puntatore ad una struttura \struct{msgbuf} analoga a quella riportata in +fig.~\ref{fig:ipc_msbuf} che è quella che deve contenere effettivamente il +messaggio. La dimensione massima per il testo di un messaggio non può +comunque superare il limite \const{MSGMAX}. + +La struttura di fig.~\ref{fig:ipc_msbuf} è comunque solo un modello, tanto che +la definizione contenuta in \file{sys/msg.h} usa esplicitamente per il secondo +campo il valore \code{mtext[1]}, che non è di nessuna utilità ai fini pratici. +La sola cosa che conta è che la struttura abbia come primo membro un campo +\var{mtype} come nell'esempio; esso infatti serve ad identificare il tipo di +messaggio e deve essere sempre specificato come intero positivo di tipo +\ctyp{long}. Il campo \var{mtext} invece può essere di qualsiasi tipo e +dimensione, e serve a contenere il testo del messaggio. + +In generale pertanto per inviare un messaggio con \func{msgsnd} si usa +ridefinire una struttura simile a quella di fig.~\ref{fig:ipc_msbuf}, adattando +alle proprie esigenze il campo \var{mtype}, (o ridefinendo come si vuole il +corpo del messaggio, anche con più campi o con strutture più complesse) avendo +però la cura di mantenere nel primo campo un valore di tipo \ctyp{long} che ne +indica il tipo. + +Si tenga presente che la lunghezza che deve essere indicata in questo +argomento è solo quella del messaggio, non quella di tutta la struttura, se +cioè \var{message} è una propria struttura che si passa alla funzione, +\param{msgsz} dovrà essere uguale a \code{sizeof(message)-sizeof(long)}, (se +consideriamo il caso dell'esempio in fig.~\ref{fig:ipc_msbuf}, \param{msgsz} +dovrà essere pari a \const{LENGTH}). + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/msgbuf.h} + \end{minipage} + \normalsize + \caption{Schema della struttura \structd{msgbuf}, da utilizzare come + argomento per inviare/ricevere messaggi.} + \label{fig:ipc_msbuf} +\end{figure} + +Per capire meglio il funzionamento della funzione riprendiamo in +considerazione la struttura della coda illustrata in +fig.~\ref{fig:ipc_mq_schema}. Alla chiamata di \func{msgsnd} il nuovo messaggio +sarà aggiunto in fondo alla lista inserendo una nuova struttura \struct{msg}, +il puntatore \var{msg\_last} di \struct{msqid\_ds} verrà aggiornato, come pure +il puntatore al messaggio successivo per quello che era il precedente ultimo +messaggio; il valore di \var{mtype} verrà mantenuto in \var{msg\_type} ed il +valore di \param{msgsz} in \var{msg\_ts}; il testo del messaggio sarà copiato +all'indirizzo specificato da \var{msg\_spot}. + +Il valore dell'argomento \param{flag} permette di specificare il comportamento +della funzione. Di norma, quando si specifica un valore nullo, la funzione +ritorna immediatamente a meno che si sia ecceduto il valore di +\var{msg\_qbytes}, o il limite di sistema sul numero di messaggi, nel qual +caso si blocca mandando il processo in stato di \textit{sleep}. Se si +specifica per \param{flag} il valore \const{IPC\_NOWAIT} la funzione opera in +modalità non bloccante, ed in questi casi ritorna immediatamente con un errore +di \errcode{EAGAIN}. + +Se non si specifica \const{IPC\_NOWAIT} la funzione resterà bloccata fintanto +che non si liberano risorse sufficienti per poter inserire nella coda il +messaggio, nel qual caso ritornerà normalmente. La funzione può ritornare, con +una condizione di errore anche in due altri casi: quando la coda viene rimossa +(nel qual caso si ha un errore di \errcode{EIDRM}) o quando la funzione viene +interrotta da un segnale (nel qual caso si ha un errore di \errcode{EINTR}). + +Una volta completato con successo l'invio del messaggio sulla coda, la +funzione aggiorna i dati mantenuti in \struct{msqid\_ds}, in particolare +vengono modificati: +\begin{itemize*} +\item Il valore di \var{msg\_lspid}, che viene impostato al \acr{pid} del + processo chiamante. +\item Il valore di \var{msg\_qnum}, che viene incrementato di uno. +\item Il valore \var{msg\_stime}, che viene impostato al tempo corrente. +\end{itemize*} + +La funzione che viene utilizzata per estrarre un messaggio da una coda è +\funcd{msgrcv}; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{ssize\_t msgrcv(int msqid, struct msgbuf *msgp, size\_t msgsz, + long msgtyp, int msgflg)} + + Legge un messaggio dalla coda \param{msqid}. + + \bodydesc{La funzione restituisce il numero di byte letti in caso di + successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà uno + dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i privilegi di accesso sulla coda. + \item[\errcode{EIDRM}] la coda è stata cancellata. + \item[\errcode{E2BIG}] il testo del messaggio è più lungo di \param{msgsz} e + non si è specificato \const{MSG\_NOERROR} in \param{msgflg}. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale mentre + era in attesa di ricevere un messaggio. + \item[\errcode{EINVAL}] si è specificato un \param{msgid} invalido o un + valore di \param{msgsz} negativo. + \end{errlist} + ed inoltre \errval{EFAULT}. +} +\end{functions} + +La funzione legge un messaggio dalla coda specificata, scrivendolo sulla +struttura puntata da \param{msgp}, che dovrà avere un formato analogo a quello +di fig.~\ref{fig:ipc_msbuf}. Una volta estratto, il messaggio sarà rimosso +dalla coda. L'argomento \param{msgsz} indica la lunghezza massima del testo +del messaggio (equivalente al valore del parametro \const{LENGTH} nell'esempio +di fig.~\ref{fig:ipc_msbuf}). + +Se il testo del messaggio ha lunghezza inferiore a \param{msgsz} esso viene +rimosso dalla coda; in caso contrario, se \param{msgflg} è impostato a +\const{MSG\_NOERROR}, il messaggio viene troncato e la parte in eccesso viene +perduta, altrimenti il messaggio non viene estratto e la funzione ritorna con +un errore di \errcode{E2BIG}. + +L'argomento \param{msgtyp} permette di restringere la ricerca ad un +sottoinsieme dei messaggi presenti sulla coda; la ricerca infatti è fatta con +una scansione della struttura mostrata in fig.~\ref{fig:ipc_mq_schema}, +restituendo il primo messaggio incontrato che corrisponde ai criteri +specificati (che quindi, visto come i messaggi vengono sempre inseriti dalla +coda, è quello meno recente); in particolare: +\begin{itemize} +\item se \param{msgtyp} è 0 viene estratto il messaggio in cima alla coda, cioè + quello fra i presenti che è stato inserito per primo. +\item se \param{msgtyp} è positivo viene estratto il primo messaggio il cui + tipo (il valore del campo \var{mtype}) corrisponde al valore di + \param{msgtyp}. +\item se \param{msgtyp} è negativo viene estratto il primo fra i messaggi con + il valore più basso del tipo, fra tutti quelli il cui tipo ha un valore + inferiore al valore assoluto di \param{msgtyp}. +\end{itemize} + +Il valore di \param{msgflg} permette di controllare il comportamento della +funzione, esso può essere nullo o una maschera binaria composta da uno o più +valori. Oltre al precedente \const{MSG\_NOERROR}, sono possibili altri due +valori: \const{MSG\_EXCEPT}, che permette, quando \param{msgtyp} è positivo, +di leggere il primo messaggio nella coda con tipo diverso da \param{msgtyp}, e +\const{IPC\_NOWAIT} che causa il ritorno immediato della funzione quando non +ci sono messaggi sulla coda. + +Il comportamento usuale della funzione infatti, se non ci sono messaggi +disponibili per la lettura, è di bloccare il processo in stato di +\textit{sleep}. Nel caso però si sia specificato \const{IPC\_NOWAIT} la +funzione ritorna immediatamente con un errore \errcode{ENOMSG}. Altrimenti la +funzione ritorna normalmente non appena viene inserito un messaggio del tipo +desiderato, oppure ritorna con errore qualora la coda sia rimossa (con +\var{errno} impostata a \errcode{EIDRM}) o se il processo viene interrotto da +un segnale (con \var{errno} impostata a \errcode{EINTR}). + +Una volta completata con successo l'estrazione del messaggio dalla coda, la +funzione aggiorna i dati mantenuti in \struct{msqid\_ds}, in particolare +vengono modificati: +\begin{itemize*} +\item Il valore di \var{msg\_lrpid}, che viene impostato al \acr{pid} del + processo chiamante. +\item Il valore di \var{msg\_qnum}, che viene decrementato di uno. +\item Il valore \var{msg\_rtime}, che viene impostato al tempo corrente. +\end{itemize*} + +Le code di messaggi presentano il solito problema di tutti gli oggetti del +SysV IPC; essendo questi permanenti restano nel sistema occupando risorse +anche quando un processo è terminato, al contrario delle pipe per le quali +tutte le risorse occupate vengono rilasciate quanto l'ultimo processo che le +utilizzava termina. Questo comporta che in caso di errori si può saturare il +sistema, e che devono comunque essere esplicitamente previste delle funzioni +di rimozione in caso di interruzioni o uscite dal programma (come vedremo in +fig.~\ref{fig:ipc_mq_fortune_server}). + +L'altro problema è non facendo uso di file descriptor le tecniche di +\textit{I/O multiplexing} descritte in sez.~\ref{sec:file_multiplexing} non +possono essere utilizzate, e non si ha a disposizione niente di analogo alle +funzioni \func{select} e \func{poll}. Questo rende molto scomodo usare più di +una di queste strutture alla volta; ad esempio non si può scrivere un server +che aspetti un messaggio su più di una coda senza fare ricorso ad una tecnica +di \itindex{polling} \textit{polling} che esegua un ciclo di attesa su +ciascuna di esse. + +Come esempio dell'uso delle code di messaggi possiamo riscrivere il nostro +server di \textit{fortunes} usando queste al posto delle 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}[!bht] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \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 \acr{pid} di questi ultimi come identificativo. Questo è possibile +in quanto, al contrario di una fifo, la lettura di una coda di messaggi può +non essere sequenziale, proprio grazie alla classificazione dei messaggi sulla +base del loro tipo. + +Il programma, oltre alle solite variabili per il nome del file da cui leggere +le \textit{fortunes} e per il vettore di stringhe che contiene le frasi, +definisce due strutture appositamente per la comunicazione; con +\var{msgbuf\_read} (\texttt{\small 8--11}) vengono passate le richieste mentre +con \var{msgbuf\_write} (\texttt{\small 12--15}) vengono restituite le frasi. + +La gestione delle opzioni si è al solito omessa, essa si curerà di impostare +in \var{n} il numero di frasi da leggere specificato a linea di comando ed in +\var{fortunefilename} il file da cui leggerle; dopo aver installato +(\texttt{\small 19--21}) i gestori dei segnali per trattare l'uscita dal +server, viene prima controllato (\texttt{\small 22}) il numero di frasi +richieste abbia senso (cioè sia maggiore di zero), le quali poi +(\texttt{\small 23}) vengono lette nel vettore in memoria con la stessa +funzione \code{FortuneParse} usata anche per il server basato sulle 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 \acr{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 \acr{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 \acr{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}[!bht] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/MQFortuneClient.c} + \end{minipage} + \normalsize + \caption{Sezione principale del codice del client di \textit{fortunes} + basato sulle \textit{message queue}.} + \label{fig:ipc_mq_fortune_client} +\end{figure} + +In fig.~\ref{fig:ipc_mq_fortune_client} si è riportato un estratto il codice +del programma client. Al solito il codice completo è con i sorgenti allegati, +nel file \file{MQFortuneClient.c}. Come sempre si sono rimosse le parti +relative alla gestione delle opzioni, ed in questo caso, anche la +dichiarazione delle variabili, che, per la parte relative alle strutture usate +per la comunicazione tramite le code, sono le stesse viste in +fig.~\ref{fig:ipc_mq_fortune_server}. + +Il client in questo caso è molto semplice; la prima parte del programma +(\texttt{\small 4--9}) si occupa di accedere alla coda di messaggi, ed è +identica a quanto visto per il server, solo che in questo caso \func{msgget} +non viene chiamata con il flag di creazione in quanto la coda deve essere +preesistente. In caso di errore (ad esempio se il server non è stato avviato) +il programma termina immediatamente. + +Una volta acquisito l'identificatore della coda il client compone il +messaggio di richiesta (\texttt{\small 12--13}) in \var{msg\_read}, usando 1 +per il tipo ed inserendo il proprio \acr{pid} come dato da passare al server. +Calcolata (\texttt{\small 14}) la dimensione, provvede (\texttt{\small 15}) ad +immettere la richiesta sulla coda. + +A questo punto non resta che (\texttt{\small 16}) rileggere dalla coda la +risposta del server richiedendo a \func{msgrcv} di selezionare i messaggi di +tipo corrispondente al valore del \acr{pid} inviato nella richiesta. L'ultimo +passo (\texttt{\small 17}) prima di uscire è quello di stampare a video il +messaggio ricevuto. + +Proviamo allora il nostro nuovo sistema, al solito occorre definire +\code{LD\_LIBRARY\_PATH} per accedere alla libreria \file{libgapil.so}, dopo di +che, in maniera del tutto analoga a quanto fatto con il programma che usa le +fifo, potremo far partire il server con: +\begin{verbatim} +[piccardi@gont sources]$ ./mqfortuned -n10 +\end{verbatim}%$ +come nel caso precedente, avendo eseguito il server in background, il comando +ritornerà immediatamente; potremo però verificare con \cmd{ps} che il +programma è effettivamente in esecuzione, e che ha creato una coda di +messaggi: +\begin{verbatim} +[piccardi@gont sources]$ ipcs + +------ 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{verbatim} +a questo punto potremo usare il client per ottenere le nostre frasi: +\begin{verbatim} +[piccardi@gont sources]$ ./mqfortune +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +[piccardi@gont sources]$ ./mqfortune +Let's call it an accidental feature. + --Larry Wall +\end{verbatim} +con un risultato del tutto equivalente al precedente. Infine potremo chiudere +il server inviando il segnale di terminazione con il comando \code{killall + mqfortuned} verificando che effettivamente la coda di messaggi viene rimossa. + +Benché funzionante questa architettura risente dello stesso inconveniente +visto anche nel caso del precedente server basato sulle 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 fifo si aveva +il problema delle 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 \index{inode} inode di un filesystem, +sia perché, con il riutilizzo dei \acr{pid} da parte dei processi, un client +eseguito in un momento successivo potrebbe ricevere un messaggio non +indirizzato a lui. + -Il primo oggetto introdotto dal \textit{SystemV IPC} è quello delle code di -messaggi. \subsection{Semafori} -\label{sec:ipc_semaph} +\label{sec:ipc_sysv_sem} -Il secondo oggetto introdotto dal \textit{SystemV IPC} è quello dei semafori. +I semafori non sono meccanismi di intercomunicazione diretta come quelli +(pipe, fifo e code di messaggi) visti finora, e non consentono di scambiare +dati fra processi, ma servono piuttosto come meccanismi di sincronizzazione o +di protezione per le \index{sezione~critica} \textsl{sezioni critiche} del +codice (si ricordi quanto detto in sez.~\ref{sec:proc_race_cond}). +Un semaforo è uno speciale contatore, mantenuto nel kernel, che permette, a +seconda del suo valore, di consentire o meno la prosecuzione dell'esecuzione +di un programma. In questo modo l'accesso ad una risorsa condivisa da più +processi può essere controllato, associando ad essa un semaforo che consente +di assicurare che non più di un processo alla volta possa usarla. -\subsection{Memoria condivisa} -\label{sec:ipc_shar_mem} +Il concetto di semaforo è uno dei concetti base nella programmazione ed è +assolutamente generico, così come del tutto generali sono modalità con cui lo +si utilizza. Un processo che deve accedere ad una risorsa eseguirà un +controllo del semaforo: se questo è positivo il suo valore sarà decrementato, +indicando che si è consumato una unità della risorsa, ed il processo potrà +proseguire nell'utilizzo di quest'ultima, provvedendo a rilasciarla, una volta +completate le operazioni volute, reincrementando il semaforo. + +Se al momento del controllo il valore del semaforo è nullo, siamo invece in +una situazione in cui la risorsa non è disponibile, ed il processo si +bloccherà in stato di \textit{sleep} fin quando chi la sta utilizzando non la +rilascerà, incrementando il valore del semaforo. Non appena il semaforo torna +positivo, indicando che la risorsa è disponibile, il processo sarà svegliato, +e si potrà operare come nel caso precedente (decremento del semaforo, accesso +alla risorsa, incremento del semaforo). + +Per poter implementare questo tipo di logica le operazioni di controllo e +decremento del contatore associato al semaforo devono essere atomiche, +pertanto una realizzazione di un oggetto di questo tipo è necessariamente +demandata al kernel. La forma più semplice di semaforo è quella del +\textsl{semaforo binario}, o \textit{mutex}, in cui un valore diverso da zero +(normalmente 1) indica la libertà di accesso, e un valore nullo l'occupazione +della risorsa. In generale però si possono usare semafori con valori interi, +utilizzando il valore del contatore come indicatore del ``numero di risorse'' +ancora disponibili. + +Il sistema di comunicazione inter-processo di \textit{SysV IPC} prevede anche i +semafori, ma gli oggetti utilizzati non sono semafori singoli, ma gruppi di +semafori detti \textsl{insiemi} (o \textit{semaphore set}); la funzione che +permette di creare o ottenere l'identificatore di un insieme di semafori è +\funcd{semget}, ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semget(key\_t key, int nsems, int flag)} + + Restituisce l'identificatore di un insieme di semafori. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ENOSPC}] si è cercato di creare una insieme di semafori + quando è stato superato o il limite per il numero totale di semafori + (\const{SEMMNS}) o quello per il numero totale degli insiemi + (\const{SEMMNI}) nel sistema. + \item[\errcode{EINVAL}] l'argomento \param{nsems} è minore di zero o + maggiore del limite sul numero di semafori per ciascun insieme + (\const{SEMMSL}), o se l'insieme già esiste, maggiore del numero di + semafori che contiene. + \item[\errcode{ENOMEM}] il sistema non ha abbastanza memoria per poter + contenere le strutture per un nuovo insieme di semafori. + \end{errlist} + ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, + \errval{EIDRM}, con lo stesso significato che hanno per \func{msgget}.} +\end{functions} + +La funzione è del tutto analoga a \func{msgget}, solo che in questo caso +restituisce l'identificatore di un insieme di semafori, in particolare è +identico l'uso degli argomenti \param{key} e \param{flag}, per cui non +ripeteremo quanto detto al proposito in sez.~\ref{sec:ipc_sysv_mq}. L'argomento +\param{nsems} permette di specificare quanti semafori deve contenere l'insieme +quando se ne richieda la creazione, e deve essere nullo quando si effettua una +richiesta dell'identificatore di un insieme già esistente. + +Purtroppo questa implementazione complica inutilmente lo schema elementare che +abbiamo descritto, dato che non è possibile definire un singolo semaforo, ma +se ne deve creare per forza un insieme. Ma questa in definitiva è solo una +complicazione inutile, il problema è che i semafori del \textit{SysV IPC} +soffrono di altri due, ben più gravi, difetti. + +Il primo difetto è che non esiste una funzione che permetta di creare ed +inizializzare un semaforo in un'unica chiamata; occorre prima creare l'insieme +dei semafori con \func{semget} e poi inizializzarlo con \func{semctl}, si +perde così ogni possibilità di eseguire l'operazione atomicamente. + +Il secondo difetto deriva dalla caratteristica generale degli oggetti del +\textit{SysV IPC} di essere risorse globali di sistema, che non vengono +cancellate quando nessuno le usa più; ci si così a trova a dover affrontare +esplicitamente il caso in cui un processo termina per un qualche errore, +lasciando un semaforo occupato, che resterà tale fino al successivo riavvio +del sistema. Come vedremo esistono delle modalità per evitare tutto ciò, ma +diventa necessario indicare esplicitamente che si vuole il ripristino del +semaforo all'uscita del processo. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/semid_ds.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{semid\_ds}, associata a ciascun insieme di + semafori.} + \label{fig:ipc_semid_ds} +\end{figure} + +A ciascun insieme di semafori è associata una struttura \struct{semid\_ds}, +riportata in fig.~\ref{fig:ipc_semid_ds}.\footnote{non si sono riportati i + campi ad uso interno del kernel, che vedremo in + fig.~\ref{fig:ipc_sem_schema}, che dipendono dall'implementazione.} Come nel +caso delle code di messaggi quando si crea un nuovo insieme di semafori con +\func{semget} questa struttura viene inizializzata, in particolare il campo +\var{sem\_perm} viene inizializzato come illustrato in +sez.~\ref{sec:ipc_sysv_access_control} (si ricordi che in questo caso il +permesso di scrittura è in realtà permesso di alterare il semaforo), per +quanto riguarda gli altri campi invece: +\begin{itemize*} +\item il campo \var{sem\_nsems}, che esprime il numero di semafori + nell'insieme, viene inizializzato al valore di \param{nsems}. +\item il campo \var{sem\_ctime}, che esprime il tempo di creazione + dell'insieme, viene inizializzato al tempo corrente. +\item il campo \var{sem\_otime}, che esprime il tempo dell'ultima operazione + effettuata, viene inizializzato a zero. +\end{itemize*} + +Ciascun semaforo dell'insieme è realizzato come una struttura di tipo +\struct{sem} che ne contiene i dati essenziali, la sua definizione\footnote{si + è riportata la definizione originaria del kernel 1.0, che contiene la prima + realizzazione del \textit{SysV IPC} in Linux. In realtà questa struttura + ormai è ridotta ai soli due primi membri, e gli altri vengono calcolati + dinamicamente. La si è utilizzata a scopo di esempio, perché indica tutti i + valori associati ad un semaforo, restituiti dalle funzioni di controllo, e + citati dalle pagine di manuale.} è riportata in fig.~\ref{fig:ipc_sem}. +Questa struttura, non è accessibile in user space, ma i valori in essa +specificati possono essere letti in maniera indiretta, attraverso l'uso delle +funzioni di controllo. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/sem.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{sem}, che contiene i dati di un singolo + semaforo.} + \label{fig:ipc_sem} +\end{figure} + +I dati mantenuti nella struttura, ed elencati in fig.~\ref{fig:ipc_sem}, +indicano rispettivamente: +\begin{description*} +\item[\var{semval}] il valore numerico del semaforo. +\item[\var{sempid}] il \acr{pid} dell'ultimo processo che ha eseguito una + operazione sul semaforo. +\item[\var{semncnt}] il numero di processi in attesa che esso venga + incrementato. +\item[\var{semzcnt}] il numero di processi in attesa che esso si annulli. +\end{description*} + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|r|p{8cm}|} + \hline + \textbf{Costante} & \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \const{SEMMNI}& 128 & Numero massimo di insiemi di semafori.\\ + \const{SEMMSL}& 250 & Numero massimo di semafori per insieme.\\ + \const{SEMMNS}&\const{SEMMNI}*\const{SEMMSL}& Numero massimo di semafori + nel sistema.\\ + \const{SEMVMX}& 32767 & Massimo valore per un semaforo.\\ + \const{SEMOPM}& 32 & Massimo numero di operazioni per chiamata a + \func{semop}. \\ + \const{SEMMNU}&\const{SEMMNS}& Massimo numero di strutture di ripristino.\\ + \const{SEMUME}&\const{SEMOPM}& Massimo numero di voci di ripristino.\\ + \const{SEMAEM}&\const{SEMVMX}& Valore massimo per l'aggiustamento + all'uscita. \\ + \hline + \end{tabular} + \caption{Valori delle costanti associate ai limiti degli insiemi di + semafori, definite in \file{linux/sem.h}.} + \label{tab:ipc_sem_limits} +\end{table} + +Come per le code di messaggi anche per gli insiemi di semafori esistono una +serie di limiti, i cui valori sono associati ad altrettante costanti, che si +sono riportate in tab.~\ref{tab:ipc_sem_limits}. Alcuni di questi limiti sono +al solito accessibili e modificabili attraverso \func{sysctl} o scrivendo +direttamente nel file \procfile{/proc/sys/kernel/sem}. + +La funzione che permette di effettuare le varie operazioni di controllo sui +semafori (fra le quali, come accennato, è impropriamente compresa anche la +loro inizializzazione) è \funcd{semctl}; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semctl(int semid, int semnum, int cmd)} + \funcdecl{int semctl(int semid, int semnum, int cmd, union semun arg)} + + Esegue le operazioni di controllo su un semaforo o un insieme di semafori. + + \bodydesc{La funzione restituisce in caso di successo un valore positivo + quanto usata con tre argomenti ed un valore nullo quando usata con + quattro. In caso di errore restituisce -1, ed \var{errno} assumerà uno dei + valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per eseguire + l'operazione richiesta. + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{EPERM}] si è richiesto \const{IPC\_SET} o \const{IPC\_RMID} + ma il processo non ha privilegi sufficienti ad eseguire l'operazione. + \item[\errcode{ERANGE}] si è richiesto \const{SETALL} \const{SETVAL} ma il + valore a cui si vuole impostare il semaforo è minore di zero o maggiore + di \const{SEMVMX}. + \end{errlist} + ed inoltre \errval{EFAULT} ed \errval{EINVAL}. +} +\end{functions} + +La funzione può avere tre o quattro argomenti, a seconda dell'operazione +specificata con \param{cmd}, ed opera o sull'intero insieme specificato da +\param{semid} o sul singolo semaforo di un insieme, specificato da +\param{semnum}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/semun.h} + \end{minipage} + \normalsize + \caption{La definizione dei possibili valori di una \direct{union} + \structd{semun}, usata come quarto argomento della funzione + \func{semctl}.} + \label{fig:ipc_semun} +\end{figure} + +Qualora la funzione operi con quattro argomenti \param{arg} è un argomento +generico, che conterrà un dato diverso a seconda dell'azione richiesta; per +unificare l'argomento esso deve essere passato come una \struct{semun}, la cui +definizione, con i possibili valori che può assumere, è riportata in +fig.~\ref{fig:ipc_semun}. + +Come già accennato sia il comportamento della funzione che il numero di +argomenti con cui deve essere invocata dipendono dal valore dell'argomento +\param{cmd}, che specifica l'azione da intraprendere; i valori validi (che +cioè non causano un errore di \errcode{EINVAL}) per questo argomento sono i +seguenti: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge i dati dell'insieme di semafori, copiando il + contenuto della relativa struttura \struct{semid\_ds} all'indirizzo + specificato con \var{arg.buf}. Occorre avere il permesso di lettura. + L'argomento \param{semnum} viene ignorato. +\item[\const{IPC\_RMID}] Rimuove l'insieme di semafori e le relative strutture + dati, con effetto immediato. Tutti i processi che erano stato di + \textit{sleep} vengono svegliati, ritornando con un errore di + \errcode{EIDRM}. L'user-ID effettivo del processo deve corrispondere o al + creatore o al proprietario dell'insieme, o all'amministratore. L'argomento + \param{semnum} viene ignorato. +\item[\const{IPC\_SET}] Permette di modificare i permessi ed il proprietario + dell'insieme. I valori devono essere passati in una struttura + \struct{semid\_ds} puntata da \param{arg.buf} di cui saranno usati soltanto i + campi \var{sem\_perm.uid}, \var{sem\_perm.gid} e i nove bit meno + significativi di \var{sem\_perm.mode}. L'user-ID 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 + \acr{pid} dell'ultimo processo che ha compiuto una operazione sul semaforo + \param{semnum} dell'insieme \param{semid} (corrispondente al campo + \var{sempid} di \struct{sem}); va invocata con tre argomenti. Occorre avere + il permesso di lettura. +\item[\const{GETVAL}] Restituisce come valore di ritorno della funzione il il + valore corrente del semaforo \param{semnum} dell'insieme \param{semid} + (corrispondente al campo \var{semval} di \struct{sem}); va invocata con tre + argomenti. Occorre avere il permesso di lettura. +\item[\const{GETZCNT}] Restituisce come valore di ritorno della funzione il + numero di processi in attesa che il valore del semaforo \param{semnum} + dell'insieme \param{semid} diventi nullo (corrispondente al campo + \var{semncnt} di \struct{sem}); va invocata con tre argomenti. Occorre avere + il permesso di lettura. +\item[\const{SETALL}] Inizializza il valore di tutti i semafori dell'insieme, + aggiornando il campo \var{sem\_ctime} di \struct{semid\_ds}. I valori devono + essere passati nel vettore indicato da \param{arg.array}. Si devono avere i + privilegi di scrittura sul semaforo. L'argomento \param{semnum} viene + ignorato. +\item[\const{SETVAL}] Inizializza il semaforo \param{semnum} al valore passato + dall'argomento \param{arg.val}, aggiornando il campo \var{sem\_ctime} di + \struct{semid\_ds}. Si devono avere i privilegi di scrittura sul semaforo. +\end{basedescript} + +Quando si imposta il valore di un semaforo (sia che lo si faccia per tutto +l'insieme con \const{SETALL}, che per un solo semaforo con \const{SETVAL}), i +processi in attesa su di esso reagiscono di conseguenza al cambiamento di +valore. Inoltre la coda delle operazioni di ripristino viene cancellata per +tutti i semafori il cui valore viene modificato. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|l|} + \hline + \textbf{Operazione} & \textbf{Valore restituito} \\ + \hline + \hline + \const{GETNCNT}& Valore di \var{semncnt}.\\ + \const{GETPID} & Valore di \var{sempid}.\\ + \const{GETVAL} & Valore di \var{semval}.\\ + \const{GETZCNT}& Valore di \var{semzcnt}.\\ + \hline + \end{tabular} + \caption{Valori di ritorno della funzione \func{semctl}.} + \label{tab:ipc_semctl_returns} +\end{table} + +Il valore di ritorno della funzione in caso di successo dipende +dall'operazione richiesta; per tutte le operazioni che richiedono quattro +argomenti esso è sempre nullo, per le altre operazioni, elencate in +tab.~\ref{tab:ipc_semctl_returns} viene invece restituito il valore richiesto, +corrispondente al campo della struttura \struct{sem} indicato nella seconda +colonna della tabella. + +Le operazioni ordinarie sui semafori, come l'acquisizione o il rilascio degli +stessi (in sostanza tutte quelle non comprese nell'uso di \func{semctl}) +vengono effettuate con la funzione \funcd{semop}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semop(int semid, struct sembuf *sops, unsigned nsops)} + + Esegue le operazioni ordinarie su un semaforo o un insieme di semafori. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per eseguire + l'operazione richiesta. + \item[\errcode{EIDRM}] l'insieme di semafori è stato cancellato. + \item[\errcode{ENOMEM}] si è richiesto un \const{SEM\_UNDO} ma il sistema + non ha le risorse per allocare la struttura di ripristino. + \item[\errcode{EAGAIN}] un'operazione comporterebbe il blocco del processo, + ma si è specificato \const{IPC\_NOWAIT} in \var{sem\_flg}. + \item[\errcode{EINTR}] la funzione, bloccata in attesa dell'esecuzione + dell'operazione, viene interrotta da un segnale. + \item[\errcode{E2BIG}] l'argomento \param{nsops} è maggiore del numero + massimo di operazioni \const{SEMOPM}. + \item[\errcode{ERANGE}] per alcune operazioni il valore risultante del + semaforo viene a superare il limite massimo \const{SEMVMX}. + \end{errlist} + ed inoltre \errval{EFAULT} ed \errval{EINVAL}. +} +\end{functions} + +La funzione permette di eseguire operazioni multiple sui singoli semafori di +un insieme. La funzione richiede come primo argomento l'identificatore +\param{semid} dell'insieme su cui si vuole operare. Il numero di operazioni da +effettuare viene specificato con l'argomento \param{nsop}, mentre il loro +contenuto viene passato con un puntatore ad un vettore di strutture +\struct{sembuf} nell'argomento \param{sops}. Le operazioni richieste vengono +effettivamente eseguite se e soltanto se è possibile effettuarle tutte quante. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/sembuf.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{sembuf}, usata per le operazioni sui + semafori.} + \label{fig:ipc_sembuf} +\end{figure} + +Il contenuto di ciascuna operazione deve essere specificato attraverso una +opportuna struttura \struct{sembuf} (la cui definizione è riportata in +fig.~\ref{fig:ipc_sembuf}) che il programma chiamante deve avere cura di +allocare in un opportuno vettore. La struttura permette di indicare il +semaforo su cui operare, il tipo di operazione, ed un flag di controllo. +Il campo \var{sem\_num} serve per indicare a quale semaforo dell'insieme fa +riferimento l'operazione; si ricordi che i semafori sono numerati come in un +vettore, per cui il primo semaforo corrisponde ad un valore nullo di +\var{sem\_num}. + +Il campo \var{sem\_flg} è un flag, mantenuto come maschera binaria, per il +quale possono essere impostati i due valori \const{IPC\_NOWAIT} e +\const{SEM\_UNDO}. Impostando \const{IPC\_NOWAIT} si fa si che, invece di +bloccarsi (in tutti quei casi in cui l'esecuzione di una operazione richiede +che il processo vada in stato di \textit{sleep}), \func{semop} ritorni +immediatamente con un errore di \errcode{EAGAIN}. Impostando \const{SEM\_UNDO} +si richiede invece che l'operazione venga registrata in modo che il valore del +semaforo possa essere ripristinato all'uscita del processo. + +Infine \var{sem\_op} è il campo che controlla l'operazione che viene eseguita +e determina il comportamento della chiamata a \func{semop}; tre sono i casi +possibili: +\begin{basedescript}{\desclabelwidth{2.0cm}} +\item[\var{sem\_op}$>0$] In questo caso il valore di \var{sem\_op} viene + aggiunto al valore corrente di \var{semval}. La funzione ritorna + immediatamente (con un errore di \errcode{ERANGE} qualora si sia superato il + limite \const{SEMVMX}) ed il processo non viene bloccato in nessun caso. + Specificando \const{SEM\_UNDO} si aggiorna il contatore per il ripristino + del valore del semaforo. Al processo chiamante è richiesto il privilegio di + alterazione (scrittura) sull'insieme di semafori. + +\item[\var{sem\_op}$=0$] Nel caso \var{semval} sia zero l'esecuzione procede + immediatamente. Se \var{semval} è diverso da zero il comportamento è + controllato da \var{sem\_flg}, se è stato impostato \const{IPC\_NOWAIT} la + funzione ritorna con un errore di \errcode{EAGAIN}, altrimenti viene + incrementato \var{semzcnt} di uno ed il processo resta in stato di + \textit{sleep} fintanto che non si ha una delle condizioni seguenti: + \begin{itemize*} + \item \var{semval} diventa zero, nel qual caso \var{semzcnt} viene + decrementato di uno. + \item l'insieme di semafori viene rimosso, nel qual caso \func{semop} ritorna + un errore di \errcode{EIDRM}. + \item il processo chiamante riceve un segnale, nel qual caso \var{semzcnt} + viene decrementato di uno e \func{semop} ritorna un errore di + \errcode{EINTR}. + \end{itemize*} + Al processo chiamante è richiesto il privilegio di lettura dell'insieme dei + semafori. + +\item[\var{sem\_op}$<0$] Nel caso in cui \var{semval} è maggiore o uguale del + valore assoluto di \var{sem\_op} (se cioè la somma dei due valori resta + positiva o nulla) i valori vengono sommati e la funzione ritorna + immediatamente; qualora si sia impostato \const{SEM\_UNDO} viene anche + aggiornato il contatore per il ripristino del valore del semaforo. In caso + contrario (quando cioè la somma darebbe luogo ad un valore di \var{semval} + negativo) se si è impostato \const{IPC\_NOWAIT} la funzione ritorna con un + errore di \errcode{EAGAIN}, altrimenti viene incrementato di uno + \var{semncnt} ed il processo resta in stato di \textit{sleep} fintanto che + non si ha una delle condizioni seguenti: + \begin{itemize*} + \item \var{semval} diventa maggiore o uguale del valore assoluto di + \var{sem\_op}, nel qual caso \var{semncnt} viene decrementato di uno, il + valore di \var{sem\_op} viene sommato a \var{semval}, e se era stato + impostato \const{SEM\_UNDO} viene aggiornato il contatore per il + ripristino del valore del semaforo. + \item l'insieme di semafori viene rimosso, nel qual caso \func{semop} + ritorna un errore di \errcode{EIDRM}. + \item il processo chiamante riceve un segnale, nel qual caso \var{semncnt} + viene decrementato di uno e \func{semop} ritorna un errore di + \errcode{EINTR}. + \end{itemize*} + Al processo chiamante è richiesto il privilegio di alterazione (scrittura) + sull'insieme di semafori. +\end{basedescript} + +In caso di successo della funzione viene aggiornato il campo \var{sempid} per +ogni semaforo modificato al valore del \acr{pid} del processo chiamante; +inoltre vengono pure aggiornati al tempo corrente i campi \var{sem\_otime} e +\var{sem\_ctime}. + +Dato che, come già accennato in precedenza, in caso di uscita inaspettata i +semafori possono restare occupati, abbiamo visto come \func{semop} permetta di +attivare un meccanismo di ripristino attraverso l'uso del flag +\const{SEM\_UNDO}. Il meccanismo è implementato tramite una apposita struttura +\struct{sem\_undo}, associata ad ogni processo per ciascun semaforo che esso +ha modificato; all'uscita i semafori modificati vengono ripristinati, e le +strutture disallocate. Per mantenere coerente il comportamento queste +strutture non vengono ereditate attraverso una \func{fork} (altrimenti si +avrebbe un doppio ripristino), mentre passano inalterate nell'esecuzione di +una \func{exec} (altrimenti non si avrebbe ripristino). + +Tutto questo però ha un problema di fondo. Per capire di cosa si tratta +occorre fare riferimento all'implementazione usata in Linux, che è riportata +in maniera semplificata nello schema di fig.~\ref{fig:ipc_sem_schema}. Si è +presa come riferimento l'architettura usata fino al kernel 2.2.x che è più +semplice (ed illustrata in dettaglio in \cite{tlk}); nel kernel 2.4.x la +struttura del \textit{SysV IPC} è stata modificata, ma le definizioni relative +a queste strutture restano per compatibilità.\footnote{in particolare con le + vecchie versioni delle librerie del C, come le libc5.} + +\begin{figure}[htb] + \centering \includegraphics[width=13cm]{img/semtruct} + \caption{Schema della struttura di un insieme di semafori.} + \label{fig:ipc_sem_schema} +\end{figure} + +Alla creazione di un nuovo insieme viene allocata una nuova strutture +\struct{semid\_ds} ed il relativo vettore di strutture \struct{sem}. Quando si +richiede una operazione viene anzitutto verificato che tutte le operazioni +possono avere successo; se una di esse comporta il blocco del processo il +kernel crea una struttura \struct{sem\_queue} che viene aggiunta in fondo alla +coda di attesa associata a ciascun insieme di semafori\footnote{che viene + referenziata tramite i campi \var{sem\_pending} e \var{sem\_pending\_last} + di \struct{semid\_ds}.}. + +Nella struttura viene memorizzato il riferimento alle operazioni richieste +(nel campo \var{sops}, che è un puntatore ad una struttura \struct{sembuf}) e +al processo corrente (nel campo \var{sleeper}) poi quest'ultimo viene messo +stato di attesa e viene invocato lo \itindex{scheduler} scheduler per passare +all'esecuzione di un altro processo. + +Se invece tutte le operazioni possono avere successo queste vengono eseguite +immediatamente, dopo di che il kernel esegue una scansione della coda di +attesa (a partire da \var{sem\_pending}) per verificare se qualcuna delle +operazioni sospese in precedenza può essere eseguita, nel qual caso la +struttura \struct{sem\_queue} viene rimossa e lo stato del processo associato +all'operazione (\var{sleeper}) viene riportato a \textit{running}; il tutto +viene ripetuto fin quando non ci sono più operazioni eseguibili o si è +svuotata la coda. Per gestire il meccanismo del ripristino tutte le volte che +per un'operazione si è specificato il flag \const{SEM\_UNDO} viene mantenuta +per ciascun insieme di semafori una apposita struttura \struct{sem\_undo} che +contiene (nel vettore puntato dal campo \var{semadj}) un valore di +aggiustamento per ogni semaforo cui viene sommato l'opposto del valore usato +per l'operazione. + +Queste strutture sono mantenute in due liste,\footnote{rispettivamente + attraverso i due campi \var{id\_next} e \var{proc\_next}.} una associata +all'insieme di cui fa parte il semaforo, che viene usata per invalidare le +strutture se questo viene cancellato o per azzerarle se si è eseguita una +operazione con \func{semctl}; l'altra associata al processo che ha eseguito +l'operazione;\footnote{attraverso il campo \var{semundo} di + \struct{task\_struct}, come mostrato in \ref{fig:ipc_sem_schema}.} quando un +processo termina, la lista ad esso associata viene scandita e le operazioni +applicate al semaforo. Siccome un processo può accumulare delle richieste di +ripristino per semafori differenti chiamate attraverso diverse chiamate a +\func{semop}, si pone il problema di come eseguire il ripristino dei semafori +all'uscita del processo, ed in particolare se questo può essere fatto +atomicamente. + +Il punto è cosa succede quando una delle operazioni previste per il ripristino +non può essere eseguita immediatamente perché ad esempio il semaforo è +occupato; in tal caso infatti, se si pone il processo in stato di +\textit{sleep} aspettando la disponibilità del semaforo (come faceva +l'implementazione originaria) si perde l'atomicità dell'operazione. La scelta +fatta dal kernel è pertanto quella di effettuare subito le operazioni che non +prevedono un blocco del processo e di ignorare silenziosamente le altre; +questo però comporta il fatto che il ripristino non è comunque garantito in +tutte le occasioni. + +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}[!bht] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \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 \index{file!locking} \textit{file locking}. + + +\subsection{Memoria condivisa} +\label{sec:ipc_sysv_shm} + +Il terzo oggetto introdotto dal \textit{SysV IPC} è quello dei segmenti di +memoria condivisa. La funzione che permette di ottenerne uno è \funcd{shmget}, +ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmget(key\_t key, int size, int flag)} + + Restituisce l'identificatore di una memoria condivisa. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{ENOSPC}] si è superato il limite (\const{SHMMNI}) sul numero + di segmenti di memoria nel sistema, o cercato di allocare un segmento le + cui dimensioni fanno superare il limite di sistema (\const{SHMALL}) per + la memoria ad essi riservata. + \item[\errcode{EINVAL}] si è richiesta una dimensione per un nuovo segmento + maggiore di \const{SHMMAX} o minore di \const{SHMMIN}, o se il segmento + già esiste \param{size} è maggiore delle sue dimensioni. + \item[\errcode{ENOMEM}] il sistema non ha abbastanza memoria per poter + contenere le strutture per un nuovo segmento di memoria condivisa. + \end{errlist} + ed inoltre \errval{EACCES}, \errval{ENOENT}, \errval{EEXIST}, + \errval{EIDRM}, con lo stesso significato che hanno per \func{msgget}.} +\end{functions} + +La funzione, come \func{semget}, è del tutto analoga a \func{msgget}, ed +identico è l'uso degli argomenti \param{key} e \param{flag} per cui non +ripeteremo quanto detto al proposito in sez.~\ref{sec:ipc_sysv_mq}. L'argomento +\param{size} specifica invece la dimensione, in byte, del segmento, che viene +comunque arrotondata al multiplo superiore di \const{PAGE\_SIZE}. + +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]{15cm} + \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 \acr{pid} del processo che ha + eseguito l'ultima operazione, viene inizializzato a zero. +\item il campo \var{shm\_cpid}, che esprime il \acr{pid} del processo che ha + creato il segmento, viene inizializzato al \acr{pid} del processo chiamante. +\item il campo \var{shm\_nattac}, che esprime il numero di processi agganciati + al segmento viene inizializzato a zero. +\end{itemize} + +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&\procrelfile{/proc/sys/kernel}{shmall} + & Numero massimo di pagine che + possono essere usate per i segmenti di + memoria condivisa.\\ + \const{SHMMAX}&0x2000000&\procrelfile{/proc/sys/kernel}{shmmax} + & Dimensione massima di un segmento di memoria + condivisa.\\ + \const{SHMMNI}& 4096&\procrelfile{/proc/sys/kernel}{msgmni} + & Numero massimo di segmenti di memoria condivisa + presenti nel kernel.\\ + \const{SHMMIN}& 1& --- & Dimensione minima di un segmento di + memoria condivisa.\\ + \const{SHMLBA}&\const{PAGE\_SIZE}&--- & Limite inferiore per le dimensioni + minime di un segmento (deve essere + allineato alle dimensioni di una + pagina di memoria).\\ + \const{SHMSEG}& --- & --- & Numero massimo di segmenti di + memoria condivisa per ciascun + processo.\\ + + + \hline + \end{tabular} + \caption{Valori delle costanti associate ai limiti dei segmenti di memoria + condivisa, insieme al relativo file in \file{/proc/sys/kernel/} ed al + valore preimpostato presente nel sistema.} + \label{tab:ipc_shm_limits} +\end{table} + +Al solito la funzione che permette di effettuare le operazioni di controllo su +un segmento di memoria condivisa è \funcd{shmctl}; il suo prototipo è: +\begin{functions} + \headdecl{sys/ipc.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmctl(int shmid, int cmd, struct shmid\_ds *buf)} + + Esegue le operazioni di controllo su un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCES}] si è richiesto \const{IPC\_STAT} ma i permessi non + consentono l'accesso in lettura al segmento. + \item[\errcode{EINVAL}] o \param{shmid} non è un identificatore valido o + \param{cmd} non è un comando valido. + \item[\errcode{EIDRM}] l'argomento \param{shmid} fa riferimento ad un + segmento che è stato cancellato. + \item[\errcode{EPERM}] si è specificato un comando con \const{IPC\_SET} o + \const{IPC\_RMID} senza i permessi necessari. + \item[\errcode{EOVERFLOW}] si è tentato il comando \const{IPC\_STAT} ma il + valore del group-ID o dell'user-ID è troppo grande per essere + memorizzato nella struttura puntata da \param{buf}. + \item[\errcode{EFAULT}] l'indirizzo specificato con \param{buf} non è + valido. + \end{errlist} +} +\end{functions} + +Il comando specificato attraverso l'argomento \param{cmd} determina i diversi +effetti della funzione; i possibili valori che esso può assumere, ed il +corrispondente comportamento della funzione, sono i seguenti: + +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{IPC\_STAT}] Legge le informazioni riguardo il segmento di memoria + condivisa nella struttura \struct{shmid\_ds} puntata da \param{buf}. Occorre + che il processo chiamante abbia il permesso di lettura sulla segmento. +\item[\const{IPC\_RMID}] Marca il segmento di memoria condivisa per la + rimozione, questo verrà cancellato effettivamente solo quando l'ultimo + processo ad esso agganciato si sarà staccato. Questo comando può essere + eseguito solo da un processo con user-ID effettivo corrispondente o al + creatore del segmento, o al proprietario del segmento, o all'amministratore. +\item[\const{IPC\_SET}] Permette di modificare i permessi ed il proprietario + del segmento. Per modificare i valori di \var{shm\_perm.mode}, + \var{shm\_perm.uid} e \var{shm\_perm.gid} occorre essere il proprietario o + il creatore del segmento, oppure l'amministratore. Compiuta l'operazione + aggiorna anche il valore del campo \var{shm\_ctime}. +\item[\const{SHM\_LOCK}] Abilita il \itindex{memory~locking} \textit{memory + locking}\footnote{impedisce cioè che la memoria usata per il segmento + venga salvata su disco dal meccanismo della \index{memoria~virtuale} + memoria virtuale; si ricordi quanto trattato in + sez.~\ref{sec:proc_mem_lock}.} sul segmento di memoria condivisa. Solo + l'amministratore può utilizzare questo comando. +\item[\const{SHM\_UNLOCK}] Disabilita il \itindex{memory~locking} + \textit{memory locking} sul segmento di memoria condivisa. Solo + l'amministratore può utilizzare questo comando. +\end{basedescript} +i primi tre comandi sono gli stessi già visti anche per le code di messaggi e +gli insiemi di semafori, gli ultimi due sono delle estensioni specifiche +previste da Linux, che permettono di abilitare e disabilitare il meccanismo +della \index{memoria~virtuale} memoria virtuale per il segmento. + +L'argomento \param{buf} viene utilizzato solo con i comandi \const{IPC\_STAT} +e \const{IPC\_SET} nel qual caso esso dovrà puntare ad una struttura +\struct{shmid\_ds} precedentemente allocata, in cui nel primo caso saranno +scritti i dati del segmento di memoria restituiti dalla funzione e da cui, nel +secondo caso, verranno letti i dati da impostare sul segmento. + +Una volta che lo si è creato, per utilizzare un segmento di memoria condivisa +l'interfaccia prevede due funzioni, \funcd{shmat} e \func{shmdt}. La prima di +queste serve ad agganciare un segmento al processo chiamante, in modo che +quest'ultimo possa inserirlo nel suo spazio di indirizzi per potervi accedere; +il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/shm.h} + + \funcdecl{void *shmat(int shmid, const void *shmaddr, int shmflg)} + Aggancia al processo un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce l'indirizzo del segmento in caso di + successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà i + valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al + segmento nella modalità richiesta. + \item[\errcode{EINVAL}] si è specificato un identificatore invalido per + \param{shmid}, o un indirizzo non allineato sul confine di una pagina + per \param{shmaddr}. + \end{errlist} + ed inoltre \errval{ENOMEM}.} +\end{functions} + +La funzione inserisce un segmento di memoria condivisa all'interno dello +spazio di indirizzi del processo, in modo che questo possa accedervi +direttamente, la situazione dopo l'esecuzione di \func{shmat} è illustrata in +fig.~\ref{fig:ipc_shmem_layout} (per la comprensione del resto dello schema si +ricordi quanto illustrato al proposito in sez.~\ref{sec:proc_mem_layout}). In +particolare l'indirizzo finale del segmento dati (quello impostato da +\func{brk}, vedi sez.~\ref{sec:proc_mem_alloc}) non viene influenzato. +Si tenga presente infine che la funzione ha successo anche se il segmento è +stato marcato per la cancellazione. + +\begin{figure}[htb] + \centering + \includegraphics[height=10cm]{img/sh_memory_layout} + \caption{Disposizione dei segmenti di memoria di un processo quando si è + agganciato un segmento di memoria condivisa.} + \label{fig:ipc_shmem_layout} +\end{figure} + +L'argomento \param{shmaddr} specifica a quale indirizzo\footnote{lo standard + SVID prevede che l'argomento \param{shmaddr} sia di tipo \ctyp{char *}, così + come il valore di ritorno della funzione; in Linux è stato così con le + \acr{libc4} e le \acr{libc5}, con il passaggio alle \acr{glibc} il tipo di + \param{shmaddr} è divenuto un \ctyp{const void *} e quello del valore di + ritorno un \ctyp{void *}.} deve essere associato il segmento, se il valore +specificato è \val{NULL} è il sistema a scegliere opportunamente un'area di +memoria libera (questo è il modo più portabile e sicuro di usare la funzione). +Altrimenti il kernel aggancia il segmento all'indirizzo specificato da +\param{shmaddr}; questo però può avvenire solo se l'indirizzo coincide con il +limite di una pagina, cioè se è un multiplo esatto del parametro di sistema +\const{SHMLBA}, che in Linux è sempre uguale \const{PAGE\_SIZE}. + +Si tenga presente però che quando si usa \val{NULL} come valore di +\param{shmaddr}, l'indirizzo restituito da \func{shmat} può cambiare da +processo a processo; pertanto se nell'area di memoria condivisa si salvano +anche degli indirizzi, si deve avere cura di usare valori relativi (in genere +riferiti all'indirizzo di partenza del segmento). + +L'argomento \param{shmflg} permette di cambiare il comportamento della +funzione; esso va specificato come maschera binaria, i bit utilizzati sono +solo due e sono identificati dalle costanti \const{SHM\_RND} e +\const{SHM\_RDONLY}, che vanno combinate con un OR aritmetico. Specificando +\const{SHM\_RND} si evita che \func{shmat} ritorni un errore quando +\param{shmaddr} non è allineato ai confini di una pagina. Si può quindi usare +un valore qualunque per \param{shmaddr}, e il segmento verrà comunque +agganciato, ma al più vicino multiplo di \const{SHMLBA} (il nome della +costante sta infatti per \textit{rounded}, e serve per specificare un +indirizzo come arrotondamento, in Linux è equivalente a \const{PAGE\_SIZE}). + +L'uso di \const{SHM\_RDONLY} permette di agganciare il segmento in sola +lettura (si ricordi che anche le pagine di memoria hanno dei permessi), in tal +caso un tentativo di scrivere sul segmento comporterà una +\itindex{segment~violation} violazione di accesso con l'emissione di un +segnale di \const{SIGSEGV}. Il comportamento usuale di \func{shmat} è quello +di agganciare il segmento con l'accesso in lettura e scrittura (ed il processo +deve aver questi permessi in \var{shm\_perm}), non è prevista la possibilità +di agganciare un segmento in sola scrittura. + +In caso di successo la funzione aggiorna anche i seguenti campi di +\struct{shmid\_ds}: +\begin{itemize*} +\item il tempo \var{shm\_atime} dell'ultima operazione di aggancio viene + impostato al tempo corrente. +\item il \acr{pid} \var{shm\_lpid} dell'ultimo processo che ha operato sul + segmento viene impostato a quello del processo corrente. +\item il numero \var{shm\_nattch} di processi agganciati al segmento viene + aumentato di uno. +\end{itemize*} + +Come accennato in sez.~\ref{sec:proc_fork} un segmento di memoria condivisa +agganciato ad un processo viene ereditato da un figlio attraverso una +\func{fork}, dato che quest'ultimo riceve una copia dello spazio degli +indirizzi del padre. Invece, dato che attraverso una \func{exec} viene +eseguito un diverso programma con uno spazio di indirizzi completamente +diverso, tutti i segmenti agganciati al processo originario vengono +automaticamente sganciati. Lo stesso avviene all'uscita del processo +attraverso una \func{exit}. + +Una volta che un segmento di memoria condivisa non serve più, si può +sganciarlo esplicitamente dal processo usando l'altra funzione +dell'interfaccia, \funcd{shmdt}, il cui prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmdt(const void *shmaddr)} + Sgancia dal processo un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di + errore, la funzione fallisce solo quando non c'è un segmento agganciato + all'indirizzo \param{shmaddr}, con \var{errno} che assume il valore + \errval{EINVAL}.} +\end{functions} + +La funzione sgancia dallo spazio degli indirizzi del processo un segmento di +memoria condivisa; questo viene identificato con l'indirizzo \param{shmaddr} +restituito dalla precedente chiamata a \func{shmat} con il quale era stato +agganciato al processo. + +In caso di successo la funzione aggiorna anche i seguenti campi di +\struct{shmid\_ds}: +\begin{itemize*} +\item il tempo \var{shm\_dtime} dell'ultima operazione di sganciamento viene + impostato al tempo corrente. +\item il \acr{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}[!bht] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/SharedMem.c} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni che permettono di creare, trovare e + rimuovere un segmento di memoria condivisa.} + \label{fig:ipc_sysv_shm_func} +\end{figure} + +Come esempio di uso di queste funzioni vediamo come implementare una serie di +funzioni di libreria che ne semplifichino l'uso, automatizzando le operazioni +più comuni; il codice, contenuto nel file \file{SharedMem.c}, è riportato in +fig.~\ref{fig:ipc_sysv_shm_func}. + +La prima funzione (\texttt{\small 3--16}) è \func{ShmCreate} che, data una +chiave, crea il segmento di memoria condivisa restituendo il puntatore allo +stesso. La funzione comincia (\texttt{\small 6}) con il chiamare +\func{shmget}, usando il flag \const{IPC\_CREATE} per creare il segmento +qualora non esista, ed assegnandogli i privilegi specificati dall'argomento +\var{perm} e la dimensione specificata dall'argomento \var{shm\_size}. In +caso di errore (\texttt{\small 7--9}) si ritorna immediatamente un puntatore +nullo, altrimenti (\texttt{\small 10}) si prosegue agganciando il segmento di +memoria condivisa al processo con \func{shmat}. In caso di errore +(\texttt{\small 11--13}) si restituisce di nuovo un puntatore nullo, infine +(\texttt{\small 14}) si inizializza con \func{memset} il contenuto del +segmento al valore costante specificato dall'argomento \var{fill}, e poi si +ritorna il puntatore al segmento stesso. + +La seconda funzione (\texttt{\small 17--31}) è \func{ShmFind}, che, data una +chiave, restituisce l'indirizzo del segmento ad essa associato. Anzitutto +(\texttt{\small 22}) si richiede l'identificatore del segmento con +\func{shmget}, ritornando (\texttt{\small 23--25}) un puntatore nullo in caso +di errore. Poi si prosegue (\texttt{\small 26}) agganciando il segmento al +processo con \func{shmat}, restituendo (\texttt{\small 27--29}) di nuovo un +puntatore nullo in caso di errore, se invece non ci sono errori si restituisce +il puntatore ottenuto da \func{shmat}. + +La terza funzione (\texttt{\small 32--51}) è \func{ShmRemove} che, data la +chiave ed il puntatore associati al segmento di memoria condivisa, prima lo +sgancia dal processo e poi lo rimuove. Il primo passo (\texttt{\small 37}) è +la chiamata a \func{shmdt} per sganciare il segmento, restituendo +(\texttt{\small 38--39}) un valore -1 in caso di errore. Il passo successivo +(\texttt{\small 41}) è utilizzare \func{shmget} per 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 pipe, le fifo o i socket, che non +necessitano di sincronizzazione esplicita, sono da preferire. Essa diventa +l'unico meccanismo possibile quando la comunicazione non è +sequenziale\footnote{come accennato in 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 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}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \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 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 directory di lavoro del programma nella directory da tenere sotto +controllo, in vista del successivo uso della funzione +\func{daemon}.\footnote{si noti come si è potuta fare questa scelta, + nonostante le indicazioni illustrate in sez.~\ref{sec:sess_daemon}, per il + particolare scopo del programma, che necessita comunque di restare + all'interno di una directory.} Infine (\texttt{\small 27--29}) si installano +i gestori per i vari segnali di terminazione che, avendo a che fare con un +programma che deve essere eseguito come server, sono il solo strumento +disponibile per concluderne l'esecuzione. + +Il passo successivo (\texttt{\small 30--39}) è quello di creare gli oggetti di +intercomunicazione necessari. Si inizia costruendo (\texttt{\small 30}) la +chiave da usare come riferimento con il nome del programma,\footnote{si è + usato un riferimento relativo alla home dell'utente, supposto che i sorgenti + di GaPiL siano stati installati direttamente in essa. Qualora si effettui + una installazione diversa si dovrà correggere il programma.} dopo di che si +richiede (\texttt{\small 31}) la creazione di un segmento di memoria condivisa +con usando la funzione \func{ShmCreate} illustrata in precedenza (una pagina +di memoria è sufficiente per i dati che useremo), uscendo (\texttt{\small + 32--35}) qualora la creazione ed il successivo agganciamento al processo non +abbia successo. Con l'indirizzo \var{shmptr} così ottenuto potremo poi +accedere alla memoria condivisa, che, per come abbiamo lo abbiamo definito, +sarà vista nella forma data da \struct{DirProp}. Infine (\texttt{\small + 36--39}) utilizzando sempre la stessa chiave, si crea, tramite le funzioni +di interfaccia già descritte in sez.~\ref{sec:ipc_sysv_sem}, anche un mutex, +che utilizzeremo per regolare l'accesso alla memoria condivisa. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/ComputeValues.c} + \end{minipage} + \normalsize + \caption{Codice delle funzioni ausiliarie usate da \file{DirMonitor.c}.} + \label{fig:ipc_dirmonitor_sub} +\end{figure} + +Completata l'inizializzazione e la creazione degli oggetti di +intercomunicazione il programma entra nel ciclo principale (\texttt{\small + 40--49}) dove vengono eseguite indefinitamente le attività di monitoraggio. +Il primo passo (\texttt{\small 41}) è eseguire \func{daemon} per proseguire +con l'esecuzione in background come si conviene ad un programma demone; si +noti che si è mantenuta, usando un valore non nullo del primo argomento, la +directory di lavoro corrente. Una volta che il programma è andato in +background l'esecuzione prosegue (\texttt{\small 42--48}) all'interno di un +ciclo infinito: si inizia (\texttt{\small 43}) bloccando il mutex con +\func{MutexLock} per poter accedere alla memoria condivisa (la funzione si +bloccherà automaticamente se qualche client sta leggendo), poi (\texttt{\small + 44}) si cancellano i valori precedentemente immagazzinati nella memoria +condivisa con \func{memset}, e si esegue (\texttt{\small 45}) un nuovo calcolo +degli stessi utilizzando la funzione \func{DirScan}; infine (\texttt{\small + 46}) si sblocca il mutex con \func{MutexUnlock}, e si attende +(\texttt{\small 47}) per il periodo di tempo specificato a riga di comando con +l'opzione \code{-p} con una \func{sleep}. + +Si noti come per il calcolo dei valori da mantenere nella memoria condivisa si +sia usata ancora una volta la funzione \func{DirScan}, già utilizzata (e +descritta in dettaglio) in sez.~\ref{sec:file_dir_read}, che ci permette di +effettuare la scansione delle voci della directory, chiamando per ciascuna di +esse la funzione \func{ComputeValues}, che esegue tutti i calcoli necessari. + +Il codice di quest'ultima è riportato in fig.~\ref{fig:ipc_dirmonitor_sub}. +Come si vede la funzione (\texttt{\small 2--16}) è molto semplice e si limita +a chiamare (\texttt{\small 5}) la funzione \func{stat} sul file indicato da +ciascuna voce, per ottenerne i dati, che poi utilizza per incrementare i vari +contatori nella memoria condivisa, cui accede grazie alla variabile globale +\var{shmptr}. + +Dato che la funzione è chiamata da \func{DirScan}, 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}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6 cm} + \includecodesample{listati/ReadMonitor.c} + \end{minipage} + \normalsize + \caption{Codice del programma client del monitor delle proprietà di una + directory, \file{ReadMonitor.c}.} + \label{fig:ipc_dirmonitor_client} +\end{figure} + +Il codice del client usato per leggere le informazioni mantenute nella memoria +condivisa è riportato in fig.~\ref{fig:ipc_dirmonitor_client}. Al solito si è +omessa la sezione di gestione delle opzioni e la funzione che stampa a video +le istruzioni; il codice completo è nei sorgenti allegati, nel file +\file{ReadMonitor.c}. + +Una volta conclusa la gestione delle opzioni a riga di comando il programma +rigenera (\texttt{\small 7}) con \func{ftok} la stessa chiave usata dal server +per identificare il segmento di memoria condivisa ed il mutex, poi +(\texttt{\small 8}) richiede con \func{ShmFind} l'indirizzo della memoria +condivisa agganciando al contempo il segmento al processo, Infine +(\texttt{\small 17--20}) con \func{MutexFind} si richiede l'identificatore del +mutex. Completata l'inizializzazione ed ottenuti i riferimenti agli oggetti +di intercomunicazione necessari viene eseguito il corpo principale del +programma (\texttt{\small 21--33}); si comincia (\texttt{\small 22}) +acquisendo il mutex con \func{MutexLock}; qui avviene il blocco del processo +se la memoria condivisa non è disponibile. Poi (\texttt{\small 23--31}) si +stampano i vari valori mantenuti nella memoria condivisa attraverso l'uso di +\var{shmptr}. Infine (\texttt{\small 41}) con \func{MutexUnlock} si rilascia +il mutex, prima di uscire. + +Verifichiamo allora il funzionamento dei nostri programmi; al solito, usando +le funzioni di libreria occorre definire opportunamente +\code{LD\_LIBRARY\_PATH}; poi si potrà lanciare il server con: +\begin{Verbatim} +[piccardi@gont sources]$ ./dirmonitor ./ +\end{Verbatim} +%$ +ed avendo usato \func{daemon} il comando ritornerà immediatamente. Una volta +che il server è in esecuzione, possiamo passare ad invocare il client per +verificarne i risultati, in tal caso otterremo: +\begin{Verbatim} +[piccardi@gont sources]$ ./readmon +Ci sono 68 file dati +Ci sono 3 directory +Ci sono 0 link +Ci sono 0 fifo +Ci sono 0 socket +Ci sono 0 device a caratteri +Ci sono 0 device a blocchi +Totale 71 file, per 489831 byte +\end{Verbatim} +%$ +ed un rapido calcolo (ad esempio con \code{ls -a | wc} per contare i file) ci +permette di verificare che il totale dei file è giusto. Un controllo con +\cmd{ipcs} ci permette inoltre di verificare la presenza di un segmento di +memoria condivisa e di un semaforo: +\begin{Verbatim} +[piccardi@gont sources]$ ipcs +------ Shared Memory Segments -------- +key shmid owner perms bytes nattch status +0xffffffff 54067205 piccardi 666 4096 1 + +------ Semaphore Arrays -------- +key semid owner perms nsems +0xffffffff 229376 piccardi 666 1 + +------ Message Queues -------- +key msqid owner perms used-bytes messages +\end{Verbatim} +%$ + +Se a questo punto aggiungiamo un file, ad esempio con \code{touch prova}, +potremo verificare che, passati nel peggiore dei casi almeno 10 secondi (o +l'eventuale altro intervallo impostato per la rilettura dei dati) avremo: +\begin{Verbatim} +[piccardi@gont sources]$ ./readmon +Ci sono 69 file dati +Ci sono 3 directory +Ci sono 0 link +Ci sono 0 fifo +Ci sono 0 socket +Ci sono 0 device a caratteri +Ci sono 0 device a blocchi +Totale 72 file, per 489887 byte +\end{Verbatim} +%$ + +A questo punto possiamo far uscire il server inviandogli un segnale di +\const{SIGTERM} con il comando \code{killall dirmonitor}, a questo punto +ripetendo la lettura, otterremo un errore: +\begin{Verbatim} +[piccardi@gont sources]$ ./readmon +Cannot find shared memory: No such file or directory +\end{Verbatim} +%$ +e inoltre potremo anche verificare che anche gli oggetti di intercomunicazione +visti in precedenza sono stati regolarmente cancellati: +\begin{Verbatim} +[piccardi@gont sources]$ ipcs +------ Shared Memory Segments -------- +key shmid owner perms bytes nattch status + +------ Semaphore Arrays -------- +key semid owner perms nsems + +------ Message Queues -------- +key msqid owner perms used-bytes messages +\end{Verbatim} +%$ + + +%% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta +%% riferimento alle strutture con cui il kernel implementa i segmenti di memoria +%% condivisa; uno schema semplificato della struttura è illustrato in +%% fig.~\ref{fig:ipc_shm_struct}. + +%% \begin{figure}[htb] +%% \centering +%% \includegraphics[width=10cm]{img/shmstruct} +%% \caption{Schema dell'implementazione dei segmenti di memoria condivisa in +%% Linux.} +%% \label{fig:ipc_shm_struct} +%% \end{figure} + + + + +\section{Tecniche alternative} +\label{sec:ipc_alternatives} + +Come abbiamo detto in sez.~\ref{sec:ipc_sysv_generic}, e ripreso nella +descrizione dei singoli oggetti che ne fan parte, il \textit{SysV IPC} +presenta numerosi problemi; in \cite{APUE}\footnote{in particolare nel + capitolo 14.} Stevens ne effettua una accurata analisi (alcuni dei concetti +sono già stati accennati in precedenza) ed elenca alcune possibili tecniche +alternative, che vogliamo riprendere in questa sezione. + + +\subsection{Alternative alle code di messaggi} +\label{sec:ipc_mq_alternative} + +Le code di messaggi sono probabilmente il meno usato degli oggetti del +\textit{SysV IPC}; esse infatti nacquero principalmente come meccanismo di +comunicazione bidirezionale quando ancora le pipe erano unidirezionali; con la +disponibilità di \func{socketpair} (vedi sez.~\ref{sec:ipc_socketpair}) o +utilizzando una coppia di pipe, si può ottenere questo risultato senza +incorrere nelle complicazioni introdotte dal \textit{SysV IPC}. + +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 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. + +\subsection{I \textsl{file di lock}} +\label{sec:ipc_file_lock} + +\index{file!di lock|(} + +Come illustrato in sez.~\ref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC} +presentano una interfaccia inutilmente complessa e con alcuni difetti +strutturali, per questo quando si ha una semplice esigenza di sincronizzazione +per la quale basterebbe un semaforo binario (quello che abbiamo definito come +\textit{mutex}), per indicare la disponibilità o meno di una risorsa, senza la +necessità di un contatore come i semafori, si possono utilizzare metodi +alternativi. + +La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare +dei \textsl{file di lock} (per i quali esiste anche una opportuna directory, +\file{/var/lock}, nel filesystem standard). Per questo si usa la +caratteristica della funzione \func{open} (illustrata in +sez.~\ref{sec:file_open}) 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}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \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}, 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 \index{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}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/MutexLocking.c} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni che permettono per la gestione dei + \textit{mutex} con il \index{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} \index{file!locking} è riportato in +fig.~\ref{fig:ipc_flock_mutex}; si è mantenuta volutamente una struttura +analoga alle precedenti funzioni che usano i semafori, anche se le due +interfacce non possono essere completamente equivalenti, specie per quanto +riguarda la rimozione del mutex. + +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 \index{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 \index{file!locking} \textit{file + locking} in semantica POSIX (si riveda quanto detto +sez.~\ref{sec:file_posix_lock}) solo il processo che ha precedentemente +eseguito il lock può sbloccare il mutex. + +La quinta funzione (\texttt{\small 36--39}) è \func{RemoveMutex} e serve a +cancellare il mutex. Anche questa funzione è stata definita per mantenere una +analogia con le funzioni basate sui semafori, e si limita a cancellare +(\texttt{\small 38}) il file con una chiamata ad \func{unlink}. Si noti che in +questo caso la funzione non ha effetto sui mutex già ottenuti con precedenti +chiamate a \func{FindMutex} o \func{CreateMutex}, che continueranno ad essere +disponibili fintanto che i relativi file descriptor restano aperti. Pertanto +per rilasciare un mutex occorrerà prima chiamare \func{UnlockMutex} oppure +chiudere il file usato per il lock. + +La sesta funzione (\texttt{\small 41--55}) è \func{ReadMutex} e serve a +leggere lo stato del mutex. In questo caso si prepara (\texttt{\small 46--49}) +la solita struttura \var{lock} come l'acquisizione del lock, ma si effettua +(\texttt{\small 51}) la chiamata a \func{fcntl} usando il comando +\const{F\_GETLK} per ottenere lo stato del lock, e si restituisce +(\texttt{\small 52}) il valore di ritorno in caso di errore, ed il valore del +campo \var{l\_type} (che descrive lo stato del lock) altrimenti +(\texttt{\small 54}). Per questo motivo la funzione restituirà -1 in caso di +errore e uno dei due valori \const{F\_UNLCK} o \const{F\_WRLCK}\footnote{non + si dovrebbe mai avere il terzo valore possibile, \const{F\_RDLCK}, dato che + la nostra interfaccia usa solo i write lock. Però è sempre possibile che + siano richiesti altri lock sul file al di fuori dell'interfaccia, nel qual + caso si potranno avere, ovviamente, interferenze indesiderate.} in caso di +successo, ad indicare che il mutex è, rispettivamente, libero o occupato. + +Basandosi sulla semantica dei file lock POSIX valgono tutte le considerazioni +relative al comportamento di questi ultimi fatte in +sez.~\ref{sec:file_posix_lock}; questo significa ad esempio che, al contrario +di quanto avveniva con l'interfaccia basata sui semafori, chiamate multiple a +\func{UnlockMutex} o \func{LockMutex} non si cumulano e non danno perciò +nessun inconveniente. + + +\subsection{Il \textit{memory mapping} anonimo} +\label{sec:ipc_mmap_anonymous} + +\itindbeg{memory~mapping} +Abbiamo già visto che quando i processi sono \textsl{correlati}\footnote{se + cioè hanno almeno un progenitore comune.} l'uso delle 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 + +\section{Il sistema di comunicazione fra processi di POSIX} +\label{sec:ipc_posix} + +Per superare i numerosi problemi del \textit{SysV IPC}, evidenziati per i suoi +aspetti generali in coda a 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 dalle \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 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 + \itindex{pathname} \textit{pathname}, in particolare non essere più lunghi di + \const{PATH\_MAX} byte e terminati da un carattere nullo. +\item se il nome inizia per una \texttt{/} chiamate differenti allo stesso + nome fanno riferimento allo stesso oggetto, altrimenti l'interpretazione del + nome dipende dall'implementazione. +\item l'interpretazione di ulteriori \texttt{/} presenti nel nome dipende + dall'implementazione. +\end{itemize*} + +Data la assoluta genericità delle specifiche, il comportamento delle funzioni +è subordinato in maniera quasi completa alla relativa +implementazione.\footnote{tanto che Stevens in \cite{UNP2} cita questo caso + come un esempio della maniera standard usata dallo standard POSIX per + consentire implementazioni non standardizzabili.} Nel caso di Linux, sia per +quanto riguarda la memoria condivisa ed i semafori, che per quanto riguarda le +code di messaggi, tutto viene creato usando come radici delle opportune +directory (rispettivamente \file{/dev/shm} e \file{/dev/mqueue}, per i +dettagli si faccia riferimento a sez.~\ref{sec:ipc_posix_shm}, +sez.~\ref{sec:ipc_posix_sem} e sez.~\ref{sec:ipc_posix_mq}) ed i nomi +specificati nelle relative funzioni sono considerati come un +\itindsub{pathname}{assoluto} \textit{pathname} assoluto (comprendente +eventuali sottodirectory) rispetto a queste radici. + +Il vantaggio degli oggetti di IPC POSIX è comunque che essi vengono inseriti +nell'albero dei file, e possono essere maneggiati con le usuali funzioni e +comandi di accesso ai file,\footnote{questo è vero nel caso di Linux, che usa + una implementazione che lo consente, non è detto che altrettanto valga per + altri kernel; in particolare, come si può facilmente verificare con uno + \cmd{strace}, sia per la memoria condivisa che per le code di messaggi le + system call utilizzate da Linux sono le stesse di quelle dei file, essendo + detti oggetti realizzati come tali in appositi filesystem.} che funzionano +come su dei file normali. + +In particolare i permessi associati agli oggetti di IPC POSIX sono identici ai +permessi dei file, ed il controllo di accesso segue esattamente la stessa +semantica (quella illustrata in sez.~\ref{sec:file_access_control}), e non +quella particolare (si ricordi quanto visto in +sez.~\ref{sec:ipc_sysv_access_control}) che viene usata per gli oggetti del +SysV IPC. Per quanto riguarda l'attribuzione dell'utente e del gruppo +proprietari dell'oggetto alla creazione di quest'ultimo essa viene effettuata +secondo la semantica SysV: corrispondono cioè a user-ID e group-ID effettivi +del processo che esegue la creazione. + + +\subsection{Code di messaggi} +\label{sec:ipc_posix_mq} + +Le code di messaggi POSIX sono supportate da Linux a partire dalla versione +2.6.6-rc1 del kernel,\footnote{l'implementazione è dovuta a Michal Wronski e + Krzysztof Benedyczak, e le relative informazioni si possono trovare su + \href{http://www.geocities.com/wronski12/posix_ipc/index.html} + {\textsf{http://www.geocities.com/wronski12/posix\_ipc/index.html}}.} In +generale, come le corrispettive del SysV IPC, le code di messaggi sono poco +usate, dato che i socket, nei casi in cui sono sufficienti, sono più comodi, e +che in casi più complessi la comunicazione può essere gestita direttamente con +mutex (o semafori) e memoria condivisa con tutta la flessibilità che occorre. + +Per poter utilizzare le code di messaggi, oltre ad utilizzare un kernel +superiore al 2.6.6 (o precedente, se sono stati opportunamente applicati i +relativi patch) occorre utilizzare la libreria \file{libmqueue}\footnote{i + programmi che usano le code di messaggi cioè devono essere compilati + aggiungendo l'opzione \code{-lmqueue} al comando \cmd{gcc}; in + corrispondenza all'inclusione del supporto nel kernel ufficiale anche + \file{libmqueue} è stata inserita nelle \acr{glibc}, a partire dalla + versione 2.3.4 delle medesime.} che contiene le funzioni dell'interfaccia +POSIX.\footnote{in realtà l'implementazione è realizzata tramite delle + opportune chiamate ad \func{ioctl} sui file del filesystem speciale su cui + vengono mantenuti questi oggetti di IPC.} + +La libreria inoltre richiede la presenza dell'apposito filesystem di tipo +\texttt{mqueue} montato su \file{/dev/mqueue}; questo può essere fatto +aggiungendo ad \conffile{/etc/fstab} una riga come: +\begin{verbatim} +mqueue /dev/mqueue mqueue defaults 0 0 +\end{verbatim} +ed esso sarà utilizzato come radice sulla quale vengono risolti i nomi delle +code di messaggi che iniziano con una ``\texttt{/}''. Le opzioni di mount +accettate sono \texttt{uid}, \texttt{gid} e \texttt{mode} che permettono +rispettivamente di impostare l'utente, il gruppo ed i permessi associati al +filesystem. + + +La funzione che permette di aprire (e crearla se non esiste ancora) una coda +di messaggi POSIX è \funcd{mq\_open}, ed il suo prototipo è: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{mqd\_t mq\_open(const char *name, int oflag)} + + \funcdecl{mqd\_t mq\_open(const char *name, int oflag, unsigned long mode, + struct mq\_attr *attr)} + + Apre una coda di messaggi POSIX impostandone le caratteristiche. + + \bodydesc{La funzione restituisce il descrittore associato alla coda in caso + di successo e -1 per un errore; nel quel caso \var{errno} assumerà i + valori: + \begin{errlist} + \item[\errcode{EACCES}] il processo non ha i privilegi per accedere al + alla memoria secondo quanto specificato da \param{oflag}. + \item[\errcode{EEXIST}] si è specificato \const{O\_CREAT} e + \const{O\_EXCL} ma la coda già esiste. + \item[\errcode{EINVAL}] il file non supporta la funzione, o si è + specificato \const{O\_CREAT} con una valore non nullo di \param{attr} e + valori non validi di \var{mq\_maxmsg} e \var{mq\_msgsize}. + \item[\errcode{ENOENT}] non si è specificato \const{O\_CREAT} ma la coda + non esiste. + \end{errlist} + ed inoltre \errval{ENOMEM}, \errval{ENOSPC}, \errval{EFAULT}, + \errval{EMFILE}, \errval{EINTR} ed \errval{ENFILE}. +} +\end{functions} + +La funzione apre la coda di messaggi identificata dall'argomento \param{name} +restituendo il descrittore ad essa associato, del tutto analogo ad un file +descriptor, con l'unica differenza che lo standard prevede un apposito tipo +\type{mqd\_t}.\footnote{nel caso di Linux si tratta in effetti proprio di un + normale file descriptor; pertanto, anche se questo comportamento non è + portabile, lo si può tenere sotto osservazione con le funzioni dell'I/O + multiplexing (vedi sez.~\ref{sec:file_multiplexing}) come possibile + alternativa all'uso dell'interfaccia di notifica di \func{mq\_notify} (che + vedremo a breve).} Se la coda esiste già il descrittore farà riferimento +allo stesso oggetto, consentendo così la comunicazione fra due processi +diversi. + +La funzione è del tutto analoga ad \func{open} ed analoghi sono i valori che +possono essere specificati per \param{oflag}, che deve essere specificato come +maschera binaria; i valori possibili per i vari bit sono quelli visti in +tab.~\ref{tab:file_open_flags} dei quali però \func{mq\_open} riconosce solo i +seguenti: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\const{O\_RDONLY}] Apre la coda solo per la ricezione di messaggi. Il + processo potrà usare il descrittore con \func{mq\_receive} ma non con + \func{mq\_send}. +\item[\const{O\_WRONLY}] Apre la coda solo per la trasmissione di messaggi. Il + processo potrà usare il descrittore con \func{mq\_send} ma non con + \func{mq\_receive}. +\item[\const{O\_RDWR}] Apre la coda solo sia per la trasmissione che per la + ricezione. +\item[\const{O\_CREAT}] Necessario qualora si debba creare la coda; la + presenza di questo bit richiede la presenza degli ulteriori argomenti + \param{mode} e \param{attr}. +\item[\const{O\_EXCL}] Se usato insieme a \const{O\_CREAT} fa fallire la + chiamata se la coda esiste già, altrimenti esegue la creazione atomicamente. +\item[\const{O\_NONBLOCK}] Imposta la coda in modalità non bloccante, le + funzioni di ricezione e trasmissione non si bloccano quando non ci sono le + risorse richieste, ma ritornano immediatamente con un errore di + \errcode{EAGAIN}. +\end{basedescript} + +I primi tre bit specificano la modalità di apertura della coda, e sono fra +loro esclusivi. Ma qualunque sia la modalità in cui si è aperta una coda, +questa potrà essere riaperta più volte in una modalità diversa, e vi si potrà +sempre accedere attraverso descrittori diversi, esattamente come si può fare +per i file normali. + +Se la coda non esiste e la si vuole creare si deve specificare +\const{O\_CREAT}, in tal caso occorre anche specificare i permessi di +creazione con l'argomento \param{mode};\footnote{fino al 2.6.14 per un bug i + valori della \textit{umask} del processo non venivano applicati a questi + permessi.} i valori di quest'ultimo sono identici a quelli usati per +\func{open}, anche se per le code di messaggi han senso solo i permessi di +lettura e scrittura. Oltre ai permessi di creazione possono essere specificati +anche gli attributi specifici della coda tramite l'argomento \param{attr}; +quest'ultimo è un puntatore ad una apposita struttura \struct{mq\_attr}, la +cui definizione è riportata in fig.~\ref{fig:ipc_mq_attr}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/mq_attr.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{mq\_attr}, contenente gli attributi di una + coda di messaggi POSIX.} + \label{fig:ipc_mq_attr} +\end{figure} + +Per la creazione della coda i campi della struttura che devono essere +specificati sono \var{mq\_maxmsg} e \var{mq\_msgsize}, che indicano +rispettivamente il numero massimo di messaggi che può contenere e la +dimensione massima di un messaggio. Il valore dovrà essere positivo e minore +dei rispettivi limiti di sistema \const{MQ\_MAXMSG} e \const{MQ\_MSGSIZE}, +altrimenti la funzione fallirà con un errore di \errcode{EINVAL}. +Se \param{attr} è un puntatore nullo gli attributi della coda saranno +impostati ai valori predefiniti. + +Quando l'accesso alla coda non è più necessario si può chiudere il relativo +descrittore con la funzione \funcd{mq\_close}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_close(mqd\_t mqdes)} + +Chiude la coda \param{mqdes}. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 per un errore; + nel quel caso \var{errno} assumerà i valori \errval{EBADF} o + \errval{EINTR}.} +\end{prototype} + +La funzione è analoga a \func{close},\footnote{in Linux, dove le code sono + implementate come file su un filesystem dedicato, è esattamente la stessa + funzione.} dopo la sua esecuzione il processo non sarà più in grado di usare +il descrittore della coda, ma quest'ultima continuerà ad esistere nel sistema +e potrà essere acceduta con un'altra chiamata a \func{mq\_open}. All'uscita di +un processo tutte le code aperte, così come i file, vengono chiuse +automaticamente. Inoltre se il processo aveva agganciato una richiesta di +notifica sul descrittore che viene chiuso, questa sarà rilasciata e potrà +essere richiesta da qualche altro processo. + + +Quando si vuole effettivamente rimuovere una coda dal sistema occorre usare la +funzione \funcd{mq\_unlink}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_unlink(const char *name)} + +Rimuove una coda di messaggi. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori riportati da + \func{unlink}.} +\end{prototype} + +Anche in questo caso il comportamento della funzione è analogo a quello di +\func{unlink} per i file,\footnote{di nuovo l'implementazione di Linux usa + direttamente \func{unlink}.} la funzione rimuove la coda \param{name}, così +che una successiva chiamata a \func{mq\_open} fallisce o crea una coda +diversa. + +Come per i file ogni coda di messaggi ha un contatore di riferimenti, per cui +la coda non viene effettivamente rimossa dal sistema fin quando questo non si +annulla. Pertanto anche dopo aver eseguito con successo \func{mq\_unlink} la +coda resterà accessibile a tutti i processi che hanno un descrittore aperto su +di essa. Allo stesso modo una coda ed i suoi contenuti resteranno disponibili +all'interno del sistema anche quando quest'ultima non è aperta da nessun +processo (questa è una delle differenze più rilevanti nei confronti di pipe e +fifo). La sola differenza fra code di messaggi POSIX e file normali è che, +essendo il filesystem delle code di messaggi virtuale e basato su oggetti +interni al kernel, il suo contenuto viene perduto con il riavvio del sistema. + +Come accennato ad ogni coda di messaggi è associata una struttura +\struct{mq\_attr}, che può essere letta e modificata attraverso le due +funzioni \funcd{mq\_getattr} e \funcd{mq\_setattr}, i cui prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{int mq\_getattr(mqd\_t mqdes, struct mq\_attr *mqstat)} + Legge gli attributi di una coda di messaggi POSIX. + + \funcdecl{int mq\_setattr(mqd\_t mqdes, const struct mq\_attr *mqstat, + struct mq\_attr *omqstat)} + Modifica gli attributi di una coda di messaggi POSIX. + + \bodydesc{Entrambe le funzioni restituiscono 0 in caso di successo e -1 in + caso di errore; nel quel caso \var{errno} assumerà i valori \errval{EBADF} + o \errval{EINVAL}.} +\end{functions} + +La funzione \func{mq\_getattr} legge i valori correnti degli attributi della +coda nella struttura puntata da \param{mqstat}; di questi l'unico relativo +allo stato corrente della coda è \var{mq\_curmsgs} che indica il numero di +messaggi da essa contenuti, gli altri indicano le caratteristiche generali +della stessa. + +La funzione \func{mq\_setattr} permette di modificare gli attributi di una +coda tramite i valori contenuti nella struttura puntata da \param{mqstat}, ma +può essere modificato solo il campo \var{mq\_flags}, gli altri campi vengono +ignorati. In particolare i valori di \var{mq\_maxmsg} e \var{mq\_msgsize} +possono essere specificati solo in fase ci creazione della coda. Inoltre i +soli valori possibili per \var{mq\_flags} sono 0 e \const{O\_NONBLOCK}, per +cui alla fine la funzione può essere utilizzata solo per abilitare o +disabilitare la modalità non bloccante. L'argomento \param{omqstat} viene +usato, quando diverso da \val{NULL}, per specificare l'indirizzo di una +struttura su cui salvare i valori degli attributi precedenti alla chiamata +della funzione. + +Per inserire messaggi su di una coda sono previste due funzioni, +\funcd{mq\_send} e \funcd{mq\_timedsend}, i cui prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{int mq\_send(mqd\_t mqdes, const char *msg\_ptr, size\_t msg\_len, + unsigned int msg\_prio)} + Esegue l'inserimento di un messaggio su una coda. + + \funcdecl{int mq\_timedsend(mqd\_t mqdes, const char *msg\_ptr, size\_t + msg\_len, unsigned msg\_prio, const struct timespec *abs\_timeout)} + Esegue l'inserimento di un messaggio su una coda entro il tempo + \param{abs\_timeout}. + + + \bodydesc{Le funzioni restituiscono 0 in caso di successo e $-1$ per un + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la + coda è piena. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio \param{msg\_len} + eccede il limite impostato per la coda. + \item[\errcode{EINVAL}] si è specificato un valore nullo per + \param{msg\_len}, o un valore di \param{msg\_prio} fuori dai limiti, o + un valore non valido per \param{abs\_timeout}. + \item[\errcode{ETIMEDOUT}] l'inserimento del messaggio non è stato + effettuato entro il tempo stabilito. + \end{errlist} + ed inoltre \errval{EBADF}, \errval{ENOMEM} ed \errval{EINTR}.} +\end{functions} + +Entrambe le funzioni richiedono un puntatore al testo del messaggio +nell'argomento \param{msg\_ptr} e la relativa lunghezza in \param{msg\_len}. +Se quest'ultima eccede la dimensione massima specificata da \var{mq\_msgsize} +le funzioni ritornano immediatamente con un errore di \errcode{EMSGSIZE}. + +L'argomento \param{msg\_prio} indica la priorità dell'argomento; i messaggi di +priorità maggiore vengono inseriti davanti a quelli di priorità inferiore (e +quindi saranno riletti per primi). A parità del valore della priorità il +messaggio sarà inserito in coda a tutti quelli con la stessa priorità. Il +valore della priorità non può eccedere il limite di sistema +\const{MQ\_PRIO\_MAX}, che nel caso è pari a 32768. + +Qualora la coda sia piena, entrambe le funzioni si bloccano, a meno che non +sia stata selezionata in fase di apertura la modalità non +bloccante,\footnote{o si sia impostato il flag \const{O\_NONBLOCK} sul file + descriptor della coda.} nel qual caso entrambe ritornano \errcode{EAGAIN}. +La sola differenza fra le due funzioni è che la seconda, passato il tempo +massimo impostato con l'argomento \param{abs\_timeout},\footnote{deve essere + specificato un tempo assoluto tramite una struttura \struct{timespec} (vedi + fig.~\ref{fig:sys_timespec_struct}) indicato in numero di secondi e + nanosecondi a partire dal 1 gennaio 1970.} ritorna comunque con un errore di +\errcode{ETIMEDOUT}, se invece il tempo è già scaduto al momento della +chiamata e la coda è vuota la funzione ritorna immediatamente. + +Come per l'inserimento, anche per l'estrazione dei messaggi da una coda sono +previste due funzioni, \funcd{mq\_receive} e \funcd{mq\_timedreceive}, i cui +prototipi sono: +\begin{functions} + \headdecl{mqueue.h} + + \funcdecl{ssize\_t mq\_receive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio)} + Effettua la ricezione di un messaggio da una coda. + + \funcdecl{ssize\_t mq\_timedreceive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio, const struct timespec *abs\_timeout)} + Effettua la ricezione di un messaggio da una coda entro il tempo + \param{abs\_timeout}. + + \bodydesc{Le funzioni restituiscono il numero di byte del messaggio in caso + di successo e -1 in caso di errore; nel quel caso \var{errno} assumerà i + valori: + \begin{errlist} + \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la + coda è vuota. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio sulla coda eccede il + valore \param{msg\_len} specificato per la ricezione. + \item[\errcode{EINVAL}] si è specificato un valore nullo per + \param{msg\_ptr}, o un valore non valido per \param{abs\_timeout}. + \item[\errcode{ETIMEDOUT}] la ricezione del messaggio non è stata + effettuata entro il tempo stabilito. + \end{errlist} + ed inoltre \errval{EBADF}, \errval{EINTR}, \errval{ENOMEM}, o + \errval{EINVAL}.} +\end{functions} + +La funzione estrae dalla coda il messaggio a priorità più alta, o il più +vecchio fra quelli della stessa priorità. Una volta ricevuto il messaggio +viene tolto dalla coda e la sua dimensione viene restituita come valore di +ritorno.\footnote{si tenga presente che 0 è una dimensione valida e che la + condizione di errore è restituita dal valore -1; Stevens in \cite{UNP2} fa + notare che questo è uno dei casi in cui vale ciò che lo standard + \textsl{non} dice, una dimensione nulla infatti, pur non essendo citata, non + viene proibita.} + +Se la dimensione specificata da \param{msg\_len} non è sufficiente a contenere +il messaggio, entrambe le funzioni, al contrario di quanto avveniva nelle code +di messaggi di SysV, ritornano un errore di \errcode{EMSGSIZE} senza estrarre +il messaggio. È pertanto opportuno eseguire sempre una chiamata a +\func{mq\_getaddr} prima di eseguire una ricezione, in modo da ottenere la +dimensione massima dei messaggi sulla coda, per poter essere in grado di +allocare dei buffer sufficientemente ampi per la lettura. + +Se si specifica un puntatore per l'argomento \param{msg\_prio} il valore della +priorità del messaggio viene memorizzato all'indirizzo da esso indicato. +Qualora non interessi usare la priorità dei messaggi si può specificare +\var{NULL}, ed usare un valore nullo della priorità nelle chiamate a +\func{mq\_send}. + +Si noti che con le code di messaggi POSIX non si ha la possibilità di +selezionare quale messaggio estrarre con delle condizioni sulla priorità, a +differenza di quanto avveniva con le code di messaggi di SysV che permettono +invece la selezione in base al valore del campo \var{mtype}. + +% TODO inserire i dati di /proc/sys/fs/mqueue + +Qualora la coda sia vuota entrambe le funzioni si bloccano, a meno che non si +sia selezionata la modalità non bloccante; in tal caso entrambe ritornano +immediatamente con l'errore \errcode{EAGAIN}. Anche in questo caso la sola +differenza fra le due funzioni è che la seconda non attende indefinitamente e +passato il tempo massimo \param{abs\_timeout} ritorna comunque con un errore +di \errcode{ETIMEDOUT}. + +Uno dei problemi sottolineati da Stevens in \cite{UNP2}, comuni ad entrambe le +tipologie di code messaggi, è che non è possibile per chi riceve identificare +chi è che ha inviato il messaggio, in particolare non è possibile sapere da +quale utente esso provenga. Infatti, in mancanza di un meccanismo interno al +kernel, anche se si possono inserire delle informazioni nel messaggio, queste +non possono essere credute, essendo completamente dipendenti da chi lo invia. +Vedremo però come, attraverso l'uso del meccanismo di notifica, sia possibile +superare in parte questo problema. + +Una caratteristica specifica delle code di messaggi POSIX è la possibilità di +usufruire di un meccanismo di notifica asincrono; questo può essere attivato +usando la funzione \funcd{mq\_notify}, il cui prototipo è: +\begin{prototype}{mqueue.h} +{int mq\_notify(mqd\_t mqdes, const struct sigevent *notification)} + +Attiva il meccanismo di notifica per la coda \param{mqdes}. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EBUSY}] c'è già un processo registrato per la notifica. + \item[\errcode{EBADF}] il descrittore non fa riferimento ad una coda di + messaggi. + \end{errlist}} +\end{prototype} + +Il meccanismo di notifica permette di segnalare in maniera asincrona ad un +processo la presenza di dati sulla coda, in modo da evitare la necessità di +bloccarsi nell'attesa. Per far questo un processo deve registrarsi con la +funzione \func{mq\_notify}, ed il meccanismo è disponibile per un solo +processo alla volta per ciascuna coda. + +Il comportamento di \func{mq\_notify} dipende dal valore dell'argomento +\param{notification}, che è un puntatore ad una apposita struttura +\struct{sigevent}, (definita in fig.~\ref{fig:struct_sigevent}) introdotta +dallo standard POSIX.1b per gestire la notifica di eventi; per altri dettagli +si può vedere quanto detto in sez.~\ref{sec:sig_timer_adv} a proposito +dell'uso della stessa struttura per la notifica delle scadenze dei +\textit{timer}. + +Attraverso questa struttura si possono impostare le modalità con cui viene +effettuata la notifica nel campo \var{sigev\_notify}, che può assumere i +valori di tab.~\ref{tab:sigevent_sigev_notify}.\footnote{la pagina di manuale + riporta soltanto i primi tre (inizialmente era possibile solo + \const{SIGEV\_SIGNAL}).} Il metodo consigliato è quello di usare +\const{SIGEV\_SIGNAL} usando il campo \var{sigev\_signo} per indicare il quale +segnale deve essere inviato al processo. Inoltre il campo \var{sigev\_value} è +un puntatore ad una struttura \struct{sigval\_t} (definita in +fig.~\ref{fig:sig_sigval}) che permette di restituire al gestore del segnale +un valore numerico o un indirizzo,\footnote{per il suo uso si riveda la + trattazione fatta in sez.~\ref{sec:sig_real_time} a proposito dei segnali + \textit{real-time}.} posto che questo sia installato nella forma estesa +vista in sez.~\ref{sec:sig_sigaction}. + +La funzione registra il processo chiamante per la notifica se +\param{notification} punta ad una struttura \struct{sigevent} opportunamente +inizializzata, o cancella una precedente registrazione se è \val{NULL}. Dato +che un solo processo alla volta può essere registrato, la funzione fallisce +con \errcode{EBUSY} se c'è un altro processo già registrato.\footnote{questo + significa anche che se si registra una notifica con \const{SIGEV\_NONE} il + processo non la riceverà, ma impedirà anche che altri possano registrarsi + per poterlo fare.} Si tenga presente inoltre che alla chiusura del +descrittore associato alla coda (e quindi anche all'uscita del processo) ogni +eventuale registrazione di notifica presente viene cancellata. + +La notifica del segnale avviene all'arrivo di un messaggio in una coda vuota +(cioè solo se sulla coda non ci sono messaggi) e se non c'è nessun processo +bloccato in una chiamata a \func{mq\_receive}, in questo caso infatti il +processo bloccato ha la precedenza ed il messaggio gli viene immediatamente +inviato, mentre per il meccanismo di notifica tutto funziona come se la coda +fosse rimasta vuota. + +Quando un messaggio arriva su una coda vuota al processo che si era registrato +viene inviato il segnale specificato da \code{notification->sigev\_signo}, e +la coda diventa disponibile per una ulteriore registrazione. Questo comporta +che se si vuole mantenere il meccanismo di notifica occorre ripetere la +registrazione chiamando nuovamente \func{mq\_notify} all'interno del gestore +del segnale di notifica. A differenza della situazione simile che si aveva con +i segnali non affidabili,\footnote{l'argomento è stato affrontato in + \ref{sec:sig_semantics}.} questa caratteristica non configura una +\itindex{race~condition} \textit{race condition} perché l'invio di un segnale +avviene solo se la coda è vuota; pertanto se si vuole evitare di correre il +rischio di perdere eventuali ulteriori segnali inviati nel lasso di tempo che +occorre per ripetere la richiesta di notifica basta avere cura di eseguire +questa operazione prima di estrarre i messaggi presenti dalla coda. + +L'invio del segnale di notifica avvalora alcuni campi di informazione +restituiti al gestore attraverso la struttura \struct{siginfo\_t} (definita in +fig.~\ref{fig:sig_siginfo_t}). In particolare \var{si\_pid} viene impostato al +valore del \acr{pid} del processo che ha emesso il segnale, \var{si\_uid} +all'userid effettivo, \var{si\_code} a \const{SI\_MESGQ}, e \var{si\_errno} a +0. Questo ci dice che, se si effettua la ricezione dei messaggi usando +esclusivamente il meccanismo di notifica, è possibile ottenere le informazioni +sul processo che ha inserito un messaggio usando un gestore per il segnale in +forma estesa.\footnote{di nuovo si faccia riferimento a quanto detto al + proposito in 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 le +\acr{glibc}\footnote{le funzioni sono state introdotte con le glibc-2.2.} +richiedono di compilare i programmi con l'opzione \code{-lrt}; inoltre è +necessario che in \file{/dev/shm} sia montato un filesystem \texttt{tmpfs}; +questo di norma viene fatto aggiungendo una riga del tipo di: +\begin{verbatim} +tmpfs /dev/shm tmpfs defaults 0 0 +\end{verbatim} +ad \conffile{/etc/fstab}. In realtà si può montare un filesystem \texttt{tmpfs} +dove si vuole, per usarlo come RAM disk, con un comando del tipo: +\begin{verbatim} +mount -t tmpfs -o size=128M,nr_inodes=10k,mode=700 tmpfs /mytmpfs +\end{verbatim} + +Il filesystem riconosce, oltre quelle mostrate, le opzioni \texttt{uid} e +\texttt{gid} che identificano rispettivamente utente e gruppo cui assegnarne +la titolarità, e \texttt{nr\_blocks} che permette di specificarne la +dimensione in blocchi, cioè in multipli di \const{PAGECACHE\_SIZE} che in +questo caso è l'unità di allocazione elementare. + +La funzione che permette di aprire un segmento di memoria condivisa POSIX, ed +eventualmente di crearlo se non esiste ancora, è \funcd{shm\_open}; il suo +prototipo è: +\begin{functions} + \headdecl{sys/mman.h} + \headdecl{sys/stat.h} + \headdecl{fcntl.h} + + \funcdecl{int shm\_open(const char *name, int oflag, mode\_t mode)} + + Apre un segmento di memoria condivisa. + + \bodydesc{La funzione restituisce un file descriptor positivo in caso di + successo e -1 in caso di errore; nel quel caso \var{errno} assumerà gli + stessi valori riportati da \func{open}.} +\end{functions} + +La funzione apre un segmento di memoria condivisa identificato dal nome +\param{name}. Come già spiegato in sez.~\ref{sec:ipc_posix_generic} questo +nome può essere specificato in forma standard solo facendolo iniziare per +``\file{/}'' e senza ulteriori ``\file{/}''. Linux supporta comunque nomi +generici, che verranno interpretati prendendo come radice +\file{/dev/shm}.\footnote{occorre pertanto evitare di specificare qualcosa del + tipo \file{/dev/shm/nome} all'interno di \param{name}, perché questo + comporta, da parte delle funzioni di libreria, il tentativo di accedere a + \file{/dev/shm/dev/shm/nome}.} + +La funzione è del tutto analoga ad \func{open} ed analoghi sono i valori che +possono essere specificati per \param{oflag}, che deve essere specificato come +maschera binaria comprendente almeno uno dei due valori \const{O\_RDONLY} e +\const{O\_RDWR}; i valori possibili per i vari bit sono quelli visti in +tab.~\ref{tab:file_open_flags} dei quali però \func{shm\_open} riconosce solo +i seguenti: +\begin{basedescript}{\desclabelwidth{2.0cm}\desclabelstyle{\nextlinelabel}} +\item[\const{O\_RDONLY}] Apre il file descriptor associato al segmento di + memoria condivisa per l'accesso in sola lettura. +\item[\const{O\_RDWR}] Apre il file descriptor associato al segmento di + memoria condivisa per l'accesso in lettura e scrittura. +\item[\const{O\_CREAT}] Necessario qualora si debba creare il segmento di + memoria condivisa se esso non esiste; in questo caso viene usato il valore + di \param{mode} per impostare i permessi, che devono essere compatibili con + le modalità con cui si è aperto il file. +\item[\const{O\_EXCL}] Se usato insieme a \const{O\_CREAT} fa fallire la + chiamata a \func{shm\_open} se il segmento esiste già, altrimenti esegue la + creazione atomicamente. +\item[\const{O\_TRUNC}] Se il segmento di memoria condivisa esiste già, ne + tronca le dimensioni a 0 byte. +\end{basedescript} + +In caso di successo la funzione restituisce un file descriptor associato al +segmento di memoria condiviso con le stesse modalità di +\func{open}\footnote{in realtà, come accennato, \func{shm\_open} è un semplice + wrapper per \func{open}, usare direttamente quest'ultima avrebbe lo stesso + effetto.} viste in sez.~\ref{sec:file_open}; in particolare viene impostato +il flag \const{FD\_CLOEXEC}. Chiamate effettuate da diversi processi usando +lo stesso nome, restituiranno file descriptor associati allo stesso segmento +(così come, nel caso di file di dati, essi sono associati allo stesso +\index{inode} inode). In questo modo è possibile effettuare una chiamata ad +\func{mmap} sul file descriptor restituito da \func{shm\_open} ed i processi +vedranno lo stesso segmento di memoria condivisa. + +Quando il nome non esiste il segmento può essere creato specificando +\const{O\_CREAT}; in tal caso il segmento avrà (così come i nuovi file) +lunghezza nulla. Dato che un segmento di lunghezza nulla è di scarsa utilità, +per impostarne la dimensione si deve usare \func{ftruncate} (vedi +sez.~\ref{sec:file_file_size}), prima di mapparlo in memoria con \func{mmap}. +Si tenga presente che una volta chiamata \func{mmap} si può chiudere il file +descriptor (con \func{close}), senza che la mappatura ne risenta. + +Come per i file, quando si vuole effettivamente rimuovere segmento di memoria +condivisa, occorre usare la funzione \funcd{shm\_unlink}, il cui prototipo è: +\begin{prototype}{sys/mman.h} +{int shm\_unlink(const char *name)} + +Rimuove un segmento di memoria condivisa. + +\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori riportati da + \func{unlink}.} +\end{prototype} + +La funzione è del tutto analoga ad \func{unlink}, e si limita a cancellare il +nome del segmento da \file{/dev/shm}, senza nessun effetto né sui file +descriptor precedentemente aperti con \func{shm\_open}, né sui segmenti già +mappati in memoria; questi verranno cancellati automaticamente dal sistema +solo con le rispettive chiamate a \func{close} e \func{munmap}. Una volta +eseguita questa funzione però, qualora si richieda l'apertura di un segmento +con lo stesso nome, la chiamata a \func{shm\_open} fallirà, a meno di non aver +usato \const{O\_CREAT}, in quest'ultimo caso comunque si otterrà un file +descriptor che fa riferimento ad un segmento distinto da eventuali precedenti. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/MemShared.c} + \end{minipage} + \normalsize + \caption{Il codice delle funzioni di gestione dei segmenti di memoria + condivisa POSIX.} + \label{fig:ipc_posix_shmmem} +\end{figure} + +Come esempio per l'uso di queste funzioni vediamo come è possibile riscrivere +una interfaccia semplificata analoga a quella vista in +fig.~\ref{fig:ipc_sysv_shm_func} per la memoria condivisa in stile SysV. Il +codice, riportato in fig.~\ref{fig:ipc_posix_shmmem}, è sempre contenuto nel +file \file{SharedMem.c} dei sorgenti allegati. + +La prima funzione (\texttt{\small 1--24}) è \func{CreateShm} che, dato un nome +nell'argomento \var{name} crea un nuovo segmento di memoria condivisa, +accessibile in lettura e scrittura, e ne restituisce l'indirizzo. Anzitutto si +definiscono (\texttt{\small 8}) i flag per la successiva (\texttt{\small 9}) +chiamata a \func{shm\_open}, che apre il segmento in lettura e scrittura +(creandolo se non esiste, ed uscendo in caso contrario) assegnandogli sul +filesystem i permessi specificati dall'argomento \var{perm}. In caso di errore +(\texttt{\small 10--12}) si restituisce un puntatore nullo, altrimenti si +prosegue impostando (\texttt{\small 14}) la dimensione del segmento con +\func{ftruncate}. Di nuovo (\texttt{\small 15--16}) si esce immediatamente +restituendo un puntatore nullo in caso di errore. Poi si passa (\texttt{\small + 18}) a mappare in memoria il segmento con \func{mmap} specificando dei +diritti di accesso corrispondenti alla modalità di apertura. Di nuovo si +restituisce (\texttt{\small 19--21}) un puntatore nullo in caso di errore, +altrimenti si inizializza (\texttt{\small 22}) il contenuto del segmento al +valore specificato dall'argomento \var{fill} con \func{memset}, e se ne +restituisce (\texttt{\small 23}) l'indirizzo. + +La seconda funzione (\texttt{\small 25--40}) è \func{FindShm} che trova un +segmento di memoria condiviso già esistente, restituendone l'indirizzo. In +questo caso si apre (\texttt{\small 31}) il segmento con \func{shm\_open} +richiedendo che il segmento sia già esistente, in caso di errore +(\texttt{\small 31--33}) si ritorna immediatamente un puntatore nullo. +Ottenuto il file descriptor del segmento lo si mappa (\texttt{\small 35}) in +memoria con \func{mmap}, restituendo (\texttt{\small 36--38}) un puntatore +nullo in caso di errore, o l'indirizzo (\texttt{\small 39}) dello stesso in +caso di successo. + +La terza funzione (\texttt{\small 40--45}) è \func{RemoveShm}, e serve a +cancellare un segmento di memoria condivisa. Dato che al contrario di quanto +avveniva con i segmenti del SysV IPC gli oggetti allocati nel kernel vengono +rilasciati automaticamente quando nessuna li usa più, tutto quello che c'è da +fare (\texttt{\small 44}) in questo caso è chiamare \func{shm\_unlink}, +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} delle \acr{glibc}.\footnote{quelle che si + accedono collegandosi alla libreria \texttt{librt}.} Esisteva inoltre una +libreria che realizzava (parzialmente) l'interfaccia POSIX usando le funzioni +dei semafori di SysV IPC (mantenendo così tutti i problemi sottolineati in +sez.~\ref{sec:ipc_sysv_sem}). + +A partire dal kernel 2.5.7 è stato introdotto un meccanismo di +sincronizzazione completamente nuovo, basato sui cosiddetti +\textit{futex},\footnote{la sigla sta per \textit{fast user mode mutex}.} con +il quale è stato possibile implementare una versione nativa dei semafori +POSIX. Grazie a questo con i kernel della serie 2.6 e le nuove versioni delle +\acr{glibc} che usano questa nuova infrastruttura per quella che viene quella +che viene chiamata \textit{New Posix Thread Library}, sono state implementate +anche tutte le funzioni dell'interfaccia dei semafori POSIX. + +Anche in questo caso è necessario appoggiarsi alla libreria per le estensioni +\textit{real-time} \texttt{librt}, questo significa che se si vuole utilizzare +questa interfaccia, oltre ad utilizzare gli opportuni file di definizione, +occorrerà compilare i programmi con l'opzione \texttt{-lrt}. + +La funzione che permette di creare un nuovo semaforo POSIX, creando il +relativo file, o di accedere ad uno esistente, è \funcd{sem\_open}, questa +prevede due forme diverse a seconda che sia utilizzata per aprire un semaforo +esistente o per crearne uno nuovi, i relativi prototipi sono: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{sem\_t *sem\_open(const char *name, int oflag)} + + \funcdecl{sem\_t *sem\_open(const char *name, int oflag, mode\_t mode, + unsigned int value)} + + Crea un semaforo o ne apre uno esistente. + + \bodydesc{La funzione restituisce l'indirizzo del semaforo in caso di + successo e \const{SEM\_FAILED} in caso di errore; nel quel caso + \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCESS}] il semaforo esiste ma non si hanno permessi + sufficienti per accedervi. + \item[\errcode{EEXIST}] si sono specificati \const{O\_CREAT} e + \const{O\_EXCL} ma il semaforo esiste. + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENAMETOOLONG}] si è utilizzato un nome troppo lungo. + \item[\errcode{ENOENT}] non si è usato \const{O\_CREAT} ed il nome + specificato non esiste. + \end{errlist} + ed inoltre \errval{ENFILE} ed \errval{ENOMEM}.} +\end{functions} + +L'argomento \param{name} definisce il nome del semaforo che si vuole +utilizzare, ed è quello che permette a processi diversi di accedere allo +stesso semaforo. Questo deve essere specificato con un pathname nella forma +\texttt{/qualchenome}, che non ha una corrispondenza diretta con un pathname +reale; con Linux infatti i file associati ai semafori sono mantenuti nel +filesystem virtuale \texttt{/dev/shm}, e gli viene assegnato automaticamente +un nome nella forma \texttt{sem.qualchenome}.\footnote{si ha cioè una + corrispondenza per cui \texttt{/qualchenome} viene rimappato, nella + creazione tramite \func{sem\_open}, su \texttt{/dev/shm/sem.qualchenome}.} + +L'argomento \param{oflag} è quello che controlla le modalità con cui opera la +funzione, ed è passato come maschera binaria; i bit corrispondono a quelli +utilizzati per l'analogo argomento di \func{open}, anche se dei possibili +valori visti in sez.~\ref{sec:file_open} sono utilizzati soltanto +\const{O\_CREAT} e \const{O\_EXCL}. + +Se si usa \const{O\_CREAT} si richiede la creazione del semaforo qualora +questo non esista, ed in tal caso occorre utilizzare la seconda forma della +funzione, in cui si devono specificare sia un valore iniziale con l'argomento +\param{value},\footnote{e si noti come così diventa possibile, differenza di + quanto avviene per i semafori del \textit{SysV IPC}, effettuare in maniera + atomica creazione ed inizializzazione di un semaforo usando una unica + funzione.} che una maschera dei permessi con l'argomento +\param{mode};\footnote{anche questo argomento prende gli stessi valori + utilizzati per l'analogo di \func{open}, che si sono illustrati in dettaglio + sez.~\ref{sec:file_perm_overview}.} questi verranno assegnati al semaforo +appena creato. Se il semaforo esiste già i suddetti valori saranno invece +ignorati. Usando il flag \const{O\_EXCL} si richiede invece la verifica che il +semaforo non esiste, usandolo insieme ad \const{O\_CREAT} la funzione fallisce +qualora un semaforo con lo stesso nome sia già presente. + +La funzione restituisce in caso di successo un puntatore all'indirizzo del +semaforo con un valore di tipo \ctyp{sem\_t *}, è questo valore che dovrà +essere passato alle altre funzioni per operare sul semaforo stesso. Si tenga +presente che, come accennato in sez.~\ref{sec:ipc_posix_generic}, i semafori +usano la semantica standard dei file per quanto riguarda i controlli di +accesso. + +Questo significa che un nuovo semaforo viene sempre creato con l'user-ID ed il +group-ID effettivo del processo chiamante, e che i permessi indicati con +\param{mode} vengono filtrati dal valore della \itindex{umask} \textit{umask} +del processo. Inoltre per poter aprire un semaforo è necessario avere su di +esso sia il permesso di lettura che quello di scrittura. + +Una volta che si sia ottenuto l'indirizzo di un semaforo, sarà possibile +utilizzarlo; se si ricorda quanto detto all'inizio di +sez.~\ref{sec:ipc_sysv_sem}, dove si sono introdotti i concetti generali +relativi ai semafori, le operazioni principali sono due, quella che richiede +l'uso di una risorsa bloccando il semaforo e quella che rilascia la risorsa +liberando il semaforo. La prima operazione è effettuata dalla funzione +\funcd{sem\_wait}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_wait(sem\_t *sem)} + + Blocca il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione cerca di decrementare il valore del semaforo indicato dal +puntatore \param{sem}, se questo ha un valore positivo, cosa che significa che +la risorsa è disponibile, la funzione ha successo, il valore del semaforo +viene diminuito di 1 ed essa ritorna immediatamente; se il valore è nullo la +funzione si blocca fintanto che il valore del semaforo non torni +positivo\footnote{ovviamente per opera di altro processo che lo rilascia + chiamando \func{sem\_post}.} così che poi essa possa decrementarlo con +successo e proseguire. + +Si tenga presente che la funzione può sempre essere interrotta da un segnale +(nel qual caso si avrà un errore di \const{EINTR}) e che questo avverrà +comunque, anche se si è richiesta la semantica BSD installando il relativo +gestore con \const{SA\_RESTART} (vedi sez.~\ref{sec:sig_sigaction}) per +riavviare le system call interrotte. + +Della funzione \func{sem\_wait} esistono due varianti che consentono di +gestire diversamente le modalità di attesa in caso di risorsa occupata, la +prima di queste è \funcd{sem\_trywait}, che serve ad effettuare un tentativo +di acquisizione senza bloccarsi; il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_trywait(sem\_t *sem)} + + Tenta di bloccare il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori: + \begin{errlist} + \item[\errcode{EAGAIN}] il semaforo non può essere acquisito senza + bloccarsi. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione è identica a \func{sem\_wait} ed se la risorsa è libera ha lo +stesso effetto, vale a dire che in caso di semaforo diverso da zero la +funzione lo decrementa e ritorna immediatamente; la differenza è che nel caso +in cui il semaforo è occupato essa non si blocca e di nuovo ritorna +immediatamente, restituendo però un errore di \errval{EAGAIN}, così che il +programma possa proseguire. + +La seconda variante di \func{sem\_wait} è una estensione specifica che può +essere utilizzata soltanto se viene definita la macro \macro{\_XOPEN\_SOURCE} +ad un valore di 600 prima di includere \texttt{semaphore.h}, la funzione è +\func{sem\_timedwait}, ed il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_timedwait(sem\_t *sem, const struct timespec + *abs\_timeout)} + + Blocca il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà gli stessi valori: + \begin{errlist} + \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. + \end{errlist} +} +\end{functions} + +Anche in questo caso il comportamento della funzione è identico a quello di +\func{sem\_wait}, la sola differenza consiste nel fatto che con questa +funzione è possibile impostare tramite l'argomento \param{abs\_timeout} un +tempo limite per l'attesa, scaduto il quale la funzione ritorna comunque, +anche se non è possibile acquisire il semaforo. In tal caso la funzione +fallirà, riportando un errore di \errval{ETIMEDOUT}. + +La seconda funzione principale utilizzata per l'uso dei semafori è +\funcd{sem\_post}, che viene usata per rilasciare un semaforo occupato o, in +generale, per aumentare di una unità il valore dello stesso anche qualora non +fosse occupato;\footnote{si ricordi che in generale un semaforo viene usato + come indicatore di un numero di risorse disponibili.} il suo prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_post(sem\_t *sem)} + + Rilascia il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione incrementa di uno il valore corrente del semaforo indicato +dall'argomento \param{sem}, se questo era nullo la relativa risorsa risulterà +sbloccata, cosicché un altro processo (o \itindex{thread} \textit{thread}) +eventualmente bloccato in una \func{sem\_wait} sul semaforo potrà essere +svegliato e rimesso in esecuzione. Si tenga presente che la funzione è sicura +\index{funzioni!sicure} per l'uso all'interno di un gestore di segnali (si +ricordi quanto detto in sez.~\ref{sec:sig_signal_handler}). + +Se invece di operare su un semaforo se ne vuole solamente leggere il valore, +si può usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_getvalue(sem\_t *sem, int *sval)} + + Richiede il valore del semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione legge il valore del semaforo indicato dall'argomento \param{sem} e +lo restituisce nella variabile intera puntata dall'argomento +\param{sval}. Qualora ci siano uno o più processi bloccati in attesa sul +semaforo lo standard prevede che la funzione possa restituire un valore nullo +oppure il numero di processi bloccati in una \func{sem\_wait} sul suddetto +semaforo; nel caso di Linux vale la prima opzione. + +Questa funzione può essere utilizzata per avere un suggerimento sullo stato di +un semaforo, ovviamente non si può prendere il risultato riportato in +\param{sval} che come indicazione, il valore del semaforo infatti potrebbe +essere già stato modificato al ritorno della funzione. + +% TODO verificare comportamento sem_getvalue + +Una volta che non ci sia più la necessità di operare su un semaforo se ne può +terminare l'uso con la funzione \funcd{sem\_close}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_close(sem\_t *sem)} + + Chiude il semaforo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione chiude il semaforo indicato dall'argomento \param{sem}; questo +comporta che tutte le risorse che il sistema può avere assegnato al processo +nell'uso dello stesso vengono rilasciate. Questo significa che un altro +processo bloccato sul semaforo a causa della acquisizione da parte del +processo che chiama \func{sem\_close} potrà essere riavviato. + +Si tenga presente poi che come per i file all'uscita di un processo tutti i +semafori che questo aveva aperto vengono automaticamente chiusi; questo +comportamento risolve il problema che si aveva con i semafori del \textit{SysV + IPC} (di cui si è parlato in sez.~\ref{sec:ipc_sysv_sem}) per i quali le +risorse possono restare bloccate. Si tenga poi presente che, a differenza di +quanto avviene per i file, in caso di una chiamata ad \func{execve} tutti i +semafori vengono chiusi automaticamente. + +Come per i semafori del \textit{SysV IPC} anche quelli POSIX hanno una +persistenza di sistema; questo significa che una volta che si è creato un +semaforo con \func{sem\_open} questo continuerà ad esistere fintanto che il +kernel resta attivo (vale a dire fino ad un successivo riavvio) a meno che non +lo si cancelli esplicitamente. Per far questo si può utilizzare la funzione +\funcd{sem\_unlink}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_unlink(const char *name)} + + Rimuove il semaforo \param{name}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EACCESS}] non si hanno i permessi necessari a cancellare il + semaforo. + \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. + \item[\errcode{ENOENT}] il semaforo \param{name} non esiste. + \end{errlist} +} +\end{functions} + +La funzione rimuove il semaforo indicato dall'argomento \param{name}, che +prende un valore identico a quello usato per creare il semaforo stesso con +\func{sem\_open}. Il semaforo viene rimosso dal filesystem immediatamente; ma +il semaforo viene effettivamente cancellato dal sistema soltanto quando tutti +i processi che lo avevano aperto lo chiudono. Si segue cioè la stessa +semantica usata con \func{unlink} per i file, trattata in dettaglio in +sez.~\ref{sec:file_link}. + +Una delle caratteristiche peculiari dei semafori POSIX è che questi possono +anche essere utilizzati anche in forma anonima, senza necessità di fare +ricorso ad un nome sul filesystem o ad altri indicativi. In questo caso si +dovrà porre la variabile che contiene l'indirizzo del semaforo in un tratto di +memoria che sia accessibile a tutti i processi in gioco. La funzione che +consente di inizializzare un semaforo anonimo è \funcd{sem\_init}, il cui +prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} + + Inizializza il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENOSYS}] il valore di \param{pshared} non è nullo ed il + sistema non supporta i semafori per i processi. + \end{errlist} +} +\end{functions} + +La funzione inizializza un semaforo all'indirizzo puntato dall'argomento +\param{sem}, e come per \func{sem\_open} consente di impostare un valore +iniziale con \param{value}. L'argomento \param{pshared} serve ad indicare se +il semaforo deve essere utilizzato dai \itindex{thread} \textit{thread} di uno +stesso processo (con un valore nullo) o condiviso fra processi diversi (con un +valore non nullo). + +Qualora il semaforo debba essere condiviso dai \itindex{thread} +\textit{thread} di uno stesso processo (nel qual caso si parla di +\textit{thread-shared semaphore}), occorrerà che \param{sem} sia l'indirizzo +di una variabile visibile da tutti i \itindex{thread} \textit{thread}, si +dovrà usare cioè una variabile globale o una variabile allocata dinamicamente +nello \itindex{heap} \textit{heap}. + +Qualora il semaforo debba essere condiviso fra più processi (nel qual caso si +parla di \textit{process-shared semaphore}) la sola scelta possibile per +renderlo visibile a tutti è di porlo in un tratto di memoria condivisa. Questo +potrà essere ottenuto direttamente sia con \func{shmget} (vedi +sez.~\ref{sec:ipc_sysv_shm}) che con \func{shm\_open} (vedi +sez.~\ref{sec:ipc_posix_shm}), oppure, nel caso che tutti i processi in gioco +abbiano un genitore comune, con una mappatura anonima con \func{mmap} (vedi +sez.~\ref{sec:file_memory_map}),\footnote{si ricordi che i tratti di memoria + condivisa vengono mantenuti nei processi figli attraverso la funzione + \func{fork}.} a cui essi poi potranno accedere. + +Una volta inizializzato il semaforo anonimo con \func{sem\_init} lo si potrà +utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e +\func{sem\_post}. Si tenga presente però che inizializzare due volte lo stesso +semaforo può dar luogo ad un comportamento indefinito. + +Una volta che non si intenda più utilizzare un semaforo anonimo questo può +essere eliminato dal sistema; per far questo di deve utilizzare una apposita +funzione, \funcd{sem\_destroy}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_destroy(sem\_t *sem)} + + Elimina il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \end{errlist} +} +\end{functions} + +La funzione prende come unico argomento l'indirizzo di un semaforo che deve +essere stato inizializzato con \func{sem\_init}; non deve quindi essere +applicata a semafori creati con \func{sem\_open}. Inoltre si deve essere +sicuri che il semaforo sia effettivamente inutilizzato, la distruzione di un +semaforo su cui sono presenti processi (o \itindex{thread} \textit{thread}) in +attesa (cioè bloccati in una \func{sem\_wait}) provoca un comportamento +indefinito. + +Si tenga presente infine che utilizzare un semaforo che è stato distrutto con +\func{sem\_destroy} di nuovo può dare esito a comportamenti indefiniti. Nel +caso ci si trovi in una tale evenienza occorre reinizializzare il semaforo una +seconda volta con \func{sem\_init}. + +Come esempio di uso sia della memoria condivisa che dei semafori POSIX si sono +scritti due semplici programmi con i quali è possibile rispettivamente +monitorare il contenuto di un segmento di memoria condivisa e modificarne il +contenuto. + +\begin{figure}[!h] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \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 +variabili globali contenenti i nomi di default del segmento di memoria +condivisa e del semaforo (il default scelto è \texttt{messages}), e delle +altre variabili utilizzate dal programma. + +Come prima istruzione (\texttt{\small 10}) si è provveduto ad installare un +gestore di segnale che consentirà di effettuare le operazioni di pulizia +(usando la funzione \func{Signal} illustrata in +fig.~\ref{fig:sig_Signal_code}), dopo di che (\texttt{\small 10--16}) si è +creato il segmento di memoria condivisa con la funzione \func{CreateShm} che +abbiamo appena trattato in sez.~\ref{sec:ipc_posix_shm}, uscendo con un +messaggio in caso di errore. + +Si tenga presente che la funzione \func{CreateShm} richiede che il segmento +non sia già presente e fallirà qualora un'altra istanza, o un altro programma +abbia già allocato un segmento con quello stesso nome. Per semplicità di +gestione si è usata una dimensione fissa pari a 256 byte, definita tramite la +costante \texttt{MSGMAXSIZE}. + +Il passo successivo (\texttt{\small 17--21}) è quello della creazione del +semaforo che regola l'accesso al segmento di memoria condivisa con +\func{sem\_open}; anche in questo caso si gestisce l'uscita con stampa di un +messaggio in caso di errore. Anche per il semaforo, avendo specificato la +combinazione di flag \code{O\_CREAT|O\_EXCL} come secondo argomento, si esce +qualora fosse già esistente; altrimenti esso verrà creato con gli opportuni +permessi specificati dal terzo argomento, (indicante lettura e scrittura in +notazione ottale). Infine il semaforo verrà inizializzato ad un valore nullo +(il quarto argomento), corrispondete allo stato in cui risulta bloccato. + +A questo punto (\texttt{\small 23}) si potrà inizializzare il messaggio posto +nel segmento di memoria condivisa usando la stringa passata come argomento al +programma. Essendo il semaforo stato creato già bloccato non ci si dovrà +preoccupare di eventuali \itindex{race~condition} \textit{race condition} +qualora il programma di modifica del messaggio venisse lanciato proprio in +questo momento. Una volta inizializzato il messaggio occorrerà però +rilasciare il semaforo (\texttt{\small 25--28}) per consentirne l'uso; in +tutte queste operazioni si provvederà ad uscire dal programma con un opportuno +messaggio in caso di errore. + +Una volta completate le inizializzazioni il ciclo principale del programma +(\texttt{\small 29--47}) viene ripetuto indefinitamente (\texttt{\small 29}) +per stampare sia il contenuto del messaggio che una serie di informazioni di +controllo. Il primo passo (\texttt{\small 30--34}) è quello di acquisire (con +\func{sem\_getvalue}, con uscita in caso di errore) e stampare il valore del +semaforo ad inizio del ciclo; seguito (\texttt{\small 35--36}) dal tempo +corrente. + +\begin{figure}[!h] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/HandSigInt.c} + \end{minipage} + \normalsize + \caption{Codice del gestore di segnale del programma + \file{message\_getter.c}.} + \label{fig:ipc_posix_sem_shm_message_server_handler} +\end{figure} + +Prima della stampa del messaggio invece si deve acquisire il semaforo +(\texttt{\small 31--34}) per evitare accessi concorrenti alla stringa da parte +del programma di modifica. Una volta eseguita la stampa (\texttt{\small 41}) +il semaforo dovrà essere rilasciato (\texttt{\small 42--45}). Il passo finale +(\texttt{\small 46}) è attendere per un secondo prima di eseguire da capo il +ciclo. + +Per uscire in maniera corretta dal programma sarà necessario interromperlo con +il break da tastiera (\texttt{C-c}), che corrisponde all'invio del segnale +\const{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}[!h] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \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{Verbatim} +piccardi@hain:~/gapil/sources$ ./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{Verbatim} +%$ +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{Verbatim} +piccardi@hain:~/gapil/sources$ ./message_setter -t 3 ciao +Sleeping for 3 seconds +\end{Verbatim} +%$ +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{Verbatim} +... +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{Verbatim} +%$ + +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 SysV IPC Process Comunication ipc perm key exec +% LocalWords: header ftok proj stat libc SunOS glibc XPG dell'inode number uid +% LocalWords: cuid cgid gid tab MSG shift group umask seq MSGMNI SEMMNI SHMMNI +% LocalWords: shmmni msgmni sem sysctl IPCMNI IPCTestId msgget EACCES EEXIST +% LocalWords: CREAT EXCL EIDRM ENOENT ENOSPC ENOMEM novo proc MSGMAX msgmax ds +% LocalWords: MSGMNB msgmnb linked list msqid msgid linux msg qnum lspid lrpid +% LocalWords: rtime ctime qbytes first last cbytes msgctl semctl shmctl ioctl +% LocalWords: cmd struct buf EPERM RMID msgsnd msgbuf msgp msgsz msgflg EAGAIN +% LocalWords: NOWAIT EINTR mtype mtext long message sizeof LENGTH ts sleep BIG +% LocalWords: msgrcv ssize msgtyp NOERROR EXCEPT ENOMSG multiplexing select ls +% LocalWords: poll polling queue MQFortuneServer write init HandSIGTERM l'IPC +% LocalWords: MQFortuneClient mqfortuned mutex risorse' inter semaphore semget +% LocalWords: nsems SEMMNS SEMMSL semid otime semval sempid semncnt semzcnt nr +% LocalWords: SEMVMX SEMOPM semop SEMMNU SEMUME SEMAEM semnum union semun arg +% LocalWords: ERANGE SETALL SETVAL GETALL array GETNCNT GETPID GETVAL GETZCNT +% LocalWords: sembuf sops unsigned nsops UNDO flg nsop num undo pending semadj +% LocalWords: sleeper scheduler running next semundo MutexCreate semunion lock +% LocalWords: MutexFind wrapper MutexRead MutexLock MutexUnlock unlock locking +% LocalWords: MutexRemove shmget SHMALL SHMMAX SHMMIN shmid shm segsz atime FD +% LocalWords: dtime lpid cpid nattac shmall shmmax SHMLBA SHMSEG EOVERFLOW brk +% LocalWords: memory shmat shmdt void shmaddr shmflg SVID RND RDONLY rounded +% LocalWords: SIGSEGV nattch exit SharedMem ShmCreate memset fill ShmFind home +% LocalWords: ShmRemove DirMonitor DirProp chdir GaPiL shmptr DirScan ipcs NFS +% LocalWords: ComputeValues ReadMonitor touch SIGTERM dirmonitor unlink fcntl +% LocalWords: LockFile UnlockFile CreateMutex FindMutex LockMutex SETLKW GETLK +% LocalWords: UnlockMutex RemoveMutex ReadMutex UNLCK WRLCK RDLCK mapping MAP +% LocalWords: SHARED ANONYMOUS thread patch names strace system call userid Di +% LocalWords: groupid Michal Wronski Krzysztof Benedyczak wrona posix mqueue +% LocalWords: lmqueue gcc mount mqd name oflag attr maxmsg msgsize receive ptr +% LocalWords: send WRONLY NONBLOCK close mqdes EBADF getattr setattr mqstat to +% LocalWords: omqstat curmsgs flags timedsend len prio timespec abs EMSGSIZE +% LocalWords: ETIMEDOUT timedreceive getaddr notify sigevent notification l'I +% LocalWords: EBUSY sigev SIGNAL signo value sigval siginfo all'userid MESGQ +% LocalWords: Konstantin Knizhnik futex tmpfs ramfs cache shared swap CONFIG +% LocalWords: lrt blocks PAGECACHE TRUNC CLOEXEC mmap ftruncate munmap FindShm +% LocalWords: CreateShm RemoveShm LIBRARY Library libmqueue FAILED EACCESS has +% LocalWords: ENAMETOOLONG qualchenome RESTART trywait XOPEN SOURCE timedwait +% LocalWords: process getvalue sval execve pshared ENOSYS heap PAGE destroy it +% LocalWords: xffffffff Arrays owner perms Queues used bytes messages device +% LocalWords: Cannot find such Segments getter Signal MSGMAXSIZE been stable +% LocalWords: for now it's break Berlin sources Let's an accidental feature +% LocalWords: Larry Wall Escape the Hell William ipctestid Identifier segment +% LocalWords: violation dell'I SIGINT setter Fri Dec Sleeping seconds -Il terzo oggetto introdotto dal \textit{SystemV IPC} è quello della memoria -condivisa. %%% Local Variables: %%% mode: latex