Fatta una bella passata di spell su tutto quanto ...
[gapil.git] / process.tex
index 84cd0fef33faac87260763e2aa09032b4967e4ca..52c1f40f95228ebe236d617825bab3b6c86f1222 100644 (file)
@@ -29,7 +29,7 @@ Anche quando all'interno di un programma possono essere presenti pi
 \textsl{filoni} di esecuzione (i cosiddetti \textit{thread}), o questo possa
 essere composto da moduli multipli completamente separati, quando questo sarà
 posto in esecuzione esso apparirà al sistema come un solo processo (il
-discorso dei \textit{thread} comunque in linux necessita di una trattazione a
+discorso dei \textit{thread} comunque in Linux necessita di una trattazione a
 parte per la peculiarità dell'implementazione).
 
 \section{La funzione \texttt{main}} 
@@ -40,7 +40,7 @@ avvio, usando il programma \texttt{ld-linux.so}, 
 carica le librerie condivise che servono al programma, effettua il link
 dinamico del codice e poi 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
+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 \texttt{/etc/ld.so.conf}, i
 dettagli sono riportati nella man page di \texttt{ld.so}.
@@ -50,7 +50,7 @@ sta al programmatore chiamare cos
 si suppone iniziale l'esecuzione; in ogni caso senza questa funzione lo stesso
 linker darebbe luogo ad errori.
 
-Lo stadard ISO C specifica che la funzione \texttt{main} può o non avere
+Lo standard ISO C specifica che la funzione \texttt{main} può o 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{verbatim}
@@ -96,7 +96,7 @@ la fine della funzione \texttt{main} senza ritornare esplicitamente si ha un
 valore di uscita indefinito, è pertanto consigliabile di concludere sempre in
 maniera esplicita detta funzione.
 
-Una altra convenzione riserva i valori da 128 in sù per usi speciali, ad
+Una altra convenzione riserva i valori da 128 in su per usi speciali, ad
 esempio 128 viene usato per indicare l'incapacità di eseguire un altro
 programma in un sottoprocesso. Benché anche questa convenzione non sia
 universalmente seguita è una buona idea tenerne conto.
@@ -107,7 +107,7 @@ una shell non si cura di tutto questo e comunque il valore dello stato di
 uscita è sempre troncato ad 8 bit, per cui si potrebbe incorrere nel caso in
 cui l'errore 256, diventando zero, verrebbe interpretato come un successo. In
 \texttt{stdlib.h} sono definite due macro \texttt{EXIT\_SUCCESS} e
-\texttt{EXIT\_FAILURE}, che in linux sono poste rispettivamente ai valori 0 e
+\texttt{EXIT\_FAILURE}, che in Linux sono poste rispettivamente ai valori 0 e
 1 (di tipo \texttt{int}), seguendo lo standard POSIX.
 
 Infine occorre distinguere fra lo stato di uscita di un programma
@@ -224,7 +224,7 @@ attraveso una delle funzioni \texttt{exec} che vedremo in
 
 Allo stesso modo l'unico modo in cui un programma può concludere
 volontariamente la sua esecuzione è attraverso una chiamata alla system call
-\texttt{\_exec} sia esplicitamnte o che in maniera indiretta attraverso l'uso
+\texttt{\_exec} sia esplicitamente o che in maniera indiretta attraverso l'uso
 di \texttt{exit} o il ritorno della funzione \texttt{main}.
 
 Lo schema delle modalità con cui si avvia e conclude normalmente un programma
@@ -248,7 +248,7 @@ attraverso l'uso di un segnale (modalit
 
 Una delle risorse base che ciascun processo ha a disposizione è la memoria, ed
 uno degli aspetti più complessi di un sistema unix (ed in particolar modo di
-linux) è appunto la gestione della memoria. Qui ci occuperemo però di come la
+Linux) è appunto la gestione della memoria. Qui ci occuperemo però di come la
 memoria viene vista dal punto di vista di un programma in esecuzione in un
 processo.
 
@@ -258,14 +258,14 @@ processo.
 
 Ci sono vari modi in cui i vari sistemi organizzano la memoria (ed i dettagli
 di basso livello dipendono in maniera diretta dall'architettura
-dell'hardware), ma quello più tipico, usato da unix (e da linux) è quello di
+dell'hardware), ma quello più tipico, usato da unix (e da Linux) è quello di
 assegnare ad ogni processo uno spazio virtuale di indirizzamento lineare in
 cui gli indirizzi vanno da zero ad un qualche valore massimo (nel caso di
-linux fino al kernel 2.2 detto massimo era per macchine a 32bit di 2Gb, con il
+Linux fino al kernel 2.2 detto massimo era per macchine a 32bit di 2Gb, con il
 kernel 2.4 il limite è stato esteso).
 
 Come accennato nell'introduzione questo spazio di indirizzi è virtuale e non
-corrisponde all'effettiva posizione dei dati nella ram del computer; in genere
+corrisponde all'effettiva posizione dei dati nella RAM del computer; in genere
 detto spazio non è neanche continuo (cioè non tutti gli indirizzi sono
 utilizzabili e/o utilizzati).
 
@@ -273,13 +273,13 @@ La memoria virtuale viene divisa in pagine (che ad esempio sono di 4kb su
 macchine a 32 bit e 8kb sulle alpha, valori strettamente connessi all'hardware
 di gestione della memoria) di dimensione fissa, e ciascuna pagina della
 memoria virtuale è associata ad un supporto che può essere una pagina di
-memoria reale o ad un dipositivo di stoccaggio secondario (in genere lo spazio
+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
 accade in genere per le pagine che contengono il codice delle librerie
-condivise). Ad esempio il codice della fuzione \texttt{printf} starà su una
+condivise). Ad esempio il codice della funzione \texttt{printf} starà su una
 sola pagina di memoria reale che farà da supporto a tutte le pagine di memoria
 virtuale di tutti i processi hanno detta funzione nel loro codice. 
 
@@ -296,7 +296,7 @@ Quando un processo cerca di accedere ad una pagina che non 
 reale avviene quello che viene chiamato un \textit{page fault}, l'hardware di
 gestione della memoria (la MMU del processore) genera una interruzione e passa
 il controllo al kernel il quale sospende il processo e si incarica di mettere
-in ram la pagina richiesta (effettuando tutte le operazioni necessarie per
+in RAM la pagina richiesta (effettuando tutte le operazioni necessarie per
 reperire lo spazio necessario), per poi restituire il controllo al
 processo. 
 
@@ -304,7 +304,7 @@ Dal punto di vista di un processo questo meccanismo 
 trasparente e tutto avviene come se tutte le pagine fossero sempre disponibili
 in memoria.  L'unica differenza avvertibile è quella dei tempi di esecuzione,
 che passano dai pochi nanosecondi necessari per l'accesso a tempi molto più
-lunghi, dovuti all'intervento del kernel. Normalemente questo è il prezzo da
+lunghi, dovuti all'intervento del kernel. Normalmente questo è il prezzo da
 pagare per avere un multitasking reale, ed in genere il sistema è molto
 efficiente in questo lavoro; quando però ci siano esigenze specifiche di
 prestazioni è possibile usare delle funzioni che permettono di bloccare il
@@ -321,32 +321,32 @@ tentativo di accedere ad un indirizzo non allocato 
 commette quando si è manipolato male un puntatore e genera quello che viene
 chiamato un \textit{segmentation fault}, si tenta cioè di leggere e scrivere
 da un indirizzo per il quale non esiste una associazione della pagina virtuale
-ed il kernel riponde al relativo \textit{page fault} mandando un segnale
+ed il kernel risponde al relativo \textit{page fault} mandando un segnale
 \texttt{SIGSEGV} al processo, che normalmente ne causa la terminazione
 immediata.
 
 È pertanto importante capire come viene strutturata la memoria virtuale di un
 processo; essa viene divisa in \textsl{segmenti}, cioè un insieme contiguo di
-indirizzi virtuali ai quali il processo può accedere. Storicamente un
+indirizzi virtuali ai quali il processo può accedere. Solitamente un
 programma C viene suddiviso nei seguenti segmenti:
 
 \begin{enumerate}
 \item Il segmento di testo (\textit{text segment}). Contiene il codice
-  macchina del programma e le costanti statiche. Normalente viene condiviso
+  macchina del programma e le costanti statiche. Normalmente viene condiviso
   così che più processi (anche diversi nel caso di librerie) possano
-  utilizzarlo e marcato in sola lettura per evitare sovrascritture accidentali
-  (o maliziose) che ne modifichino le istruzioni. 
+  utilizzarlo e viene marcato in sola lettura per evitare sovrascritture
+  accidentali (o maliziose) che ne modifichino le istruzioni.
   
   Viene allocato da \texttt{exec} all'avvio del programma e resta invariato
   per tutto il tempo dell'esecuzione.
   
-\item Il segmento dei dati (\textit{data segment}). Contiene le varibili
+\item Il segmento dei dati (\textit{data segment}). Contiene le variabili
   globali (cioè quelle definite al di fuori di tutte le funzioni). Di norma è
   diviso in due parti.
   
-  La prima è il segmento dei dati inizializzati, che contiene le variabili
-  globali il cui valore è stato assegnato esplicitamente. Ad esempio se si
-  definisce:
+  La prima parte è il segmento dei dati inizializzati, che contiene le
+  variabili globali il cui valore è stato assegnato esplicitamente. Ad esempio
+  se si definisce:
 \begin{verbatim}
     double pi = 3.14;
 \end{verbatim}
@@ -354,14 +354,14 @@ programma C viene suddiviso nei seguenti segmenti:
   segmento viene preallocato dalla \texttt{exec} e inizializzata ai valori
   specificati.
   
-  La seconda è il segmento dei dati non inizializzati, che contiene le
+  La seconda parte è il segmento dei dati non inizializzati, che contiene le
   variabili globali il cui valore è stato non è assegnato esplicitamente. Ad
   esempio se si definisce:
 \begin{verbatim}
     int vect[100];
 \end{verbatim}
   questo valore sarà immagazzinato in questo segmento. Anch'esso viene
-  allocato all'avvio, e tutte le varibili vengono inizializzate a
+  allocato all'avvio, e tutte le variabili vengono inizializzate a
   zero (ed i puntatori a \texttt{NULL}). 
   
   Storicamente questo segmento viene chiamato BBS (da \textit{block started by
@@ -400,17 +400,78 @@ salvato sul file, in quanto viene inizializzato a zero al caricamento del
 programma.
 
 
-\subsection{La librerie condivise}
-\label{sec:proc_mem_shlib}
 
+\subsection{Allocazione della memoria per i programmi C}
+\label{sec:proc_mem_alloc}
+
+Il C supporta due tipi di allocazione della memoria, l'allocazione statica è
+quella in cui vanno le variabili globali e le variabili statiche (e viene
+effettuata nel segmento dei dati), lo spazio per queste variabili viene
+allocati all'avvio del programma (come parte delle operazioni svolte da
+\texttt{exec}) e non viene liberato fino alla sua conclusione.
+
+L'allocazione automatica è quella che avviene per le cosiddette variabili
+automatiche, cioè gli argomenti delle funzioni o le variabili locali. Lo
+spazio per queste variabili viene allocato nello stack quando viene eseguito
+comando di invocazione della funzione e liberato quando si esce dalla
+medesima.
+
+Esiste però un terzo tipo di allocazione, che non è prevista dal linguaggio C,
+che è l'allocazione dinamica della memoria, necessaria quando il quantitativo
+di memoria che serve è determinabile solo in corso di esecuzione del
+programma.
 
+Il C non consente di usare variabili allocate dinamicamente, non è possibile
+cioè definire in fase di programmazione una variabile le cui dimensioni
+possano essere modificate durante l'esecuzione del programma; però le librerie
+del C forniscono una serie opportuna di funzioni per permettere l'allocazione
+dinamica di spazio in memoria (in genere nello heap, usando la system call
+\texttt{sbrk}), solo che a questo punto sarà possibile usarlo solo in maniera
+indiretta attraverso dei puntatori.
+
+Le funzioni previste dallo standard ANSI C per la gestione della memoria sono
+quattro, i prototipi sono i seguenti:
+\begin{prototype}{stdlib.h}{void *calloc(size\_t size)}
+  Alloca \texttt{size} bytes nello heap. La memoria viene inizializzata a 0.
+  
+  La funzione restituisce il puntatore alla zona di memoria allocata in caso
+  di successo e \texttt{NULL} in caso di fallimento, nel qual caso
+  \texttt{errno} viene settata a \texttt{ENOMEM}.
+\end{prototype}
+\begin{prototype}{stdlib.h}{void *malloc(size\_t size)}
+  Alloca \texttt{size} bytes nello heap. La memoria non viene inizializzata.
 
+  La funzione restituisce il puntatore alla zona di memoria allocata in caso
+  di successo e \texttt{NULL} in caso di fallimento, nel qual caso
+  \texttt{errno} viene settata a \texttt{ENOMEM}.
+\end{prototype}
+\begin{prototype}{stdlib.h}{void free(void *ptr)}
+  Disalloca lo spazio di memoria puntato da \texttt{ptr}.
+
+  La funzione non ritorna nulla.
+\end{prototype}
+\begin{prototype}{stdlib.h}{void *realloc(void *ptr, size\_t size)}
+  Cambia la dimensione del blocco allocato all'indirizzo \texttt{ptr}
+  portandola a \texttt{size}.
+
+  La funzione restituisce il puntatore alla zona di memoria allocata in caso
+  di successo e \texttt{NULL} in caso di fallimento, nel qual caso
+  \texttt{errno} viene settata a \texttt{ENOMEM}.
+\end{prototype}
+
+Il puntatore che le funzioni di allocazione ritornano è garantito essere
+sempre correttamente allineato per tutti i tipi di dati; ad esempio sulle
+macchine a 32 bit in genere è allineato a multipli di 4 bytes e sulle macchine
+a 64 bit a multipli di 8 bytes. Il puntatori ritornati sono di tipo generico
+così non è necessario effettuare un cast per assegnarli a puntatori di altro
+tipo.
 
-\subsection{Allocazione e disallocazione della memoria}
-\label{sec:proc_mem_alloc}
 
 
 
+
+
 
 
 \section{La gestione di parametri e opzioni}
@@ -430,7 +491,7 @@ ciascuna delle quali viene considerata un parametro; di default per
 individuare le parole viene usato come separatore lo spazio (comportamento
 modificabile attraverso il settaggio della variabile di ambiente IFS).
 
-Nella scansione viene costruito l'array di puntatori \texttt{argv} inserendo
+Nella scansione viene costruito il vettore di puntatori \texttt{argv} inserendo
 in successione il puntatore alla stringa costituente l'$n$-simo parametro; la
 variabile \texttt{argc} viene inizializzata al numero di parametri trovati, in
 questo modo il primo parametro è sempre il nome del programma (vedi \nfig).
@@ -442,7 +503,7 @@ In generale un programma unix riceve da linea di comando sia i parametri che
 le opzioni, queste ultime sono standardizzate per essere riconosciute come
 tali: un elemento di \texttt{argv} che inizia con \texttt{-} e che non sia un
 singolo \texttt{-} o \texttt{--} viene considerato un'opzione.  In in genere
-le opzioni sono costituite da un lettera preceduta dal meno e possono avere o
+le opzioni sono costituite da una lettera preceduta dal meno e possono avere o
 no un parametro associato; un comando tipico può essere cioè qualcosa del
 tipo:
 \begin{verbatim}
@@ -479,7 +540,7 @@ considerata conclusa.
 
 Quando la funzione trova un'opzione essa ritorna il valore numerico del
 carattere, in questo modo si possono prendere le azioni relative usando un
-case; la funzione inizializza inoltre alcune varibili globali:
+case; la funzione inizializza inoltre alcune variabili globali:
 \begin{itemize}
 \item \texttt{char * optarg} contiene il puntatore alla stringa argomento
   dell'opzione.
@@ -490,8 +551,7 @@ case; la funzione inizializza inoltre alcune varibili globali:
 \item \texttt{int optopt} contiene il carattere dell'opzione non riconosciuta.
 \end{itemize}
 
-In \nfig è mostrato un programma di esempio, 
-
+In \nfig\ è mostrato un programma di esempio, 
 
 \begin{figure}[htbp]
   \footnotesize
@@ -547,6 +607,7 @@ la gestione di queste ultime 
 versione estesa di \texttt{getopt}.
 
 
+
 \subsection{Le variabili di ambiente}
 \label{sec:proc_env_var}