X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=e0fef633d8d75c4f75cb8cb805f5aad3f10f3a41;hp=bdbdf2c13d9068e85617b49f2ff16cc321cf416e;hb=52958c279452903b8c4edea25643096a32c67235;hpb=6c705d16151fa82874be6aced255d346c9d2bd7b diff --git a/ipc.tex b/ipc.tex index bdbdf2c..e0fef63 100644 --- a/ipc.tex +++ b/ipc.tex @@ -6,7 +6,7 @@ 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 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 @@ -20,12 +20,11 @@ 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} @@ -33,39 +32,36 @@ evoluto. 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 è: +sostanza di una una coppia di file descriptor\footnote{si tenga presente che + le pipe sono oggetti creati dal kernel e non risiedono su disco.} connessi +fra di loro in modo che se quanto scrive su di uno si può rileggere +dall'altro. Si viene così a costituire un canale di comunicazione tramite i +due file descriptor, nella forma di un \textsl{tubo} (da cui il nome) +attraverso cui fluiscono i dati. + +La funzione che permette di creare questa speciale coppia di file descriptor +associati ad una \textit{pipe} è appunto \func{pipe}, ed il suo prototipo è: \begin{prototype}{unistd.h} {int pipe(int filedes[2])} -Crea una coppia di file descriptor associati ad una 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}.} \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 nell'array +\param{filedes}; il primo è aperto in lettura ed il secondo in scrittura. Come +accennato concetto di funzionamento di una pipe è semplice: quello che si scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale -nel file descriptor aperto in lettura, 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 ad un buffer nel kernel, la cui dimensione è +specificata dalla costante \macro{PIPE\_BUF}, (vedi +\secref{sec:sys_file_limits}). Lo schema di funzionamento di una pipe è +illustrato in \figref{fig:ipc_pipe_singular}, in cui sono illustrati i due +capi della pipe, associati a ciascun file descriptor, con le frecce che +indicano la direzione del flusso dei dati. \begin{figure}[htb] \centering @@ -74,12 +70,12 @@ 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 +Chiaramente creare una pipe all'interno di un singolo processo non serve a +niente; se però ricordiamo quanto esposto in \secref{sec:file_sharing} +riguardo al comportamento dei file descriptor nei processi figli, è immediato +capire come una pipe possa diventare un meccanismo di intercomunicazione. Un +processo figlio infatti condivide gli stessi file descriptor del padre, +compresi quelli associati ad una pipe (secondo la situazione illustrata in \figref{fig:ipc_pipe_fork}). In questo modo se uno dei processi scrive su un capo della pipe, l'altro può leggere. @@ -92,16 +88,16 @@ capo della pipe, l'altro pu \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 +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, in realtà questo è un limite facilmente superabile usando + 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 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ò +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 @@ -122,14 +118,14 @@ 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 +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}\footnote{Un CGI (\textit{Common Gateway Interface}) è un programma - che permette la creazione dinamica di un oggetto da inserire all'interno di - una pagina HTML.} per apache, che genera una immagine JPEG di un codice a -barre, specificato come parametro di input. +di un'altro. Realizzeremo il programma di esempio nella forma di un +\textit{CGI}\footnote{Un CGI (\textit{Common Gateway Interface}) è un + programma che permette la creazione dinamica di un oggetto da inserire + all'interno di una pagina HTML.} per apache, che genera una immagine JPEG +di un codice a barre, specificato come parametro di input. Un programma che deve essere eseguito come \textit{CGI} deve rispondere a delle caratteristiche specifiche, esso infatti non viene lanciato da una @@ -143,13 +139,13 @@ che ne descrive il mime-type) sullo standard output, in modo che il web-server possa reinviarlo al browser che ha effettuato la richiesta, che in questo modo è in grado di visualizzarlo opportunamente. -Per fare questo useremo in sequenza i programmi \cmd{barcode} e \cmd{gs}, il -primo infatti è in grado di generare immagini postscript di codici a barre -corrispondenti ad una qualunque stringa, mentre il secondo serve per poter -effettuare la conversione della stessa immagine in formato JPEG. Usando una -pipe potremo inviare l'output del primo sull'input del secondo, secondo lo -schema mostrato in \figref{fig:ipc_pipe_use}, in cui la direzione del flusso -dei dati è data dalle frecce continue. +Per realizzare quanto voluto useremo in sequenza i programmi \cmd{barcode} e +\cmd{gs}, il primo infatti è in grado di generare immagini postscript di +codici a barre corrispondenti ad una qualunque stringa, mentre il secondo +serve per poter effettuare la conversione della stessa immagine in formato +JPEG. Usando una pipe potremo inviare l'output del primo sull'input del +secondo, secondo lo schema mostrato in \figref{fig:ipc_pipe_use}, in cui la +direzione del flusso dei dati è data dalle frecce continue. \begin{figure}[htb] \centering @@ -168,7 +164,8 @@ file.\footnote{il problema potrebbe essere superato determinando in anticipo un nome appropriato per il file temporaneo, che verrebbe utilizzato dai vari sotto-processi, e cancellato alla fine della loro esecuzione; ma a questo le cose non sarebbero più tanto semplici.} L'uso di una pipe invece permette -di risolvere il problema in maniera semplice ed elegante. +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 @@ -523,23 +520,105 @@ stare molto attenti alla possibili deadlock.\footnote{se si cerca di leggere processo si blocca e non potrà quindi mai eseguire le funzioni di scrittura.} - +Per la loro caratteristica di essere accessibili attraverso il filesystem, è +piuttosto frequente l'utilizzo di una fifo come canale di comunicazione nelle +situazioni un processo deve ricevere informazioni dagli altri. In questo caso +è fondamentale che le operazioni di scrittura siano atomiche; per questo si +deve sempre tenere presente che questo è vero soltanto fintanto che non si +supera il limite delle dimensioni di \macro{PIPE\_BUF} (si ricordi quanto +detto in \secref{sec:ipc_pipes}). + +A parte il precedente, che resta probabilmente il più comune, Stevens riporta +in \cite{APUE} altre due casistiche principali per l'uso delle fifo: +\begin{itemize} +\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 un client ed un server (il modello + \textit{client-server} è illustrato in \secref{sec:net_cliserv}). +\end{itemize} + +Nel primo caso quello che si fa è creare tante pipe quanti sono i processi a +cui i vogliono inviare i dati, da usare come standard input per gli stessi; una +volta che li si saranno posti in esecuzione ridirigendo lo standard input si +potrà eseguire il processo iniziale replicandone, con il comando \cmd{tee}, +l'output sulle pipe. + +Il secondo caso è relativamente semplice qualora si debba comunicare con un +processo alla volta (nel qual caso basta usare due pipe, una per leggere ed +una per scrivere), le cose diventano invece molto più complesse quando si +vuole effettuare una comunicazione fra il server ed un numero imprecisato di +client; se il primo infatti può ricevere le richieste attraverso una fifo +``nota'', per le risposte non si può fare altrettanto, dato che, per la +struttura sequenziale delle fifo, i client dovrebbero sapere, prima di +leggerli, quando i dati inviati sono destinati a loro. + +Per risolvere questo problema, si può usare un'architettura come quella +illustrata da Stevens in \cite{APUE}, in cui le risposte vengono inviate su +fifo temporanee identificate dal \acr{pid} dei client, ma in ogni caso il +sistema è macchinoso e continua ad avere vari inconvenienti\footnote{lo stesso + Stevens nota come sia impossibile per il server sapere se un client è andato + in crash, con la possibilità di far restare le fifo temporanee sul + filesystem, come sia necessario intercettare \macro{SIGPIPE} dato che un + client può terminare dopo aver fatto una richiesta, ma prima che la risposta + sia inviata, e come occorra gestire il caso in cui non ci sono client attivi + (e la lettura dalla fifo nota restituisca al serve un end-of-file.}; in +generale infatti l'interfaccia delle fifo non è adatta a risolvere questo tipo +di problemi, che possono essere affrontati in maniera più semplice ed efficace +o usando i \textit{socket}\index{socket} (che tratteremo in dettaglio a +partire da \capref{cha:socket_intro}) o ricorrendo a diversi meccanismi di +comunicazione, come quelli che esamineremo in \secref{sec:ipc_sysv}. + + + \section{La comunicazione fra processi di System V} \label{sec:ipc_sysv} Benché le pipe (e le fifo) siano ancora ampiamente usate, esse presentano numerosi limiti, il principale dei quali è che il meccanismo di comunicazione -è rigidamente sequenziale; una situazione in cui un processo scrive qualcosa -che molti altri devono poter leggere non può essere implementata con una pipe. +è rigidamente sequenziale; per questo una situazione in cui un processo scrive +qualcosa che molti altri devono poter leggere non può essere implementata in +maniera semplice con una pipe. + +Per superare questi limiti nello sviluppo di System V vennero introdotti una +serie di nuovi oggetti di comunicazione e relative interfacce di +programmazione 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 \textit{System V IPC + (Inter-Process Comunication)}. + + + +\subsection{Considerazioni generali} +\label{sec:ipc_sysv_generic} + +La principale caratteristica del sistema di IPC di System V è quella di essere +basato su oggetti permanenti che risiedono nel kernel. Questi, a differenza di +quanto avviene per i file descriptor, non mantengono un contatore dei +riferimenti, pertanto 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 fintanto che non si riavvia il sistema. + +Gli oggetti usati nel System V IPC vengono creati direttamente dal kernel, e +sono accessibili solo specificando il relativo \textsl{identificatore}, che è +il numero progressivo che il kernel gli assengna quanto vengono creati (il +prodedimento è simile a quello con cui si assegna il \acr{pid} dei processi). + +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. + + +Questa caratteristica mostra il primo dei problemi associati al sistema di IPC +di System V. Un secondo problema riguarda le modalità per l'accesso a questi +oggetti. -Per superarne i vari limiti, nello sviluppo di System V vennero introdotti una -serie di nuovi oggetti di comunicazione e relative interfacce id -programmazione che garantissero una maggiore flessibilità; in questa sezione -esamineremo quello che viene ormai chiamato il \textsl{Sistema di - comunicazione inter-processo} di System V , più comunemente noto come -\textit{System V IPC (Inter-Process Comunication)}. - \subsection{Code di messaggi} \label{sec:ipc_messque} @@ -559,6 +638,19 @@ Il secondo oggetto introdotto dal \textit{System V IPC} Il terzo oggetto introdotto dal \textit{System V IPC} è quello della memoria condivisa. + + + +\section{La comunicazione fra processi di POSIX} +\label{sec:ipc_posix} + +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 ... + + + + %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil"