X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=916a27c58855851a26021e1cdb749a1844ce22b1;hp=3bf821c56c6da3fb19dcf4028754f089f0b0c4f1;hb=d46995abfd6f4e2fcd46cdb8ba369dbb6ba36492;hpb=6a27b6ddf1f27d332f1de65b7c4ac0e1293c2174 diff --git a/ipc.tex b/ipc.tex index 3bf821c..916a27c 100644 --- a/ipc.tex +++ b/ipc.tex @@ -10,13 +10,12 @@ diversi, come quelli tradizionali che coinvolgono \textit{pipe} e 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} @@ -51,7 +50,7 @@ Crea una coppia di file descriptor associati ad una \textit{pipe}. \macro{ENFILE} e \macro{EFAULT}.} \end{prototype} -La funzione restituisce la coppia di file descriptor nell'array +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 @@ -94,8 +93,9 @@ ordinarie dei file, ma ci mostra anche qual' 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 in cui è avvenuta la -creazione della 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 @@ -124,7 +124,7 @@ consiste nell'inviare l'output di un processo (lo standard output) sull'input di un'altro. Realizzeremo il programma di esempio nella forma di un \textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un programma che permette la creazione dinamica di un oggetto da inserire - all'interno di una pagina HTML.} per apache, che genera una immagine JPEG + all'interno di una pagina HTML.} 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} deve rispondere a @@ -376,7 +376,7 @@ originarie del codice a barre e produce un JPEG di dimensioni corrette. Questo approccio però non funziona, per via di una delle caratteristiche principali delle pipe. Per poter effettuare la conversione di un PDF infatti è necessario, per la struttura del formato, potersi spostare (con \func{lseek}) -all'interno del file da convertire; se si eseguela conversione con \cmd{gs} su +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 \macro{ESPIPE}, rendendo impossibile la conversione. Questo ci dice che in @@ -517,7 +517,7 @@ apertura (bloccante e non bloccante); questo pu comunque una fifo in scrittura anche se non ci sono ancora processi il lettura; è possibile anche usare la fifo all'interno di un solo processo, nel qual caso però occorre stare molto attenti alla possibili -deadlock.\footnote{se si cerca di leggere da una fifo che non contiene dati si +situazioni di stallo.\footnote{se si cerca di leggere da una fifo che non contiene dati si avrà un deadlock immediato, dato che il processo si blocca e non potrà quindi mai eseguire le funzioni di scrittura.} @@ -556,20 +556,305 @@ struttura sequenziale delle fifo, i client dovrebbero sapere, prima di leggerli, quando i dati inviati sono destinati a loro. Per risolvere questo problema, si può usare un'architettura come quella -illustrata da Stevens in \cite{APUE}, in cui le risposte vengono inviate su -fifo temporanee identificate dal \acr{pid} dei client, ma in ogni caso il -sistema è macchinoso e continua ad avere vari inconvenienti\footnote{lo stesso - Stevens nota come sia impossibile per il server sapere se un client è andato - in crash, con la possibilità di far restare le fifo temporanee sul - filesystem, come sia necessario intercettare \macro{SIGPIPE} dato che un - client può terminare dopo aver fatto una richiesta, ma prima che la risposta - sia inviata, e come occorra gestire il caso in cui non ci sono client attivi - (e la lettura dalla fifo nota restituisca al serve un end-of-file.}; in -generale infatti l'interfaccia delle fifo non è adatta a risolvere questo tipo -di problemi, che possono essere affrontati in maniera più semplice ed efficace -o usando i \textit{socket}\index{socket} (che tratteremo in dettaglio a -partire da \capref{cha:socket_intro}) o ricorrendo a meccanismi di -comunicazione diversi, come quelli che esamineremo in seguito. +illustrata in \figref{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 +\figref{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} + \begin{lstlisting}{} +char *fifoname = "/tmp/fortune.fifo"; +int main(int argc, char *argv[]) +{ +/* Variables definition */ + int i, n = 0; + char *fortunefilename = "/usr/share/games/fortunes/italia"; + char **fortune; + char line[80]; + int fifo_server, fifo_client; + int nread; + ... + if (n==0) usage(); /* if no pool depth exit printing usage info */ + Signal(SIGTERM, HandSIGTERM); /* set handlers for termination */ + Signal(SIGINT, HandSIGTERM); + Signal(SIGQUIT, HandSIGTERM); + i = FortuneParse(fortunefilename, fortune, n); /* parse phrases */ + if (mkfifo(fifoname, 0622)) { /* create well known fifo if does't exist */ + if (errno!=EEXIST) { + perror("Cannot create well known fifo"); + exit(1); + } + } + /* open fifo two times to avoid EOF */ + fifo_server = open(fifoname, O_RDONLY); + if (fifo_server < 0) { + perror("Cannot open read only well known fifo"); + exit(1); + } + if (open(fifoname, O_WRONLY) < 0) { + perror("Cannot open write only well known fifo"); + exit(1); + } + /* Main body: loop over requests */ + while (1) { + nread = read(fifo_server, line, 79); /* read request */ + if (nread < 0) { + perror("Read Error"); + exit(1); + } + line[nread] = 0; /* terminate fifo name string */ + n = random() % i; /* select random value */ + fifo_client = open(line, O_WRONLY); /* open client fifo */ + if (fifo_client < 0) { + perror("Cannot open"); + exit(1); + } + nread = write(fifo_client, /* write phrase */ + fortune[n], strlen(fortune[n])+1); + close(fifo_client); /* close client fifo */ + } +} + \end{lstlisting} + \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 +routine (non riportata) che ne stampa le modalità d'uso. Dopo di che installa +(\texttt{\small 13--15}) la funzione che gestisce i segnali di interruzione +(anche questa non è riportata in \figref{fig:ipc_fifo_server}) che si limita a +rimuovere dal filesystem la fifo usata dal server per comunicare. + +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 si procede +(\texttt{\small 23--32}) alla sua apertura. Questo viene fatto due volte +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 e a questo punto +\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 \macro{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 in + modalità non bloccante, per evitare il rischio di uno stallo (se nessuno + apre la fifo in scrittura il processo non ritornerà mai dalla \func{open}) + che nel nostro caso 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, ma lasciando la fifo comunque aperta anche in scrittura, +cosicché le successive possano bloccarsi. + +A questo punto si può entrare nel ciclo principale del programma che fornisce +le risposte ai client (\texttt{\small 34--50}), che viene eseguito +indefinitamente (l'uscita del server viene effettuata inviando un segnale, in +modo da passare attraverso la routine 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 \texttt{\small 47--48}) poi vi +sarà scritta. Infine (\texttt{\small 49}) si chiude la fifo di risposta che +non serve più. + +Il codice del client è invece riportato in \figref{fig:ipc_fifo_client}, anche +in questo caso si è omessa la gestione delle opzioni e la funzione che stampa +a video le informazioni di utilizzo ed esce, riportando solo la sezione +principale del programma e le definizioni delle variabili. Il codice completo +è nel file \file{FortuneClient.c} dei sorgenti allegati. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} +int main(int argc, char *argv[]) +{ +/* Variables definition */ + int n = 0; + char *fortunefilename = "/tmp/fortune.fifo"; + char line[80]; + int fifo_server, fifo_client; + char fifoname[80]; + int nread; + char buffer[PIPE_BUF]; + ... + snprintf(fifoname, 80, "/tmp/fortune.%d", getpid()); /* compose name */ + if (mkfifo(fifoname, 0622)) { /* open client fifo */ + if (errno!=EEXIST) { + perror("Cannot create well known fifo"); + exit(-1); + } + } + fifo_server = open(fortunefilename, O_WRONLY); /* open server fifo */ + if (fifo_server < 0) { + perror("Cannot open well known fifo"); + exit(-1); + } + nread = write(fifo_server, fifoname, strlen(fifoname)+1); /* write name */ + close(fifo_server); /* close server fifo */ + fifo_client = open(fifoname, O_RDONLY); /* open client fifo */ + if (fifo_client < 0) { + perror("Cannot open well known fifo"); + exit(-1); + } + nread = read(fifo_client, buffer, sizeof(buffer)); /* read answer */ + printf("%s", buffer); /* print fortune */ + close(fifo_client); /* close client */ + close(fifo_server); /* close server */ + unlink(fifoname); /* remove client fifo */ +} + \end{lstlisting} + \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 \macro{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. + +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 \macro{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 +\textit{socket}\index{socket} (che tratteremo in dettaglio a partire da +\capref{cha:socket_intro}) o ricorrendo a meccanismi di comunicazione diversi, +come quelli che esamineremo in seguito. + + + +\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 +\textit{socket} locali (o \textit{Unix domain socket}). Tratteremo l'argomento +dei socket in \capref{cha:socket_intro}, nell'ambito dell'interfaccia generale +che essi forniscono per la programmazione di rete; e vedremo +(in~\secref{sec:sock_sa_local}) come in tal caso si possono definire dei file +speciali (di tipo \textit{socket}, analoghi alle fifo) cui si accede però +attraverso quella interfaccia; vale però la pena esaminare qui una +modalità\footnote{la funzione \func{socketpair} è stata introdotta in BSD4.4, + ma è supportata in genere da qualunque sistema che fornisca l'interfaccia + dei socket.} di uso di questi socket che li rende sostanzialmente identici +ad una pipe bidirezionale. + +Attraverso la funzione \func{socketpair} infatti è possibile creare una coppia +di socket (che sono trattati com file descriptor) connessi fra di loro, senza +fare nessun riferimento ad un file speciale sul filesystem, in maniera analoga +a quello che si fa con \func{pipe}; la differenza è che in questo caso il +flusso dei dati è bidirezionale. 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. + + \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[\macro{EAFNOSUPPORT}] I socket locali non sono supportati. + \item[\macro{EPROTONOSUPPORT}] Il protocollo specificato non è supportato. + \item[\macro{EOPNOTSUPP}] Il protocollo specificato non supporta la + creazione di coppie di socket. + \end{errlist} + ed inoltre \macro{EMFILE}, \macro{EFAULT}. +} +\end{functions} + +La funzione restituisce in \param{sv} una coppia di descrittori di socket +(come vedremo in \capref{cha:socket_intro} i file descriptor vengono usati +anche per i socket) connessi fra di loro, così che quello che si scrive da una +parte può essere riletto dall'altra e viceversa. I parametri \param{domain}, +\param{type} e \param{protocol} derivano dall'interfaccia dei socket, ma in +questo caso i soli valori validi sono rispettivamente \macro{AF\_UNIX}, +\macro{SOCK\_STREAM} e \macro{0}. @@ -583,9 +868,9 @@ molti altri devono poter leggere non pu 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 quello che viene ormai chiamato il -\textsl{Sistema di comunicazione inter-processo} di System V, (o +programmazione, che fossero in grado di garantire una maggiore flessibilità. +In questa sezione esamineremo come Linux supporta quello che viene ormai +chiamato il \textsl{Sistema di comunicazione inter-processo} di System V, o \textit{System V IPC (Inter-Process Comunication)}. @@ -597,45 +882,1126 @@ La principale caratteristica del sistema di IPC di System V basato su oggetti permanenti che risiedono nel kernel. Questi, a differenza di quanto avviene per i file descriptor, non mantengono un contatore dei riferimenti, e non vengono cancellati dal sistema una volta che non sono più -in uso. Questo comporta che, al contrario di quanto avviene per pipe e fifo, -la memoria allocata per questi oggetti non viene rilasciata automaticamente, -ed essi devono essere cancellati esplicitamente, altrimenti resteranno attivi -fino al riavvio del sistema. +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 nessuno li vuole più utilizzare, ed essi devono essere +cancellati esplicitamente, se non si vuole che restino attivi fino al riavvio +del sistema. Il secondo è che, dato che non c'è un contatore di riferimenti, +essi possono essere cancellati anche se ci sono dei processi che li stanno +utilizzando, con tutte le conseguenze (negative) del caso. Gli oggetti usati nel System V IPC vengono creati direttamente dal kernel, e sono accessibili solo specificando il relativo \textsl{identificatore}. Questo -è il numero progressivo che il kernel assengna a ciascuno di questi oggetti -quanto vengono creati (il prodedimento è simile a quello con cui si assegna il -\acr{pid} ai processi). +è un numero progressivo (un po' come il \acr{pid} dei processi) che il kernel +assegna a ciascuno di essi quanto vengono creati (sul procedimento di +assegnazione torneremo in \secref{sec:ipc_sysv_id_use}). L'identificatore +viene restituito dalle funzioni che creano l'oggetto, ed è quindi locale al +processo che le ha eseguite. Dato che l'identificatore viene assegnato +dinamicamente dal kernel non è possibile prevedere quale sarà, ne utilizzare +un qualche valore statico, si pone perciò il problema di come processi diversi +possono accedere allo stesso oggetto. + +Per risolvere il problema il kernel associa a ciascun oggetto una struttura +\var{ipc\_perm}; questa contiene una \textsl{chiave}, identificata da una +variabile del tipo primitivo \type{key\_t}, che viene specificata in fase di +creazione e tramite la quale è possibile ricavare l'identificatore. La +struttura, la cui definizione è riportata in \figref{fig:ipc_ipc_perm}, +contiene anche le varie proprietà associate all'oggetto. -L'identificatore è in genere restituito dalle funzioni che creano l'oggetto, -nasce quindi il problema di come processi diversi possono accedere allo stesso -oggetto. Per far questo a ciascuno di essi viene anche associata una -\textsl{chiave}, che può essere indicata in fasi di creazione. Usando la -stessa chiave due processi diversi potranno ricavare l'identificatore -associato ad un oggetto e accedervi entrambi. +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm ]{} +struct ipc_perm +{ + key_t key; /* Key. */ + uid_t uid; /* Owner's user ID. */ + gid_t gid; /* Owner's group ID. */ + uid_t cuid; /* Creator's user ID. */ + gid_t cgid; /* Creator's group ID. */ + unsigned short int mode; /* Read/write permission. */ + unsigned short int seq; /* Sequence number. */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{ipc\_perm}, come definita in \file{sys/ipc.h}.} + \label{fig:ipc_ipc_perm} +\end{figure} -Il problema che sorge a questo punto è come due processi diversi possono +Usando la stessa chiave due processi diversi possono ricavare l'identificatore +associato ad un oggetto ed accedervi. Il problema che sorge a questo punto è +come devono fare per accordarsi sull'uso di una stessa chiave. Se i processi +sono \textsl{parenti} la soluzione è relativamente semplice, in tal caso +infatti si può usare il valore speciale \texttt{IPC\_PRIVATE} per creare un +nuovo oggetto nel processo padre, l'identificatore così ottenuto sarà +disponibile in tutti i figli, e potrà essere passato come parametro attraverso +una \func{exec}. + +Però quando i processi non sono \textsl{parenti} (come capita tutte le volte +che si ha a che fare con un sistema client-server) tutto questo non è +possibile; si 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, +\func{ftok}, che permette di ottenere una chiave specificando il nome di un +file ed un numero di versione; il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + + \funcdecl{key\_t ftok(const char *pathname, int proj\_id)} + + Restituisce una chiave per identificare un oggetto del System V IPC. + + \bodydesc{La funzione restituisce la chiave in caso di successo e -1 + altrimenti, nel qual caso \var{errno} 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 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 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 \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 \var{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 sistema di IPC di System V. Non esiste infatti una modalità +chiara per identificare un oggetto, come sarebbe stato se lo si fosse +associato ad in file, e tutta l'interfaccia è inutilmente complessa. Per +questo ne è stata effettuata una revisione completa nello standard POSIX.1b, +che tratteremo in \secref{sec:ipc_posix}. + + +\subsection{Il controllo di accesso} +\label{sec:ipc_sysv_access_control} + +Oltre alle chiavi, abbiamo visto che ad ogni oggetto sono associate in +\var{ipc\_perm} ulteriori informazioni, come gli identificatori del creatore +(nei campi \var{cuid} e \var{cgid}) e del proprietario (nei campi \var{uid} e +\var{gid}) dello stesso, e un insieme di permessi (nel campo \var{mode}). In +questo modo è possibile definire un controllo di accesso sugli oggetti di IPC, +simile a quello che si ha per i file (vedi \secref{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 +\secref{tab:file_mode_flags}\footnote{se però si vogliono usare le costanti + simboliche ivi definite occorrerà includere il file \file{sys/stat.h}, + alcuni sistemi definiscono le costanti \macro{MSG\_R} (\texttt{0400}) e + \macro{MSG\_W} (\texttt{0200}) per indicare i permessi base di lettura e + scrittura per il proprietario, da utilizzare, con gli opportuni shift, pure + per il gruppo e gli altri, in Linux, visto la loro scarsa utilità, queste + costanti non sono definite.} e come per i file definiscono gli accessi per +il proprietario, il suo gruppo e tutti gli altri. + +Quando l'oggetto viene creato i campi \var{cuid} e \var{uid} di +\var{ipc\_perm} ed i campi \var{cgid} e \var{gid} vengono settati +rispettivamente al valore dell'userid e del groupid effettivo del processo che +ha chiamato la funzione, ma, mentre i campi \var{uid} e \var{gid} possono +essere cambiati, 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'userid effettivo del processo corrisponde o al valore del campo + \var{cuid} o a quello del campo \var{uid} ed il permesso per il proprietario + in \var{mode} è appropriato\footnote{per appropriato si intende che è + settato il permesso di scrittura per le operazioni di scrittura e quello + di lettura per le operazioni di lettura.} l'accesso è consentito. +\item se il groupid effettivo del processo corrisponde o al + 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 \var{umask} (si ricordi quanto esposto in +\secref{sec:file_umask}) non ha alcun significato. + + +\subsection{Gli identificatori ed il loro utilizzo} +\label{sec:ipc_sysv_id_use} + +L'unico campo di \var{ipc\_perm} del quale non abbiamo ancora parlato è +\var{seq}, che in \figref{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{req} 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 \macro{MSGMNI}, + \macro{SEMMNI} e \macro{SHMMNI}, potevano essere cambiati (come tutti gli + altri limiti relativi al \textit{System V 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 \file{shmmni}, \file{msgmni} e \file{sem} di + \file{/proc/sys/kernel} o con l'uso di \texttt{syscntl}.} 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 \macro{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} + \begin{lstlisting}{} +int main(int argc, char *argv[]) +{ + ... + switch (type) { + case 'q': /* Message Queue */ + debug("Message Queue Try\n"); + for (i=0; i 0 */ + char mtext[LENGTH]; /* message data */ + }; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Schema della struttura \var{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 +\figref{fig:ipc_mq_schema}. Alla chiamata di \func{msgsnd} il nuovo messaggio +sarà aggiunto in fondo alla lista inserendo una nuova struttura \var{msg}, il +puntatore \var{msg\_last} di \var{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 \macro{IPC\_NOWAIT} la funzione opera in +modalità non bloccante, ed in questi casi ritorna immediatamente con un errore +di \macro{EAGAIN}. + +Se non si specifica \macro{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 \macro{EIDRM}) o quando la funzione viene +interrotta da un segnale (nel qual caso si ha un errore di \macro{EINTR}). + +Una volta completato con successo l'invio del messaggio sulla coda, la +funzione aggiorna i dati mantenuti in \var{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 permette di estrarre da una coda un messaggio (che sarà +rimosso dalla stessa) è \func{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[\macro{EACCES}] Non si hanno i privilegi di accesso sulla coda. + \item[\macro{EIDRM}] La coda è stata cancellata. + \item[\macro{E2BIG}] Il testo del messaggio è più lungo di \param{msgsz} e + non si è specificato \macro{MSG\_NOERROR} in \param{msgflg}. + \item[\macro{EINTR}] La funzione è stata interrotta da un segnale mentre era + in attesa di ricevere un messaggio. + \item[\macro{EINVAL}] Si è specificato un \param{msgid} invalido o un valore + di \param{msgsz} negativo. + \end{errlist} + ed inoltre \macro{EFAULT}. +} +\end{functions} + +La funzione legge un messaggio dalla coda specificata scrivendolo nel buffer +indicato da \param{msgp}, che avrà un formato analogo a quello di +\figref{fig:ipc_msbuf}. L'argomento \param{msgsz} indica la lunghezza massima +del testo del messaggio (equivalente al valore del parametro \macro{LENGHT} +nell'esempio di \figref{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 +\macro{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 \macro{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 \figref{fig:ipc_mq_schema}, +restituendo il primo messaggio incontrato che corrisponde ai criteri +specificati (che quindi, visto che i messaggi vengono sempre inseriti dalla +coda, è quello meno recente); in particolare: +\begin{itemize*} +\item se \param{msgtyp} è 0 viene estratto il messaggio in cima alla coda, cioè + quello fra i presenti che è stato inserito inserito per primo. +\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 tipo di valore più basso, fra tutti quelli con un tipo 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 \macro{MSG\_NOERROR}, sono possibili altri due +valori: \macro{MSG\_EXCEPT}, che permette, quando \param{msgtyp} è positivo, +di leggere il primo messaggio nella coda con tipo diverso da \param{msgtyp}, e +\macro{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 \macro{IPC\_NOWAIT} la +funzione ritorna immediatamente con un errore \macro{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 \macro{EIDRM}) o se il processo viene interrotto da un +segnale (con \var{errno} impostata a \macro{EINTR}). + +Una volta completata con successo l'estrazione del messaggio dalla coda, la +funzione aggiorna i dati mantenuti in \var{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*} + +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 coda di messaggi, usando il \acr{pid} del client come valore per +il tipo di messaggio, per restituire indietro le frasi ai client. \subsection{Semafori} -\label{sec:ipc_semaph} +\label{sec:ipc_sysv_sem} + +I semafori non sono meccanismi di intercomunicazione diretta come quelli +(pipe, fifo e code di messaggi) visti finora, e non consentono di scambiare +dati fra processi, ma servono piuttosto come meccanismi di sincronizzazione o +di protezione per le \textsl{sezioni critiche}\index{sezioni critiche} del +codice (si ricordi quanto detto in \secref{sec:proc_race_cond}). + +Un semaforo è uno speciale contatore, mantenuto nel kernel, che permette, a +seconda del suo valore, di consentire o meno la prosecuzione dell'esecuzione +del codice. In questo modo l'accesso ad una risorsa condivisa da più processi +può essere controllato, associando ad essa un semaforo che consente di +assicurare che non più di un processo alla volta possa usarla. + +Il concetto di semaforo è uno dei concetti base nella programmazione ed è +assolutamente generico, così come del tutto generali sono modalità con cui lo +si utilizza. Un processo che deve accedere ad una risorsa eseguirà un +controllo del semaforo: se questo è positivo il suo valore sarà decrementato, +indicando che si è consumato una unità della risorsa, ed il processo potrà +proseguire nell'utilizzo di quest'ultima, provvedendo a rilasciarla, una volta +completate le operazioni volute, reincrementando il semaforo. + +Se al momento del controllo il valore del semaforo è nullo, siamo invece in +una situazione in cui la risorsa non è disponibile, ed il processo si +bloccherà in stato di \textit{sleep} fin quando chi la sta utilizzando non la +rilascerà, incrementando il valore del semaforo. Non appena il semaforo torna +positivo, indicando che la risorsa è disponibile, il processo sarà svegliato, +e si potrà operare come nel caso precedente (decremento del semaforo, accesso +alla risorsa, incremento del semaforo). + +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 interprocesso di System V 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 è +\func{semget}, ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semget(key\_t key, int nsems, int flag)} + + Restituisce l'identificatore di un insieme di semafori. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà gli stessi valori + visti 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 \secref{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 System V IPC soffrono +di altri due ben più gravi difetti. + +Il primo grave 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 atomicamente questa operazione. + +Il secondo difetto deriva dalla caratteristica generale degli oggetti del +System V 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} + \begin{lstlisting}[labelstep=0]{} +struct semid_ds +{ + struct ipc_perm sem_perm; /* operation permission struct */ + time_t sem_otime; /* last semop() time */ + time_t sem_ctime; /* last time changed by semctl() */ + unsigned long int sem_nsems; /* number of semaphores in set */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{semid\_ds}, associata a ciascun insieme di + semafori.} + \label{fig:ipc_semid_sd} +\end{figure} + +A ciascun insieme di semafori è associata una struttura \var{semid\_ds}, +riportata in \figref{fig:ipc_semid_sd}. Come nel caso delle code di messaggi +quando si crea un nuovo insieme di semafori con \func{semget} questa struttura +viene inizializzata, in particolare il campo \var{sem\_perm} viene +inizializzato come illustrato in \secref{sec:ipc_sysv_access_control} (si +ricordi che in questo caso il permesso di scrittura è in realtà permesso di +alterare il semaforo), per quanto riguarda gli altri campi invece: +\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 +\var{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 System V 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 dalla pagine di manuale.} è riportata in \figref{fig:ipc_sem}. Di +norma questa struttura non è accessibile in user space, ma lo sono, in maniera +indiretta, tramite l'uso delle funzioni di controllo, i valori in essa +specificati, che indicano rispettivamente: il valore del semaforo, il +\acr{pid} dell'ultimo processo che ha eseguito una operazione, il numero di +processi in attesa che esso venga incrementato ed il numero di processi in +attesa che esso si annulli. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{} +struct sem { + short sempid; /* pid of last operation */ + ushort semval; /* current value */ + ushort semncnt; /* num procs awaiting increase in semval */ + ushort semzcnt; /* num procs awaiting semval = 0 */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{sem}, che contiene i dati di un singolo semaforo.} + \label{fig:ipc_sem} +\end{figure} + +Come per le code di messaggi anche per gli insiemi di semafori esistono una +serie di limiti, i cui valori sono associati ad altrettante costanti, che si +sono riportate in \tabref{tab:ipc_sem_limits}. Alcuni di questi limiti sono +al solito accessibili e modificabili attraverso \func{sysctl} o scrivendo +direttamente nel file \file{/proc/sys/kernel/sem}. + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|r|p{8cm}|} + \hline + \textbf{Costante} & \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \macro{SEMMNI}& 128 & Numero massimo di insiemi di semafori. \\ + \macro{SEMMSL}& 250 & Numero massimo di semafori per insieme.\\ + \macro{SEMMNS}&\macro{SEMMNI}*\macro{SEMMSL}& Numero massimo di semafori + nel sistema .\\ + \macro{SEMVMX}& 32767 & Massimo valore per un semaforo.\\ + \macro{SEMOPM}& 32 & Massimo numero di operazioni per chiamata a + \func{semop}. \\ + \macro{SEMMNU}&\macro{SEMMNS}& Massimo numero di strutture di ripristino.\\ + \macro{SEMUME}&\macro{SEMOPM}& Massimo numero di voci di ripristino.\\ + \macro{SEMAEM}&\macro{SEMVMX}& valore massimo per l'aggiustamento + all'uscita. \\ + \hline + \end{tabular} + \caption{Valori delle costanti associate ai limiti degli insiemi di + semafori, definite in \file{linux/sem.h}.} + \label{tab:ipc_sem_limits} +\end{table} + +La funzione che permette di effettuare le varie operazioni di controllo sui +semafori (fra le quali, come accennato, è impropriamente compresa anche la +loro inizializzazione) è \func{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[\macro{EACCES}] Il processo non ha i privilegi per eseguire + l'operazione richiesta. + \item[\macro{EIDRM}] L'insieme di semafori è stato cancellato. + \item[\macro{EPERM}] Si è richiesto \macro{IPC\_SET} o \macro{IPC\_RMID} ma + il processo non ha privilegi sufficienti ad eseguire l'operazione. + \item[\macro{ERANGE}] Si è richiesto \macro{SETALL} \macro{SETVAL} ma il + valore a cui si vuole impostare il semaforo è minore di zero o maggiore + di \macro{SEMVMX}. + \end{errlist} + ed inoltre \macro{EFAULT} ed \macro{EINVAL}. +} +\end{functions} + +La funzione può avere tre o quattro parametri, 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}. Qualora la funzione operi con quattro argomenti \param{arg} è +un argomento generico che deve essere una \var{union semun}, si è riportato la +relativa definizione in \figref{fig:ipc_semun}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{} +union semun { + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* array for GETALL, SETALL */ + /* Linux specific part: */ + struct seminfo *__buf; /* buffer for IPC_INFO */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La definizione dei possibili valori di una \var{union semun}, usata + come quarto argomento della funzione \func{semctl}.} + \label{fig:ipc_semun} +\end{figure} + +Il comportamento della funzione, come il numero di parametri, dipende dal +valore dell'argomento \param{cmd}, che specifica l'azione da intraprendere; i +possibili valori legali per questo argomento sono: +\begin{basedescript}{\desclabelwidth{2.2cm}\desclabelstyle{\nextlinelabel}} +\item[\macro{IPC\_STAT}] Legge i dati dell'insieme di semafori, copiando il + contenuto della relativa struttura \var{semid\_ds} all'indirizzo specificato + con \var{arg.buf}. Occorre avere il permesso di lettura. L'argomento + \param{semnum} viene ignorato. +\item[\macro{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 \macro{EIDRM}. + L'userid effettivo del processo deve corrispondere o al creatore o al + proprietario dell'insieme, o all'amministratore. L'argomento \param{semnum} + viene ignorato. +\item[\macro{IPC\_SET}] Permette di modificare i permessi ed il proprietario + dell'insieme. I valori devono essere passati in una struttura + \var{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'userid effettivo del processo deve + corrispondere o al creatore o al proprietario dell'insieme, o + all'amministratore. L'argomento \param{semnum} viene ignorato. +\item[\macro{GETALL}] Restituisce il valore corrente di ciascun semaforo + dell'insieme (il campo \var{semval} di \var{sem}) nel vettore indicato da + \param{arg.array}. Occorre avere il permesso di lettura. L'argomento + \param{semnum} viene ignorato. +\item[\macro{GETNCNT}] Restituisce come valore di ritorno della funzione il + numero di processi in attesa che il semaforo \param{semnum} dell'insieme + venga incrementato (il campo \var{semncnt} di \var{sem}); va invocata con + tre argomenti. Occorre avere il permesso di lettura. +\item[\macro{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 (il campo \var{sempid} di \var{sem}); va + invocata con tre argomenti. Occorre avere il permesso di lettura. +\item[\macro{GETVAL}] Restituisce come valore di ritorno della funzione il il + valore corrente del semaforo \param{semnum} dell'insieme (il campo + \var{semval} di \var{sem}); va invocata con tre argomenti. Occorre avere il + permesso di lettura. +\item[\macro{GETZCNT}] Restituisce come valore di ritorno della funzione il + numero di processi in attesa che il valore del semaforo \param{semnum} + dell'insieme diventi nullo (il campo \var{semncnt} di \var{sem}); va + invocata con tre argomenti. Occorre avere il permesso di lettura. +\item[\macro{SETALL}] Inizializza il valore di tutti i semafori dell'insieme, + aggiornando il campo \var{sem\_ctime} di \var{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[\macro{SETVAL}] Inizializza il semaforo \param{semnum} al valore passato + dall'argomento \param{arg.val}, aggiornando il campo \var{sem\_ctime} di + \var{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 \macro{SETALL}, che per uno solo con \macro{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|p{8cm}|} + \hline + \textbf{Operazione} & \textbf{Valore restituito} \\ + \hline + \hline + \macro{GETNCNT}& valore di \var{semncnt}.\\ + \macro{GETPID} & valore di \var{sempid}.\\ + \macro{GETVAL} & valore di \var{semval}.\\ + \macro{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; in generale esso è nullo, a meno che non si sia +specificata una delle operazioni riportate in +\tabref{tab:ipc_semctl_returns}, nel qual caso viene restituito il +corrispondente campo della struttura \var{sem}. + + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{} +struct sembuf +{ + unsigned short int sem_num; /* semaphore number */ + short int sem_op; /* semaphore operation */ + short int sem_flg; /* operation flag */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{sembuf}, usata per le operazioni sui + semafori.} + \label{fig:ipc_sembuf} +\end{figure} + -Il secondo oggetto introdotto dal \textit{System V IPC} è quello dei semafori. \subsection{Memoria condivisa} -\label{sec:ipc_shar_mem} +\label{sec:ipc_sysv_shm} Il terzo oggetto introdotto dal \textit{System V IPC} è quello della memoria -condivisa. +condivisa. La funzione che permette di ottenerne uno è \func{shmget} ed il suo +prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/shm.h} + + \funcdecl{int shmget(key\_t key, int size, int flag)} + + Restituisce l'identificatore di una memoria condivisa. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} assumerà gli stessi valori + visti 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}. L'argomento @@ -645,10 +2011,25 @@ condivisa. Lo standard POSIX.1b ha introdotto dei nuovi meccanismi di comunicazione, rifacendosi a quelli di System V, introducendo una nuova interfaccia che evitasse i principali problemi evidenziati in coda a -\secref{sec:ipc_sysv_generic}. +\secref{sec:ipc_sysv_generic}. +\subsection{Considerazioni generali} +\label{sec:ipc_posix_generic} + + + +\subsection{Code di messaggi} +\label{sec:ipc_posix_mq} + + +\subsection{Semafori} +\label{sec:ipc_posix_sem} + + +\subsection{Memoria condivisa} +\label{sec:ipc_posix_shm} %%% Local Variables: %%% mode: latex