X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=cb1fa55c6d08085c353b156bc01fab9ba1de09c9;hp=43642887bab32ae35d35f858c0504c339aa680c8;hb=2535dbace6167c4bcff7dd9c98f4d397bb699afd;hpb=99fa5a06cd27160cf673e3483ad552d32efa2c05 diff --git a/process.tex b/process.tex index 4364288..cb1fa55 100644 --- a/process.tex +++ b/process.tex @@ -1,6 +1,6 @@ %% process.tex %% -%% Copyright (C) 2000-2015 by Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2019 by 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 "Un preambolo", @@ -36,7 +36,7 @@ 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. Questo non è del tutto vero nel caso di un programma \textit{multi-thread}, ma la gestione dei \textit{thread} in Linux sarà -trattata a parte in cap.~\ref{cha:threads}. +trattata a parte\unavref{in cap.~\ref{cha:threads}}. \subsection{L'avvio e l'esecuzione di un programma} @@ -44,9 +44,9 @@ trattata a parte in cap.~\ref{cha:threads}. \itindbeg{link-loader} \itindbeg{shared~objects} -Quando un programma viene messo in esecuzione cosa che può essere fatta solo -con una funzione della famiglia \func{exec} (vedi sez.~\ref{sec:proc_exec}) il -kernel esegue un opportuno codice di avvio, il cosiddetto +Quando un programma viene messo in esecuzione, cosa che può essere fatta solo +con una funzione della famiglia \func{exec} (vedi sez.~\ref{sec:proc_exec}), +il kernel esegue un opportuno codice di avvio, il cosiddetto \textit{link-loader}, costituito dal programma \cmd{ld-linux.so}. Questo programma è una parte fondamentale del sistema il cui compito è quello della gestione delle cosiddette \textsl{librerie condivise}, quelle che nel mondo @@ -64,15 +64,15 @@ una sola volta per tutti i programmi che lo usano. Questo significa però che normalmente il codice di un programma è incompleto, contenendo solo i riferimenti alle funzioni di libreria che vuole utilizzare e non il relativo codice. Per questo motivo all'avvio del programma è necessario -l'intervento del \textit{link-loader} il cui compito è -caricare in memoria le librerie condivise eventualmente assenti, ed effettuare -poi il collegamento dinamico del codice del programma alle funzioni di -libreria da esso utilizzate prima di metterlo in esecuzione. +l'intervento del \textit{link-loader} il cui compito è caricare in memoria le +librerie condivise eventualmente assenti, ed effettuare poi il collegamento +dinamico del codice del programma alle funzioni di libreria da esso utilizzate +prima di metterlo in esecuzione. Il funzionamento di \cmd{ld-linux.so} è controllato da alcune variabili di -ambiente e dal contenuto del file \conffile{/etc/ld.so.conf}, che consentono -di elencare le directory un cui cercare le librerie e determinare quali -verranno utilizzate. In particolare con la variabile di ambiente +ambiente e dal contenuto del file \conffile{/etc/ld.so.conf} che consentono di +elencare le directory un cui cercare le librerie e determinare quali verranno +utilizzate. In particolare con la variabile di ambiente \envvar{LD\_LIBRARY\_PATH} si possono indicare ulteriori directory rispetto a quelle di sistema in cui inserire versioni personali delle librerie che hanno la precedenza su quelle di sistema, mentre con la variabile di ambiente @@ -292,18 +292,28 @@ una qualunque funzione ordinaria, la situazione è totalmente diversa nell'esecuzione del programma. Una funzione ordinaria infatti viene eseguita, esattamente come il codice che si è scritto nel corpo del programma, in \textit{user space}. Quando invece si esegue una \textit{system call} -l'esecuzione ordinaria del programma viene interrotta, i dati forniti (come -argomenti della chiamata) vengono trasferiti al kernel che esegue il codice -della \textit{system call} (che è codice del kernel) in \textit{kernel space}. +l'esecuzione ordinaria del programma viene interrotta con quello che viene +usualmente chiamato un \itindex{context~switch} \textit{context + switch};\footnote{in realtà si parla più comunemente di \textit{context + switch} quando l'esecuzione di un processo viene interrotta dal kernel + (tramite lo \textit{scheduler}) per metterne in esecuzione un altro, ma il + concetto generale resta lo stesso: l'esecuzione del proprio codice in + \textit{user space} viene interrotta e lo stato del processo deve essere + salvato per poterne riprendere l'esecuzione in un secondo tempo.} il +contesto di esecuzione del processo viene salvato in modo da poterne +riprendere in seguito l'esecuzione ed i dati forniti (come argomenti della +chiamata) vengono trasferiti al kernel che esegue il codice della +\textit{system call} (che è codice del kernel) in \textit{kernel space}; al +completamento della \textit{system call} i dati salvati nel \textit{context + switch} saranno usati per riprendere l'esecuzione ordinaria del programma. Dato che il passaggio dei dati ed il salvataggio del contesto di esecuzione -del programma che consentirà di riprenderne l'esecuzione ordinaria al -completamento della \textit{system call} sono operazioni critiche per le -prestazioni del sistema, per rendere il più veloce possibile questa -operazione, usualmente chiamata \textit{context switch} sono state sviluppate -una serie di ottimizzazioni che richiedono alcune preparazioni abbastanza -complesse dei dati, che in genere dipendono dall'architettura del processore -sono scritte direttamente in \textit{assembler}. +sono operazioni critiche per le prestazioni del sistema, per rendere il più +veloce possibile questa operazione sono state sviluppate una serie di +ottimizzazioni che richiedono alcune preparazioni abbastanza complesse dei +dati, che in genere dipendono dall'architettura del processore e sono scritte +direttamente in \textit{assembler}. + % % TODO:trattare qui, quando sarà il momento vsyscall e vDSO, vedi: @@ -325,14 +335,19 @@ associazione, e lavorare a basso livello con una specifica versione, oppure si può voler utilizzare una \textit{system call} che non è stata ancora associata ad una funzione di libreria. In tal caso, per evitare di dover effettuare esplicitamente le operazioni di preparazione citate, all'interno della -\textsl{glibc} è fornita una specifica funzione, \funcd{syscall}, che consente -eseguire direttamente una \textit{system call}; il suo prototipo, accessibile -se si è definita la macro \macro{\_GNU\_SOURCE}, è: +\textsl{glibc} è fornita una specifica funzione, +\funcd{syscall},\footnote{fino a prima del kernel 2.6.18 per l'esecuzione + diretta delle \textit{system call} erano disponibili anche una serie di + macro \texttt{\_syscall\textsl{N}} (con $N$ pari al numero di argomenti + della \textit{system call}); queste sono deprecate e pertanto non ne + parleremo ulteriormente.} che consente eseguire direttamente una +\textit{system call}; il suo prototipo, accessibile se si è definita la macro +\macro{\_GNU\_SOURCE}, è: \begin{funcproto}{ \fhead{unistd.h} \fhead{sys/syscall.h} - \fdecl{int syscall(int number, ...)} + \fdecl{long syscall(int number, ...)} \fdesc{Esegue la \textit{system call} indicata da \param{number}.} } {La funzione ritorna un intero dipendente dalla \textit{system call} invocata, @@ -366,7 +381,7 @@ linguaggio C all'interno della stessa, o se si richiede esplicitamente la chiusura invocando direttamente la funzione \func{exit}. Queste due modalità sono assolutamente equivalenti, dato che \func{exit} viene chiamata in maniera trasparente anche quando \code{main} ritorna, passandogli come argomento il -valore di ritorno (che essendo . +valore indicato da \instruction{return}. La funzione \funcd{exit}, che è completamente generale, essendo definita dallo standard ANSI C, è quella che deve essere invocata per una terminazione @@ -391,7 +406,7 @@ vedremo a breve) che completa la terminazione del processo. \itindbeg{exit~status} -Il valore dell'argomento \param{status} o il valore di ritorno di \code{main}, +Il valore dell'argomento \param{status} o il valore di ritorno di \code{main} costituisce quello che viene chiamato lo \textsl{stato di uscita} (l'\textit{exit status}) del processo. In generale si usa questo valore per fornire al processo padre (come vedremo in sez.~\ref{sec:proc_wait}) delle @@ -400,22 +415,22 @@ terminato. Anche se l'argomento \param{status} (ed il valore di ritorno di \code{main}) sono numeri interi di tipo \ctyp{int}, si deve tener presente che il valore -dello stato di uscita viene comunque troncato ad 8 bit, -per cui deve essere sempre compreso fra 0 e 255. Si tenga presente che se si -raggiunge la fine della funzione \code{main} senza ritornare esplicitamente si -ha un valore di uscita indefinito, è pertanto consigliabile di concludere -sempre in maniera esplicita detta funzione. - -Non esiste un valore significato intrinseco della stato di uscita, ma una -convenzione in uso pressoché universale è quella di restituire 0 in caso di -successo e 1 in caso di fallimento. Una eccezione a questa convenzione è 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. Un'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é le convenzioni citate non siano seguite universalmente è -una buona idea tenerle presenti ed adottarle a seconda dei casi. +dello stato di uscita viene comunque troncato ad 8 bit, per cui deve essere +sempre compreso fra 0 e 255. Si tenga presente che se si raggiunge la fine +della funzione \code{main} senza ritornare esplicitamente si ha un valore di +uscita indefinito, è pertanto consigliabile di concludere sempre in maniera +esplicita detta funzione. + +Non esiste un significato intrinseco della stato di uscita, ma una convenzione +in uso pressoché universale è quella di restituire 0 in caso di successo e 1 +in caso di fallimento. Una eccezione a questa convenzione è 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. Un'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é le +convenzioni citate non siano seguite universalmente è una buona idea tenerle +presenti ed adottarle a seconda dei casi. Si tenga presente inoltre che non è una buona idea usare eventuali codici di errore restituiti nella variabile \var{errno} (vedi sez.~\ref{sec:sys_errors}) @@ -437,8 +452,12 @@ i valori 0 e 1. Una forma alternativa per effettuare una terminazione esplicita di un programma è quella di chiamare direttamente la \textit{system call} \funcd{\_exit},\footnote{la stessa è definita anche come \funcd{\_Exit} in - \headfile{stdlib.h}.} che restituisce il controllo direttamente al kernel, -concludendo immediatamente il processo, il suo prototipo è: + \headfile{stdlib.h}, inoltre a partire dalla \acr{glibc} 2.3 usando questa + funzione viene invocata \func{exit\_group} che termina tutti i + \textit{thread} del processo e non solo quello corrente (fintanto che non si + usano i \textit{thread}\unavref{, vedi sez.~\ref{cha:threads},} questo non + fa nessuna differenza).} che restituisce il controllo direttamente al +kernel, concludendo immediatamente il processo, il suo prototipo è: \begin{funcproto}{ \fhead{unistd.h} \fdecl{void \_exit(int status)} \fdesc{Causa la conclusione immediata del programma.} } {La funzione non @@ -454,8 +473,8 @@ sez.~\ref{sec:file_unix_interface} e sez.~\ref{sec:files_std_interface}). Infine fa sì che ogni figlio del processo sia adottato da \cmd{init} (vedi sez.~\ref{sec:proc_termination}), manda un segnale \signal{SIGCHLD} al processo padre (vedi -sez.~\ref{sec:sig_job_control}) e ritorna lo stato di uscita specificato -in \param{status} che può essere raccolto usando la funzione \func{wait} (vedi +sez.~\ref{sec:sig_job_control}) e salva lo stato di uscita specificato in +\param{status} che può essere raccolto usando la funzione \func{wait} (vedi sez.~\ref{sec:proc_wait}). Si tenga presente infine che oltre alla conclusione ``\textsl{normale}'' @@ -469,12 +488,12 @@ funzione \func{abort}; torneremo su questo in sez.~\ref{sec:proc_termination}. \label{sec:proc_atexit} Un'esigenza comune che si incontra è quella di dover effettuare una serie di -operazioni di pulizia (ad esempio salvare dei dati, ripristinare delle -impostazioni, eliminare dei file temporanei, ecc.) prima della conclusione di -un programma. In genere queste operazioni vengono fatte in un'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. +operazioni di pulizia prima della conclusione di un programma, ad esempio +salvare dei dati, ripristinare delle impostazioni, eliminare dei file +temporanei, ecc. In genere queste operazioni vengono fatte in un'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 fare effettuare automaticamente la @@ -503,10 +522,13 @@ opportuna funzione di pulizia da chiamare all'uscita del programma, che non deve prendere argomenti e non deve ritornare niente. In sostanza deve la funzione di pulizia dovrà essere definita come \code{void function(void)}. -Un'estensione di \func{atexit} è la funzione \funcd{on\_exit}, che le -\acr{glibc} includono per compatibilità con SunOS ma che non è detto sia -definita su altri sistemi,\footnote{non essendo prevista dallo standard POSIX - è in genere preferibile evitarne l'uso.} il suo prototipo è: +Un'estensione di \func{atexit} è la funzione \funcd{on\_exit}, che la +\acr{glibc} include per compatibilità con SunOS ma che non è detto sia +definita su altri sistemi,\footnote{la funzione è disponibile dalla + \acr{glibc} 2.19 definendo la macro \macro{\_DEFAULT\_SOURCE}, mentre in + precedenza erano necessarie \macro{\_BSD\_SOURCE} o \macro{\_SVID\_SOURCE}; + non essendo prevista dallo standard POSIX è in generale preferibile evitarne + l'uso.} il suo prototipo è: \begin{funcproto}{ \fhead{stdlib.h} @@ -576,44 +598,44 @@ normalmente un programma è riportato in fig.~\ref{fig:proc_prog_start_stop}. \begin{figure}[htb] \centering -% \includegraphics[width=9cm]{img/proc_beginend} - \begin{tikzpicture}[>=stealth] - \filldraw[fill=black!35] (-0.3,0) rectangle (12,1); - \draw(5.5,0.5) node {\large{\textsf{kernel}}}; + \includegraphics[width=9cm]{img/proc_beginend} + % \begin{tikzpicture}[>=stealth] + % \filldraw[fill=black!35] (-0.3,0) rectangle (12,1); + % \draw(5.5,0.5) node {\large{\textsf{kernel}}}; - \filldraw[fill=black!15] (1.5,2) rectangle (4,3); - \draw (2.75,2.5) node {\texttt{ld-linux.so}}; - \draw [->] (2.75,1) -- (2.75,2); - \draw (2.75,1.5) node [anchor=west]{\texttt{execve}}; + % \filldraw[fill=black!15] (1.5,2) rectangle (4,3); + % \draw (2.75,2.5) node {\texttt{ld-linux.so}}; + % \draw [->] (2.75,1) -- (2.75,2); + % \draw (2.75,1.5) node [anchor=west]{\texttt{execve}}; - \filldraw[fill=black!15,rounded corners] (1.5,4) rectangle (4,5); - \draw (2.75,4.5) node {\texttt{main}}; + % \filldraw[fill=black!15,rounded corners] (1.5,4) rectangle (4,5); + % \draw (2.75,4.5) node {\texttt{main}}; - \draw [<->, dashed] (2.75,3) -- (2.75,4); - \draw [->] (1.5,4.5) -- (0.3,4.5) -- (0.3,1); - \draw (0.9,4.5) node [anchor=south] {\texttt{\_exit}}; + % \draw [<->, dashed] (2.75,3) -- (2.75,4); + % \draw [->] (1.5,4.5) -- (0.3,4.5) -- (0.3,1); + % \draw (0.9,4.5) node [anchor=south] {\texttt{\_exit}}; - \filldraw[fill=black!15,rounded corners] (1.5,6) rectangle (4,7); - \draw (2.75,6.5) node {\texttt{funzione}}; + % \filldraw[fill=black!15,rounded corners] (1.5,6) rectangle (4,7); + % \draw (2.75,6.5) node {\texttt{funzione}}; - \draw [<->, dashed] (2.75,5) -- (2.75,6); - \draw [->] (1.5,6.5) -- (0.05,6.5) -- (0.05,1); - \draw (0.9,6.5) node [anchor=south] {\texttt{\_exit}}; + % \draw [<->, dashed] (2.75,5) -- (2.75,6); + % \draw [->] (1.5,6.5) -- (0.05,6.5) -- (0.05,1); + % \draw (0.9,6.5) node [anchor=south] {\texttt{\_exit}}; - \draw (6.75,4.5) node (exit) [rectangle,fill=black!15,minimum width=2.5cm,minimum height=1cm,rounded corners, draw]{\texttt{exit}}; + % \draw (6.75,4.5) node (exit) [rectangle,fill=black!15,minimum width=2.5cm,minimum height=1cm,rounded corners, draw]{\texttt{exit}}; - \draw[->] (4,6.5) -- node[anchor=south west]{\texttt{exit}} (exit); - \draw[->] (4,4.5) -- node[anchor=south]{\texttt{exit}} (exit); - \draw[->] (exit) -- node[anchor=east]{\texttt{\_exit}}(6.75,1); + % \draw[->] (4,6.5) -- node[anchor=south west]{\texttt{exit}} (exit); + % \draw[->] (4,4.5) -- node[anchor=south]{\texttt{exit}} (exit); + % \draw[->] (exit) -- node[anchor=east]{\texttt{\_exit}}(6.75,1); - \draw (10,4.5) node (exithandler1) [rectangle,fill=black!15,rounded corners, draw]{\textsf{exit handler}}; - \draw (10,5.5) node (exithandler2) [rectangle,fill=black!15,rounded corners, draw]{\textsf{exit handler}}; - \draw (10,3.5) node (stream) [rectangle,fill=black!15,rounded corners, draw]{\textsf{chiusura stream}}; + % \draw (10,4.5) node (exithandler1) [rectangle,fill=black!15,rounded corners, draw]{\textsf{exit handler}}; + % \draw (10,5.5) node (exithandler2) [rectangle,fill=black!15,rounded corners, draw]{\textsf{exit handler}}; + % \draw (10,3.5) node (stream) [rectangle,fill=black!15,rounded corners, draw]{\textsf{chiusura stream}}; - \draw[<->, dashed] (exithandler1) -- (exit); - \draw[<->, dashed] (exithandler2) -- (exit); - \draw[<->, dashed] (stream) -- (exit); - \end{tikzpicture} + % \draw[<->, dashed] (exithandler1) -- (exit); + % \draw[<->, dashed] (exithandler2) -- (exit); + % \draw[<->, dashed] (stream) -- (exit); + % \end{tikzpicture} \caption{Schema dell'avvio e della conclusione di un programma.} \label{fig:proc_prog_start_stop} \end{figure} @@ -722,11 +744,11 @@ riposte nella \textit{swap}. \itindend{page~fault} -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 della paginazione e mantenere fisse delle -pagine in memoria (vedi sez.~\ref{sec:proc_mem_lock}). +Normalmente questo è il prezzo da pagare per avere un \textit{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 della paginazione e +mantenere fisse delle pagine in memoria (vedi sez.~\ref{sec:proc_mem_lock}). \index{paginazione|)} \index{memoria~virtuale|)} @@ -838,27 +860,27 @@ programma C viene suddiviso nei seguenti segmenti: \begin{figure}[htb] \centering -% \includegraphics[height=12cm]{img/memory_layout} - \begin{tikzpicture} - \draw (0,0) rectangle (4,1); - \draw (2,0.5) node {\textit{text}}; - \draw (0,1) rectangle (4,2.5); - \draw (2,1.75) node {dati inizializzati}; - \draw (0,2.5) rectangle (4,5); - \draw (2,3.75) node {dati non inizializzati}; - \draw (0,5) rectangle (4,9); - \draw[dashed] (0,6) -- (4,6); - \draw[dashed] (0,8) -- (4,8); - \draw (2,5.5) node {\textit{heap}}; - \draw (2,8.5) node {\textit{stack}}; - \draw [->] (2,6) -- (2,6.5); - \draw [->] (2,8) -- (2,7.5); - \draw (0,9) rectangle (4,10); - \draw (2,9.5) node {\textit{environment}}; - \draw (4,0) node [anchor=west] {\texttt{0x08000000}}; - \draw (4,5) node [anchor=west] {\texttt{0x08xxxxxx}}; - \draw (4,9) node [anchor=west] {\texttt{0xC0000000}}; - \end{tikzpicture} + \includegraphics[height=10cm]{img/memory_layout} + % \begin{tikzpicture} + % \draw (0,0) rectangle (4,1); + % \draw (2,0.5) node {\textit{text}}; + % \draw (0,1) rectangle (4,2.5); + % \draw (2,1.75) node {dati inizializzati}; + % \draw (0,2.5) rectangle (4,5); + % \draw (2,3.75) node {dati non inizializzati}; + % \draw (0,5) rectangle (4,9); + % \draw[dashed] (0,6) -- (4,6); + % \draw[dashed] (0,8) -- (4,8); + % \draw (2,5.5) node {\textit{heap}}; + % \draw (2,8.5) node {\textit{stack}}; + % \draw [->] (2,6) -- (2,6.5); + % \draw [->] (2,8) -- (2,7.5); + % \draw (0,9) rectangle (4,10); + % \draw (2,9.5) node {\textit{environment}}; + % \draw (4,0) node [anchor=west] {\texttt{0x08000000}}; + % \draw (4,5) node [anchor=west] {\texttt{0x08xxxxxx}}; + % \draw (4,9) node [anchor=west] {\texttt{0xC0000000}}; + % \end{tikzpicture} \caption{Disposizione tipica dei segmenti di memoria di un processo.} \label{fig:proc_mem_layout} \end{figure} @@ -944,7 +966,7 @@ in caso di successo e \val{NULL} in caso di fallimento, nel qual caso In genere si usano \func{malloc} e \func{calloc} per allocare dinamicamente un'area di memoria.\footnote{queste funzioni presentano un comportamento - diverso fra le \acr{glibc} e le \acr{uClib} quando il valore di \param{size} + diverso fra la \acr{glibc} e la \acr{uClib} quando il valore di \param{size} è nullo. Nel primo caso viene comunque restituito un puntatore valido, anche se non è chiaro a cosa esso possa fare riferimento, nel secondo caso viene restituito \val{NULL}. Il comportamento è analogo con @@ -976,9 +998,9 @@ essere esplicitamente rilasciata usando la funzione \funcd{free},\footnote{le Questa funzione vuole come argomento \var{ptr} il puntatore restituito da una precedente chiamata ad una qualunque delle funzioni di allocazione che non sia -già stato liberato da un'altra chiamata a \func{free}. Se il valore -di \param{ptr} è \val{NULL} la funzione non fa niente, mentre se l'area di -memoria era già stata liberata da un precedente chiamata il comportamento +già stato liberato da un'altra chiamata a \func{free}. Se il valore di +\param{ptr} è \val{NULL} la funzione non fa niente, mentre se l'area di +memoria era già stata liberata da una precedente chiamata il comportamento della funzione è dichiarato indefinito, ma in genere comporta la corruzione dei dati di gestione dell'allocazione, che può dar luogo a problemi gravi, ad esempio un \textit{segmentation fault} in una successiva chiamata di una di @@ -1169,7 +1191,7 @@ programma\footnote{questo comporta anche il fatto che non è possibile modificare il puntatore nello \textit{stack} e non c'è modo di sapere se se ne sono superate le dimensioni, per cui in caso di fallimento nell'allocazione il comportamento del programma può risultare indefinito, dando luogo ad una -\textit{segment violation} la prima volta che cercherà di accedere alla +\textit{segment violation} la prima volta che si cerchi di accedere alla memoria non effettivamente disponibile. \index{segmento!dati|(} @@ -1224,12 +1246,12 @@ segmento dati\footnote{in questo caso si tratta soltanto di una funzione di caso \var{errno} assumerà il valore \errcode{ENOMEM}.} \end{funcproto} -La funzione incrementa la dimensione dello \textit{heap} di un -programma del valore indicato dall'argomento \param{increment}, restituendo il -nuovo indirizzo finale dello stesso. L'argomento è definito come di tipo -\type{intptr\_t}, ma a seconda della versione delle librerie e del sistema può -essere indicato con una serie di tipi equivalenti come \ctyp{ptrdiff\_t}, -\ctyp{ssize\_t}, \ctyp{int}. Se invocata con un valore nullo la funzione +La funzione incrementa la dimensione dello \textit{heap} di un programma del +valore indicato dall'argomento \param{increment}, restituendo il nuovo +indirizzo finale dello stesso. L'argomento è definito come di tipo +\typed{intptr\_t}, ma a seconda della versione delle librerie e del sistema +può essere indicato con una serie di tipi equivalenti come \type{ptrdiff\_t}, +\type{ssize\_t}, \ctyp{int}. Se invocata con un valore nullo la funzione permette di ottenere l'attuale posizione della fine del segmento dati. Queste due funzioni sono state deliberatamente escluse dallo standard POSIX.1 @@ -1286,7 +1308,7 @@ memoria virtuale è disponibile una apposita funzione di sistema, \funcd{mincore}, che però non è standardizzata da POSIX e pertanto non è disponibile su tutte le versioni di kernel unix-like;\footnote{nel caso di Linux devono essere comunque definite le macro \macro{\_BSD\_SOURCE} e - \macro{\_SVID\_SOURCE}.} il suo prototipo è: + \macro{\_SVID\_SOURCE} o \macro{\_DEFAULT\_SOURCE}.} il suo prototipo è: \begin{funcproto}{ \fhead{unistd.h} @@ -1322,6 +1344,11 @@ processo,\footnote{in caso contrario si avrà un errore di \errcode{ENOMEM}; positivo di grandi dimensioni.} ma il risultato verrà comunque fornito per l'intervallo compreso fino al multiplo successivo. +% TODO: verificare i cambiamenti di sematica con il kernel 5.0 (restrizione +% solo alle pagine relative al processo stesso) vedi: +% https://lwn.net/Articles/776034/, +% https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=574823bfab82d9d8fa47f422778043fbb4b4f50e + I risultati della funzione vengono forniti nel vettore puntato da \param{vec}, che deve essere allocato preventivamente e deve essere di dimensione sufficiente a contenere tanti byte quante sono le pagine contenute @@ -1332,7 +1359,7 @@ la pagina di memoria corrispondente è al momento residente in memoria, o cancellato altrimenti. Il comportamento sugli altri bit è indefinito, essendo questi al momento riservati per usi futuri. Per questo motivo in genere è comunque opportuno inizializzare a zero il contenuto del vettore, così che le -pagine attualmente residenti in memoria saranno indicata da un valore non +pagine attualmente residenti in memoria saranno indicate da un valore non nullo del byte corrispondente. Dato che lo stato della memoria di un processo può cambiare continuamente, il @@ -1403,11 +1430,15 @@ loro prototipi sono: {Entrambe le funzioni ritornano $0$ in caso di successo e $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EINVAL}] \param{len} non è un valore positivo. + \item[\errcode{EAGAIN}] una parte o tutto l'intervallo richiesto non può + essere bloccato per una mancanza temporanea di risorse. + \item[\errcode{EINVAL}] \param{len} non è un valore positivo o la somma con + \param{addr} causa un overflow. \item[\errcode{ENOMEM}] alcuni indirizzi dell’intervallo specificato non - corrispondono allo spazio di indirizzi del processo o si è superato il - limite di \const{RLIMIT\_MEMLOCK} per un processo non privilegiato (solo - per kernel a partire dal 2.6.9). + corrispondono allo spazio di indirizzi del processo o con \func{mlock} si + è superato il limite di \const{RLIMIT\_MEMLOCK} per un processo non + privilegiato (solo per kernel a partire dal 2.6.9) o si è superato il + limite di regioni di memoria con attributi diversi. \item[\errcode{EPERM}] il processo non è privilegiato (per kernel precedenti il 2.6.9) o si ha un limite nullo per \const{RLIMIT\_MEMLOCK} e il processo non è privilegiato (per kernel a partire dal 2.6.9). @@ -1416,12 +1447,13 @@ loro prototipi sono: Le due funzioni permettono rispettivamente di bloccare e sbloccare la paginazione per l'intervallo di memoria iniziante all'indirizzo \param{addr} e -lungo \param{len} byte. Tutte le pagine che contengono una parte -dell'intervallo bloccato sono mantenute in RAM per tutta la durata del -blocco. Con kernel diversi da Linux si può ottenere un errore di -\errcode{EINVAL} se \param{addr} non è un multiplo della dimensione delle -pagine di memoria, pertanto se si ha a cuore la portabilità si deve avere cura -di allinearne correttamente il valore. +lungo \param{len} byte. Al ritorno di \func{mlock} tutte le pagine che +contengono una parte dell'intervallo bloccato sono garantite essere in RAM e +vi verranno mantenute per tutta la durata del blocco. Con kernel diversi da +Linux si può ottenere un errore di \errcode{EINVAL} se \param{addr} non è un +multiplo della dimensione delle pagine di memoria, pertanto se si ha a cuore +la portabilità si deve avere cura di allinearne correttamente il valore. Il +blocco viene rimosso chiamando \func{munlock}. Altre due funzioni di sistema, \funcd{mlockall} e \funcd{munlockall}, consentono di bloccare genericamente la paginazione per l'intero spazio di @@ -1441,7 +1473,7 @@ indirizzi di un processo. I prototipi di queste funzioni sono: L'argomento \param{flags} di \func{mlockall} permette di controllarne il comportamento; esso deve essere specificato come maschera binaria dei valori -espressi dalle costanti riportate in tab.~\ref{tab:mlockall_flags}. +espressi dalle costanti riportate in tab.~\ref{tab:mlockall_flags}. \begin{table}[htb] \footnotesize @@ -1455,6 +1487,8 @@ espressi dalle costanti riportate in tab.~\ref{tab:mlockall_flags}. spazio di indirizzi del processo.\\ \constd{MCL\_FUTURE} & blocca tutte le pagine che verranno mappate nello spazio di indirizzi del processo.\\ + \constd{MCL\_ONFAULT}& esegue il blocco delle pagine selezionate solo + quando vengono utilizzate (dal kernel 4.4).\\ \hline \end{tabular} \caption{Valori e significato dell'argomento \param{flags} della funzione @@ -1465,26 +1499,73 @@ espressi dalle costanti riportate in tab.~\ref{tab:mlockall_flags}. Con \func{mlockall} si possono bloccare tutte le pagine mappate nello spazio di indirizzi del processo, sia che comprendano il segmento di testo, di dati, lo \textit{stack}, lo \textit{heap} e pure le funzioni di libreria chiamate, i -file mappati in memoria, i dati del kernel mappati in user space, la memoria -condivisa. L'uso dell'argomento \param{flags} permette di selezionare con -maggior finezza le pagine da bloccare, ad esempio usando \const{MCL\_FUTURE} -ci si può limitare a tutte le pagine allocate a partire dalla chiamata della -funzione. - -In ogni caso un processo \textit{real-time} che deve entrare in una sezione -critica (vedi sez.~\ref{sec:proc_race_cond}) deve provvedere a riservare -memoria sufficiente prima dell'ingresso, per scongiurare l'occorrenza di un -eventuale \textit{page fault} causato dal meccanismo di \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 \textit{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 \textit{stack}, dopo di che, per essere -sicuri che esse siano state effettivamente portate in memoria, ci si scrive -sopra. +file mappati in memoria, i dati del kernel mappati in \textit{user space}, la +memoria condivisa. L'uso dell'argomento \param{flags} permette di selezionare +con maggior finezza le pagine da bloccare, ad esempio usando +\const{MCL\_FUTURE} ci si può limitare a tutte le pagine allocate a partire +dalla chiamata della funzione, mentre \const{MCL\_CURRENT} blocca tutte quelle +correntemente mappate. L'uso di \func{munlockall} invece sblocca sempre tutte +le pagine di memoria correntemente mappate nello spazio di indirizzi del +programma. + +A partire dal kernel 4.4 alla funzione \func{mlockall} è stato aggiunto un +altro flag, \const{MCL\_ONFAULT}, che può essere abbinato a entrambi gli altri +due flag, e consente di modificare il comportamento della funzione per +ottenere migliori prestazioni. + +Il problema che si presenta infatti è che eseguire un \textit{memory lock} per +un intervallo ampio di memoria richiede che questa venga comunque allocata in +RAM, con altrettanti \textit{page fault} che ne assicurino la presenza; questo +vale per tutto l'intervallo e può avere un notevole costo in termini di +prestazioni, anche quando poi, nell'esecuzione del programma, venisse usata +solo una piccola parte dello stesso. L'uso di \const{MCL\_ONFAULT} previene il +\textit{page faulting} immediato di tutto l'intervallo, le pagine +dell'intervallo verranno bloccate, ma solo quando un \textit{page fault} +dovuto all'accesso ne richiede l'allocazione effettiva in RAM. + +Questo stesso comportamento non è ottenibile con \func{mlock}, che non dispone +di un argomento \param{flag} che consenta di richiederlo, per questo sempre +con il kernel 4.4 è stata aggiunta una ulteriore funzione di sistema, +\funcd{mlock2}, il cui prototipo è: + +\begin{funcproto}{ + \fhead{sys/mman.h} + \fdecl{int mlock2(const void *addr, size\_t len, int flags)} + \fdesc{Blocca la paginazione su un intervallo di memoria.} +} +{Le funzione ritornano $0$ in caso di successo e $-1$ in caso di errore, nel + qual caso \var{errno} assume gli stessi valori di \func{mlock} con + l'aggiunta id un possibile \errcode{EINVAL} anche se si è indicato un valore + errato di \param{flags}.} +\end{funcproto} + +% NOTA: per mlock2, introdotta con il kernel 4.4 (vedi +% http://lwn.net/Articles/650538/) + +Indicando un valore nullo per \param{flags} il comportamento della funzione è +identico a quello di \func{mlock}, l'unico altro valore possibile è +\constd{MLOCK\_ONFAULT} che ha lo stesso effetto sull'allocazione delle pagine +in RAM già descritto per \const{MCL\_ONFAULT}. + +Si tenga presente che un processo \textit{real-time} che intende usare il +\textit{memory locking} con \func{mlockall} per prevenire l'avvenire di un +eventuale \textit{page fault} ed il conseguente rallentamento (probabilmente +inaccettabile) dei tempi di esecuzione, deve comunque avere delle accortezze. +In particolare si deve assicurare di aver preventivamente bloccato una +quantità di spazio nello \textit{stack} sufficiente a garantire l'esecuzione +di tutte le funzioni che hanno i requisiti di criticità sui tempi. Infatti, +anche usando \const{MCL\_FUTURE}, in caso di allocazione di una nuova pagina +nello \textit{stack} durante l'esecuzione di una funzione (precedentemente non +usata e quindi non bloccata) si potrebbe avere un \textit{page fault}. + +In genere si ovvia a questa problematica chiamando inizialmente una funzione +che definisca una quantità sufficientemente ampia di variabili automatiche +(che si ricordi vengono allocate nello \textit{stack}) e ci scriva, in modo da +esser sicuri che le corrispondenti pagine vengano mappate nello spazio di +indirizzi del processo, per poi bloccarle. La scrittura è necessaria perché il +kernel usa il meccanismo di \textit{copy on write} (vedi +sez.~\ref{sec:proc_fork}) e le pagine potrebbero non essere allocate +immediatamente. \itindend{memory~locking} \index{memoria~virtuale|)} @@ -1512,8 +1593,9 @@ tal caso l'uso di \func{malloc} non è sufficiente, ed occorre utilizzare una funzione specifica. Tradizionalmente per rispondere a questa esigenza sono state create due -funzioni diverse, \funcd{memalign} e \funcd{valloc}, oggi obsolete; i -rispettivi prototipi sono: +funzioni diverse, \funcd{memalign} e \funcd{valloc}, oggi obsolete, cui si +aggiunge \funcd{pvalloc} come estensione GNU, anch'essa obsoleta; i rispettivi +prototipi sono: \begin{funcproto}{ \fhead{malloc.h} @@ -1523,6 +1605,9 @@ rispettivi prototipi sono: \fdecl{void *memalign(size\_t boundary, size\_t size)} \fdesc{Alloca un blocco di memoria allineato ad un multiplo di \param{boundary}.} +\fdecl{void *pvalloc(size\_t size)} +\fdesc{Alloca un blocco di memoria allineato alla dimensione di una pagina di + memoria.} } {Entrambe le funzioni ritornano un puntatore al blocco di memoria allocato in caso di successo e \val{NULL} in caso di errore, nel qual caso \var{errno} @@ -1534,21 +1619,23 @@ rispettivi prototipi sono: \end{funcproto} Le funzioni restituiscono il puntatore al buffer di memoria allocata di -dimensioni pari a \param{size}, che per \func{memalign} sarà un multiplo -di \param{boundary} mentre per \func{valloc} un multiplo della dimensione di -una pagina di memoria. Nel caso della versione fornita dalla \acr{glibc} la -memoria allocata con queste funzioni deve essere liberata con \func{free}, -cosa che non è detto accada con altre implementazioni. +dimensioni pari a \param{size}, che per \func{memalign} sarà un multiplo di +\param{boundary} mentre per \func{valloc} un multiplo della dimensione di una +pagina di memoria; lo stesso vale per \func{pvalloc} che però arrotonda +automaticamente la dimensione dell'allocazione al primo multiplo di una +pagina. Nel caso della versione fornita dalla \acr{glibc} la memoria allocata +con queste funzioni deve essere liberata con \func{free}, cosa che non è detto +accada con altre implementazioni. Nessuna delle due funzioni ha una chiara standardizzazione e nessuna delle due compare in POSIX.1, inoltre ci sono indicazioni discordi sui file che ne contengono la definizione;\footnote{secondo SUSv2 \func{valloc} è definita in - \headfile{stdlib.h}, mentre sia le \acr{glibc} che le precedenti \acr{libc4} + \headfile{stdlib.h}, mentre sia la \acr{glibc} che le precedenti \acr{libc4} e \acr{libc5} la dichiarano in \headfile{malloc.h}, lo stesso vale per \func{memalign} che in alcuni sistemi è dichiarata in \headfile{stdlib.h}.} per questo motivo il loro uso è sconsigliato, essendo state sostituite dalla -nuova \funcd{posix\_memalign}, che è stata standardizzata in POSIX.1d; il suo -prototipo è: +nuova \funcd{posix\_memalign}, che è stata standardizzata in POSIX.1d e +disponibile dalla \acr{glibc} 2.1.91; il suo prototipo è: \begin{funcproto}{ \fhead{stdlib.h} @@ -1560,7 +1647,7 @@ prototipo è: caso di successo e \val{NULL} in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EINVAL}] \param{alignment} non è potenza di due e multiplo + \item[\errcode{EINVAL}] \param{alignment} non è potenza di due o un multiplo di \code{sizeof(void *)}. \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'allocazione. \end{errlist}} @@ -1572,10 +1659,26 @@ indicato da \param{memptr}. La funzione fallisce nelle stesse condizioni delle due funzioni precedenti, ma a loro differenza restituisce direttamente come valore di ritorno il codice di errore. Come per le precedenti la memoria allocata con \func{posix\_memalign} deve essere disallocata con \func{free}, -che in questo caso però è quanto richiesto dallo standard. Si tenga presente -infine che nessuna di queste funzioni inizializza il buffer di memoria -allocato, il loro comportamento cioè è analogo, allineamento a parte, a quello -di \func{malloc}. +che in questo caso però è quanto richiesto dallo standard. + +Dalla versione 2.16 della \acr{glibc} è stata aggiunta anche la funzione +\funcd{aligned\_alloc}, prevista dallo standard C11 (e disponibile definendo +\const{\_ISOC11\_SOURCE}), il cui prototipo è: + +\begin{funcproto}{ +\fhead{malloc.h} +\fdecl{void *aligned\_alloc(size\_t alignment, size\_t size)} +\fdesc{Alloca un blocco di memoria allineato ad un multiplo + di \param{alignment}.} +} +{La funzione ha gli stessi valori di ritorno e codici di errore di + \func{memalign}.} +\end{funcproto} + +La funzione è identica a \func{memalign} ma richiede che \param{size} sia un +multiplo di \param{alignment}. Infine si tenga presente infine che nessuna di +queste funzioni inizializza il buffer di memoria allocato, il loro +comportamento cioè è analogo, allineamento a parte, a quello di \func{malloc}. Un secondo caso in cui risulta estremamente utile poter avere un maggior controllo delle modalità di allocazione della memoria è quello in cui cercano @@ -1601,7 +1704,7 @@ suo prototipo è: \fdecl{int mcheck(void (*abortfn) (enum mcheck\_status status))} \fdesc{Attiva i controlli di consistenza delle allocazioni di memoria.} } -{La funzione ritorna $0$ in caso di successo e $-1$ per un errorre; +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore; \var{errno} non viene impostata.} \end{funcproto} @@ -1716,34 +1819,32 @@ contengono degli spazi evitando di spezzarli in parole diverse. \begin{figure}[htb] \centering -% \includegraphics[width=13cm]{img/argv_argc} -% \includegraphics[width=13cm]{img/argv_argc} - \begin{tikzpicture}[>=stealth] - \draw (0.5,2.5) rectangle (3.5,3); - \draw (2,2.75) node {\texttt{argc = 5}}; - \draw (5,2.5) rectangle (8,3); - \draw (6.5,2.75) node {\texttt{argv[0]}}; - \draw [->] (8,2.75) -- (9,2.75); - \draw (9,2.75) node [anchor=west] {\texttt{"touch"}}; - \draw (5,2) rectangle (8,2.5); - \draw (6.5,2.25) node {\texttt{argv[1]}}; - \draw [->] (8,2.25) -- (9,2.25); - \draw (9,2.25) node [anchor=west] {\texttt{"-r"}}; - \draw (5,1.5) rectangle (8,2); - \draw (6.5,1.75) node {\texttt{argv[2]}}; - \draw [->] (8,1.75) -- (9,1.75); - \draw (9,1.75) node [anchor=west] {\texttt{"riferimento.txt"}}; - \draw (5,1.0) rectangle (8,1.5); - \draw (6.5,1.25) node {\texttt{argv[3]}}; - \draw [->] (8,1.25) -- (9,1.25); - \draw (9,1.25) node [anchor=west] {\texttt{"-m"}}; - \draw (5,0.5) rectangle (8,1.0); - \draw (6.5,0.75) node {\texttt{argv[4]}}; - \draw [->] (8,0.75) -- (9,0.75); - \draw (9,0.75) node [anchor=west] {\texttt{"questofile.txt"}}; - \draw (4.25,3.5) node{\texttt{"touch -r riferimento.txt -m questofile.txt"}}; - - \end{tikzpicture} + \includegraphics[width=13cm]{img/argv_argc} + % \begin{tikzpicture}[>=stealth] + % \draw (0.5,2.5) rectangle (3.5,3); + % \draw (2,2.75) node {\texttt{argc = 5}}; + % \draw (5,2.5) rectangle (8,3); + % \draw (6.5,2.75) node {\texttt{argv[0]}}; + % \draw [->] (8,2.75) -- (9,2.75); + % \draw (9,2.75) node [anchor=west] {\texttt{"touch"}}; + % \draw (5,2) rectangle (8,2.5); + % \draw (6.5,2.25) node {\texttt{argv[1]}}; + % \draw [->] (8,2.25) -- (9,2.25); + % \draw (9,2.25) node [anchor=west] {\texttt{"-r"}}; + % \draw (5,1.5) rectangle (8,2); + % \draw (6.5,1.75) node {\texttt{argv[2]}}; + % \draw [->] (8,1.75) -- (9,1.75); + % \draw (9,1.75) node [anchor=west] {\texttt{"riferimento.txt"}}; + % \draw (5,1.0) rectangle (8,1.5); + % \draw (6.5,1.25) node {\texttt{argv[3]}}; + % \draw [->] (8,1.25) -- (9,1.25); + % \draw (9,1.25) node [anchor=west] {\texttt{"-m"}}; + % \draw (5,0.5) rectangle (8,1.0); + % \draw (6.5,0.75) node {\texttt{argv[4]}}; + % \draw [->] (8,0.75) -- (9,0.75); + % \draw (9,0.75) node [anchor=west] {\texttt{"questofile.txt"}}; + % \draw (4.25,3.5) node{\texttt{"touch -r riferimento.txt -m questofile.txt"}}; + % \end{tikzpicture} \caption{Esempio dei valori di \param{argv} e \param{argc} generati nella scansione di una riga di comando.} \label{fig:proc_argv_argc} @@ -1769,9 +1870,11 @@ tali: un elemento di \param{argv} successivo al primo che inizia con il carattere ``\texttt{-}'' e che non sia un singolo ``\texttt{-}'' o un ``\texttt{-{}-}'' viene considerato un'opzione. In genere le opzioni sono costituite da una lettera singola (preceduta dal carattere ``\texttt{-}'') e -possono avere o no un parametro associato. Un esempio tipico può essere quello -mostrato in fig.~\ref{fig:proc_argv_argc}. In quel caso le opzioni sono -\cmd{-r} e \cmd{-m} e la prima vuole un parametro mentre la seconda no +possono avere o no un parametro associato. + +Un esempio tipico può essere quello mostrato in +fig.~\ref{fig:proc_argv_argc}. In quel caso le opzioni sono \cmd{-r} e +\cmd{-m} e la prima vuole un parametro mentre la seconda no (\cmd{questofile.txt} è un argomento del programma, non un parametro di \cmd{-m}). @@ -1812,16 +1915,6 @@ ritornato il carattere ``\texttt{:}'', infine se viene incontrato il valore ``\texttt{-{}-}'' la scansione viene considerata conclusa, anche se vi sono altri elementi di \param{argv} che cominciano con il carattere ``\texttt{-}''. -\begin{figure}[!htb] - \footnotesize \centering - \begin{minipage}[c]{\codesamplewidth} - \includecodesample{listati/option_code.c} - \end{minipage} - \normalsize - \caption{Esempio di codice per la gestione delle opzioni.} - \label{fig:proc_options_code} -\end{figure} - Quando \func{getopt} trova un'opzione fra quelle indicate in \param{optstring} essa ritorna il valore numerico del carattere, in questo modo si possono eseguire azioni specifiche usando uno \instruction{switch}; la funzione @@ -1836,6 +1929,16 @@ inoltre inizializza alcune variabili globali: \item \var{int optopt} contiene il carattere dell'opzione non riconosciuta. \end{itemize*} +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/option_code.c} + \end{minipage} + \normalsize + \caption{Esempio di codice per la gestione delle opzioni.} + \label{fig:proc_options_code} +\end{figure} + In fig.~\ref{fig:proc_options_code} si è mostrata la sezione del programma \file{fork\_test.c}, che useremo nel prossimo capitolo per effettuare dei test sulla creazione dei processi, deputata alla decodifica delle opzioni a riga di @@ -1901,34 +2004,34 @@ più comuni che normalmente sono definite dal sistema, è riportato in fig.~\ref{fig:proc_envirno_list}. \begin{figure}[htb] \centering -% \includegraphics[width=15 cm]{img/environ_var} - \begin{tikzpicture}[>=stealth] - \draw (2,3.5) node {\textsf{Environment pointer}}; - \draw (6,3.5) node {\textsf{Environment list}}; - \draw (10.5,3.5) node {\textsf{Environment string}}; - \draw (0.5,2.5) rectangle (3.5,3); - \draw (2,2.75) node {\texttt{environ}}; - \draw [->] (3.5,2.75) -- (4.5,2.75); - \draw (4.5,2.5) rectangle (7.5,3); - \draw (6,2.75) node {\texttt{environ[0]}}; - \draw (4.5,2) rectangle (7.5,2.5); - \draw (6,2.25) node {\texttt{environ[1]}}; - \draw (4.5,1.5) rectangle (7.5,2); - \draw (4.5,1) rectangle (7.5,1.5); - \draw (4.5,0.5) rectangle (7.5,1); - \draw (4.5,0) rectangle (7.5,0.5); - \draw (6,0.25) node {\texttt{NULL}}; - \draw [->] (7.5,2.75) -- (8.5,2.75); - \draw (8.5,2.75) node[right] {\texttt{HOME=/home/piccardi}}; - \draw [->] (7.5,2.25) -- (8.5,2.25); - \draw (8.5,2.25) node[right] {\texttt{PATH=:/bin:/usr/bin}}; - \draw [->] (7.5,1.75) -- (8.5,1.75); - \draw (8.5,1.75) node[right] {\texttt{SHELL=/bin/bash}}; - \draw [->] (7.5,1.25) -- (8.5,1.25); - \draw (8.5,1.25) node[right] {\texttt{EDITOR=emacs}}; - \draw [->] (7.5,0.75) -- (8.5,0.75); - \draw (8.5,0.75) node[right] {\texttt{OSTYPE=linux-gnu}}; - \end{tikzpicture} + \includegraphics[width=13cm]{img/environ_var} + % \begin{tikzpicture}[>=stealth] + % \draw (2,3.5) node {\textsf{Environment pointer}}; + % \draw (6,3.5) node {\textsf{Environment list}}; + % \draw (10.5,3.5) node {\textsf{Environment string}}; + % \draw (0.5,2.5) rectangle (3.5,3); + % \draw (2,2.75) node {\texttt{environ}}; + % \draw [->] (3.5,2.75) -- (4.5,2.75); + % \draw (4.5,2.5) rectangle (7.5,3); + % \draw (6,2.75) node {\texttt{environ[0]}}; + % \draw (4.5,2) rectangle (7.5,2.5); + % \draw (6,2.25) node {\texttt{environ[1]}}; + % \draw (4.5,1.5) rectangle (7.5,2); + % \draw (4.5,1) rectangle (7.5,1.5); + % \draw (4.5,0.5) rectangle (7.5,1); + % \draw (4.5,0) rectangle (7.5,0.5); + % \draw (6,0.25) node {\texttt{NULL}}; + % \draw [->] (7.5,2.75) -- (8.5,2.75); + % \draw (8.5,2.75) node[right] {\texttt{HOME=/home/piccardi}}; + % \draw [->] (7.5,2.25) -- (8.5,2.25); + % \draw (8.5,2.25) node[right] {\texttt{PATH=:/bin:/usr/bin}}; + % \draw [->] (7.5,1.75) -- (8.5,1.75); + % \draw (8.5,1.75) node[right] {\texttt{SHELL=/bin/bash}}; + % \draw [->] (7.5,1.25) -- (8.5,1.25); + % \draw (8.5,1.25) node[right] {\texttt{EDITOR=emacs}}; + % \draw [->] (7.5,0.75) -- (8.5,0.75); + % \draw (8.5,0.75) node[right] {\texttt{OSTYPE=linux-gnu}}; + % \end{tikzpicture} \caption{Esempio di lista delle variabili di ambiente.} \label{fig:proc_envirno_list} \end{figure} @@ -2053,7 +2156,7 @@ stringa che ne contiene il valore, nella forma ``\texttt{NOME=valore}''. Oltre a questa funzione di lettura, che è l'unica definita dallo standard ANSI C, nell'evoluzione dei sistemi Unix ne sono state proposte altre, da -utilizzare per impostare, modificare e per cancellare le variabili di +utilizzare per impostare, modificare e cancellare le variabili di ambiente. Uno schema delle funzioni previste nei vari standard e disponibili in Linux è riportato in tab.~\ref{tab:proc_env_func}. Tutte le funzioni sono state comunque inserite nello standard POSIX.1-2001, ad eccetto di @@ -2092,12 +2195,12 @@ sostituendo il relativo puntatore;\footnote{il comportamento è lo stesso delle vecchie \acr{libc4} e \acr{libc5}; nella \acr{glibc}, dalla versione 2.0 alla 2.1.1, veniva invece fatta una copia, 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, + standard il comportamento è stato modificato a partire dalla 2.1.2, eliminando anche, sempre in conformità a SUSv2, l'attributo \direct{const} dal prototipo.} 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 -sez.~\ref{sec:proc_var_passing}). Benché non sia richiesto dallo standard +sez.~\ref{sec:proc_var_passing}). Benché non sia richiesto dallo standard, nelle versioni della \acr{glibc} a partire dalla 2.1 la funzione è rientrante (vedi sez.~\ref{sec:proc_reentrant}). @@ -2213,7 +2316,7 @@ versione ``\textsl{sicura}'' da zero. %\label{sec:proc_opt_extended} %Oltre alla modalità ordinaria di gestione delle opzioni trattata in -%sez.~\ref{sec:proc_opt_handling} le \acr{glibc} forniscono una modalità +%sez.~\ref{sec:proc_opt_handling} la \acr{glibc} fornisce una modalità %alternativa costituita dalle cosiddette \textit{long-options}, che consente di %esprimere le opzioni in una forma più descrittiva che nel caso più generale è %qualcosa del tipo di ``\texttt{-{}-option-name=parameter}''. @@ -2233,8 +2336,8 @@ Benché questo non sia un libro sul linguaggio C, è opportuno affrontare alcune delle problematiche generali che possono emergere nella programmazione con questo linguaggio e di quali precauzioni o accorgimenti occorre prendere per risolverle. Queste problematiche non sono specifiche di sistemi unix-like o -multitasking, ma avendo trattato in questo capitolo il comportamento dei -processi visti come entità a sé stanti, le riportiamo qui. +\textit{multitasking}, ma avendo trattato in questo capitolo il comportamento +dei processi visti come entità a sé stanti, le riportiamo qui. \subsection{Il passaggio di variabili e valori di ritorno nelle funzioni} @@ -2390,10 +2493,10 @@ una lista degli argomenti, la sua definizione è: } \end{funcbox}} -La macro inizializza il puntatore alla lista di argomenti \param{ap} che -deve essere una apposita variabile di tipo \type{va\_list}; il +La macro inizializza il puntatore alla lista di argomenti \param{ap} che deve +essere una apposita variabile di tipo \type{va\_list}; il parametro \param{last} deve indicare il nome dell'ultimo degli argomenti fissi -dichiarati nel prototipo della funzione \textit{variadic}. +dichiarati nel prototipo della funzione \textit{variadic}. \macrobeg{va\_arg} @@ -2442,8 +2545,7 @@ Dopo l'uso di \macro{va\_end} la variabile \param{ap} diventa indefinita e successive chiamate a \macro{va\_arg} non funzioneranno. Nel caso del \cmd{gcc} l'uso di \macro{va\_end} può risultare inutile, ma è comunque necessario usarla per chiarezza del codice, per compatibilità con diverse -implementazioni e per eventuali eventuali modifiche future a questo -comportamento. +implementazioni e per eventuali modifiche future a questo comportamento. Riassumendo la procedura da seguire per effettuare l'estrazione degli argomenti di una funzione \textit{variadic} è la seguente: @@ -2485,15 +2587,15 @@ assolutamente normale pensare di poter effettuare questa operazione. \index{tipo!opaco|(} In generale però possono esistere anche realizzazioni diverse, ed è per questo -motivo che invece che di un semplice puntatore viene \type{va\_list} è quello -che viene chiamato un \textsl{tipo opaco}. Si chiamano così quei tipi di dati, -in genere usati da una libreria, la cui struttura interna non deve essere -vista dal programma chiamante (da cui deriva il nome opaco) che li devono -utilizzare solo attraverso dalle opportune funzioni di gestione. +motivo che invece che un semplice puntatore, \typed{va\_list} è quello che +viene chiamato un \textsl{tipo opaco}. Si chiamano così quei tipi di dati, in +genere usati da una libreria, la cui struttura interna non deve essere vista +dal programma chiamante (da cui deriva il nome opaco) che li devono utilizzare +solo attraverso dalle opportune funzioni di gestione. \index{tipo!opaco|)} -Per questo motivo una variabile di tipo \type{va\_list} non può essere +Per questo motivo una variabile di tipo \typed{va\_list} non può essere assegnata direttamente ad un'altra variabile dello stesso tipo, ma lo standard ISO C99\footnote{alcuni sistemi che non hanno questa macro provvedono al suo posto \macrod{\_\_va\_copy} che era il nome proposto in una bozza dello @@ -2598,7 +2700,7 @@ di salvare il contesto dello \textit{stack} è \funcd{setjmp}, il cui prototipo Quando si esegue la funzione il contesto corrente dello \textit{stack} viene salvato nell'argomento \param{env}, una variabile di tipo -\type{jmp\_buf}\footnote{anche questo è un classico esempio di variabile di +\typed{jmp\_buf}\footnote{anche questo è un classico esempio di variabile di \textsl{tipo opaco}.} che deve essere stata definita in precedenza. In genere le variabili di tipo \type{jmp\_buf} vengono definite come variabili globali in modo da poter essere viste in tutte le funzioni del programma. @@ -2656,10 +2758,11 @@ dei seguenti casi: \item come espressione a sé stante. \end{itemize*} -In generale, dato che l'unica differenza fra la chiamata diretta e quella -ottenuta nell'uscita con un \func{longjmp} è costituita dal valore di ritorno -di \func{setjmp}, pertanto quest'ultima viene usualmente chiamata all'interno -di un una istruzione \instr{if} che permetta di distinguere i due casi. +In generale, dato che l'unica differenza fra il risultato di una chiamata +diretta di \func{setjmp} e quello ottenuto nell'uscita con un \func{longjmp} è +costituita dal valore di ritorno della funzione, quest'ultima viene usualmente +chiamata all'interno di un una istruzione \instr{if} che permetta di +distinguere i due casi. Uno dei punti critici dei salti non-locali è quello del valore delle variabili, ed in particolare quello delle variabili automatiche della funzione @@ -2687,19 +2790,23 @@ dichiarandole tutte come \direct{volatile}.\footnote{la direttiva \index{salto~non-locale|)} +% TODO trattare qui le restartable sequences (vedi +% https://lwn.net/Articles/664645/ e https://lwn.net/Articles/650333/) se e +% quando saranno introdotte + \subsection{La \textit{endianness}} \label{sec:endianness} \itindbeg{endianness} Un altro dei problemi di programmazione che può dar luogo ad effetti -imprevisti è quello relativo alla cosiddetta \textit{endianness}. Questa è una -caratteristica generale dell'architettura hardware di un computer che dipende -dal fatto che la rappresentazione di un numero binario può essere fatta in due -modi, chiamati rispettivamente \textit{big endian} e \textit{little endian} a -seconda di come i singoli bit vengono aggregati per formare le variabili -intere (ed in genere in diretta corrispondenza a come sono poi in realtà -cablati sui bus interni del computer). +imprevisti è quello relativo alla cosiddetta \textit{endianness}. Questa è +una caratteristica generale dell'architettura hardware di un computer che +dipende dal fatto che la rappresentazione di un numero binario può essere +fatta in due modi, chiamati rispettivamente \textit{big endian} e +\textit{little endian}, a seconda di come i singoli bit vengono aggregati per +formare le variabili intere (ed in genere in diretta corrispondenza a come +sono poi in realtà cablati sui bus interni del computer). \begin{figure}[!htb] \centering \includegraphics[height=3cm]{img/endianness} @@ -2798,12 +2905,11 @@ il valore del confronto delle due variabili. In generale non ci si deve preoccupare della \textit{endianness} all'interno di un programma fintanto che questo non deve generare o manipolare dei dati -che sono scambiati con altre macchine, ad esempio tramite via rete o tramite -dei file binari. Nel primo caso la scelta è già stata fatta nella -standardizzazione dei protocolli, che hanno adottato il \textit{big endian} -(che viene detto anche per questo \textit{network order} e vedremo in -sez.~\ref{sec:sock_func_ord} le funzioni di conversione che devono essere -usate. +che sono scambiati con altre macchine, ad esempio via rete o tramite dei file +binari. Nel primo caso la scelta è già stata fatta nella standardizzazione dei +protocolli, che hanno adottato il \textit{big endian} (che viene detto anche +per questo \textit{network order}); vedremo in sez.~\ref{sec:sock_func_ord} le +funzioni di conversione che devono essere usate. Nel secondo caso occorre sapere quale \textit{endianness} è stata usata nei dati memorizzati sul file e tenerne conto nella rilettura e nella @@ -2828,7 +2934,7 @@ basterà scegliere una volta per tutte quale usare e attenersi alla scelta. % LocalWords: capability MEMLOCK limits getpagesize RLIMIT munlock sys const % LocalWords: addr len EINVAL EPERM mlockall munlockall flags l'OR CURRENT IFS % LocalWords: argc argv parsing questofile txt getopt optstring switch optarg -% LocalWords: optind opterr optopt POSIXLY CORRECT long options NdA +% LocalWords: optind opterr optopt POSIXLY CORRECT long options NdA group % LocalWords: option parameter list environ PATH HOME XPG tab LOGNAME LANG PWD % LocalWords: TERM PAGER TMPDIR getenv name SVr setenv unsetenv putenv opz gcc % LocalWords: clearenv libc value overwrite string reference result argument @@ -2845,7 +2951,8 @@ basterà scegliere una volta per tutte quale usare e attenersi alla scelta. % LocalWords: is to LC SIG str mem wcs assert ctype dirent fcntl signal stdio % LocalWords: times library utmp syscall number Filesystem Hierarchy pathname % LocalWords: context assembler sysconf fork Dinamic huge segmentation program -% LocalWords: break store Using +% LocalWords: break store using intptr ssize overflow ONFAULT faulting alloc +% LocalWords: scheduler pvalloc aligned ISOC ABCDEF %%% Local Variables: %%% mode: latex