X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=aeacae83df46fd91a9f3c6a104236432b3f48ca5;hp=f29d5e31e1e1844f6c627613e26a342cc437ccbd;hb=d25090faca15102552d77c38161a8a34b0bac41e;hpb=613d2f30d1c3ec28c569578a7b7bab23a40e8fea diff --git a/process.tex b/process.tex index f29d5e3..aeacae8 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. @@ -358,9 +356,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 +364,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 +386,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 +525,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 +535,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 +549,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,7 +563,7 @@ 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 @@ -615,10 +609,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 +628,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 +676,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 +685,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 +955,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 +1026,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 +1286,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 +1319,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 +1329,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}. @@ -1581,7 +1545,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 @@ -1631,7 +1595,7 @@ e statiche mantengono i valori che avevano al momento della chiamata di 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