X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=7dd8c67907ad02b670c4e76a5a8482cee72705f8;hp=6c1041ef5271eadb33f5027e1b0fd98d9080868d;hb=c4ee73fb823eafce8bb5e4faa9caebc4105dbb17;hpb=525464ecdd7e6cbdccc733e73b5f1cc9dbe6022a diff --git a/ipc.tex b/ipc.tex index 6c1041e..7dd8c67 100644 --- a/ipc.tex +++ b/ipc.tex @@ -3,72 +3,306 @@ Uno degli aspetti fondamentali della programmazione in un sistema unix-like è -la comunicazione fra processi. In questo capitolo affronteremo solo alcuni dei +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. -Esistono pure sistemi più complessi ed evoluti come le RPC (\textit{Remote - Procedure Calls}) e CORBA (\textit{Common Object Request Brocker - Architecture}) che non saranno affrontati qui. Inoltre tratteremo nei -capitoli successivi tutta la problematica relativa alla comunicazione -attraverso la rete. +Tralasceremo invece tutte le problematiche relative alla comunicazione +attraverso la rete (e le relative interfacce) che saranno affrontate in +dettaglio in un secondo tempo. Non affronteremo invece meccanismi più +complessi ed evoluti come le RPC (\textit{Remote Procedure Calls}) e CORBA +(\textit{Common Object Request Brocker Architecture}) che in genere sono +implementati con un ulteriore livello sopra i meccanismi elementari. \section{La comunicazione fra processi tradizionale} \label{sec:ipc_unix} -Il primo meccanismo di comunicazione fra processi usato dai sistemi unix-like -è quello delle \textit{pipe}, 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 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. \subsection{Le \textit{pipe} standard} \label{sec:ipc_pipes} -Le \textit{pipe} nascono sostanzialmente con Unix, e sono il primo, e più -comunemente usato, meccanismo di comunicazione fra processi. Esse sono un file -speciale in cui un processo scrive ed un altro legge. Si viene così a -costituire un canale di comunicazione fra i due processi, nella forma di un -\textsl{tubo} (da cui il nome) in cui uno immette dati che arriveranno -all'altro. +Le \textit{pipe} nascono sostanzialmente con Unix, e sono il primo, e tuttora +uno dei più usati, meccanismi di comunicazione fra processi. Si tratta in +sostanza di uno speciale tipo di file descriptor, più precisamente una coppia +di file descriptor,\footnote{si tenga presente che le pipe sono oggetti creati + dal kernel e non risiedono su disco.} su cui da una parte si scrive e da +un'altra si legge. Si viene così a costituire un canale di comunicazione +tramite i due file descriptor, nella forma di un \textsl{tubo} (da cui il +nome) in cui in genere un processo immette dati che poi arriveranno ad un +altro. + +La funzione che permette di creare una pipe è appunto \func{pipe}; il suo +prototipo è: +\begin{prototype}{unistd.h} +{int pipe(int filedes[2])} + +Crea una coppia di file descriptor associati ad una pipe. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} potrà assumere i valori \macro{EMFILE}, + \macro{ENFILE} e \macro{EFAULT}.} +\end{prototype} + +La funzione restituisce una coppia di file descriptor nell'array +\param{filedes}; il primo aperto in lettura ed il secondo in scrittura. Il +concetto di funzionamento di una pipe è relativamente semplice, quello che si +scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale +nel file descriptor aperto in lettura, da cui può essere riletto. + +I file descriptor infatti non sono connessi a nessun file reale, ma ad un +buffer nel kernel, la cui dimensione è specificata dalla costante +\macro{PIPE\_BUF}, (vedi \secref{sec:sys_file_limits}); lo schema di +funzionamento di una pipe è illustrato in \figref{fig:ipc_pipe_singular}, in +cui sono illustrati i due capi della pipe, associati a ciascun file +descriptor, con le frecce che indicano la direzione del flusso dei dati +attraverso la pipe. + +\begin{figure}[htb] + \centering + \includegraphics[height=5cm]{img/pipe} + \caption{Schema della struttura di una 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. + +\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}.} + \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 +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. + + + +\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 +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: +\begin{verbatim} +http://www.sito.it/cgi-bin/programma?parametro +\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. + + +\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} + \end{minipage} + \normalsize + \caption{Codice del \textit{cgi-bin} \cmd{BarCode}.} + \label{fig:ipc_barcode_code} +\end{figure} + + +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-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 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. + +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. + +Alla conclusione della sua esecuzione \cmd{barcode} avrà effettuato inviato +l'immagine postscript del codice a barre sul capo in scrittura della seconda +pipe; + + + + +\subsection{Le funzioni \func{popen} e \func{pclose}} +\label{sec:ipc_popen} + +Come si è visto la modalità più comune di utilizzo di una pipe è quella di +utilizzarla per fare da tramite fra output ed input di due programmi invocati +in sequenza; per questo motivo lo standard POSIX.2 ha introdotto due funzioni +che permettono di sintetizzare queste operazioni comuni in una sola +chiamata. La prima di esse si chiama \func{popen} ed il suo prototipo è: + + +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. + + -Perché questo accada però, e questo è il principale limite nell'uso delle -\textit{pipe}, è necessario che questi processi possano condividere il file -descriptor della \textit{pipe}; per questo essi devono comunque derivare da -uno stesso processo padre, o, più comunemente, essere nella relazione -padre/figlio. \subsection{Le \textit{pipe} con nome, o \textit{fifo}} \label{sec:ipc_named_pipe} -Per poter superare il problema delle \textit{pipe} originali, che consentono -la comunicazione solo fra processi correlati, passando attraverso strutture -interne del kernel, sono stati introdotti 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. - +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. + \section{La comunicazione fra processi di System V} \label{sec:ipc_sysv} Per ovviare ai vari limiti dei meccanismo tradizionale di comunicazione fra -processi basato sulle \textit{pipe}, nello sviluppo di System V vennero -introdotti una serie di nuovi oggetti 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}. +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}. + \subsection{Code di messaggi} \label{sec:ipc_messque} Il primo oggetto introdotto dal \textit{SystemV IPC} è quello delle code di -messaggi. +messaggi. \subsection{Semafori} \label{sec:ipc_semaph}