From: Simone Piccardi Date: Thu, 15 Aug 2002 23:27:11 +0000 (+0000) Subject: Sistemata introduzione IPC e message queues X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=dab4fcffe459c15bf2a92a86ac7145200fe19318 Sistemata introduzione IPC e message queues --- diff --git a/ipc.tex b/ipc.tex index 3bf821c..53e51b9 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} @@ -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 @@ -583,9 +583,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 quello che viene ormai chiamato il +\textsl{Sistema di comunicazione inter-processo} di System V, o \textit{System V IPC (Inter-Process Comunication)}. @@ -604,38 +604,239 @@ fino al riavvio del sistema. Gli oggetti usati nel System V IPC vengono creati direttamente dal kernel, e sono accessibili solo specificando il relativo \textsl{identificatore}. Questo -è il numero progressivo che il kernel assengna a ciascuno di questi oggetti -quanto vengono creati (il prodedimento è simile a quello con cui si assegna il -\acr{pid} ai processi). +è il numero progressivo che il kernel assengna a ciascuno di essi quanto +vengono creati (il prodedimento è simile a quello con cui si assegna il +\acr{pid} ai processi). L'identificatore viene restituito dalle funzioni che +creano l'oggetto, ed è quindi locale al processo che le ha eseguite. Dato che +l'identificatore viene assegnato dinamicamente dal kernel non è possibile +prevedere quale sarà, ne utilizzare un qualche valore statico, si pone perciò +il problema di come processi diversi possono accedere allo stesso oggetto. + +Per risolvere il problema il kernel associa a ciascun oggetto una struttura +\var{ipc\_perm}; questa contiene una \textsl{chiave}, identificata da una +variabile del tipo primitivo \type{key\_t}, che viene specificata in fase di +creazione e tramite la quale è possibile ricavare l'identificatore. La +struttura, la cui definizione è riportata in \figref{fig:ipc_ipc_perm}, +contiene anche le varie proprietà associate all'oggetto, come gli +identificatori del creatore (\var{cuid} e \var{cgid}) e del proprietario +(\var{uid} e \var{gid}), e le modalità di accesso (\var{mode}) che ne +specifica i permessi, che sono analoghi a quelli usati per i file (vedi +\secref{sec:file_perm_overview}). -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'idenficatore 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 potebbe 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} viene settata ad 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} + han modificato il prototipo, ma vengono lo stesso utilizzati gli 8 bit meno + significativi.} + +Il problema è che anche così non c'è la sicurezza che il valore della chiave +sia univoco, infatti esso è costruito combinando il byte di \param{proj\_id)} +con i 16 bit meno significativi dell'inode del file \param{pathname} (che +vengono ottenuti attraverso \func{stat}, da cui derivano i possibili errori), +e gli 8 bit meno significativi del numero del device su cui è il file. Diventa +perciò relativamente facile ottenere delle collisioni, specie se i file sono +su dispositivi con lo stesso \textit{minor number}, come \file{/dev/hda1} e +\file{/dev/sda1}. + +In genere quello che si fa è utilizzare un file comune usato dai programmi che +devono comunicare (ad esempio un haeder, o uno dei programmi che devono usare +l'oggetto in questione), utilizzando il numero di progetto per ottere le +chiavi che interessano. In ogni caso occorre sempre controllare, prima di +creare un oggetto, che la chiave non sia già stata utilizzata. Se questo va +bene in fase di creazione, le cose possono complicarsi per i programmi che +devono solo accedere, in quanto, a parte gli eventuali controlli sugli altri +attributi di \var{ipc\_perm}, non esiste una modalità semplice per essere +sicuri della validità di una certa chiave. + +Questo è, insieme al fatto che gli oggetti sono permanenti e devono essere +cancellati esplicitamente, il principale problema del sistema di IPC di System +V. Non esiste infatti una modalità chiara per identificare un oggetto, come +sarebbe stato se lo si fosse associato ad in file, e tutta l'interfaccia è +inutilmente complessa. Per questo ne è stata effettuata una revisione +completa nello standard POSIX.1b, che tratteremo in \secref{sec:ipc_posix}. \subsection{Code di messaggi} -\label{sec:ipc_messque} +\label{sec:ipc_sysv_mq} Il primo oggetto introdotto dal \textit{System V IPC} è quello delle code di -messaggi. +messaggi. Le code di messaggi sono oggetti analoghi alle pipe o alle fifo, +anche se la loro struttura è diversa. La funzione che permette di ottenerne +una è \func{msgget} ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{int msgget(key\_t key, int flag)} + + Restituisce l'identificatore di una cosa di messaggi. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} viene settato ad uno dei + valori: + \begin{errlist} + \item[\macro{EACCES}] Il processo chiamante non ha i provilegi per accedere + alla coda richiesta. + \item[\macro{EEXIST}] Si è richiesta la creazione di una coda che già + esiste, ma erano specificati sia \macro{IPC\_CREAT} che \macro{IPC\_EXCL}. + \item[\macro{EIDRM}] La coda richiesta è marcata per essere cancellata. + \item[\macro{ENOENT}] Si è cercato di ottenere l'identificatore di una coda + di messaggi specificando una chiave che non esiste e \macro{IPC\_CREAT} + non era specificato. + \item[\macro{ENOSPC}] Si è cercato di creare una coda di messaggi quando è + stato il limite massimo del sistema. + \end{errlist} + ed inoltre \macro{ENOMEM}. +} +\end{functions} + +Le funzione (come le analoghe che si usano per gli altri oggetti) serve sia a +ottenere l'identificatore di una coda di messaggi esistente, che a crearne una +nuova. L'argomento \param{key} specifica la chiave che è associata +all'oggetto, eccetto il caso in cui si specifichi il valore +\macro{IPC\_PRIVATE}, nel qual caso la coda è creata ex-novo e non vi è +associata alcuna chiave. + +Se invece si specifica un valore l'effetto della funzione dipende dal valore +di \param{flag}, se questo è nullo la funzione si limita ad effettuare una +ricerca sugli oggetti esistenti, restituendo l'identificatore se trova una +corrispondenza, o fallendo con un errore di \macro{ENOENT} altrimenti. + +Se invece si vuole creare una nuova coda di messaggi \param{flag} non può +essere nullo e deve essere fornito come maschera binaria, impostando il bit +corrispondente al valore \macro{IPC\_CREAT}. In questo caso i nove bit meno +significativi di \param{flag} saranno usati come permessi per il nuovo +oggetto, con gli stessi valori e significati di quelli riportati in +\secref{tab:file_mode_flags}.\footnote{per usare i quali però occorrerà + includere il file \file{sys/stat.h}.} Se si imposta anche il bit +corrispondente a \macro{IPC\_EXCL} la funzione avrà successo solo se l'oggetto +non esiste già, fallendo con un errore di \macro{EEXIST} altrimenti. + +Si tenga presente che per gli oggetti di IPC han senso solo i permessi di +lettura e scrittura, quelli di esecuzione vengono ignorati. Quando l'oggetto +viene creato i campi \var{cuid} e \var{uid} di \var{ipc\_perm} ed i campi +\var{cgid} e \var{gid} vengono settati rispettivamente al valore dell'userid e +del groupid effettivo del processo che ha chiamato la funzione. + + + + +Esse sono infatti costituite da una \textit{linked list} in cui nuovi messaggi +vengono inseriti in coda e letti dalla cima. + + + \subsection{Semafori} -\label{sec:ipc_semaph} +\label{sec:ipc_sysv_sem} Il secondo oggetto introdotto dal \textit{System V IPC} è quello dei semafori. +Un semaforo è uno speciale contatore che permette di controllare l'accesso a +risorse condivise. La funzione che permette di ottenere un insieme di semafori +è \func{semget} ed il suo prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/sem.h} + + \funcdecl{int semget(key\_t key, int nsems, int flag)} + + Restituisce l'identificatore di un semaforo. + + \bodydesc{La funzione restituisce l'identificatore (un intero positivo) o -1 + in caso di errore, nel qual caso \var{errno} viene settato agli stessi + valori visti per \func{msgget}.} +\end{functions} + +La funzione è del tutto analoga a \func{msgget} ed identico è l'uso degli +argomenti \param{key} e \param{flag}, per cui non ripeteremo quanto detto al +proposito in \secref{sec:ipc_sysv_mq}. L'argomento \param{nsems} permette di +specificare quanti semfori deve contenere l'insieme qualora se ne richieda la +creazione, e deve essere nullo quando si effettua una richiesta +dell'identificatore di un insieme già esistente. + \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} viene settato agli stessi + valori visti per \func{msgget}.} +\end{functions} +La funzione, come per \func{semget}, è del tutto analoga a \func{msgget}, ed +identico è l'uso degli argomenti \param{key} e \param{flag}. L'argomento @@ -645,7 +846,7 @@ 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}.