From 9aeb46d93d970f26f1939d3853e4a9e62b2eb5b9 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 1 Mar 2002 18:47:21 +0000 Subject: [PATCH] Integrate una altra serie di correzioni e riscritte parti coi suggerimenti di Alessio e Daniele. Messa pure una sezione ringraziamenti, e aggiornato il ChangeLog, per dare il relativo credito ai contributors ... --- ChangeLog | 14 +++ fileintro.tex | 38 ++++---- gapil.tex | 1 + ipprot.tex | 11 ++- process.tex | 47 ++++++---- prochand.tex | 227 +++++++++++++++++++++++---------------------- ringraziamenti.tex | 18 ++++ 7 files changed, 202 insertions(+), 154 deletions(-) create mode 100644 ringraziamenti.tex diff --git a/ChangeLog b/ChangeLog index f1a6a7b..8228743 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2002-03-01 Simone Piccardi + + * prochand.tex: correzioni varie da D. Masini. + + * process.tex: Spiegazione delle calling convention per le + pulitura dello stack da parte delle funzioni, contributo di + D. Masini, chiarimenti sui memory leak secondo quanto suggerito da + A. Frusciante. + +2002-02-28 Simone Piccardi + + * prochand.tex: un altro blocco di correzioni suggerite da Daniele + Masini. + 2002-02-27 Simone Piccardi * pref.tex, intro.tex, process.tex, prochand.tex: Correzioni diff --git a/fileintro.tex b/fileintro.tex index 40d8e25..15e2fd8 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -90,10 +90,11 @@ risoluzione del nome (\textit{file name resolution} o \textit{pathname resolution}). La risoluzione viene fatta esaminando il \textit{pathname} da sinistra a destra e localizzando ogni nome nella directory indicata dal nome precedente usando \file{/} come separatore\footnote{nel caso di nome vuoto, il - costrutto \file{//} viene considerato equivalente a \file{/}.}: ovviamente -perché il procedimento funzioni occorre che i nomi indicati come directory + costrutto \file{//} viene considerato equivalente a \file{/}.}: ovviamente, +perché il procedimento funzioni, occorre che i nomi indicati come directory esistano e siano effettivamente directory, inoltre i permessi (si veda -\secref{sec:file_access_control}) devono consentire l'accesso. +\secref{sec:file_access_control}) devono consentire l'accesso all'intero +\textit{pathname}. Se il \textit{pathname} comincia per \file{/} la ricerca parte dalla directory radice del processo; questa, a meno di un \func{chroot} (su cui torneremo in @@ -105,16 +106,16 @@ parte dalla directory corrente (su cui torneremo in relativo}\index{pathname relativo}. I nomi \file{.} e \file{..} hanno un significato speciale e vengono inseriti -in ogni directory, il primo fa riferimento alla directory corrente e il +in ogni directory: il primo fa riferimento alla directory corrente e il secondo alla directory \textsl{genitrice} (o \textit{parent directory}) cioè la directory che contiene il riferimento alla directory corrente; nel caso -questa sia la directory radice allora il riferimento è a se stessa. +questa sia la directory radice, allora il riferimento è a se stessa. \subsection{I tipi di file} \label{sec:file_file_types} -Come detto in precedenza in Unix esistono vari tipi di file, in Linux questi +Come detto in precedenza in Unix esistono vari tipi di file; in Linux questi sono implementati come oggetti del \textit{Virtual File System} (vedi \secref{sec:file_vfs_work}) e sono presenti in tutti i filesystem unix-like utilizzabili con Linux. L'elenco dei vari tipi di file definiti dal @@ -143,10 +144,10 @@ dati) in base al loro contenuto, o tipo di accesso. un file che identifica una periferica ad accesso sequenziale \\ \textit{block device} & \textsl{dispositivo a blocchi} & un file che identifica una periferica ad accesso diretto \\ - \textit{fifo} & \textsl{``tubo''} & + \textit{fifo} & \textsl{``coda''} & un file speciale che identifica una linea di comunicazione software (unidirezionale) \\ - \textit{socket} & \textsl{``spina''} & + \textit{socket} & \textsl{``presa''} & un file speciale che identifica una linea di comunicazione software (bidirezionale) \\ \hline @@ -160,27 +161,28 @@ VMS o Windows) un flusso continuo di byte. Non esiste cioè differenza per come vengono visti dal sistema file di diverso contenuto o formato (come nel caso di quella fra file di testo e binari che c'è in Windows) né c'è una strutturazione a record -per il cosiddetto ``accesso diretto'' come nel caso del VMS\footnote{con i +per il cosiddetto ``accesso diretto'' come nel caso del VMS.\footnote{con i kernel della serie 2.4 è disponibile una forma di accesso diretto ai dischi (il \textit{raw access}) attraverso dei device file appositi, che però non - ha nulla a che fare con questo}. + ha nulla a che fare con questo.} Una seconda differenza è nel formato dei file ASCII; in Unix la fine riga è -codificata in maniera diversa da Windows o Mac, in particolare il fine -riga è il carattere \texttt{LF} (o \verb|\n|) al posto del \texttt{CR} -(\verb|\r|) del Mac e del \texttt{CR LF} di Windows. Questo può causare alcuni +codificata in maniera diversa da Windows o Mac, in particolare il fine riga è +il carattere \texttt{LF} (o \verb|\n|) al posto del \texttt{CR} (\verb|\r|) +del Mac e del \texttt{CR LF} di Windows.\footnote{per questo esistono in Linux + dei programmi come \cmd{unix2dos} e \cmd{dos2unix} che effettuano una + conversione fra questi due formati di testo.} Questo può causare alcuni problemi qualora nei programmi si facciano assunzioni sul terminatore della riga. Si ricordi infine che in ambiente Unix non esistono tipizzazioni dei file di dati e che non c'è nessun supporto del sistema per le estensioni come parte -del filesystem. Ciò non ostante molti programmi adottano delle convenzioni per +del filesystem. Ciò nonostante molti programmi adottano delle convenzioni per i nomi dei file, ad esempio il codice C normalmente si mette in file con l'estensione \file{.c}, ma questa è, per quanto usata ed accettata in maniera universale, solo una convenzione. - \subsection{Le due interfacce ai file} \label{sec:file_io_api} @@ -203,21 +205,21 @@ rappresentati da numeri interi (cio L'interfaccia è definita nell'header \file{unistd.h}. La seconda interfaccia è quella che il manuale della \acr{glibc} chiama degli -\textit{stream}\index{stream}, essa provvede funzioni più evolute e un accesso +\textit{stream}\index{stream}. Essa provvede funzioni più evolute e un accesso bufferizzato (controllato dalla implementazione fatta dalle \acr{glibc}), la tratteremo in dettaglio nel \capref{cha:files_std_interface}. Questa è l'interfaccia standard specificata dall'ANSI C e perciò si trova anche su tutti i sistemi non Unix. Gli \textit{stream} sono oggetti complessi e sono rappresentati da puntatori ad un opportuna struttura definita dalle -librerie del C, si accede ad essi sempre in maniera indiretta utilizzando il +librerie del C; si accede ad essi sempre in maniera indiretta utilizzando il tipo \type{FILE *}. L'interfaccia è definita nell'header \type{stdio.h}. Entrambe le interfacce possono essere usate per l'accesso ai file come agli altri oggetti del VFS (pipe, socket, device, sui quali torneremo in dettaglio a tempo opportuno), ma per poter accedere alle operazioni di controllo su un qualunque tipo di oggetto del VFS occorre usare l'interfaccia standard di Unix -coi \textit{file descriptor}. Allo stesso modo devono essere usati i +con i \textit{file descriptor}. Allo stesso modo devono essere usati i \textit{file descriptor} se si vuole ricorrere a modalità speciali di I/O come il polling o il non-bloccante (vedi \capref{cha:file_advanced}). diff --git a/gapil.tex b/gapil.tex index 25211c9..36fc3c7 100644 --- a/gapil.tex +++ b/gapil.tex @@ -125,6 +125,7 @@ \include{ipprot} \include{tcpprot} \include{errors} +\include{ringraziamenti} \include{fdl} % at the end put the bibliography diff --git a/ipprot.tex b/ipprot.tex index 4618a30..11954fa 100644 --- a/ipprot.tex +++ b/ipprot.tex @@ -752,11 +752,12 @@ prima di avere un indirizzo globale. \end{table} Ci sono due tipi di indirizzi, \textit{link-local} e \textit{site-local}. Il -primo è usato per un singolo link; la struttura è mostrata in \curtab, questi -indirizzi iniziano sempre per \texttt{FE80} e vengono in genere usati per la -configurazione automatica dell'indirizzo al bootstrap e per la ricerca dei -vicini (vedi \ref{sec:IP_ipv6_autoconf}); un pacchetto che abbia tale -indirizzo come sorgente o destinazione non deve venire ritrasmesso dai router. +primo è usato per un singolo link; la struttura è mostrata in +\tabref{tab:IP_ipv6_linklocal}, questi indirizzi iniziano sempre per +\texttt{FE80} e vengono in genere usati per la configurazione automatica +dell'indirizzo al bootstrap e per la ricerca dei vicini (vedi +\ref{sec:IP_ipv6_autoconf}); un pacchetto che abbia tale indirizzo come +sorgente o destinazione non deve venire ritrasmesso dai router. Un indirizzo \textit{site-local} invece è usato per l'indirizzamento all'interno di un sito che non necessita di un prefisso globale; la struttura diff --git a/process.tex b/process.tex index 935f34e..483fbb9 100644 --- a/process.tex +++ b/process.tex @@ -380,7 +380,10 @@ programma C viene suddiviso nei seguenti segmenti: del chiamante (tipo il contenuto di alcuni registri della CPU). Poi la funzione chiamata alloca qui lo spazio per le sue variabili locali: in questo modo le funzioni possono essere chiamate ricorsivamente. Al ritorno - della funzione lo spazio è automaticamente rilasciato. + della funzione lo spazio è automaticamente rilasciato. Al ritorno della + funzione lo spazio è automaticamente ripulito. La pulizia in C e C++ viene + fatta dal chiamante.\footnote{a meno che non sia stato specificato + l'utilizzo di una calling convention diversa da quella standard.} La dimensione di questo segmento aumenta seguendo la crescita dello stack del programma, ma non viene ridotta quando quest'ultimo si restringe. @@ -538,15 +541,18 @@ routine di allocazione non più utilizzata, quello che in inglese viene chiamato \textit{memory-leak}, (cioè \textsl{perdita di memoria}). -Un caso tipico che illustra il problema è quello in cui l'allocazione di una -variabile viene fatta da una subroutine per un uso locale, ma la memoria non -viene liberata; la funzione esce e la memoria resta allocata (fino alla -terminazione del processo). Chiamate ripetute alla stessa subroutine -continueranno ad allocarne ancora, causando a lungo andare un esaurimento -della memoria disponibile e l'impossibilità di proseguire il programma. Il -problema è che l'esaurimento che può avvenire in qualunque momento, e senza -nessuna relazione con la subroutine che contiene l'errore, per questo motivo è -sempre complesso trovare un \textit{memory leak}. +Un caso tipico che illustra il problema è quello in cui in una subroutine si +alloca della memoria per uso locale senza liberarla prima di uscire. La +memoria resta così allocata fino alla terminazione del processo. Chiamate +ripetute alla stessa subroutine continueranno ad effettuare altre allocazioni, +causando a lungo andare un esaurimento della memoria disponibile (e la +probabile l'impossibilità di proseguire l'esecuzione programma). + +Il problema è che l'esaurimento della memoria può avvenire in qualunque +momento, in corrispondenza ad una qualunque chiamata di \func{malloc}, che può +essere in una sezione del codice che non ha alcuna relazione con la subroutine +che contiene l'errore. Per questo motivo è sempre molto difficile trovare un +\textit{memory leak}. Per ovviare a questi problemi l'implementazione delle routine di allocazione delle \acr{glibc} mette a disposizione una serie di funzionalità (su cui @@ -1173,7 +1179,7 @@ problematiche generali che possono emergere nella programmazione e di quali precauzioni o accorgimenti occorre prendere per risolverle. Queste problematiche non sono specifiche di sistemi unix-like o multitasking, ma avendo trattato in questo capitolo il comportamento dei processi visti come -entità a se stanti, le riportiamo qui. +entità a sé stanti, le riportiamo qui. \subsection{Il passaggio delle variabili e dei valori di ritorno} @@ -1258,8 +1264,8 @@ inoltre che l'ultimo degli argomenti fissi sia di tipo per compatibilità; ad esempio i tipi \type{float} vengono convertiti automaticamente a \type{double} ed i \type{char} e gli \type{short} ad \type{int}. Un tipo \textit{self-promoting} è un tipo che verrebbe promosso - a se stesso.} il che esclude array, puntatori a funzioni e interi di tipo -\type{char} o \type{short} (con segno o meno). Un'ulteriore restrizione di + a sé stesso.} il che esclude array, puntatori a funzioni e interi di tipo +\type{char} o \type{short} (con segno o meno). Una restrizione ulteriore di alcuni compilatori è di non dichiarare l'ultimo parametro fisso come \type{register}. @@ -1284,7 +1290,7 @@ in generale potrebbero essere stati effettivamente forniti, e nella esecuzione delle \macro{va\_arg} ci si può fermare in qualunque momento ed i restanti argomenti saranno ignorati; se invece si richiedono più argomenti di quelli forniti si -otterranno dei valori indefiniti. Nel caso del \cmd{gcc} poi l'uso della macro +otterranno dei valori indefiniti. Nel caso del \cmd{gcc} l'uso della macro \macro{va\_end} è inutile, ma si consiglia di usarlo ugualmente per compatibilità. @@ -1334,7 +1340,7 @@ motivo \macro{va\_list} direttamente ad un altra variabile dello stesso tipo. Per risolvere questo problema lo standard ISO C99\footnote{alcuni sistemi che non hanno questa macro provvedono al suo posto \macro{\_\_va\_copy} che era il nome proposto - in una bozza dello standard} ha previsto un'ulteriore macro che permette di + in una bozza dello standard} ha previsto una macro ulteriore che permette di eseguire la copia di un puntatore alla lista degli argomenti: \begin{prototype}{stdarg.h}{void va\_copy(va\_list dest, va\_list src)} Copia l'attuale valore \param{src} del puntatore alla lista degli argomenti @@ -1395,10 +1401,12 @@ dinamicamente con una delle funzioni della famiglia \func{malloc}. \label{sec:proc_longjmp} Il controllo del flusso di un programma in genere viene effettuato con le -varie istruzioni del linguaggio C, la più bistrattata delle quali è il -\code{goto}, ampiamente deprecato in favore di costrutti più puliti; esiste -però un caso in l'uso di questa istruzione porta all'implementazione più -efficiente, quello dell'uscita in caso di errore. +varie istruzioni del linguaggio C; fra queste la più bistrattata è il +\code{goto}, che viene deprecato in favore dei costrutti della programmazione +strutturata, che rendono il codice più leggibile e mantenibile . Esiste però +un caso in cui l'uso di questa istruzione porta all'implementazione più +efficiente e chiara anche dal punto di vista della struttura del programma, +quello dell'uscita in caso di errore. Il C però non consente di effettuare un salto ad una label definita in un'altra funzione, per cui se l'errore avviene in funzioni profondamente @@ -1406,7 +1414,6 @@ annidate occorre usare quello che viene chiamato un salto \textsl{non-locale}; questo viene fatto usando salvando il contesto dello stack nel punto in cui si vuole tornare in caso di errore, e ripristinandolo quando l'occorrenza capita. - La funzione che permette di salvare il contesto dello stack è \func{setjmp}, il cui prototipo è: diff --git a/prochand.tex b/prochand.tex index dcad876..38a9b2f 100644 --- a/prochand.tex +++ b/prochand.tex @@ -14,13 +14,12 @@ finale introdurremo alcune problematiche generiche della programmazione in ambiente multitasking. - \section{Introduzione} \label{sec:proc_gen} -Inizieremo con una introduzione generale ai concetti che stanno alla base -della gestione dei processi in un sistema unix-like. Introdurremo in questa -sezione l'architettura della gestione dei processi e le sue principali +Inizieremo con un'introduzione generale ai concetti che stanno alla base della +gestione dei processi in un sistema unix-like. Introdurremo in questa sezione +l'architettura della gestione dei processi e le sue principali caratteristiche, dando una panoramica sull'uso delle principali funzioni di gestione. @@ -37,9 +36,9 @@ numero unico, il cosiddetto \textit{process identifier} o, pi \acr{pid}. Una seconda caratteristica di un sistema Unix è che la generazione di un -processo è una operazione separata rispetto al lancio di un programma. In +processo è un'operazione separata rispetto al lancio di un programma. In genere la sequenza è sempre quella di creare un nuovo processo, il quale -eseguirà, in un passo successivo, il programma voluto: questo è ad esempio +eseguirà, in un passo successivo, il programma desiderato: questo è ad esempio quello che fa la shell quando mette in esecuzione il programma che gli indichiamo nella linea di comando. @@ -107,10 +106,10 @@ Dato che tutti i processi attivi nel sistema sono comunque generati da vero, in Linux ci sono alcuni processi che pur comparendo come figli di init, o con \acr{pid} successivi, sono in realtà generati direttamente dal kernel, (come \cmd{keventd}, \cmd{kswapd}, etc.)} si possono classificare i -processi con la relazione padre/figlio in una organizzazione gerarchica ad +processi con la relazione padre/figlio in un'organizzazione gerarchica ad albero, in maniera analoga a come i file sono organizzati in un albero di directory (si veda \secref{sec:file_organization}); in \curfig\ si è mostrato -il risultato del comando \cmd{pstree} che permette di mostrare questa +il risultato del comando \cmd{pstree} che permette di visualizzare questa struttura, alla cui base c'è \cmd{init} che è progenitore di tutti gli altri processi. @@ -162,7 +161,7 @@ figlio sono affrontate in dettaglio in \secref{sec:proc_fork}). Se si vuole che il processo padre si fermi fino alla conclusione del processo figlio questo deve essere specificato subito dopo la \func{fork} chiamando la funzione \func{wait} o la funzione \func{waitpid} (si veda -\secref{sec:proc_wait}); queste funzioni restituiscono anche una informazione +\secref{sec:proc_wait}); queste funzioni restituiscono anche un'informazione abbastanza limitata sulle cause della terminazione del processo figlio. Quando un processo ha concluso il suo compito o ha incontrato un errore non @@ -182,8 +181,8 @@ coi processi che Il programma che un processo sta eseguendo si chiama immagine del processo (o \textit{process image}), le funzioni della famiglia \func{exec} permettono di caricare un'altro programma da disco sostituendo quest'ultimo all'immagine -corrente; questo fa si che l'immagine precedente venga completamente -cancellata. Questo significa che quando il nuovo programma esce anche il +corrente; questo fa sì che l'immagine precedente venga completamente +cancellata. Questo significa che quando il nuovo programma esce, anche il processo termina, e non si può tornare alla precedente immagine. Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto @@ -207,17 +206,18 @@ programmi. \subsection{Gli identificatori dei processi} \label{sec:proc_pid} -Come accennato nell'introduzione ogni processo viene identificato dal sistema +Come accennato nell'introduzione, ogni processo viene identificato dal sistema da un numero identificativo unico, il \textit{process id} o \acr{pid}; quest'ultimo è un tipo di dato standard, il \type{pid\_t} che in genere è un -intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato è \type{int}). +intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato è +\type{int}). Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo processo viene creato, fino ad un limite massimo (in genere essendo detto numero memorizzato in un intero a 16 bit si arriva a 32767) oltre il quale si riparte dal numero più basso disponibile\footnote{FIXME: verificare, non sono - sicuro}. Per questo motivo processo il processo di avvio (\cmd{init}) ha -sempre il \acr{pid} uguale a uno. + sicuro}. Per questo motivo, come visto in \secref{sec:proc_hierarchy}, il +processo di avvio (\cmd{init}) ha sempre il \acr{pid} uguale a uno. Tutti i processi inoltre memorizzano anche il \acr{pid} del genitore da cui sono stati creati, questo viene chiamato in genere \acr{ppid} (da @@ -273,9 +273,9 @@ prototipo della funzione \funcdecl{pid\_t fork(void)} Crea un nuovo processo. - \bodydesc{Restituisce zero al padre e il \acr{pid} al figlio in caso di - successo, ritorna -1 al padre (senza creare il figlio) in caso di errore; - \var{errno} può assumere i valori: + \bodydesc{In caso di successo restituisce il \acr{pid} del figlio al padre e + zero al figlio; ritorna -1 al padre (senza creare il figlio) in caso di + errore; \var{errno} può assumere i valori: \begin{errlist} \item[\macro{EAGAIN}] non ci sono risorse sufficienti per creare un'altro processo (per allocare la tabella delle pagine e le strutture del task) o @@ -286,12 +286,12 @@ prototipo della funzione \end{functions} Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che -il processo figlio continuano ad essere eseguiti normalmente alla istruzione +il processo figlio continuano ad essere eseguiti normalmente all'istruzione seguente la \func{fork}; il processo figlio è però una copia del padre, e riceve una copia dei segmenti di testo, stack e dati (vedi \secref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del -padre. Si tenga presente però che la memoria è copiata, non condivisa, pertanto -padre e figlio vedono variabili diverse. +padre. Si tenga presente però che la memoria è copiata, non condivisa, +pertanto padre e figlio vedono variabili diverse. Per quanto riguarda la gestione della memoria in generale il segmento di testo, che è identico, è condiviso e tenuto in read-only per il padre e per i @@ -392,7 +392,7 @@ operazione che viene chiamata \textit{spawn}. Nei sistemi unix-like scelto di mantenere questa separazione, dato che, come per la prima modalità d'uso, esistono numerosi scenari in cui si può usare una \func{fork} senza aver bisogno di eseguire una \func{exec}. Inoltre, anche nel caso della -seconda modalità di uso, avere le due funzioni separate permette al figlio di +seconda modalità d'uso, avere le due funzioni separate permette al figlio di cambiare gli attributi del processo (maschera dei segnali, redirezione dell'output, \textit{user id}) prima della \func{exec}, rendendo così relativamente facile intervenire sulle le modalità di esecuzione del nuovo @@ -401,11 +401,14 @@ programma. In \curfig\ si è riportato il corpo del codice del programma di esempio \cmd{forktest}, che ci permette di illustrare molte caratteristiche dell'uso della funzione \func{fork}. Il programma permette di creare un numero di figli -specificato a linea di comando, e prende anche alcune opzioni per indicare +specificato da linea di comando, e prende anche alcune opzioni per indicare degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione \func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la descrizione delle opzioni); il codice completo, compresa la parte che gestisce -le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}. +le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}, +distribuito insieme agli altri sorgenti degli esempi su +\href{http://firenze.linux.it/~piccardi/gapil_source.tgz} +{\texttt{http://firenze.linux.it/\~~\hspace{-2.0mm}piccardi/gapil\_source.tgz}}. Decifrato il numero di figli da creare, il ciclo principale del programma (\texttt{\small 24--40}) esegue in successione la creazione dei processi figli @@ -441,8 +444,8 @@ Go to next child \end{verbatim} %$ \normalsize -Esaminiamo questo risultato: una prima conclusione che si può trarre è non si -può dire quale processo fra il padre ed il figlio venga eseguito per +Esaminiamo questo risultato: una prima conclusione che si può trarre è che non +si può dire quale processo fra il padre ed il figlio venga eseguito per primo\footnote{a partire dal kernel 2.5.2-pre10 è stato introdotto il nuovo scheduler di Ingo Molnar che esegue sempre per primo il figlio; per mantenere la portabilità è opportuno non fare comunque affidamento su questo @@ -463,7 +466,7 @@ cui il processo padre ha eseguito pi figli venisse messo in esecuzione. Pertanto non si può fare nessuna assunzione sulla sequenza di esecuzione delle -istruzioni del codice fra padre e figli, nè sull'ordine in cui questi potranno +istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno essere messi in esecuzione. Se è necessaria una qualche forma di precedenza occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il rischio di incorrere nelle cosiddette \textit{race condition} \index{race @@ -472,9 +475,9 @@ rischio di incorrere nelle cosiddette \textit{race condition} \index{race Si noti inoltre che essendo i segmenti di memoria utilizzati dai singoli processi completamente separati, le modifiche delle variabili nei processi figli (come l'incremento di \var{i} in \texttt{\small 31}) sono visibili solo -a loro, e non hanno alcun effetto sul valore che le stesse variabili hanno nel -processo padre (ed in eventuali altri processi figli che eseguano lo stesso -codice). +a loro (ogni processo vede solo la propria copia della memoria), e non hanno +alcun effetto sul valore che le stesse variabili hanno nel processo padre (ed +in eventuali altri processi figli che eseguano lo stesso codice). Un secondo aspetto molto importante nella creazione dei processi figli è quello dell'interazione dei vari processi con i file; per illustrarlo meglio @@ -687,7 +690,7 @@ eseguite alla chiusura di un processo Oltre queste operazioni è però necessario poter disporre di un meccanismo ulteriore che consenta di sapere come la terminazione è avvenuta: dato che in -un sistema unix-like tutto viene gestito attraverso i processi il meccanismo +un sistema unix-like tutto viene gestito attraverso i processi, il meccanismo scelto consiste nel riportare lo stato di terminazione (il cosiddetto \textit{termination status}) al processo padre. @@ -715,7 +718,7 @@ terminato (si potrebbe avere cio Questa complicazione viene superata facendo in modo che il processo orfano venga \textsl{adottato} da \cmd{init}. Come già accennato quando un processo -termina il kernel controlla se è il padre di altri processi in esecuzione: in +termina, il kernel controlla se è il padre di altri processi in esecuzione: in caso positivo allora il \acr{ppid} di tutti questi processi viene sostituito con il \acr{pid} di \cmd{init} (e cioè con 1); in questo modo ogni processo avrà sempre un padre (nel caso possiamo parlare di un padre \textsl{adottivo}) @@ -932,7 +935,7 @@ per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}), per questo la modalità più usata per chiamare queste funzioni è quella di utilizzarle all'interno di un \textit{signal handler} (torneremo sui segnali e su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}). In questo -caso infatti, dato che il segnale è generato dalla terminazione un figlio, +caso infatti, dato che il segnale è generato dalla terminazione di un figlio, avremo la certezza che la chiamata a \func{wait} non si bloccherà. \begin{table}[!htb] @@ -986,12 +989,12 @@ anomala), uno per indicare se Lo standard POSIX.1 definisce una serie di macro di preprocessore da usare per analizzare lo stato di uscita. Esse sono definite sempre in -\file{} ed elencate in \curtab\ (si tenga presente che queste -macro prendono come parametro la variabile di tipo \type{int} puntata da -\var{status}). +\file{} ed elencate in \tabref{tab:proc_status_macro} (si tenga +presente che queste macro prendono come parametro la variabile di tipo +\type{int} puntata da \var{status}). Si tenga conto che nel caso di conclusione anomala il valore restituito da -\macro{WTERMSIG} può essere controllato contro le costanti definite in +\macro{WTERMSIG} può essere confrontato con le costanti definite in \file{signal.h} ed elencate in \tabref{tab:sig_signal_list}, e stampato usando le apposite funzioni trattate in \secref{sec:sig_strsignal}. @@ -999,7 +1002,7 @@ le apposite funzioni trattate in \secref{sec:sig_strsignal}. \subsection{Le funzioni \func{wait3} e \func{wait4}} \label{sec:proc_wait4} -Linux, seguendo una estensione di BSD, supporta altre due funzioni per la +Linux, seguendo un'estensione di BSD, supporta altre due funzioni per la lettura dello stato di terminazione di un processo \func{wait3} e \func{wait4}, analoghe alle precedenti ma che prevedono un ulteriore parametro attraverso il quale il kernel può restituire al padre informazioni @@ -1164,11 +1167,12 @@ specificare il comando da eseguire; quando il parametro \var{file} non contiene una \file{/} esso viene considerato come un nome di programma, e viene eseguita automaticamente una ricerca fra i file presenti nella lista di directory specificate dalla variabile di ambiente \var{PATH}. Il file che -viene posto in esecuzione è il primo che viene trovato. Se si ha un errore di -permessi negati (cioè l'esecuzione della sottostante \func{execve} ritorna un -\macro{EACCESS}), la ricerca viene proseguita nelle eventuali ulteriori -directory indicate nel \var{PATH}, solo se non viene trovato nessun altro file -viene finalmente restituito \macro{EACCESS}. +viene posto in esecuzione è il primo che viene trovato. Se si ha un errore +relativo a permessi di accesso insufficienti (cioè l'esecuzione della +sottostante \func{execve} ritorna un \macro{EACCESS}), la ricerca viene +proseguita nelle eventuali ulteriori directory indicate in \var{PATH}; solo se +non viene trovato nessun altro file viene finalmente restituito +\macro{EACCESS}. Le altre quattro funzioni si limitano invece a cercare di eseguire il file indicato dal parametro \var{path}, che viene interpretato come il @@ -1177,7 +1181,7 @@ indicato dal parametro \var{path}, che viene interpretato come il \begin{figure}[htb] \centering \includegraphics[width=13cm]{img/exec_rel} - \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}} + \caption{La interrelazione fra le sei funzioni della famiglia \func{exec}.} \label{fig:proc_exec_relat} \end{figure} @@ -1213,12 +1217,11 @@ la lista completa \var{tms\_cutime}, \var{tms\_ustime} (vedi \secref{sec:xxx_xxx}). \end{itemize*} -Oltre a questo i segnali che sono stati settati per essere ignorati nel -processo chiamante mantengono lo stesso settaggio pure nel nuovo programma, -tutti gli altri segnali vengono settati alla loro azione di default. Un caso -speciale è il segnale \macro{SIGCHLD} che, quando settato a \macro{SIG\_IGN}, -può anche non essere resettato a \macro{SIG\_DFL} (si veda -\secref{sec:sig_gen_beha}). +Inoltre i segnali che sono stati settati per essere ignorati nel processo +chiamante mantengono lo stesso settaggio pure nel nuovo programma, tutti gli +altri segnali vengono settati alla loro azione di default. Un caso speciale è +il segnale \macro{SIGCHLD} che, quando settato a \macro{SIG\_IGN}, può anche +non essere resettato a \macro{SIG\_DFL} (si veda \secref{sec:sig_gen_beha}). La gestione dei file aperti dipende dal valore che ha il flag di \textit{close-on-exec} (trattato in \secref{sec:file_fcntl}) per ciascun file @@ -1227,7 +1230,7 @@ restano aperti. Questo significa che il comportamento di default restano aperti attraverso una \func{exec}, a meno di una chiamata esplicita a \func{fcntl} che setti il suddetto flag. -Per le directory lo standard POSIX.1 richiede che esse vengano chiuse +Per le directory, lo standard POSIX.1 richiede che esse vengano chiuse attraverso una \func{exec}, in genere questo è fatto dalla funzione \func{opendir} (vedi \secref{sec:file_dir_read}) che effettua da sola il settaggio del flag di \textit{close-on-exec} sulle directory che apre, in @@ -1356,13 +1359,13 @@ identificatori ai valori corrispondenti all'utente che entra nel sistema. Al secondo gruppo appartengono l'\textit{effective user id} e l'\textit{effective group id} (a cui si aggiungono gli eventuali -\textit{supplementary group id} dei gruppi dei quale l'utente fa parte). +\textit{supplementary group id} dei gruppi dei quali l'utente fa parte). Questi sono invece gli identificatori usati nella verifiche dei permessi del processo e per il controllo di accesso ai file (argomento affrontato in dettaglio in \secref{sec:file_perm_overview}). Questi identificatori normalmente sono identici ai corrispondenti del gruppo -\textsl{reale} tranne nel caso in cui, come accennato in +\textit{real} tranne nel caso in cui, come accennato in \secref{sec:proc_exec}, il programma che si è posto in esecuzione abbia i bit \acr{suid} o \acr{sgid} settati (il significato di questi bit è affrontato in dettaglio in \secref{sec:file_suid_sgid}). In questo caso essi saranno settati @@ -1401,10 +1404,10 @@ servano di nuovo. Questo in Linux viene fatto usando altri due gruppi di identificatori, il \textit{saved} ed il \textit{filesystem}, analoghi ai precedenti. Il primo gruppo è lo stesso usato in SVr4, e previsto dallo standard POSIX quando è -definita la costante \macro{\_POSIX\_SAVED\_IDS}\footnote{in caso si abbia a +definita la costante \macro{\_POSIX\_SAVED\_IDS},\footnote{in caso si abbia a cuore la portabilità del programma su altri Unix è buona norma controllare sempre la disponibilità di queste funzioni controllando se questa costante è - definita}, il secondo gruppo è specifico di Linux e viene usato per + definita.} il secondo gruppo è specifico di Linux e viene usato per migliorare la sicurezza con NFS. Il \textit{saved user id} e il \textit{saved group id} sono copie @@ -1473,7 +1476,7 @@ riportare l'\textit{effective user id} a quello dell'utente che ha lanciato il programma, effettuare il lavoro che non necessita di privilegi aggiuntivi, ed eventualmente tornare indietro. -Come esempio per chiarire dell'uso di queste funzioni prendiamo quello con cui +Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene registrato chi sta usando il sistema al momento corrente; chiaramente non può essere lasciato aperto in scrittura a qualunque utente, che potrebbe @@ -1484,7 +1487,7 @@ esempio tutti i programmi di terminale in X, o il programma \cmd{screen} che crea terminali multipli su una console) appartengono a questo gruppo ed hanno il bit \acr{sgid} settato. -Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato la +Quando uno di questi programmi (ad esempio \cmd{xterm}) viene lanciato, la situazione degli identificatori è la seguente: \begin{eqnarray*} \label{eq:1} @@ -1493,7 +1496,7 @@ situazione degli identificatori \textit{saved group id} &=& \textrm{\acr{utmp}} \end{eqnarray*} in questo modo, dato che l'\textit{effective group id} è quello giusto, il -programma può accedere a \file{/var/log/utmp} in scrittura ed aggiornarlo, a +programma può accedere a \file{/var/log/utmp} in scrittura ed aggiornarlo. A questo punto il programma può eseguire una \code{setgid(getgid())} per settare l'\textit{effective group id} a quello dell'utente (e dato che il \textit{real group id} corrisponde la funzione avrà successo), in questo modo non sarà @@ -1509,9 +1512,9 @@ e ogni processo lanciato dal terminale avrebbe comunque \acr{gid} come \textit{effective group id}. All'uscita dal terminale, per poter di nuovo aggiornare lo stato di \file{/var/log/utmp} il programma eseguirà una \code{setgid(utmp)} (dove \var{utmp} è il valore numerico associato al gruppo -\acr{utmp}, ottenuto ad esempio con una \func{getegid}), dato che in questo -caso il valore richiesto corrisponde al \textit{saved group id} la funzione -avrà successo e riporterà la situazione a: +\acr{utmp}, ottenuto ad esempio con una precedente \func{getegid}), dato che +in questo caso il valore richiesto corrisponde al \textit{saved group id} la +funzione avrà successo e riporterà la situazione a: \begin{eqnarray*} \label{eq:3} \textit{real group id} &=& \textrm{\acr{gid} (invariato)} \\ @@ -1580,7 +1583,7 @@ Lo stesso problema di propagazione dei privilegi ad eventuali processi figli si porrebbe per i \textit{saved id}: queste funzioni derivano da un'implementazione che non ne prevede la presenza, e quindi non è possibile usarle per correggere la situazione come nel caso precedente. Per questo -motivo in Linux tutte le volte che vengono usata per modificare uno degli +motivo in Linux tutte le volte che vengono usate per modificare uno degli identificatori ad un valore diverso dal \textit{real id} precedente, il \textit{saved id} viene sempre settato al valore dell'\textit{effective id}. @@ -1616,7 +1619,7 @@ il settaggio di tutti gli identificatori. \subsection{Le funzioni \func{setresuid} e \func{setresgid}} \label{sec:proc_setresuid} -Queste due funzioni sono una estensione introdotta in Linux dal kernel 2.1.44, +Queste due funzioni sono un'estensione introdotta in Linux dal kernel 2.1.44, e permettono un completo controllo su tutti gli identificatori (\textit{real}, \textit{effective} e \textit{saved}), i prototipi sono: \begin{functions} @@ -1663,7 +1666,7 @@ prototipi sono: variabili di ritorno non sono validi.} \end{functions} -Anche queste funzioni sono una estensione specifica di Linux, e non richiedono +Anche queste funzioni sono un'estensione specifica di Linux, e non richiedono nessun privilegio. I valori sono restituiti negli argomenti, che vanno specificati come puntatori (è un'altro esempio di \textit{value result argument}). Si noti che queste funzioni sono le uniche in grado di leggere i @@ -1715,11 +1718,10 @@ coincide con uno dei \textit{real}, \textit{effective} o \textit{saved id}. \subsection{Le funzioni \func{setgroups} e \func{getgroups}} \label{sec:proc_setgroups} -Le ultime funzioni che esamineremo sono quelle sono quelle che permettono di -operare sui gruppi supplementari. Ogni processo può avere fino a -\macro{NGROUPS\_MAX} gruppi supplementari in aggiunta al gruppo primario, -questi vengono ereditati dal processo padre e possono essere cambiati con -queste funzioni. +Le ultime funzioni che esamineremo sono quelle che permettono di operare sui +gruppi supplementari. Ogni processo può avere fino a \macro{NGROUPS\_MAX} +gruppi supplementari in aggiunta al gruppo primario, questi vengono ereditati +dal processo padre e possono essere cambiati con queste funzioni. La funzione che permette di leggere i gruppi supplementari è \func{getgroups}; questa funzione è definita nello standard POSIX ed il suo prototipo è: @@ -1760,8 +1762,8 @@ ottenere tutti i gruppi a cui appartiene un utente; il suo prototipo \noindent la funzione esegue una scansione del database dei gruppi (si veda \secref{sec:sys_user_group}) e ritorna in \param{groups} la lista di quelli a cui l'utente appartiene. Si noti che \param{ngroups} è passato come puntatore -perché qualora il valore specificato sia troppo piccolo la funzione ritorna -1 -e passando indietro il numero dei gruppi trovati. +perché qualora il valore specificato sia troppo piccolo la funzione ritorna +-1, passando indietro il numero dei gruppi trovati. Per settare i gruppi supplementari di un processo ci sono due funzioni, che possono essere usate solo se si hanno i privilegi di amministratore. La prima @@ -1784,7 +1786,7 @@ delle due \end{functions} Se invece si vogliono settare i gruppi supplementari del processo a quelli di -un utente specifico si può usare \func{initgroups} il cui prototipo è: +un utente specifico, si può usare \func{initgroups} il cui prototipo è: \begin{functions} \headdecl{sys/types.h} \headdecl{grp.h} @@ -1800,9 +1802,9 @@ un utente specifico si pu \end{functions} La funzione esegue la scansione del database dei gruppi (usualmente -\file{/etc/groups}) cercando i gruppi di cui è membro \param{user} costruendo -una lista di gruppi supplementari a cui aggiunge \param{group}, che poi setta -usando \func{setgroups}. +\file{/etc/groups}) cercando i gruppi di cui è membro \param{user} e +costruendo una lista di gruppi supplementari a cui aggiunge \param{group}, che +poi setta usando \func{setgroups}. Si tenga presente che sia \func{setgroups} che \func{initgroups} non sono definite nello standard POSIX.1 e che pertanto non è possibile utilizzarle @@ -1817,8 +1819,8 @@ In questa sezione tratteremo pi lo \textit{scheduler}\footnote{che è la parte del kernel che si occupa di stabilire quale processo dovrà essere posto in esecuzione.} assegna la CPU ai vari processi attivi. In particolare prendremo in esame i vari meccanismi -con cui viene gestita l'assgnazione del tempo di CPU, ed illustreremo le varie -funzioni di gestione. +con cui viene gestita l'assegnazione del tempo di CPU, ed illustreremo le +varie funzioni di gestione. \subsection{I meccanismi di \textit{scheduling}} @@ -1829,16 +1831,20 @@ il tempo di CPU per l'esecuzione dei processi ed oggetto di numerose ricerche; in ogni caso essa dipende in maniera essenziale anche dal tipo di utilizzo che deve essere fatto del sistema. -La cosa è resa ancora più complicata dal fatto che con sistemi -multi-processore si introduce anche la complessità dovuta alla scelta di quale -sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni la - presenza di ampie cache può rendere poco efficiente trasferire l'esecuzione - di un processo da una CPU ad un'altra, per cui occorrono meccanismi per - determininare quale è la migliore scelta fra le diverse CPU.} Tutto questo -comunque appartiene alle sottigliezze dell'implementazione del kernel, e dal -punto di vista dei programmi che girano in user space di può pensare sempre -alla risorsa tempo di esecuzione, governata dagli stessi mecca, che nel caso -di più processori sarà a disposizione di più di un processo alla volta. +La cosa è resa ancora più complicata dal fatto che con le architetture +multi-processore si introduce anche la problematica dovuta alla scelta di +quale sia la CPU più opportuna da utilizzare.\footnote{nei processori moderni + la presenza di ampie cache può rendere poco efficiente trasferire + l'esecuzione di un processo da una CPU ad un'altra, per cui occorrono + meccanismi per determininare quale è la migliore scelta fra le diverse CPU.} +Tutto questo comunque appartiene alle sottigliezze dell'implementazione del +kernel, e dal punto di vista dei programmi che girano in user space anche +quando si hanno più processori, e quindi potenzialmente anche dei processi che +sono eseguiti davvero in contemporanea, si può pensare alle politiche di +scheduling come concernenti la risorsa \textsl{tempo di esecuzione}, la cui +assegnazione sarà governata dagli stessi meccanismi di scelta di priorità, +solo che nel caso di più processori sarà a disposizione di più di un processo +alla volta. Si tenga presente inoltre che l'utilizzo della CPU è soltanto una delle risorse (insieme alla memoria e all'accesso alle periferiche) che sono @@ -1849,8 +1855,8 @@ prestazioni. La politica tradizionale di scheduling di Unix (che tratteremo in \secref{sec:proc_sched_stand}) è sempre stata basata su delle priorità -dinamiche, che assicurassaro che tutti i processi, anche i meno importanti, -potessero ricevere un po' di tempo di CPU. +dinamiche, che assicurassero che tutti i processi, anche i meno importanti, +potessero ricevere un po' di tempo di CPU. Lo standard POSIX però per tenere conto dei sistemi real-time,\footnote{per sistema real-time si intende un sistema in grado di eseguire operazioni in @@ -1859,8 +1865,8 @@ Lo standard POSIX per determinabili con certezza assoluta, come nel caso di meccanismi di controllo di macchine, dove uno sforamento dei tempi avrebbe conseguenze disastrose, e \textit{soft-real-time} in cui un occasionale sforamento è - ritenuto accettabile.} in cui è vitale che i processi in che devono essere -eseguiti in un determinato momento non debbano aspettare la conclusione di un + ritenuto accettabile.} in cui è vitale che i processi che devono essere +eseguiti in un determinato momento non debbano aspettare la conclusione di altri processi che non hanno questa necessità, ha introdotto il concetto di \textsl{priorità assoluta}, chimata anche \textsl{priorità statica}, in contrapposizione con la normale priorità dinamica. @@ -1882,7 +1888,6 @@ Questa viene in genere indicata con un numero - \subsection{Il meccanismo di \textit{scheduling} standard} \label{sec:proc_sched_stand} @@ -1917,7 +1922,7 @@ abbiamo affrontato la gestione dei processi. \label{sec:proc_atom_oper} La nozione di \textsl{operazione atomica} deriva dal significato greco della -parola atomo, cioè indivisibile; si dice infatti che una operazione è atomica +parola atomo, cioè indivisibile; si dice infatti che un'operazione è atomica quando si ha la certezza che, qualora essa venga effettuata, tutti i passaggi che devono essere compiuti per realizzarla verranno eseguiti senza possibilità di interruzione in una fase intermedia. @@ -1942,7 +1947,7 @@ processi. Nel caso dei segnali invece la situazione è molto più delicata, in quanto lo stesso processo, e pure alcune system call, possono essere interrotti in qualunque momento, e le operazioni di un eventuale \textit{signal handler} -sono compiute nello stesso spazio di indirizzi del processo. Per questo anche +sono compiute nello stesso spazio di indirizzi del processo. Per questo, anche il solo accesso o l'assegnazione di una variabile possono non essere più operazioni atomiche (torneremo su questi aspetti in \secref{sec:sign_xxx}). @@ -1963,7 +1968,7 @@ condiviso, onde evitare problemi con le ottimizzazioni del codice. Si definiscono \textit{race condition} tutte quelle situazioni in cui processi diversi operano su una risorsa comune, ed in cui il risultato viene a dipendere dall'ordine in cui essi effettuano le loro operazioni. Il caso -tipico è quello di una operazione che viene eseguita da un processo in più +tipico è quello di un'operazione che viene eseguita da un processo in più passi, e può essere compromessa dall'intervento di un altro processo che accede alla stessa risorsa quando ancora non tutti i passi sono stati completati. @@ -2010,26 +2015,26 @@ eseguire in maniera atomica le operazioni necessarie. Si dice \textsl{rientrante} una funzione che può essere interrotta in qualunque punto della sua esecuzione ed essere chiamata una seconda volta da -un altro thread di esecuzione senza che questo comporti nessun problema nella -esecuzione della stessa. La problematica è comune nella programmazione +un altro thread di esecuzione senza che questo comporti nessun problema +nell'esecuzione della stessa. La problematica è comune nella programmazione multi-thread, ma si hanno gli stessi problemi quando si vogliono chiamare delle funzioni all'interno dei manipolatori dei segnali. Fintanto che una funzione opera soltanto con le variabili locali è rientrante; -queste infatti vengono tutte le volte allocate nello stack, e un'altra -invocazione non fa altro che allocarne un'altra copia. Una funzione può non -essere rientrante quando opera su memoria che non è nello stack. Ad esempio -una funzione non è mai rientrante se usa una variabile globale o statica. - -Nel caso invece la funzione operi su un oggetto allocato dinamicamente la cosa -viene a dipendere da come avvengono le operazioni; se l'oggetto è creato ogni -volta e ritornato indietro la funzione può essere rientrante, se invece esso -viene individuato dalla funzione stessa due chiamate alla stessa funzione -potranno interferire quando entrambe faranno riferimento allo stesso oggetto. -Allo stesso modo una funzione può non essere rientrante se usa e modifica un -oggetto che le viene fornito dal chiamante: due chiamate possono interferire -se viene passato lo stesso oggetto; in tutti questi casi occorre molta cura da -parte del programmatore. +queste infatti vengono allocate nello stack, e un'altra invocazione non fa +altro che allocarne un'altra copia. Una funzione può non essere rientrante +quando opera su memoria che non è nello stack. Ad esempio una funzione non è +mai rientrante se usa una variabile globale o statica. + +Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la +cosa viene a dipendere da come avvengono le operazioni: se l'oggetto è creato +ogni volta e ritornato indietro la funzione può essere rientrante, se invece +esso viene individuato dalla funzione stessa, due chiamate alla stessa +funzione potranno interferire quando entrambe faranno riferimento allo stesso +oggetto. Allo stesso modo una funzione può non essere rientrante se usa e +modifica un oggetto che le viene fornito dal chiamante: due chiamate possono +interferire se viene passato lo stesso oggetto; in tutti questi casi occorre +molta cura da parte del programmatore. In genere le funzioni di libreria non sono rientranti, molte di esse ad esempio utilizzano variabili statiche, le \acr{glibc} però mettono a diff --git a/ringraziamenti.tex b/ringraziamenti.tex new file mode 100644 index 0000000..2965001 --- /dev/null +++ b/ringraziamenti.tex @@ -0,0 +1,18 @@ +\chapter{Ringraziamenti} +\label{cha:ringraziamenti} + +Desidero ringraziare tutti coloro che a vario titolo e a più riprese mi hanno +aiutato ed han contribuito a migliorare in molteplici aspetti la qualità di +GaPiL. In ordine rigorosamente alfabetico desidero citare: +\begin{description} +\item[Alessio Frusciante] per l'apprezzamento, le molteplici correzioni ed i + suggerimenti per rendere più chiara l'esposizione. +\item[Daniele Masini] per la rilettura puntuale, le innumerevoli correzioni, i + consigli sull'esposizione ed il contributo relativo alle calling convention + dei linguaggi. +\end{description} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "gapil" +%%% End: -- 2.30.2