\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}}
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}.
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}
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.
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
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
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.
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).
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.
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.
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
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}
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
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}
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).
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}
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.
\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
versione estesa di \texttt{getopt}.
+
\subsection{Le variabili di ambiente}
\label{sec:proc_env_var}