X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=512e2d7b1e748c8800a2c86cd00a11a435f9eb5d;hp=08a5e6d76cb80335af6258353c4f8a364a3391ad;hb=66e83c068629844f84fe4a0d44b382f756c9ef32;hpb=9efe28fd24b23b1fca69f4c5296cb29e4372438c diff --git a/process.tex b/process.tex index 08a5e6d..512e2d7 100644 --- a/process.tex +++ b/process.tex @@ -1,9 +1,9 @@ %% process.tex %% -%% Copyright (C) 2000-2004 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2005 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", +%% Free Software Foundation; with the Invariant Sections being "Un preambolo", %% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the %% license is included in the section entitled "GNU Free Documentation %% License". @@ -14,7 +14,7 @@ Come accennato nell'introduzione il \textsl{processo} è l'unità di base con cui un sistema unix-like alloca ed utilizza le risorse. Questo capitolo tratterà l'interfaccia base fra il sistema e i processi, come vengono passati -i parametri, come viene gestita e allocata la memoria, come un processo può +gli argomenti, come viene gestita e allocata la memoria, come un processo può richiedere servizi al sistema e cosa deve fare quando ha finito la sua esecuzione. Nella sezione finale accenneremo ad alcune problematiche generiche di programmazione. @@ -80,7 +80,7 @@ controllo direttamente alla routine di conclusione dei processi del kernel. 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 +segnale (si veda cap.~\ref{cha:signals}) o della chiamata alla funzione \func{abort}; torneremo su questo in sez.~\ref{sec:proc_termination}. Il valore di ritorno della funzione \func{main}, o quello usato nelle chiamate @@ -152,10 +152,10 @@ non vengono salvati e le eventuali funzioni registrate con \func{atexit} e 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 sì che ogni figlio del processo sia ereditato da \cmd{init} (vedi -sez.~\ref{cha:process_handling}), manda un segnale \const{SIGCHLD} al processo -padre (vedi sez.~\ref{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 sez.~\ref{sec:proc_wait}). +cap.~\ref{cha:process_handling}), manda un segnale \const{SIGCHLD} al processo +padre (vedi sez.~\ref{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 sez.~\ref{sec:proc_wait}). \subsection{Le funzioni \func{atexit} e \func{on\_exit}} @@ -200,7 +200,7 @@ definita su altri sistemi; il suo prototipo fallimento, \var{errno} non viene modificata.} \end{prototype} -In questo caso la funzione da chiamare all'uscita prende i due parametri +In questo caso la funzione da chiamare all'uscita prende i due argomenti specificati nel prototipo, dovrà cioè essere definita come \code{void function(int status, void *argp)}. Il primo argomento sarà inizializzato allo stato di uscita con cui è stata chiamata \func{exit} ed il secondo al @@ -240,7 +240,7 @@ normalmente un programma Si ricordi infine che un programma può anche essere interrotto dall'esterno attraverso l'uso di un segnale (modalità di conclusione non mostrata in fig.~\ref{fig:proc_prog_start_stop}); torneremo su questo aspetto in -\capref{cha:signals}. +cap.~\ref{cha:signals}. @@ -260,14 +260,14 @@ esecuzione, e le varie funzioni utilizzabili per la sua gestione. Ci sono vari modi in cui i vari sistemi organizzano la memoria (ed i dettagli 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}\index{memoria virtuale} che consiste +la cosiddetta \textsl{memoria virtuale}\index{memoria~virtuale} 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 è +Come accennato in cap.~\ref{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 @@ -291,22 +291,23 @@ condivise). Ad esempio il codice della funzione \func{printf} star sola pagina di memoria reale che farà da supporto a tutte le pagine di memoria virtuale di tutti i processi che hanno detta funzione nel loro codice. -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). -Poiché in genere la memoria fisica è 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 \textsl{paginazione}\index{paginazione} (o -\textit{paging}), ed è uno dei compiti principali del kernel. +La corrispondenza fra le pagine della \index{memoria~virtuale}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). Poiché in genere la memoria fisica è 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 +\textsl{paginazione}\index{paginazione} (o \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}\index{page fault}; -l'hardware di gestione della memoria genera un'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. +reale, avviene quello che viene chiamato un \textit{page + fault}\index{\textit{page~fault}}; l'hardware di gestione della memoria +genera un'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 @@ -318,7 +319,7 @@ Normalmente questo 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 della paginazione\index{paginazione} e -mantenere fisse delle pagine in memoria (vedi \ref{sec:proc_mem_lock}). +mantenere fisse delle pagine in memoria (vedi sez.~\ref{sec:proc_mem_lock}). \subsection{La struttura della memoria di un processo} @@ -330,12 +331,12 @@ 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}. Se si tenta cioè di leggere o scrivere da un indirizzo per il quale non esiste un'associazione della pagina -virtuale, il kernel risponde al relativo \textit{page fault}\index{page fault} -mandando un segnale \const{SIGSEGV} al processo, che normalmente ne causa la -terminazione immediata. +virtuale, il kernel risponde al relativo \textit{page + fault}\index{\textit{page~fault}} mandando un segnale \const{SIGSEGV} al +processo, che normalmente ne causa la terminazione immediata. È pertanto importante capire come viene strutturata \textsl{la memoria - virtuale}\index{page fault} di un processo. Essa viene divisa in + virtuale}\index{\textit{page~fault}} di un 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: @@ -553,7 +554,7 @@ 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}\index{memory leak}, cioè una \textsl{perdita di memoria}. + leak}\index{\textit{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 @@ -566,7 +567,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}\index{memory leak}. +\textit{memory leak}\index{\textit{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 @@ -612,10 +613,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}\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 è: +problemi di \textit{memory leak}\index{\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 è: \begin{prototype}{stdlib.h}{void *alloca(size\_t size)} Alloca \param{size} byte nello stack. @@ -631,10 +632,11 @@ 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\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 sez.~\ref{sec:proc_longjmp}). +evitare alla radice i problemi di memory leak\index{\textit{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 +sez.~\ref{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 @@ -710,9 +712,10 @@ standard descritte in precedenza, che sono costruite su di esse. % \label{sec:proc_mem_malloc_custom} -\subsection{Il controllo della memoria virtuale\index{memoria virtuale}} +\subsection{Il controllo della memoria virtuale} \label{sec:proc_mem_lock} +\index{memoria~virtuale|(} Come spiegato in sez.~\ref{sec:proc_mem_gen} il kernel gestisce la memoria virtuale in maniera trasparente ai processi, decidendo quando rimuovere pagine dalla memoria per metterle nello swap, sulla base dell'utilizzo corrente da @@ -749,6 +752,7 @@ motivi per cui si possono avere di queste necessit crittografia richiedono il blocco di alcune pagine di memoria. \end{itemize} +\index{\textit{memory~locking}|(} Il meccanismo che previene la paginazione\index{paginazione} di parte della memoria virtuale di un processo è chiamato \textit{memory locking} (o \textsl{blocco della memoria}). Il blocco è sempre associato alle pagine della @@ -766,10 +770,10 @@ memoria bloccata non la sblocca. Chiaramente la terminazione del processo comporta anche la fine dell'uso della sua memoria virtuale, e quindi anche di tutti i suoi \textit{memory lock}. Infine \textit{memory lock} non sono ereditati dai processi figli.\footnote{ma siccome Linux usa il \textit{copy on - write}\index{copy on write} (vedi sez.~\ref{sec:proc_fork}) gli indirizzi - virtuali del figlio sono mantenuti sullo stesso segmento di RAM del padre, - quindi fintanto che un figlio non scrive su un segmento, può usufruire del - memory lock del padre.} + write} (vedi sez.~\ref{sec:proc_fork}) gli indirizzi virtuali del figlio + sono mantenuti sullo stesso segmento di RAM del padre, quindi fintanto che + un figlio non scrive su un segmento, può usufruire del \textit{memory lock} + del padre.} Siccome la richiesta di un \textit{memory lock} da parte di un processo riduce la memoria fisica disponibile nel sistema, questo ha un evidente impatto su @@ -852,44 +856,46 @@ esempio limitandosi a tutte le pagine allocate a partire da un certo momento. In ogni caso un processo real-time che deve entrare in una sezione critica deve provvedere a riservare memoria sufficiente prima dell'ingresso, per -scongiurare l'occorrenza di un eventuale \textit{page fault}\index{page fault} -causato dal meccanismo di \textit{copy on write}\index{copy on write}. -Infatti se nella sezione critica si va ad utilizzare memoria che non è ancora -stata riportata in RAM si potrebbe avere un page fault durante l'esecuzione -della stessa, con conseguente rallentamento (probabilmente inaccettabile) dei -tempi di esecuzione. +scongiurare l'occorrenza di un eventuale \textit{page + fault}\index{\textit{page~fault}} causato dal meccanismo di \textit{copy on + write}\index{\textit{copy~on~write}}. Infatti se nella sezione critica si +va ad utilizzare memoria che non è ancora stata riportata in RAM si potrebbe +avere un page fault durante l'esecuzione della stessa, con conseguente +rallentamento (probabilmente inaccettabile) dei tempi di esecuzione. In genere si ovvia a questa problematica chiamando una funzione che ha allocato una quantità sufficientemente ampia di variabili automatiche, in modo che esse vengano mappate in RAM dallo stack, dopo di che, per essere sicuri che esse siano state effettivamente portate in memoria, ci si scrive sopra. +\index{memoria~virtuale|)} +\index{\textit{memory~locking}|)} -\section{Parametri, opzioni ed ambiente di un processo} +\section{Argomenti, opzioni ed ambiente di un processo} \label{sec:proc_options} -Tutti i programmi hanno la possibilità di ricevere parametri e opzioni quando -vengono lanciati. Il passaggio dei parametri è effettuato attraverso gli +Tutti i programmi hanno la possibilità di ricevere argomenti e opzioni quando +vengono lanciati. Il passaggio degli argomenti è effettuato attraverso gli argomenti \param{argc} e \param{argv} della funzione \func{main}, che vengono passati al programma dalla shell (o dal processo che esegue la \func{exec}, secondo le modalità che vedremo in sez.~\ref{sec:proc_exec}) quando questo viene messo in esecuzione. -Oltre al passaggio dei parametri, un'altra modalità che permette di passare +Oltre al passaggio degli argomenti, un'altra modalità che permette di passare delle informazioni che modifichino il comportamento di un programma è quello dell'uso del cosiddetto \textit{environment} (cioè l'uso delle \textsl{variabili di ambiente}). In questa sezione esamineremo le funzioni che -permettono di gestire parametri ed opzioni, e quelle che consentono di +permettono di gestire argomenti ed opzioni, e quelle che consentono di manipolare ed utilizzare le variabili di ambiente. -\subsection{Il formato dei parametri} +\subsection{Il formato degli argomenti} \label{sec:proc_par_format} -In genere passaggio dei parametri al programma viene effettuato dalla shell, +In genere passaggio degli argomenti al programma viene effettuato dalla shell, che si incarica di leggere la linea di comando e di effettuarne la scansione (il cosiddetto \textit{parsing}) per individuare le parole che la compongono, -ciascuna delle quali viene considerata un parametro. Di norma per individuare +ciascuna delle quali viene considerata un argomento. Di norma per individuare le parole viene usato come carattere di separazione lo spazio o il tabulatore, ma il comportamento è modificabile attraverso l'impostazione della variabile di ambiente \cmd{IFS}. @@ -904,7 +910,7 @@ di ambiente \cmd{IFS}. Nella scansione viene costruito il vettore di puntatori \param{argv} inserendo in successione il puntatore alla stringa costituente l'$n$-simo parametro; la -variabile \param{argc} viene inizializzata al numero di parametri trovati, in +variabile \param{argc} viene inizializzata al numero di argomenti trovati, in questo modo il primo parametro è sempre il nome del programma; un esempio di questo meccanismo è mostrato in fig.~\ref{fig:proc_argv_argc}. @@ -1029,8 +1035,8 @@ 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 vettore di puntatori a -caratteri, ciascuno dei quali punta ad una stringa, terminata da un +Come per la lista degli argomenti 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 del vettore data da un equivalente di \param{argc}, ma la lista è terminata da un puntatore nullo. @@ -1066,13 +1072,13 @@ configurazione. queste variabili al programma messo in esecuzione attraverso un uso opportuno delle relative chiamate (si veda sez.~\ref{sec:proc_exec}). -La shell ad esempio ne usa molte per il suo funzionamento (come \var{PATH} per -la ricerca dei comandi, o \cmd{IFS} per la scansione degli argomenti), e -alcune di esse (come \var{HOME}, \var{USER}, etc.) sono definite al login (per -i dettagli si veda sez.~\ref{sec:sess_login}). In genere è cura +La shell ad esempio ne usa molte per il suo funzionamento (come \texttt{PATH} +per la ricerca dei comandi, o \texttt{IFS} per la scansione degli argomenti), +e alcune di esse (come \texttt{HOME}, \texttt{USER}, etc.) sono definite al +login (per i dettagli si veda sez.~\ref{sec:sess_login}). In genere è cura dell'amministratore definire le opportune variabili di ambiente in uno script di avvio. Alcune servono poi come riferimento generico per molti programmi -(come \var{EDITOR} che indica l'editor preferito da invocare in caso di +(come \texttt{EDITOR} che indica l'editor preferito da invocare in caso di necessità). Gli standard POSIX e XPG3 definiscono alcune di queste variabili (le più @@ -1089,22 +1095,22 @@ controllare \cmd{man environ}. & \textbf{Linux} & \textbf{Descrizione} \\ \hline \hline - \val{USER} & $\bullet$ & $\bullet$ & $\bullet$ & Nome utente\\ - \val{LOGNAME} & $\bullet$ & $\bullet$ & $\bullet$ & Nome di login\\ - \val{HOME} & $\bullet$ & $\bullet$ & $\bullet$ & - Directory base dell'utente\\ - \val{LANG} & $\bullet$ & $\bullet$ & $\bullet$ & Localizzazione\\ - \val{PATH} & $\bullet$ & $\bullet$ & $\bullet$ & Elenco delle directory - dei programmi\\ - \val{PWD} & $\bullet$ & $\bullet$ & $\bullet$ & Directory corrente\\ - \val{SHELL} & $\bullet$ & $\bullet$ & $\bullet$ & Shell in uso\\ - \val{TERM} & $\bullet$ & $\bullet$ & $\bullet$ & Tipo di terminale\\ - \val{PAGER} & $\bullet$ & $\bullet$ & $\bullet$ & Programma per vedere i - testi\\ - \val{EDITOR} & $\bullet$ & $\bullet$ & $\bullet$ & Editor preferito\\ - \val{BROWSER} & $\bullet$ & $\bullet$ & $\bullet$ & Browser preferito\\ - \val{TMPDIR} & $\bullet$ & $\bullet$ & $\bullet$ & Directory dei file - temporanei\\ + \texttt{USER} &$\bullet$&$\bullet$&$\bullet$& Nome utente\\ + \texttt{LOGNAME}&$\bullet$&$\bullet$&$\bullet$& Nome di login\\ + \texttt{HOME} &$\bullet$&$\bullet$&$\bullet$& Directory base + dell'utente\\ + \texttt{LANG} &$\bullet$&$\bullet$&$\bullet$& Localizzazione\\ + \texttt{PATH} &$\bullet$&$\bullet$&$\bullet$& Elenco delle directory + dei programmi\\ + \texttt{PWD} &$\bullet$&$\bullet$&$\bullet$& Directory corrente\\ + \texttt{SHELL} &$\bullet$&$\bullet$&$\bullet$& Shell in uso\\ + \texttt{TERM} &$\bullet$&$\bullet$&$\bullet$& Tipo di terminale\\ + \texttt{PAGER} &$\bullet$&$\bullet$&$\bullet$& Programma per vedere i + testi\\ + \texttt{EDITOR} &$\bullet$&$\bullet$&$\bullet$& Editor preferito\\ + \texttt{BROWSER}&$\bullet$&$\bullet$&$\bullet$& Browser preferito\\ + \texttt{TMPDIR} &$\bullet$&$\bullet$&$\bullet$& Directory dei file + temporanei\\ \hline \end{tabular} \caption{Esempi delle variabili di ambiente più comuni definite da vari @@ -1204,7 +1210,7 @@ invece esiste il suo valore sar seguendo il comportamento di BSD4.4; dato che questo può dar luogo a perdite di memoria e non rispetta lo standard. Il comportamento è stato modificato a partire dalle 2.1.2, eliminando anche, sempre in conformità a SUSv2, - l'attributo \ctyp{const} dal prototipo.} \param{string} alla lista delle + l'attributo \direct{const} dal prototipo.} \param{string} alla lista delle variabili di ambiente; pertanto ogni cambiamento alla stringa in questione si riflette automaticamente sull'ambiente, e quindi si deve evitare di passare a questa funzione una variabile automatica (per evitare i problemi esposti in @@ -1279,16 +1285,17 @@ chiamante. Nella maggior parte delle funzioni di libreria e delle system call i puntatori vengono usati per scambiare dati (attraverso buffer o strutture) e le -variabili semplici vengono usate per specificare parametri; in genere le +variabili semplici vengono usate per specificare argomenti; in genere le informazioni a riguardo dei risultati vengono passate alla routine chiamante attraverso il valore di ritorno. È buona norma seguire questa pratica anche nella programmazione normale. Talvolta però è necessario che la funzione possa restituire indietro alla -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 +funzione chiamante un valore relativo ad uno dei suoi argomenti. Per far +questo si usa il cosiddetto +\index{\textit{value~result~argument}}\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 sez.~\ref{sec:TCP_functions}), in cui, per permettere al kernel di restituire informazioni sulle dimensioni delle strutture degli indirizzi utilizzate, viene usato questo meccanismo. @@ -1298,7 +1305,7 @@ viene usato questo meccanismo. \label{sec:proc_variadic} Come vedremo nei capitoli successivi, non sempre è possibile specificare un -numero fisso di parametri per una funzione. Lo standard ISO C prevede nella +numero fisso di argomenti per una funzione. Lo standard ISO C prevede nella sua sintassi la possibilità di definire delle \textit{variadic function}\index{variadic} che abbiano un numero variabile di argomenti, attraverso l'uso della \textit{ellipsis} \code{...} nella dichiarazione della @@ -1323,8 +1330,8 @@ deve essere incluso l'apposito header file \file{stdarg.h}; un esempio di dichiarazione è il prototipo della funzione \func{execl} che vedremo in sez.~\ref{sec:proc_exec}: \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 +in questo caso la funzione prende due argomenti fissi ed un numero variabile +di altri argomenti (che verranno a costituire gli elementi successivi al primo del vettore \param{argv} passato al nuovo processo). Lo standard ISO C richiede inoltre che l'ultimo degli argomenti fissi sia di tipo \textit{self-promoting}\footnote{il linguaggio C prevede che quando si @@ -1335,11 +1342,11 @@ inoltre che l'ultimo degli argomenti fissi sia 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}. +\direct{register}. -Una volta dichiarata la funzione il secondo passo è accedere ai vari parametri -quando la si va a definire. I parametri fissi infatti hanno un loro nome, ma -quelli variabili vengono indicati in maniera generica dalla ellipsis. +Una volta dichiarata la funzione il secondo passo è accedere ai vari argomenti +quando la si va a definire. Gli argomenti fissi infatti hanno un loro nome, ma +quelli variabili vengono indicati in maniera generica dalla \textit{ellipsis}. L'unica modalità in cui essi possono essere recuperati è pertanto quella sequenziale; essi verranno estratti dallo stack secondo l'ordine in cui sono @@ -1351,7 +1358,7 @@ macro; la procedura da seguire \item Accedere ai vari argomenti opzionali con chiamate successive alla macro \macro{va\_arg}, la prima chiamata restituirà il primo argomento, la seconda il secondo e così via. -\item Dichiarare la conclusione dell'estrazione dei parametri invocando la +\item Dichiarare la conclusione dell'estrazione degli argomenti invocando la macro \macro{va\_end}. \end{enumerate*} in generale è perfettamente legittimo richiedere meno argomenti di quelli che @@ -1368,7 +1375,7 @@ Le definizioni delle tre macro sono le seguenti: \funcdecl{void va\_start(va\_list ap, last)} Inizializza il puntatore alla lista di argomenti \param{ap}; il parametro \param{last} \emph{deve} essere - l'ultimo dei parametri fissi. + l'ultimo degli argomenti fissi. \funcdecl{type va\_arg(va\_list ap, type)} Restituisce il valore del successivo parametro opzionale, modificando opportunamente \param{ap}; la @@ -1395,12 +1402,12 @@ caso per usato (lo standard richiederebbe la chiamata esplicita di \macro{va\_end}), dato che il valore di \param{ap} risulterebbe indefinito. -Esistono dei casi in cui è necessario eseguire più volte la scansione dei -parametri e poter memorizzare una posizione durante la stessa. La cosa più +Esistono dei casi in cui è necessario eseguire più volte la scansione degli +argomenti e poter memorizzare una posizione durante la stessa. La cosa più naturale in questo caso sembrerebbe quella di copiarsi il puntatore alla lista degli argomenti con una semplice assegnazione. Dato che una delle realizzazioni più comuni di \macro{va\_list} è quella di un puntatore nello -stack all'indirizzo dove sono stati salvati i parametri, è assolutamente +stack all'indirizzo dove sono stati salvati gli argomenti, è assolutamente normale pensare di poter effettuare questa operazione. In generale però possono esistere anche realizzazioni diverse, per questo @@ -1421,9 +1428,9 @@ alla lista degli argomenti. La chiamata di una funzione con un numero variabile di argomenti, posto che la si sia dichiarata e definita come tale, non prevede nulla di particolare; -l'invocazione è identica alle altre, con i parametri, sia quelli fissi che +l'invocazione è identica alle altre, con gli argomenti, sia quelli fissi che quelli opzionali, separati da virgole. Quello che però è necessario tenere -presente è come verranno convertiti gli argomenti variabili. +presente è come verranno convertiti gli argomenti variabili. In Linux gli argomenti dello stesso tipo sono passati allo stesso modo, sia che siano fissi sia che siano opzionali (alcuni sistemi trattano diversamente @@ -1434,7 +1441,7 @@ dei medesimi occorrer Uno dei problemi che si devono affrontare con le funzioni con un numero variabile di argomenti è che non esiste un modo generico che permetta di -stabilire quanti sono i parametri passati effettivamente in una chiamata. +stabilire quanti sono gli argomenti passati effettivamente in una chiamata. Esistono varie modalità per affrontare questo problema; una delle più immediate è quella di specificare il numero degli argomenti opzionali come uno @@ -1442,8 +1449,8 @@ degli argomenti fissi. Una variazione di questo metodo per specificare anche il tipo degli argomenti (come fa la stringa di formato per \func{printf}). -Una modalità diversa, che può essere applicata solo quando il tipo dei -parametri lo rende possibile, è quella che prevede di usare un valore speciale +Una modalità diversa, che può essere applicata solo quando il tipo degli +argomenti lo rende possibile, è quella che prevede di usare un valore speciale come ultimo argomento (come fa ad esempio \func{execl} che usa un puntatore \val{NULL} per indicare la fine della lista degli argomenti). @@ -1477,10 +1484,12 @@ caso in cui l'uso di questa istruzione porta all'implementazione pi efficiente e più chiara anche dal punto di vista della struttura del programma: quello dell'uscita in caso di errore. -Il C però non consente di effettuare un salto ad una etichetta definita in -un'altra funzione, per cui se l'errore avviene in una funzione, e la sua -gestione ordinaria è in un'altra, occorre usare quello che viene chiamato un -\textsl{salto non-locale}\index{salto non-locale}. Il caso classico in cui si +\index{salto~non-locale|(} + +Il C però non consente di effettuare un salto ad +una etichetta definita in un'altra funzione, per cui se l'errore avviene in +una funzione, e la sua gestione ordinaria è in un'altra, occorre usare quello +che viene chiamato un \textsl{salto non-locale}. Il caso classico in cui si ha questa necessità, citato sia da \cite{APUE} che da \cite{glibc}, è quello di un programma nel cui corpo principale vengono letti dei dati in ingresso sui quali viene eseguita, tramite una serie di funzioni di analisi, una @@ -1497,7 +1506,7 @@ molto pi scartando l'input come errato.\footnote{a meno che, come precisa \cite{glibc}, alla chiusura di ciascuna fase non siano associate operazioni di pulizia specifiche (come deallocazioni, chiusure di file, ecc.), che non potrebbero - essere eseguite con un salto non-locale\index{salto non-locale}.} + essere eseguite con un salto non-locale.} Tutto ciò può essere realizzato proprio con un salto non-locale; questo di norma viene realizzato salvando il contesto dello stack nel punto in cui si @@ -1529,13 +1538,12 @@ essere viste in tutte le funzioni del programma. Quando viene eseguita direttamente la funzione ritorna sempre zero, un valore diverso da zero viene restituito solo quando il ritorno è dovuto ad una chiamata di \func{longjmp} in un'altra parte del programma che ripristina lo -stack effettuando il salto non-locale\index{salto non-locale}. Si tenga conto -che il contesto salvato in \param{env} viene invalidato se la routine che ha -chiamato \func{setjmp} ritorna, nel qual caso un successivo uso di -\func{longjmp} può comportare conseguenze imprevedibili (e di norma fatali) -per il processo. +stack effettuando il salto non-locale. Si tenga conto che il contesto salvato +in \param{env} viene invalidato se la routine che ha chiamato \func{setjmp} +ritorna, nel qual caso un successivo uso di \func{longjmp} può comportare +conseguenze imprevedibili (e di norma fatali) per il processo. -Come accennato per effettuare un salto non-locale\index{salto non-locale} ad +Come accennato per effettuare un salto non-locale ad un punto precedentemente stabilito con \func{setjmp} si usa la funzione \funcd{longjmp}; il suo prototipo è: \begin{functions} @@ -1580,11 +1588,11 @@ In generale, dato che l'unica differenza fra la chiamata diretta e quella ottenuta da un \func{longjmp}, è il valore di ritorno di \func{setjmp}, essa è usualmente chiamata all'interno di un comando \code{if}. -Uno dei punti critici dei salti non-locali\index{salto non-locale} è quello -del valore delle variabili, ed in particolare quello delle variabili -automatiche della funzione a cui si ritorna. In generale le variabili globali -e statiche mantengono i valori che avevano al momento della chiamata di -\func{longjmp}, ma quelli delle variabili automatiche (o di quelle dichiarate +Uno dei punti critici dei salti non-locali è quello del valore delle +variabili, ed in particolare quello delle variabili automatiche della funzione +a cui si ritorna. In generale le variabili globali 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 è @@ -1602,15 +1610,15 @@ 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 -\direct{volatile}\footnote{la direttiva \ctyp{volatile} informa il compilatore - che la variabile che è dichiarata può essere modificata, durante +\direct{volatile}\footnote{la direttiva \direct{volatile} informa il + compilatore che la variabile che è dichiarata può essere modificata, durante l'esecuzione del nostro, da altri programmi. Per questo motivo occorre dire al compilatore che non deve essere mai utilizzata l'ottimizzazione per cui quanto opportuno essa viene mantenuta in un registro, poiché in questo modo si perderebbero le eventuali modifiche fatte dagli altri programmi (che avvengono solo in una copia posta in memoria).}. - +\index{salto~non-locale|)} %%% Local Variables: %%% mode: latex