X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=6ae11a30a8e157e0f0d181583034dcb47f7489c9;hp=3bf821c56c6da3fb19dcf4028754f089f0b0c4f1;hb=6c8d59152cff88b5835eeb749445148bb3546a5b;hpb=6a27b6ddf1f27d332f1de65b7c4ac0e1293c2174 diff --git a/ipc.tex b/ipc.tex index 3bf821c..6ae11a3 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 @@ -556,20 +556,127 @@ 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'occazione. + +\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}{} +int main(int argc, char *argv[]) +{ + int i, n = 10; + char *fortunefilename = "/usr/share/games/fortunes/kids"; + char *fifoname = "/tmp/fortune.fifo"; + char **fortune; + char line[80]; + int fifo_server, fifo_client; + int nread; + ... + if (n==0) usage(); /* if no pool depth exit printing usage info */ + i = FortuneParse(fortunefilename, fortune, n); /* parse phrases */ + /* + * Comunication section + */ + if (mkfifo(fifoname, 0622)) { /* create well known fifo if does't exist */ + if (errno!=EEXIST) { + perror("Cannot create well known fifo"); + exit(-1); + } + } + while (1) { + fifo_server = open(fifoname, O_RDONLY); /* open well known fifo */ + if (fifo_server < 0) { + perror("Cannot open well known fifo"); + exit(-1); + } + nread = read(fifo_server, line, 79); /* read request */ + line[nread] = 0; + n = random() % i; /* select random value */ + fifo_client = open(line, O_WRONLY); /* open client fifo */ + nread = write(fifo_client, /* write phrase */ + fortune[n], strlen(fortune[n])+1); + close(fifo_client); /* close well known fifo */ + close(fifo_server); /* 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 11}) che sia stata impostata una dimensione +dell'insieme delle frasi non nulla, stampando, nel caso ciò non avvenga, un +messaggio apposito ed uscendo. Poi (\texttt{\small 12}) effettua la chiamata +alla funzione \code{FortuneParse} che legge dal file specificato in +\var{fortunefilename} le prime \var{n} frasi e le memorizza nel vettore di +puntatori \var{fortune}. Il codice della funzione non è riportato, in quanto +non direttamente attinente allo scopo dell'esempio, lo si può trovare nel file +\file{FortuneParse.c} allegato coi sorgenti degli esempi. + +Il passo successivo \texttt{\small 16--21}) è quello di creare, se non esiste +già, 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. + +Fatto questo si entra nel ciclo principale del programma \texttt{\small + 22--36}), che viene eseguito indefinitamente (l'uscita del server deve +essere effettuata tramite segnale), e che provvede a fornire le risposte ai +client. Il server è progettato per accettare le richieste dai client che +devono scrivere il nome della fifo sulla quale vogliono ricevere la risposta +sulla fifo su cui il server è in ascolto. + +Il primo passo è aprire in lettura la fifo (\texttt{\small 23}), se nessun +client ha effettuato una richiesta la fifo non ha capi aperti in scrittura, e +pertanto il server si bloccherà. Una volta che un client ha aperto la fifo in +scrittura il server si sbloccherà ed effetturà la lettura (\texttt{\small 28}) +della richiesta del client (nel caso limitata a 79 byte). + +Dopo di che verrà calcolato (\texttt{\small 30}) un numero casuale nel range +delle frasi disponibili nella nostra lista, e verrà aperta (\texttt{\small + 31}) la fifo sulla quale il client vuole ricevere la risposta, che sarà poi +scritta (\texttt{\small 32}). Dopo di che \texttt{\small 34--35}) entrambe le +fifo verranno chiuse. + + +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, 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. @@ -583,9 +690,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 +704,449 @@ 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 prodedimento 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. + +\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} -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. +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 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, simile +a quello che si ha per i file (vedi \secref{sec:file_perm_overview}). + +Benché il controllo di accesso relativo agli oggetti di intercomunicazione sia +molto simile a quello dei file, restano delle importanti differenze. La prima +è che il permesso di esecuzione non esiste (e viene ignorato), per cui si può +parlare solo di permessi di lettura e scrittura (nel caso dei semafori poi +quest'ultimo è più propriamente il permesso di modificarne lo stato). 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. + +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, ma mentre i +campi \var{uid} e \var{gid} possono essere cambiati, \var{cuid} e \var{cgid} +restano sempre gli stessi. + +Il controllo di accesso è effettuato a due livelli. Il primo è nelle funzioni +che richiedono l'identificatore di un oggetto data la chiave, che specificano +tutte un argomento \param{flag}. In tal caso quando viene effettuata la +ricerca di una chiave, se \param{flag} specifica dei permessi, questi vengono +controllati e l'identificatore viene restituito solo se essi corrispondono a +quelli dell'oggetto. Se sono presenti dei permessi non presenti in \var{mode} +l'accesso sarà invece negato. Questo però è di utilità indicativa, dato che è +sempre possibile specificare un valore nullo per \param{flag}, nel qual caso +il controllo avrà sempre successo. + +Il secondo livello è quello delle varie funzioni che accedono (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 + 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'altra differenza è +che per gli oggetti di IPC il valore di \var{umask} (si ricordi quanto esposto +in \secref{sec:file_umask}) non ha alcun effetto. + + +\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 +``\textit{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 comportamente 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 ed inferiori al numero massimo di oggetti diponibili. + +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 haeder 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 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. + +In \figref{fig:ipc_sysv_idtest} è riportato il codice di un semplice programma +di test che si limita a creare un oggetto (specificato a riga di comando), +stamparne il numero di identificatore e cancellarlo per un numero specificato +di volte. + +\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