From 7d036afbf1cd25abdb0d6b5ea78bc6dca461719a Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 25 Aug 2002 13:27:57 +0000 Subject: [PATCH 1/1] Avanti sulle message queues --- ipc.tex | 207 +++++++++++++++++++++++++++++++++++++--------------- network.tex | 1 + 2 files changed, 150 insertions(+), 58 deletions(-) diff --git a/ipc.tex b/ipc.tex index 290fe67..b5e7981 100644 --- a/ipc.tex +++ b/ipc.tex @@ -50,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 @@ -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.} @@ -694,7 +694,7 @@ Per questo motivo, dopo aver eseguito l'apertura in lettura (\texttt{\small 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. +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 @@ -707,7 +707,7 @@ 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 invaire, si procederà (\texttt{\small 42--46}) +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ù. @@ -772,8 +772,8 @@ 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, pe questo -prima si apre la fifo nota (\texttt{\small 19--23}), e poi ci si scrive +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}). @@ -845,7 +845,7 @@ 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 è un numero progressivo (un po' come il \acr{pid} dei processi) che il kernel -assegna a ciascuno di essi quanto vengono creati (sul prodedimento di +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 @@ -886,13 +886,13 @@ 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à +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 potebbe comunque salvare l'identificatore su un file noto, ma +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'è @@ -927,15 +927,15 @@ Il problema 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}. +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 haeder comune, 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 +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 @@ -997,7 +997,7 @@ Il secondo livello di controllo 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 amministatore l'accesso è sempre +\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 @@ -1028,10 +1028,10 @@ 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 comportamente fosse sempre questo sarebbe identico a quello +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 diponibili). +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 @@ -1168,7 +1168,7 @@ una 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 + \item[\macro{EACCES}] Il processo chiamante non ha i privilegi 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}. @@ -1209,45 +1209,17 @@ successo solo se l'oggetto non esiste gi Si tenga conto che l'uso di \macro{IPC\_PRIVATE} non impedisce ad altri processi di accedere alla coda (se hanno privilegi sufficienti) una volta che -questi possano indovinare o ricavare l'identificatore ad essa associato. Per -la loro implementazione non esiste cioè una maniera che garantisca l'accesso -esclusivo ad una coda di messaggi. Usare \macro{IPC\_PRIVATE} o -macro{IPC\_CREAT} e \macro{IPC\_EXCL} per \param{flag} comporta solo la -creazione di una nuova coda. - -Una coda di messaggi è costituita da una \textit{linked list}\footnote{una - \textit{linked list} è una tipica struttura di dati, organizzati in una - lista in cui ciascun elemento contiene un puntatore al successivo. In questo - modo la struttura è veloce nell'estrazione ed immissione dei dati dalle - estremità dalla lista (basta aggiungere un elemento in testa o in coda ed - aggiornare un puntatore), e relativamente veloce da attraversare in ordine - sequenziale (seguendo i puntatori), è invece relativamente lenta - nell'accesso casuale e nella ricerca.} in cui nuovi messaggi vengono -inseriti in coda per poi essere letti dalla cima, con una struttura del tipo -di quella illustrata in \secref{fig:ipc_mq_schema}.\footnote{la struttura è - effettivamente in uso fino ai kernel della serie 2.2.x. Viene mantenuta nei - kernel della serie 2.4.x, dove la gestione è effettuata con strutture - diverse, solo per compatibilità; essa comunque è quella restituita dalle - funzioni dell'interfaccia ed illustra in maniera adeguata i principi di - funzionamento delle code di messaggi.} - -\begin{figure}[htb] - \centering - \includegraphics[width=15cm]{img/mqstruct} - \caption{Schema della struttura di una coda messaggi.} - \label{fig:ipc_mq_schema} -\end{figure} - -Le code di messaggi sono caratterizzate da tre limiti fondamentali, definiti -negli header e corrispondenti alle prime tre costanti riportate in , come -accennato però in Linux è possibile modificare questi limiti attraverso l'uso -di \func{syscntl} o scrivendo nei file \file{msgmax}, \file{msgmnb} e -\file{msgmni} di \file{/proc/sys/kernel/}. - +questi possano indovinare o ricavare (ad esempio per tentativi) +l'identificatore ad essa associato. Per come sono implementati gli oggetti di +IPC infatti non esiste una maniera che garantisca l'accesso esclusivo ad una +coda di messaggi. Usare \macro{IPC\_PRIVATE} o macro{IPC\_CREAT} e +\macro{IPC\_EXCL} per \param{flag} comporta solo la creazione di una nuova +coda. \begin{table}[htb] + \footnotesize \centering - \begin{tabular}[c]{|c|c|l|l} + \begin{tabular}[c]{|c|r|l|l|} \hline \textbf{Costante} & \textbf{Valore} & \textbf{File in \texttt{proc}} & \textbf{Significato} \\ @@ -1265,8 +1237,127 @@ di \func{syscntl} o scrivendo nei file \file{msgmax}, \file{msgmnb} e \label{tab:ipc_msg_limits} \end{table} -Una volta creata una coda di messaggi se ne può controllare +Le code di messaggi sono caratterizzate da tre limiti fondamentali, definiti +negli header e corrispondenti alle prime tre costanti riportate in +\tabref{tab:ipc_msg_limits}, come accennato però in Linux è possibile +modificare questi limiti attraverso l'uso di \func{syscntl} o scrivendo nei +file \file{msgmax}, \file{msgmnb} e \file{msgmni} di \file{/proc/sys/kernel/}. + + +\begin{figure}[htb] + \centering \includegraphics[width=15cm]{img/mqstruct} + \caption{Schema della struttura di una coda messaggi.} + \label{fig:ipc_mq_schema} +\end{figure} + + +Una coda di messaggi è costituita da una \textit{linked list};\footnote{una + \textit{linked list} è una tipica struttura di dati, organizzati in una + lista in cui ciascun elemento contiene un puntatore al successivo. In questo + modo la struttura è veloce nell'estrazione ed immissione dei dati dalle + estremità dalla lista (basta aggiungere un elemento in testa o in coda ed + aggiornare un puntatore), e relativamente veloce da attraversare in ordine + sequenziale (seguendo i puntatori), è invece relativamente lenta + nell'accesso casuale e nella ricerca.} i nuovi messaggi vengono inseriti in +coda alla lista e vengono letti dalla cima, in \figref{fig:ipc_mq_schema} si è +riportato lo schema con cui queste strutture vengono mantenute dal +kernel.\footnote{lo schema illustrato in figura è in realtà una semplificazione + di quanto usato fino ai kernel della serie 2.2.x, nei kernel della serie + 2.4.x la gestione è effettuata in maniera diversa; ma esso illustra comunque + in maniera adeguata i principi di funzionamento delle code di messaggi.} + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0]{} +struct msqid_ds { + struct ipc_perm msg_perm; /* structure for operation permission */ + time_t msg_stime; /* time of last msgsnd command */ + time_t msg_rtime; /* time of last msgrcv command */ + time_t msg_ctime; /* time of last change */ + unsigned long int msg_cbytes; /* current number of bytes on queue */ + msgqnum_t msg_qnum; /* number of messages currently on queue */ + msglen_t msg_qbytes; /* max number of bytes allowed on queue */ + pid_t msg_lspid; /* pid of last msgsnd() */ + pid_t msg_lrpid; /* pid of last msgrcv() */ + struct msg *msg_first; /* first message on queue, unused */ + struct msg *msg_last; /* last message in queue, unused */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{msgid\_ds}, associata a ciascuna coda di + messaggi. Si sono riportati i campi significativi definiti in + \file{sys/msg.h}, aggiungendo anche due campi interni, non visibili in + user space.} + \label{fig:ipc_msgid_sd} +\end{figure} + +A ciascuna coda è associata una struttura \var{msgid\_ds}, la cui definizione +è riportata in \secref{fig:ipc_msgid_sd}, il significato dei vari campi è +riportato nella figura. In questa struttura il kernel\footnote{come accennato + questo vale fino ai kernel della serie 2.2.x, essa viene usata nei kernel + della serie 2.4.x solo per compatibilità in quanto è quella restituita dalle + funzioni dell'interfaccia.} mantiene le principali informazioni riguardo lo +stato corrente della coda. Quando si crea una nuova coda con \func{msgget} +questa struttura viene inizializzata, in particolare il campo \var{msg\_perm} +viene inizializzato come illustrato in \secref{sec:ipc_sysv_access_control}, +per quanto riguarda gli altri campi invece: +\begin{itemize} +\item i campi \var{msg\_qnum}, \var{msg\_lspid}, \var{msg\_lrpid}, + \var{msg\_stime}, \var{msg\_rtime} sono inizializzati a 0 +\item il campo \var{msg\_ctime} viene settato al tempo corrente +\item il campo \var{msg\_qbytes} al limite di sistema. +\item i campi \var{msg\_first} e \var{msg\_last} (che non vengono visti in + user space) sono inizializzati a \macro{NULL} essendo la coda vuota. +\end{itemize} + +Una volta creata una coda di messaggi le operazioni di controllo vengono +effettuate con la funzione \func{msgctl}, che (come le analoghe \func{semctl} +e \func{shmctl}) fa le veci di quello che \func{ioctl} è per i file; il suo +prototipo è: +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/ipc.h} + \headdecl{sys/msg.h} + + \funcdecl{int msgctl(int msqid, int cmd, struct msqid\_ds *buf)} + + Esegue l'operazione specificata da \param{cmd} sulla coda \param{msqid}. + + \bodydesc{La funzione restituisce 0 in caso di successo o -1 in caso di + errore, nel qual caso \var{errno} viene settato a: + \begin{errlist} + \item[\macro{EACCES}] Si è richiesto \macro{IPC\_STAT} ma processo chiamante + non ha i privilegi di lettura sulla coda. + \item[\macro{EIDRM}] La coda richiesta è marcata per essere cancellata. + \item[\macro{EPERM}] Si è richiesto \macro{IPC\_SET} o \macro{IPC\_RMID} ma + il processo non ha i privilegi, o si è richiesto di aumentare il valore di + \var{msg\_qbytes} oltre il limite \macro{MSGMNB} senza essere + amministratore. + \end{errlist} + ed inoltre \macro{EFAULT} ed \macro{EINVAL}. +} +\end{functions} +La funzione permette di accedere ai valori della struttura \var{msqid\_ds}, +mantenuta all'indirizzo \param{buf}, per la coda specificata +dall'identificatore \param{msqid}. Il comportamento della funzione dipende dal +valore dell'argomento \param{cmd}, che specifica il tipo di azione da +eseguire; i valori possibili sono: +\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}} +\item[\macro{IPC\_STAT}] Legge le informazioni riguardo la coda nella + struttura indicata da \param{buf}. Occorre avere il permesso di lettura + sulla coda. +\item[\macro{IPC\_RMID}] Rimuove la coda, cancellando tutti i dati, con + effetto immediato. Tutti i processi che cercheranno di accedere alla coda + riceveranno un errore di \macro{EIDRM}, e tutti processi in attesa su + funzioni di di lettura o di scrittura sulla coda saranno svegliati ricevendo + il medesimo errore. Questo comando può essere eseguito solo da un processo + con userid effettivo corrispondente al creatore a o al proprietario della + coda, o all'amministratore. +\item[\macro{IPC\_SET}] +\end{basedescript} \subsection{Semafori} @@ -1293,7 +1384,7 @@ risorse condivise. La funzione che permette di ottenere un insieme di semafori 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 +specificare quanti semafori 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. diff --git a/network.tex b/network.tex index cb17b81..e59115e 100644 --- a/network.tex +++ b/network.tex @@ -265,6 +265,7 @@ nella maggior parte delle applicazioni. \subsection{Il quadro generale} +\label{sec:net_tcpip_general} Benché si parli di TCP/IP questa famiglia di protocolli è composta anche da altri membri. In \nfig\ si è riportato uno schema che mostra un panorama sui -- 2.30.2