X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=a5695b7dd004974e5b137e924f70a21cfbe09b91;hp=d23e54f01cecde1c041d75116a2399c8129261d8;hb=996f31582fc276069a62f707bc5bdb4255342062;hpb=2f9a4c398ce7f9c40b6f07a3d643098b6ed4ff27 diff --git a/process.tex b/process.tex index d23e54f..a5695b7 100644 --- a/process.tex +++ b/process.tex @@ -1213,7 +1213,6 @@ 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è, @@ -1363,7 +1362,6 @@ argomenti opzionali, questi verranno sempre promossi, pertanto nella ricezione dei medesimi occorrerà tenerne conto (ad esempio un \ctyp{char} verrà visto da \macro{va\_arg} come \ctyp{int}). - 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. @@ -1397,6 +1395,7 @@ anche dalla funzione chiamante queste devono essere allocate esplicitamente, o in maniera statica (usando variabili di tipo \ctyp{static} o \ctyp{extern}), o dinamicamente con una delle funzioni della famiglia \func{malloc}. + \subsection{Il controllo di flusso non locale} \label{sec:proc_longjmp} @@ -1405,46 +1404,119 @@ varie istruzioni del linguaggio C; fra queste la pi \code{goto}, che viene deprecato in favore dei costrutti della programmazione strutturata, che rendono il codice più leggibile e mantenibile . Esiste però un caso in cui l'uso di questa istruzione porta all'implementazione più -efficiente e chiara anche dal punto di vista della struttura del programma, +efficiente e 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 label definita in -un'altra funzione, per cui se l'errore avviene in funzioni profondamente -annidate occorre usare quello che viene chiamato un salto \textsl{non-locale}; -questo viene fatto usando salvando il contesto dello stack nel punto in cui si -vuole tornare in caso di errore, e ripristinandolo quando l'occorrenza capita. - -La funzione che permette di salvare il contesto dello stack è \func{setjmp}, -il cui prototipo è: - +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 da \cite{glibc}, è quello di un programma nel +ccui corpo principale in cui viene letto un input del quale viene eseguita +attraverso una serie di funzioni di analisi una scansione dei contenuti da cui +ottenere le indicazioni per l'esecuzione di opportune operazioni. + +Dato che l'analisi può risultare molto complessa, ed opportunamente suddivisa +in fasi diverse, la rilevazione di un errore nell'input può accadere +all'interno di funzioni profondamente annidate l'una nell'altra. In questo +caso si dovrebbe per ciascuna fase dover gestire tutta la casistica del +passaggio all'indietro di tutti gli errori rilevabili dalle funzioni usate +nelle fasi successive, mentre sarebbe molto più comodo poter tornare +direttamente al ciclo di lettura principale, 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.} + +Tutto ciò può essere realizzato salvando il contesto dello stack nel punto in +cui si vuole tornare in caso di errore, e ripristinandolo quando l'occorrenza +capita. La funzione che permette di salvare il contesto dello stack è +\func{setjmp}, il cui prototipo è: \begin{functions} \headdecl{setjmp.h} \funcdecl{void setjmp(jmp\_buf env)} Salva il contesto dello stack in \param{env} per un successivo uso da parte - di \func{longjmp}. Il contesto viene invalidato se la routine che ha - chiamato \func{setjmp} ritorna. - + di \func{longjmp}. + \bodydesc{La funzione ritorna zero quando è chiamata direttamente e un valore diverso da zero quando ritorna da una chiamata di \func{longjmp} che usa il contesto salvato in precedenza.} \end{functions} - -Per poter effettuare un salto non locale si usa la funzione \func{longjmp}; il -suo prototipo è: +Quando si esegue la funzione il contesto viene salvato in appositi oggetti (di +tipo \type{jmp\_buf}), passati come primo argomento alla funzione, in genere +questi vengono definiti come variabili globali in modo da poter essere visti +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 programa. Si tenga conto che +il contesto salvato in \param{env} viene invalidato se la routine che ha +chiamato \func{setjmp} ritorna, nel qual caso l'uso di \func{longjmp} può +comportare conseguenze imprevedibili (e di norma fatali per il processo). + +Come accennato per effettuare un salto non-locale ad un punto precedentemente +stabilito con \func{setjmp} si usa la funzione \func{longjmp}; il suo +prototipo è: \begin{functions} \headdecl{setjmp.h} \funcdecl{void longjmp(jmp\_buf env, int val)} - Ripristina il contesto dello stack salvato dall'ultima chiamata di - \func{setjmp} con l'argomento \param{env}. Il programma prosegue dal ritorno - di \func{setjmp} con un valore \param{val}. Il valore di \param{val} deve - essere diverso da zero, se viene specificato 0 sarà usato 1 al suo posto. - + Ripristina il contesto dello stack salvato nell'ultima chiamata di + \func{setjmp} con l'argomento \param{env}. + \bodydesc{La funzione non ritorna.} \end{functions} +Dopo l'esecuzione della funzione programma prosegue dal 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 da zero, se si è specificato 0 sarà +comunque restituito 1 al suo posto. + +In sostanza un \func{longjmp} è analogo ad un \code{return}, solo che invece +di ritornare alla riga succesiva della funzione chiamante, il programma +ritorna alla posizione della relativa \func{setjmp}, ed il ritorno può essere +effettuato anche attraverso diversi livelli di funzioni annidate. + +L'implementazione di queste funzioni comporta alcune restrizioni dato che esse +interagiscono direttamente con la gestione dello stack ed il funzionamento del +compilatore stesso. In particolare \func{setjmp} è implementata con una macro, +pertanto non si può cercare di ottenerne l'indirizzo, ed inoltre delle +chiamate a questa funzione sono sicure solo in uno dei seguenti casi: +\begin{itemize} +\item come espressione di controllo in un comando condizionale, di selezione + o di iterazione (come \code{if}, \code{switch} o \code{while}). +\item come operando per un operatore di uguaglianza o confronto in una + espressione di controllo di un comando condizionale, di selezione o di + iterazione. +\item come operando per l'operatore di negazione (\code{!}) in una espressione + di controllo di un comando condizionale, di selezione o di iterazione. +\item come espressione a sé stante. +\end{itemize} + +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 è 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 \code{register}) sono in +genere indeterminati. + +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) +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 +\code{volatile}. + + %%% Local Variables: %%% mode: latex