ciascun processo vedrà la sua copia del codice (in realtà il kernel fa si che
tutte le parti uguali siano condivise), avrà un suo spazio di indirizzi,
variabili proprie e sarà eseguito in maniera completamente indipendente da
-tutti gli altri.
-
-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
-parte per la peculiarità dell'implementazione).
+tutti gli altri\footnote{questo non è del tutto vero nel caso di un programma
+ \textit{multi-thread}, ma sulla gestione dei \textit{thread} in Linux
+ torneremo più avanti}.
\subsection{La funzione \func{main}}
\subsection{Come chiudere un programma}
\label{sec:proc_conclusion}
-La via normale per la quale un programma finisce è quando la funzione
-\func{main} ritorna, una modalità equivalente di conclusione è quella di
-chiamare direttamente la funzione \func{exit} (che viene comunque chiamata
-dalla routine di avvio del programma quando la funzione \func{main} ritorna).
-Una forma alternativa è quella di chiamare direttamente la system call
-\func{\_exit} che passa il controllo direttamente al kernel.
+Normalmente un programma finisce è quando la funzione \func{main} ritorna, una
+modalità equivalente di concludere il programma è quella di chiamare
+direttamente la funzione \func{exit} (che viene comunque chiamata
+automaticamente quando \func{main} ritorna). Una forma alternativa è quella
+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 di segnali o della chiamata alla
-funzione \func{abort} (che comunque genera un segnale che termina il
-programma); torneremo su questo in \secref{sec:proc_termination}.
-
-Il valore di ritorno della funzione main, o quello usato nelle chiamate ad
-\func{exit} e \func{\_exit}, viene chiamato \textit{exit status} e passato
-al processo padre che aveva lanciato il programma (in genere la shell). In
-generale si usa questo valore per fornire un'informazione generica sulla
-riuscita o il fallimento del programma; l'informazione è necessariamente
-generica, ed il valore deve essere compreso fra 0 e 255.
-
-In generale si usa la convenzione di restituire 0 in caso di successo e 1 in
-caso di fallimento, i programmi che effettuano dei confronti (come
-\cmd{diff}) usano invece una notazione leggermente diversa, usando 0 per
-indicare la corrispondenza, 1 per indicare la non corrispondenza e 2 per
-indicare l'incapacità di effettuare il confronto. È opportuno adottare una di
-queste convenzioni a seconda dei casi. Si tenga presente che se si raggiunge
-la fine della funzione \func{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 su per usi speciali, ad
+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}.
+
+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
+\textit{exit status}) e passato al processo padre che aveva lanciato il
+programma (in genere la shell). In generale si usa questo valore per fornire
+informazioni sulla riuscita o il fallimento del programma; l'informazione è
+necessariamente generica, ed il valore deve essere compreso fra 0 e 255.
+
+La convenzione in uso pressoché universale è quella di restituire 0 in caso di
+successo e 1 in caso di fallimento; l'unica eccezione è per i programmi che
+effettuano dei confronti (come \cmd{diff}), che usano 0 per indicare la
+corrispondenza, 1 per indicare la non corrispondenza e 2 per indicare
+l'incapacità di effettuare il confronto. È opportuno adottare una di queste
+convenzioni a seconda dei casi. Si tenga presente che se si raggiunge la fine
+della funzione \func{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 a 256 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
+programma in un sottoprocesso. Benché questa convenzione non sia
universalmente seguita è una buona idea tenerne conto.
-Si tenga presente inoltre che non è una buona idea usare il valore dell'errore
-restituito dalla variabile \var{errno} come stato di uscita, in generale
-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
-\file{stdlib.h} sono definite due macro \macro{EXIT\_SUCCESS} e
-\macro{EXIT\_FAILURE}, che in Linux sono poste rispettivamente ai valori 0 e
-1 (di tipo \type{int}), seguendo lo standard POSIX.
+Si tenga presente inoltre che non è una buona idea usare il codice di errore
+restituito dalla variabile \var{errno} (per i dettagli si veda
+\secref{sec:sys_errors}) come stato di uscita. In generale infatti una shell
+non si cura del valore se non per vedere se è diverso da zero; inoltre il
+valore dello stato di uscita è sempre troncato ad 8 bit, per cui si potrebbe
+incorrere nel caso in cui restituendo un codice di errore 256, si otterrebbe
+uno stato di uscita uguale a zero, che verrebbe interpretato come un successo.
-Infine occorre distinguere fra lo stato di uscita di un programma
-(l'\textit{exit status}) e lo stato di conclusione di un processo (il
-\textit{termination status}), abbiamo già accennato infatti che è comunque
-possibile un processo possa essere terminato (da un segnale) prima che il
-programma in esecuzione si sia concluso. In caso di conclusione normale del
-programma però lo stato di uscita diventa parte dello stato di conclusione del
-processo (vedi \secref{sec:proc_termination}).
+In \file{stdlib.h} sono definite, seguendo lo standard POSIX, le due macro
+\macro{EXIT\_SUCCESS} e \macro{EXIT\_FAILURE}, da usare sempre per specificare
+lo stato di uscita di un processo. In Linux esse sono poste rispettivamente ai
+valori di tipo \type{int} 0 e 1.
\subsection{Le funzioni \func{exit} e \func{\_exit}}
\label{sec:proc_exit}
-Come accennato funzioni per l'uscita ``normale'' da un programma sono due, la
-prima è la funzione \func{exit} che è definita dallo standard ANSI C; il
-prototipo della funzione è il seguente:
+Come accennato le funzioni usate per effettuare una uscita ``normale'' da un
+programma sono due, la prima è la funzione \func{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 restituendo il valore
\var{status} al processo padre.
\bodydesc{La funzione non ritorna. Il processo viene terminato.}
\end{prototype}
-La funzione \func{exit} è pensata per una conclusione pulita di un programma
-che usa le librerie standard del C; essa esegue tutte le funzioni che sono
-state registrate con \func{atexit} e \func{on\_exit} (vedi
-\secref{sec:proc_atexit}), e chiude tutti gli stream di I/O effettuando il
+La funzione \func{exit} è pensata per eseguire una conclusione pulita di un
+programma che usi le librerie standard del C; essa esegue tutte le funzioni
+che sono state registrate con \func{atexit} e \func{on\_exit} (vedi
+\secref{sec:proc_atexit}), e chiude tutti gli stream effettuando il
salvataggio dei dati sospesi (chiamando \func{fclose}, vedi
-\secref{sec:file_fclose}), infine ripassa il controllo al kernel chiamando
-\func{\_exit} e passando il valore \var{status} come stato di uscita.
+\secref{sec:file_fopen}), infine passa il controllo al kernel chiamando
+\func{\_exit} e passando \param{status} come stato di uscita.
-La system call \func{\_exit} restituisce direttamente il controllo al
-kernel, concludendo immediatamente il processo, le eventuali funzioni
-registrate con \func{atexit} e \func{on\_exit} non vengono eseguite. Il
-prototipo della funzione è il seguente:
+La system call \func{\_exit} restituisce direttamente il controllo al kernel,
+concludendo immediatamente il processo; i dati sospesi nei buffer degli stream
+non vengono salvati e le eventuali funzioni registrate con \func{atexit} e
+\func{on\_exit} non vengono eseguite. Il prototipo della funzione è:
\begin{prototype}{unistd.h}{void \_exit(int status)}
- Causa la conclusione immediata del programma restituendo il valore
- \var{status} al processo padre.
+ Causa la conclusione immediata del programma restituendo \param{status} al
+ processo padre come stato di uscita.
\bodydesc{La funzione non ritorna. Il processo viene terminato.}
\end{prototype}
La funzione chiude tutti i file descriptor appartenenti al processo (si tenga
presente che questo non comporta il salvataggio dei dati bufferizzati degli
-stream), fa si che ogni figlio del processo sia ereditato da \cmd{init}
-(vedi \secref{cha:process_handling}), manda un segnale \macro{SIGCHLD} al
-processo padre (vedi \ref{sec:sig_job_control}) ed infine ritorna lo stato di
-uscita specificato in \var{status} che può essere raccolto usando la
-funzione \func{wait} (vedi \secref{sec:proc_wait}).
+stream), fa si che ogni figlio del processo sia ereditato da \cmd{init} (vedi
+\secref{cha:process_handling}), manda un segnale \macro{SIGCHLD} al processo
+padre (vedi \secref{sec:sig_job_control}) ed infine ritorna lo stato di uscita
+specificato in \param{status} che può essere raccolto usando la funzione
+\func{wait} (vedi \secref{sec:proc_wait}).
\subsection{Le funzioni \func{atexit} e \func{on\_exit}}
\label{sec:proc_atexit}
-Come accennato l'uso di \func{exit} al posto della \func{\_exit} è fatto
-principalmente per permettere una uscita pulita dalle funzioni delle librerie
-standard del C (in particolare per quel che riguarda la chiusura degli
-stream).
-
-Quando si realizza una libreria da usare in varie applicazioni può essere
-perciò utile evitare di richiedere di chiamare esplicitamente un funzione di
-uscita che esegua tutte le operazioni di pulizia prima di uscire (come quella
-di salvare eventuali dati sospesi). È invece molto meno soggetto ad errori e
-completamente trasparente all'utente poter effettuare una chiamata automatica
-di 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 è:
+Una esigenza comune che si incontra nella programmazione è quella di dover
+effettuare una serie di operazioni di pulizia (ad esempio salvare dei dati,
+ripristinare dei settaggi, eliminare dei file temporanei, ecc.) prima della
+conclusione di un programma. In genere queste operazioni vengono fatte in una
+apposita sezione del programma, ma quando si realizza una libreria diventa
+antipatico dover richiedere una chiamata esplicita ad una funzione di pulizia
+al programmatore che la utilizza.
+
+È invece molto meno soggetto ad errori, e completamente trasparente
+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 è:
\begin{prototype}{stdlib.h}{void atexit(void (*function)(void))}
Registra la funzione \param{function} per essere chiamata all'uscita dal
programma.
\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di
fallimento, \var{errno} non viene settata.}
\end{prototype}
-
-La funzione richiede come argomento l'indirizzo della opportuna da chiamare
-all'uscita che non deve prendere argomenti e non deve ritornare niente. Una
-estensione di \func{atexit} è la funzione \func{on\_exit} (che la glibc
-include per compatibilità con SunOS e che non è detto sia definita su altri
-sistemi), il cui prototipo è:
+\noindent la funzione richiede come argomento l'indirizzo della opportuna
+funzione di pulizia da chiamare all'uscita, che non deve prendere argomenti e
+non deve ritornare niente (deve essere essere cioè definita come \func{void
+ function(void)}).
+
+Una estensione di \func{atexit} è la funzione \func{on\_exit}, che le
+\acr{glibc} includono per compatibilità con SunOS, ma che non è detto sia
+definita su altri sistemi; il suo prototipo è:
\begin{prototype}{stdlib.h}
{void on\_exit(void (*function)(int status, void *arg), void *arg)}
- Registra la funzione \var{function} per essere chiamata all'uscita dal
+ Registra la funzione \param{function} per essere chiamata all'uscita dal
programma. Tutte le funzioni registrate vengono chiamate in ordine inverso
rispetto a quello di registrazione.
In questo caso la funzione da chiamare prende due parametri, il primo dei
quali sarà inizializzato allo stato di uscita con cui è stata chiamata
\func{exit} ed il secondo al puntatore generico specificato come secondo
-argomento nella chiamata di \func{on\_exit}.
+argomento nella chiamata di \func{on\_exit}. Così diventa possibile passare
+dei dati alla funzione di chiusura.
Nella sequenza di chiusura tutte le funzioni registrate verranno chiamate in
ordine inverso rispetto a quello di registrazione (ed una stessa funzione
Data l'importanza dell'argomento è opportuno sottolineare ancora una volta che
in un sistema unix l'unico modo in cui un programma può essere eseguito dal
-kernel è attraverso la chiamata alla system call \func{execve} (in genere
-attraverso una delle funzioni \func{exec} che vedremo in
+kernel è attraverso la chiamata alla system call \func{execve} (o attraverso
+una delle funzioni della famiglia \func{exec} che vedremo in
\secref{sec:proc_exec}).
Allo stesso modo l'unico modo in cui un programma può concludere
volontariamente la sua esecuzione è attraverso una chiamata alla system call
-\func{\_exit} sia esplicitamente o che in maniera indiretta attraverso l'uso
-di \func{exit} o il ritorno della funzione \func{main}.
+\func{\_exit}, o esplicitamente, o in maniera indiretta attraverso l'uso di
+\func{exit} o il ritorno di \func{main}.
-Lo schema delle modalità con cui si avvia e conclude normalmente un programma
-è riportato in \nfig.
+Uno schema riassuntivo che illustra le modalità con cui si avvia e conclude
+normalmente un programma è riportato in \nfig.
\begin{figure}[htb]
\centering
\label{fig:proc_prog_start_stop}
\end{figure}
-
Si ricordi infine che un programma può anche essere interrotto dall'esterno
attraverso l'uso di un segnale (modalità di conclusione non mostrata in
-\curfig); torneremo su questo aspetto in \secref{cha:signals}.
+\curfig); torneremo su questo aspetto in \capref{cha:signals}.
\section{I processi e l'uso della memoria}
\label{sec:proc_memory}
-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
-memoria viene vista dal punto di vista di un programma in esecuzione in un
-processo.
+Una delle risorse base che ciascun processo ha a disposizione è la memoria, e
+la gestione della memoria è appunto uno degli aspetti più complessi di un
+sistema unix-like. In questa sezione, dopo una breve introduzione ai concetti
+base, esamineremo come la memoria viene vista da parte di un programma in
+esecuzione, e le varie funzioni utilizzabili per la sua gestione.
\subsection{I concetti generali}
\label{sec:proc_mem_gen}
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
-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
-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
-detto spazio non è neanche continuo (cioè non tutti gli indirizzi sono
-utilizzabili e/o utilizzati).
-
-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).
+di basso livello dipendono spesso in maniera diretta dall'architettura
+dell'hardware), ma quello più tipico, usato dai sistemi unix-like come Linux è
+la cosiddetta \textsl{memoria virtuale}m che consiste nell'assegnare ad ogni
+processo uno spazio virtuale di indirizzamento lineare, in cui gli indirizzi
+vanno da zero ad un qualche valore massimo\footnote{nel caso di Linux fino al
+ kernel 2.2 detto massimo era, per macchine a 32bit, di 2Gb, con il kernel
+ 2.4 ed il supporto per la \textit{high-memory} il limite è stato esteso}.
+
+Come accennato in \capref{cha:intro_unix} questo spazio di indirizzi è
+virtuale e non corrisponde all'effettiva posizione dei dati nella RAM del
+computer; in genere detto spazio non è neppure continuo (cioè non tutti gli
+indirizzi possibili sono utilizzabili, e quelli usabili non sono
+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).
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
La corrispondenza fra le pagine della memoria virtuale e quelle della memoria
fisica della macchina viene gestita in maniera trasparente dall'hardware di
-gestione della memoria (la \textit{Memory Management Unit} del processore),
-ma poiché in genere quest'ultima è solo una piccola frazione della memoria
-virtuale è necessario un meccanismo che permetta di trasferire le pagine
-virtuali che servono dal supporto su cui si trovano in memoria, eliminando
-quelle che non servono. Questo meccanismo è detto \textit{paging}, ed è uno
-dei compiti principali del kernel.
+gestione della memoria (la \textit{Memory Management Unit} del processore).
+Poiché in genere quest'ultima è solo una piccola frazione della memoria
+virtuale, è necessario un meccanismo che permetta di trasferire le pagine che
+servono dal supporto su cui si trovano in memoria, eliminando quelle che non
+servono. Questo meccanismo è detto \textit{paging}, ed è uno dei compiti
+principali del kernel.
Quando un processo cerca di accedere ad una pagina che non è nella memoria
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
-reperire lo spazio necessario), per poi restituire il controllo al
-processo.
+gestione della memoria 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 reperire lo spazio
+necessario), per poi restituire il controllo al processo.
Dal punto di vista di un processo questo meccanismo è completamente
-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. 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
-meccanismo del paging e mantenere fisse delle pagine in memoria (vedi
-\ref{sec:proc_mem_lock}).
+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 in RAM,
+a tempi molto più 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 meccanismo del paging e mantenere fisse delle pagine
+in memoria (vedi \ref{sec:proc_mem_lock}).
\subsection{La struttura della memoria di un processo}
immediata.
È pertanto importante capire come viene strutturata la memoria virtuale di un
-processo; essa viene divisa in \textsl{segmenti}, cioè un insieme contiguo di
+processo. Essa viene divisa in \textsl{segmenti}, cioè un insieme contiguo di
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
+\item Il segmento di testo (o \textit{text segment}). Contiene il codice
macchina del programma e le costanti statiche. Normalmente viene condiviso,
in modo che più processi (anche diversi nel caso di librerie) possano
utilizzarlo, e viene marcato in sola lettura per evitare sovrascritture
Viene allocato da \func{exec} all'avvio del programma e resta invariato
per tutto il tempo dell'esecuzione.
-\item Il segmento dei dati (\textit{data segment}). Contiene le variabili
+\item Il segmento dei dati (o \textit{data segment}). Contiene le variabili
globali (cioè quelle definite al di fuori di tutte le funzioni). Di norma è
diviso in due parti.
\item Il segmento di \textit{stack}, che contiene lo \textit{stack} del
programma. Tutte le volte che si effettua una chiamata ad una funzione è
qui che viene salvato l'indirizzo di ritorno e le informazioni dello stato
- del chiamante (tipo il contenuto di alcuni registri della CPU); poi la
- funzione chiamata alloca qui lo spazio per le sue variabili locali, in
+ 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.
\label{fig:proc_mem_layout}
\end{figure}
-Una disposizione tipica di questi segmenti è riportata in \nfig. Usando il
-comando \cmd{size} su un programma se ne può stampare le dimensioni dei
-segmenti di testo e di dati (inizializzati e BSS); il BSS però non è mai
-salvato sul file, in quanto viene inizializzato a zero al caricamento del
-programma.
+Una disposizione tipica di questi segmenti è riportata in
+\figref{fig:proc_mem_layout}. Usando il comando \cmd{size} su un programma se
+ne può stampare le dimensioni dei segmenti di testo e di dati (inizializzati e
+BSS); il BSS però non è mai salvato sul file, in quanto viene inizializzato a
+zero al caricamento del programma.
\subsection{Allocazione della memoria per i programmi C}