X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=0678e277a3882de71aec3782dddb610eb7f0e72f;hp=f29d5e31e1e1844f6c627613e26a342cc437ccbd;hb=6907e5e6ff5c69a34dc9d3664388cdb9f76ede82;hpb=613d2f30d1c3ec28c569578a7b7bab23a40e8fea diff --git a/process.tex b/process.tex index f29d5e3..0678e27 100644 --- a/process.tex +++ b/process.tex @@ -1,6 +1,6 @@ %% process.tex %% -%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2003 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the %% Free Software Foundation; with the Invariant Sections being "Prefazione", @@ -46,22 +46,20 @@ avvio, usando il programma \cmd{ld-linux.so}. Questo programma prima carica le librerie condivise che servono al programma, poi effettua il link dinamico del codice e alla fine lo esegue. Infatti, a meno di non aver specificato il flag \texttt{-static} durante la compilazione, tutti i programmi in Linux sono -incompleti e necessitano di essere linkati alle librerie condivise quando -vengono avviati. La procedura è controllata da alcune variabili di ambiente e -dal contenuto di \file{/etc/ld.so.conf}. I dettagli sono riportati nella man -page di \cmd{ld.so}. +incompleti e necessitano di essere \textit{linkati} alle librerie condivise +quando vengono avviati. La procedura è controllata da alcune variabili di +ambiente e dal contenuto di \file{/etc/ld.so.conf}. I dettagli sono riportati +nella man page di \cmd{ld.so}. Il sistema fa partire qualunque programma chiamando la funzione \func{main}; sta al programmatore chiamare così la funzione principale del programma da cui si suppone iniziare l'esecuzione; in ogni caso senza questa funzione lo stesso -linker darebbe luogo ad errori. +\textit{linker} darebbe luogo ad errori. Lo standard ISO C specifica che la funzione \func{main} può non avere argomenti o prendere due argomenti che rappresentano gli argomenti passati da linea di comando, in sostanza un prototipo che va sempre bene è il seguente: -\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} - int main (int argc, char *argv[]) -\end{lstlisting} +\includecodesnip{listati/main_def.c} In realtà nei sistemi Unix esiste un'altro modo per definire la funzione \func{main}, che prevede la presenza di un terzo parametro, \code{char @@ -80,10 +78,10 @@ automaticamente quando \func{main} ritorna). Una forma alternativa di chiamare direttamente la system call \func{\_exit}, che restituisce il controllo direttamente alla routine di conclusione dei processi del kernel. -Oltre alla conclusione ``normale'' esiste anche la possibilità di una -conclusione ``anomala'' del programma a causa della ricezione di un segnale -(si veda \capref{cha:signals}) o della chiamata alla funzione \func{abort}; -torneremo su questo in \secref{sec:proc_termination}. +Oltre alla conclusione ``\textsl{normale}'' esiste anche la possibilità di una +conclusione ``\textsl{anomala}'' del programma a causa della ricezione di un +segnale (si veda \capref{cha:signals}) o della chiamata alla funzione +\func{abort}; torneremo su questo in \secref{sec:proc_termination}. Il valore di ritorno della funzione \func{main}, o quello usato nelle chiamate ad \func{exit} e \func{\_exit}, viene chiamato \textsl{stato di uscita} (o @@ -124,9 +122,9 @@ valori di tipo \ctyp{int} 0 e 1. \subsection{Le funzioni \func{exit} e \func{\_exit}} \label{sec:proc_exit} -Come accennato le funzioni usate per effettuare un'uscita ``normale'' da un -programma sono due, la prima è la funzione \funcd{exit}, che è definita dallo -standard ANSI C ed il cui prototipo è: +Come accennato le funzioni usate per effettuare un'uscita ``\textit{normale}'' +da un programma sono due, la prima è la funzione \funcd{exit}, che è definita +dallo standard ANSI C ed il cui prototipo è: \begin{prototype}{stdlib.h}{void exit(int status)} Causa la conclusione ordinaria del programma. @@ -175,9 +173,9 @@ pulizia al programmatore che la utilizza. all'utente, avere la possibilità di effettuare automaticamente la chiamata ad una funzione che effettui tali operazioni all'uscita dal programma. A questo scopo lo standard ANSI C prevede la possibilità di registrare un certo numero -funzioni che verranno eseguite all'uscita dal programma (sia per la chiamata -ad \func{exit} che per il ritorno di \func{main}). La prima funzione che si -può utilizzare a tal fine è \funcd{atexit} il cui prototipo è: +di funzioni che verranno eseguite all'uscita dal programma (sia per la +chiamata ad \func{exit} che per il ritorno di \func{main}). La prima funzione +che si può utilizzare a tal fine è \funcd{atexit} il cui prototipo è: \begin{prototype}{stdlib.h}{void atexit(void (*function)(void))} Registra la funzione \param{function} per la chiamata all'uscita dal programma. @@ -187,8 +185,8 @@ pu \end{prototype} \noindent la funzione richiede come argomento l'indirizzo di una opportuna funzione di pulizia da chiamare all'uscita del programma, che non deve -prendere argomenti e non deve ritornare niente (deve essere essere cioè -definita come \code{void function(void)}). +prendere argomenti e non deve ritornare niente (deve essere cioè definita come +\code{void function(void)}). Un'estensione di \func{atexit} è la funzione \funcd{on\_exit}, che le \acr{glibc} includono per compatibilità con SunOS, ma che non è detto sia @@ -278,10 +276,13 @@ necessariamente adiacenti). Per la gestione da parte del kernel la memoria virtuale viene divisa in pagine di dimensione fissa (che ad esempio sono di 4kb su macchine a 32 bit e 8kb sulle alpha, valori strettamente connessi all'hardware di gestione della -memoria), e ciascuna pagina della memoria virtuale è associata ad un supporto -che può essere una pagina di memoria reale o ad un dispositivo di stoccaggio -secondario (in genere lo spazio disco riservato alla swap, o i file che -contengono il codice). +memoria),\footnote{con le versioni più recenti del kernel è possibile anche + utilizzare pagine di dimensioni maggiori, per sistemi con grandi + quantitativi di memoria in cui l'uso di pagine troppo piccole comporta una + perdita di prestazioni.} e ciascuna pagina della memoria virtuale è +associata ad un supporto che può essere una pagina di memoria reale o ad un +dispositivo di stoccaggio secondario (in genere lo spazio disco riservato alla +swap, o i file che contengono il codice). Lo stesso pezzo di memoria reale (o di spazio disco) può fare da supporto a diverse pagine di memoria virtuale appartenenti a processi diversi (come @@ -358,9 +359,7 @@ seguenti segmenti: La prima parte è il segmento dei dati inizializzati, che contiene le variabili il cui valore è stato assegnato esplicitamente. Ad esempio se si definisce: - \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} - double pi = 3.14; - \end{lstlisting} +\includecodesnip{listati/pi.c} questo valore sarà immagazzinato in questo segmento. La memoria di questo segmento viene preallocata all'avvio del programma e inizializzata ai valori specificati. @@ -368,9 +367,7 @@ seguenti segmenti: La seconda parte è il segmento dei dati non inizializzati, che contiene le variabili il cui valore non è stato assegnato esplicitamente. Ad esempio se si definisce: - \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} - int vect[100]; - \end{lstlisting} +\includecodesnip{listati/vect.c} questo vettore sarà immagazzinato in questo segmento. Anch'esso viene allocato all'avvio, e tutte le variabili vengono inizializzate a zero (ed i puntatori a \val{NULL}).\footnote{si ricordi che questo vale solo per le @@ -392,10 +389,10 @@ 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 e ``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.} + della funzione lo spazio è automaticamente rilasciato e + ``\textsl{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. @@ -531,7 +528,7 @@ in ingresso; per questo si dovr ad un adeguato aggiornamento di tutti gli altri puntatori all'interno del blocco di dati ridimensionato. -Un errore abbastanza frequente (specie se si ha a che fare con array di +Un errore abbastanza frequente (specie se si ha a che fare con vettori di puntatori) è quello di chiamare \func{free} più di una volta sullo stesso puntatore; per evitare questo problema una soluzione di ripiego è quella di assegnare sempre a \val{NULL} ogni puntatore liberato con \func{free}, dato @@ -541,7 +538,7 @@ operazione. Le \acr{glibc} hanno un'implementazione delle routine di allocazione che è controllabile dall'utente attraverso alcune variabili di ambiente, in particolare diventa possibile tracciare questo tipo di errori usando la -variabile d'ambiente \val{MALLOC\_CHECK\_} che quando viene definita mette in +variabile di ambiente \val{MALLOC\_CHECK\_} che quando viene definita mette in uso una versione meno efficiente delle funzioni suddette, che però è più tollerante nei confronti di piccoli errori come quello di chiamate doppie a \func{free}. In particolare: @@ -555,8 +552,8 @@ tollerante nei confronti di piccoli errori come quello di chiamate doppie a Il problema più comune e più difficile da risolvere che si incontra con le routine di allocazione è quando non viene opportunamente liberata la memoria -non più utilizzata, quello che in inglese viene chiamato \textit{memory-leak}, -cioè una \textsl{perdita di memoria}. +non più utilizzata, quello che in inglese viene chiamato \textit{memory + leak}\index{memory leak}, cioè una \textsl{perdita di memoria}. 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 @@ -569,12 +566,12 @@ Il problema 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}. +\textit{memory leak}\index{memory leak}. In C e C++ il problema è particolarmente sentito. In C++, per mezzo della programmazione ad oggetti, il problema dei \textit{memory leak} è notevolmente ridimensionato attraverso l'uso accurato di appositi oggetti come gli -\textit{smartpointers}. Questo però va a scapito delle performance +\textit{smartpointers}. Questo però va a scapito delle prestazioni dell'applicazione in esecuzione. In altri linguaggi come il java e recentemente il C\# il problema non si pone @@ -587,11 +584,11 @@ In tal caso, attraverso meccanismi simili a quelli del \textit{reference riferita da nessuna parte del codice in esecuzione, può essere deallocata automaticamente in qualunque momento dall'infrastruttura. -Anche questo va a scapito delle performance dell'applicazione in esecuzione +Anche questo va a scapito delle prestazioni dell'applicazione in esecuzione (inoltre le applicazioni sviluppate con tali linguaggi di solito non sono eseguibili compilati, come avviene invece per il C ed il C++, ed è necessaria la presenza di una infrastruttura per la loro interpretazione e pertanto hanno -di per sé delle performance più scadenti rispetto alle stesse applicazioni +di per sé delle prestazioni più scadenti rispetto alle stesse applicazioni compilate direttamente). Questo comporta però il problema della non predicibilità del momento in cui viene deallocata la memoria precedentemente allocata da un oggetto. @@ -615,10 +612,10 @@ molto complesse riguardo l'allocazione della memoria. \label{sec:proc_mem_alloca} Una possibile alternativa all'uso di \func{malloc}, che non soffre dei -problemi di \textit{memory leak} descritti in precedenza, è la funzione -\funcd{alloca}, che invece di allocare la memoria nello heap usa il segmento -di stack della funzione corrente. La sintassi è identica a quella di -\func{malloc}, il suo prototipo è: +problemi di \textit{memory leak}\index{memory leak} descritti in precedenza, è +la funzione \funcd{alloca}, che invece di allocare la memoria nello heap usa +il segmento di stack della funzione corrente. La sintassi è identica a quella +di \func{malloc}, il suo prototipo è: \begin{prototype}{stdlib.h}{void *alloca(size\_t size)} Alloca \param{size} byte nello stack. @@ -634,10 +631,10 @@ quindi non esiste un analogo della \func{free}) in quanto essa viene rilasciata automaticamente al ritorno della funzione. Come è evidente questa funzione ha molti vantaggi, anzitutto permette di -evitare alla radice i problemi di memory leak, dato che non serve più la -deallocazione esplicita; inoltre la deallocazione automatica funziona anche -quando si usa \func{longjmp} per uscire da una subroutine con un salto non -locale da una funzione (vedi \secref{sec:proc_longjmp}). +evitare alla radice i problemi di memory leak\index{memory leak}, dato che non +serve più la deallocazione esplicita; inoltre la deallocazione automatica +funziona anche quando si usa \func{longjmp} per uscire da una subroutine con +un salto non locale da una funzione (vedi \secref{sec:proc_longjmp}). Un altro vantaggio è che in Linux la funzione è molto più veloce di \func{malloc} e non viene sprecato spazio, infatti non è necessario gestire un @@ -682,7 +679,7 @@ prima funzione fallimento, nel qual caso \var{errno} assumerà il valore \errval{ENOMEM}.} \end{prototype} -La funzione è un'interfaccia diretta all'ominima system call ed imposta +La funzione è un'interfaccia diretta all'omonima system call ed imposta l'indirizzo finale del segmento dati di un processo all'indirizzo specificato da \param{end\_data\_segment}. Quest'ultimo deve essere un valore ragionevole, ed inoltre la dimensione totale del segmento non deve comunque eccedere un @@ -691,7 +688,7 @@ dimensioni massime dello spazio dati del processo. La seconda funzione per la manipolazione delle dimensioni del segmento dati\footnote{in questo caso si tratta soltanto di una funzione di libreria, e - non di una sistem call.} è \funcd{sbrk}, ed il suo prototipo è: + non di una system call.} è \funcd{sbrk}, ed il suo prototipo è: \begin{prototype}{unistd.h}{void *sbrk(ptrdiff\_t increment)} Incrementa la dimensione dello spazio dati. @@ -961,37 +958,11 @@ ritornato il carattere \texttt{':'}, infine se viene incontrato il valore elementi di \param{argv} che cominciano con il carattere \texttt{'-'}. \begin{figure}[htb] - \footnotesize - \begin{lstlisting}{} - opterr = 0; /* don't want writing to stderr */ - while ( (i = getopt(argc, argv, "hp:c:e:")) != -1) { - switch (i) { - /* - * Handling options - */ - case 'h': /* help option */ - printf("Wrong -h option use\n"); - usage(); - return -1; - break; - case 'c': /* take wait time for childen */ - wait_child = strtol(optarg, NULL, 10); /* convert input */ - break; - case 'p': /* take wait time for childen */ - wait_parent = strtol(optarg, NULL, 10); /* convert input */ - break; - case 'e': /* take wait before parent exit */ - wait_end = strtol(optarg, NULL, 10); /* convert input */ - break; - case '?': /* unrecognized options */ - printf("Unrecognized options -%c\n",optopt); - usage(); - default: /* should not reached */ - usage(); - } - } - debug("Optind %d, argc %d\n",optind,argc); - \end{lstlisting} + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/option_code.c} + \end{minipage} + \normalsize \caption{Esempio di codice per la gestione delle opzioni.} \label{fig:proc_options_code} \end{figure} @@ -1058,18 +1029,16 @@ sistema un \textsl{ambiente}, nella forma di una lista di variabili (detta \textit{environment list}) messa a disposizione dal processo, e costruita nella chiamata alla funzione \func{exec} quando questo viene lanciato. -Come per la lista dei parametri anche questa lista è un array di puntatori a +Come per la lista dei parametri anche questa lista è un vettore di puntatori a caratteri, ciascuno dei quali punta ad una stringa, terminata da un \val{NULL}. A differenza di \code{argv[]} in questo caso non si ha una -lunghezza dell'array data da un equivalente di \param{argc}, ma la lista è +lunghezza del vettore data da un equivalente di \param{argc}, ma la lista è terminata da un puntatore nullo. L'indirizzo della lista delle variabili di ambiente è passato attraverso la variabile globale \var{environ}, a cui si può accedere attraverso una semplice dichiarazione del tipo: -\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} -extern char ** environ; -\end{lstlisting} +\includecodesnip{listati/env_ptr.c} un esempio della struttura di questa lista, contenente alcune delle variabili più comuni che normalmente sono definite dal sistema, è riportato in \figref{fig:proc_envirno_list}. @@ -1320,7 +1289,7 @@ funzione chiamante un valore relativo ad uno dei suoi parametri. Per far questo si usa il cosiddetto \textit{value result argument}, si passa cioè, invece di una normale variabile, un puntatore alla stessa; vedremo alcuni esempi di questa modalità nelle funzioni che gestiscono i socket (in -\secref{sec:TCPel_functions}), in cui, per permettere al kernel di restituire +\secref{sec:TCP_functions}), in cui, per permettere al kernel di restituire informazioni sulle dimensioni delle strutture degli indirizzi utilizzate, viene usato questo meccanismo. @@ -1353,9 +1322,7 @@ abbia sempre almeno un argomento fisso; prima di effettuare la dichiarazione deve essere incluso l'apposito header file \file{stdarg.h}; un esempio di dichiarazione è il prototipo della funzione \func{execl} che vedremo in \secref{sec:proc_exec}: -\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} - int execl(const char *path, const char *arg, ...); -\end{lstlisting} +\includecodesnip{listati/exec_sample.c} in questo caso la funzione prende due parametri fissi ed un numero variabile di altri parametri (che verranno a costituire gli elementi successivi al primo del vettore \param{argv} passato al nuovo processo). Lo standard ISO C richiede @@ -1365,7 +1332,7 @@ inoltre che l'ultimo degli argomenti fissi sia di tipo per compatibilità; ad esempio i tipi \ctyp{float} vengono convertiti automaticamente a \ctyp{double} ed i \ctyp{char} e gli \ctyp{short} ad \ctyp{int}. Un tipo \textit{self-promoting} è un tipo che verrebbe promosso - a sé stesso.} il che esclude array, puntatori a funzioni e interi di tipo + a sé stesso.} il che esclude vettori, puntatori a funzioni e interi di tipo \ctyp{char} o \ctyp{short} (con segno o meno). Una restrizione ulteriore di alcuni compilatori è di non dichiarare l'ultimo parametro fisso come \ctyp{register}. @@ -1437,7 +1404,7 @@ stack all'indirizzo dove sono stati salvati i parametri, normale pensare di poter effettuare questa operazione. In generale però possono esistere anche realizzazioni diverse, per questo -motivo \macro{va\_list} è definito come \textsl{tipo opaco}\index{tipo opaco} +motivo \macro{va\_list} è definito come \textsl{tipo opaco}\index{tipo!opaco} e non può essere assegnato 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 @@ -1581,7 +1548,7 @@ un punto precedentemente stabilito con \func{setjmp} si usa la funzione \end{functions} La funzione ripristina il contesto dello stack salvato da una chiamata a -\func{setjmp} nell'argomento \param{env}. Dopo l'esecuzione della funzione +\func{setjmp} nell'argomento \param{env}. Dopo l'esecuzione della funzione il programma prosegue nel codice successivo al ritorno della \func{setjmp} con cui si era salvato \param{env}, che restituirà il valore \param{val} invece di zero. Il valore di \param{val} specificato nella chiamata deve essere diverso @@ -1620,18 +1587,18 @@ e statiche mantengono i valori che avevano al momento della chiamata di \func{longjmp}, ma quelli delle variabili automatiche (o di quelle dichiarate \direct{register}\footnote{la direttiva \direct{register} del compilatore chiede che la variabile dichiarata tale sia mantenuta, nei limiti del - possibile, all'interno di un registro del processore. Questa direttiva - origina dai primi compilatori, quando stava al programmatore scrivere codice - ottimizzato, riservando esplicitamente alle variabili più usate l'uso dei - registri del processore. Oggi questa direttiva oggi è in disuso dato che - tutti i compilatori sono normalmente in grado di valutare con maggior - efficacia degli stessi programmatori quando sia il caso di eseguire questa - ottimizzazione.}) sono in genere indeterminati. + possibile, all'interno di un registro del processore. Questa direttiva è + originaria dell'epoca dai primi compilatori, quando stava al programmatore + scrivere codice ottimizzato, riservando esplicitamente alle variabili più + usate l'uso dei registri del processore. Oggi questa direttiva è in disuso + dato che tutti i compilatori sono normalmente in grado di valutare con + maggior efficacia degli stessi programmatori quando sia il caso di eseguire + questa ottimizzazione.}) sono in genere indeterminati. Quello che succede infatti è che i valori delle variabili che sono tenute in memoria manterranno il valore avuto al momento della chiamata di \func{longjmp}, mentre quelli tenuti nei registri del processore (che nella -chiamata ad un'altra funzioni vengono salvati nel contesto nello stack) +chiamata ad un'altra funzione vengono salvati nel contesto nello stack) torneranno al valore avuto al momento della chiamata di \func{setjmp}; per questo quando si vuole avere un comportamento coerente si può bloccare l'ottimizzazione che porta le variabili nei registri dichiarandole tutte come