From 9a577c89dd563aacbc619e09bf8b6d99b533274a Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Tue, 3 Jan 2012 23:55:53 +0000 Subject: [PATCH] Ulteriori revisioni, finita l'introduzione con vari spostamenti e riscritta buona parte della sezione 1 del capitolo sui processi (con qualche spiegazione piu` approfondita delle system call. --- biblio.bib | 12 +- fileadv.tex | 2 +- filestd.tex | 2 +- gapil.tex | 10 +- intro.tex | 120 +----- listati/main_def.c | 5 +- listati/main_include.c | 6 + macro.tex | 8 +- process.tex | 931 ++++++++++++++++++++++++++++------------- prochand.tex | 4 +- sockctrl.tex | 4 +- 11 files changed, 684 insertions(+), 420 deletions(-) create mode 100644 listati/main_include.c diff --git a/biblio.bib b/biblio.bib index b2f12df..3e987ac 100644 --- a/biblio.bib +++ b/biblio.bib @@ -1,5 +1,5 @@ @Book{APUE, - author = {W. Richard Stevens}, + author = {Richard W. Stevens}, editor = {}, title = {Advanced Programming in the UNIX Environment}, publisher = {Prentice Hall PTR}, @@ -15,7 +15,7 @@ OPTannote = {} } @Book{TCPIll1, - author = {W. Richard Stevens}, + author = {Richard W. Stevens}, editor = {}, title = {TCP/IP Illustrated, Volume 1, the protocols}, publisher = {Addison Wesley}, @@ -31,7 +31,7 @@ OPTannote = {} } @Book{UNP1, - author = {W. Richard Stevens}, + author = {Richard W. Stevens}, editor = {}, title = {UNIX Network Programming, volume 1}, publisher = {Prentice Hall PTR}, @@ -47,7 +47,7 @@ OPTannote = {} } @Book{UNP2, - author = {W. Richard Stevens}, + author = {Richard W. Stevens}, editor = {}, title = {UNIX Network Programming, volume 2}, publisher = {Prentice Hall PTR}, @@ -95,7 +95,7 @@ OPTannote = {} } @Book{glibc, - author = {Sandra Loosemore Richard M. Stallman Roland McGrath Andrew Oram and Ulrich Drepper}, + author = {Sandra Loosemore, Richard M. Stallman, Roland McGrath, Andrew Oram and Ulrich Drepper}, editor = {Free Software Foundation}, title = {The GNU C Library Reference Manual}, publisher = {Free Software Foundation}, @@ -187,7 +187,7 @@ @Book{flex, author = {Vern Paxson}, editor = {Free Software Foundation}, - title = {Flex, varsion 2.5}, + title = {Flex, version 2.5}, publisher = {Free Software Foundation}, year = {1995}, OPTkey = {}, diff --git a/fileadv.tex b/fileadv.tex index 647822a..accebc0 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -5356,7 +5356,7 @@ Trattandosi di una funzione di servizio, ed ovviamente disponibile esclusivamente su Linux, inizialmente \funcd{fallocate} non era stata definita come funzione di libreria,\footnote{pertanto poteva essere invocata soltanto in maniera indiretta con l'ausilio di \func{syscall}, vedi - sez.~\ref{sec:intro_syscall}, come \code{long fallocate(int fd, int mode, + sez.~\ref{sec:proc_syscall}, come \code{long fallocate(int fd, int mode, loff\_t offset, loff\_t len)}.} ma a partire dalle \acr{glibc} 2.10 è stato fornito un supporto esplicito; il suo prototipo è: \begin{functions} diff --git a/filestd.tex b/filestd.tex index ce45b85..87eb446 100644 --- a/filestd.tex +++ b/filestd.tex @@ -385,7 +385,7 @@ e scarta quelli in ingresso, chiudendo tutti i file. Questa funzione è provvista solo per i casi di emergenza, quando si è verificato un errore ed il programma deve essere abortito, ma si vuole compiere qualche altra operazione dopo aver chiuso i file e prima di uscire (si ricordi quanto -visto in sez.~\ref{sec:proc_exit}). +visto in sez.~\ref{sec:proc_conclusion}). \subsection{Lettura e scrittura su uno stream} diff --git a/gapil.tex b/gapil.tex index 34c22f7..cb0bb1d 100644 --- a/gapil.tex +++ b/gapil.tex @@ -104,7 +104,7 @@ hyperfootnotes=false]{hyperref} \begin{quote} - Copyright \copyright\ 2000-2009 Simone Piccardi. Permission is granted to + Copyright \copyright\ 2000-2011 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'' @@ -190,12 +190,10 @@ hyperfootnotes=false]{hyperref} % at the end put the bibliography \backmatter - -%\bibliographystyle{phaip} -%\bibliography{biblio} \printindex - -\bibliographystyle{ieeetr} +%\bibliographystyle{phaip} +%\bibliographystyle{ieeetr} +\bibliographystyle{abstract} \bibliography{biblio} \end{document} diff --git a/intro.tex b/intro.tex index 785f010..d0513cf 100644 --- a/intro.tex +++ b/intro.tex @@ -29,10 +29,7 @@ introdurremo alcuni degli standard principali a cui viene fatto riferimento. In questa prima sezione faremo una breve panoramica sull'architettura di un sistema operativo di tipo Unix, come GNU/Linux, e della relazione fra le varie parti che lo compongono. Chi avesse già una conoscenza di questa materia può -tranquillamente saltare questa sezione ad eccezione di -sez.~\ref{sec:intro_syscall} dove introdotti alcuni concetti fondamentali -relativi alle funzioni di libreria. - +tranquillamente saltare questa sezione. \subsection{Concetti base} \label{sec:intro_base_concept} @@ -293,51 +290,6 @@ fondamentale per capire il funzionamento del sistema, l'uso da parte dei programmi di una di queste funzioni resta lo stesso, sia che si tratti di una funzione interna della libreria che di una \textit{system call}. -Come accennato in genere ogni \textit{system call} è associata ad una omonima -funzione di libreria, che è quella che si usa normalmente per invocarla. Le -\textsl{glibc} consentono comunque, nel caso non sia presente una specifica -funzione di libreria corrispondente o qualora si voglia eseguire una specifica -versione,\footnote{alcune \textit{system call} sono state modificate nel corso - degli anni per aggiungere ad esempio delle funzionalità, l'interfaccia - proposta dalle \textsl{glibc} si cura in genere di mantenere una uniformità - chiamando le versioni corrette, ma qualora si voglia lavorare a basso - livello ed usare una specifica versione, si può fare ricorso a questa - funzione.} di eseguire direttamente una \textit{system call} tramite la -funzione \funcd{syscall}, il cui prototipo (accessibile se si è definita la -macro \macro{\_GNU\_SOURCE}, vedi sez.~\ref{sec:intro_gcc_glibc_std}) è: - -\begin{funcproto}{ - \fhead{unistd.h} - \fhead{sys/syscall.h} - \fdecl{int syscall(int number, ...)} - \fdesc{Esegue la \textit{system call} indicata da \param{number}.} -} -{La funzione ritorna un intero dipendente dalla \textit{system call} invocata, -in generale $0$ indica il successo e un valore negativo un errore.} -\end{funcproto} - -La funzione richiede come primo argomento il numero della \textit{system call} -da invocare, seguita dagli argomenti da passare alla stessa, che ovviamente -dipendono da quest'ultima, e restituisce il codice di ritorno della -\textit{system call} invocata. In generale un valore nullo indica il successo -ed un valore negativo è un codice di errore che poi viene memorizzato nella -variabile \var{errno} (sulla gestione degli errori torneremo in dettaglio in -sez.~\ref{sec:sys_errors}). - -Il valore di \param{number} dipende sia dalla versione di kernel che -dall'architettura,\footnote{in genere le vecchie \textit{system call} non - vengono eliminate e se ne aggiungono di nuove con nuovi numeri.} ma -ciascuna \textit{system call} viene in genere identificata da una costante -nella forma \texttt{SYS\_*} dove al prefisso viene aggiunto il nome che spesso -corrisponde anche alla omonima funzione di libreria; queste costanti sono -definite nel file \texttt{sys/syscall.h}, ma si possono anche usare -direttamente valori numerici. - -% -% TODO:trattare non so se qui o altrove vsyscall e vDSO, vedi -% http://davisdoesdownunder.blogspot.com/2011/02/linux-syscall-vsyscall-and-vdso-oh-my.html -% http://www.win.tue.nl/~aeb/linux/lk/lk-4.html -% \subsection{Un sistema multiutente} \label{sec:intro_multiuser} @@ -427,7 +379,7 @@ interfacce di programmazione e le altre caratteristiche di un sistema unix-like (alcuni standardizzano pure i comandi base del sistema e la shell) ed in particolare ci concentreremo sul come ed in che modo essi sono supportati sia per quanto riguarda il kernel che la Libreria Standard del C, -con una particolare attenzione alle \acr{glibc}. +con una particolare attenzione alla \acr{glibc}. \subsection{Lo standard ANSI C} @@ -449,65 +401,21 @@ linguaggio C (operatori, parole chiave, tipi di dati) lo standard prevede anche una libreria di funzioni che devono poter essere implementate su qualunque sistema operativo. -\begin{table}[htb] - \footnotesize - \centering - \begin{tabular}[c]{|l|c|c|l|} - \hline - \multirow{2}{*}{\textbf{Header}}& - \multicolumn{2}{|c|}{\textbf{Standard}}& - \multirow{2}{*}{\textbf{Contenuto}} \\ - \cline{2-3} - & ANSI C& POSIX& \\ - \hline - \hline - \file{assert.h}&$\bullet$& -- & Verifica le asserzioni fatte in un - programma.\\ - \file{ctype.h} &$\bullet$& -- & Tipi standard.\\ - \file{dirent.h}& -- &$\bullet$& Manipolazione delle directory.\\ - \file{errno.h} & -- &$\bullet$& Errori di sistema.\\ - \file{fcntl.h} & -- &$\bullet$& Controllo sulle opzioni dei file.\\ - \file{limits.h}& -- &$\bullet$& Limiti e parametri del sistema.\\ - \file{malloc.h}&$\bullet$& -- & Allocazione della memoria.\\ - \file{setjmp.h}&$\bullet$& -- & Salti non locali.\\ - \file{signal.h}& -- &$\bullet$& Gestione dei segnali.\\ - \file{stdarg.h}&$\bullet$& -- & Gestione di funzioni a argomenti - variabili.\\ - \file{stdio.h} &$\bullet$& -- & I/O bufferizzato in standard ANSI C.\\ - \file{stdlib.h}&$\bullet$& -- & Definizioni della libreria standard.\\ - \file{string.h}&$\bullet$& -- & Manipolazione delle stringhe.\\ - \file{time.h} & -- &$\bullet$& Gestione dei tempi.\\ - \file{times.h} &$\bullet$& -- & Gestione dei tempi.\\ - \file{unistd.h}& -- &$\bullet$& Unix standard library.\\ - \file{utmp.h} & -- &$\bullet$& Registro connessioni utenti.\\ - \hline - \end{tabular} - \caption{Elenco dei vari header file definiti dallo standard POSIX.} - \label{tab:intro_posix_header} -\end{table} - Per questo motivo, anche se lo standard non ha alcun riferimento ad un sistema di tipo Unix, GNU/Linux (per essere precisi la \acr{glibc}), come molti Unix moderni, provvede la compatibilità con questo standard, fornendo le funzioni di libreria da esso previste. Queste sono dichiarate in una serie di -\textit{header file}\footnote{si chiamano così i file di dichiarazione di - variabili, tipi e funzioni, usati normalmente da un compilatore C; per poter - accedere alle funzioni occorre includere con la direttiva \code{\#include} - questi file nei propri programmi ed in seguito per ciascuna funzione che - tratteremo indicheremo anche gli \textit{header file} necessari ad usarla.} -(anch'essi forniti dalla \acr{glibc}); in tab.~\ref{tab:intro_posix_header} si -sono riportati i principali \textit{header file} definiti negli standard POSIX -ed ANSI C, che sono anche quelli definiti negli altri standard descritti nelle -sezioni successive. +\textit{header file} anch'essi forniti dalla \acr{glibc} (tratteremo +l'argomento in sez.~\ref{sec:proc_syscall}). In realtà la \acr{glibc} ed i relativi \textit{header file} definiscono un insieme di funzionalità in cui sono incluse come sottoinsieme anche quelle previste dallo standard ANSI C. È possibile ottenere una conformità stretta allo standard (scartando le funzionalità addizionali) usando il \cmd{gcc} con l'opzione \cmd{-ansi}. Questa opzione istruisce il compilatore a definire nei -vari header file soltanto le funzionalità previste dallo standard ANSI C e a -non usare le varie estensioni al linguaggio e al preprocessore da esso -supportate. +vari \textit{header file} soltanto le funzionalità previste dallo standard +ANSI C e a non usare le varie estensioni al linguaggio e al preprocessore da +esso supportate. \subsection{I tipi di dati primitivi} @@ -572,7 +480,7 @@ infinita serie di problemi di portabilità. Per questo motivo tutte le funzioni di libreria di solito non fanno riferimento ai tipi elementari dello standard del linguaggio C, ma ad una serie di \index{tipo!primitivo} \textsl{tipi primitivi} del sistema, riportati -in tab.~\ref{tab:intro_primitive_types}, e definiti nell'header file +in tab.~\ref{tab:intro_primitive_types}, e definiti nell'\textit{header file} \file{sys/types.h}, in modo da mantenere completamente indipendenti i tipi utilizzati dalle funzioni di sistema dai tipi elementari supportati dal compilatore C. @@ -802,8 +710,8 @@ Issue 4, da cui la sigla XPG4, che aggiungeva l'interfaccia XTI (\textit{X Transport Interface}) mirante a soppiantare (senza molto successo) l'interfaccia dei socket derivata da BSD. Una seconda versione della guida fu rilasciata nel 1994; questa è nota con il nome di Spec 1170 (dal numero delle -interfacce, header e comandi definiti) ma si fa riferimento ad essa anche come -XPG4v2. +interfacce, intestazioni e comandi definiti) ma si fa riferimento ad essa +anche come XPG4v2. Nel 1993 il marchio Unix passò di proprietà dalla Novell (che a sua volta lo aveva comprato dalla AT\&T) al consorzio X/Open che iniziò a pubblicare le sue @@ -849,8 +757,8 @@ dall'aggiornamento vada a definire la quarta versione delle \textit{Single In Linux, se si usa la \acr{glibc}, la conformità agli standard appena descritti può essere richiesta sia attraverso l'uso di opportune opzioni del compilatore (il \texttt{gcc}) che definendo delle specifiche costanti prima -dell'inclusione dei file di dichiarazione (gli \textit{header file}) che -definiscono le funzioni di libreria. +dell'inclusione dei file di intestazione (gli \textit{header file}, vedi +sez.~\ref{sec:proc_syscall}) che definiscono le funzioni di libreria. Ad esempio se si vuole che i programmi seguano una stretta attinenza allo standard ANSI C si può usare l'opzione \texttt{-ansi} del compilatore, e non @@ -876,7 +784,7 @@ funzionalità che la \acr{glibc} può mettere a disposizione:\footnote{le macro che non devono assolutamente mai essere usate direttamente. } questo può essere fatto attraverso l'opzione \texttt{-D} del compilatore, ma è buona norma farlo inserendo gli opportuni \code{\#define} prima della inclusione dei -propri \textit{header file}. +propri \textit{header file} (vedi sez.~\ref{sec:proc_syscall}). Le macro disponibili per controllare l'aderenza ai vari standard messi a disposizione della \acr{glibc}, che rendono disponibili soltanto le funzioni @@ -1125,7 +1033,7 @@ sempre definite prima dell'inclusione dei file di dichiarazione. % LocalWords: assert ctype dirent errno fcntl limits malloc setjmp signal utmp % LocalWords: stdarg stdio stdlib string times unistd library int short caddr % LocalWords: address clock dev ino inode key IPC loff nlink off pid rlim size -% LocalWords: sigset ssize ptrdiff sys nell'header IEEE Richard Portable of TR +% LocalWords: sigset ssize ptrdiff sys IEEE Richard Portable of TR % LocalWords: Operating Interface dell'IEEE Electrical and Electronics thread % LocalWords: Engeneers Socket NT matching regular expression scheduling l'I % LocalWords: XPG Portability Issue Application Programmable XTI Transport AT diff --git a/listati/main_def.c b/listati/main_def.c index f653bde..9d4eade 100644 --- a/listati/main_def.c +++ b/listati/main_def.c @@ -1 +1,4 @@ - int main (int argc, char *argv[]) +int main (int argc, char *argv[]) +{ +... +} diff --git a/listati/main_include.c b/listati/main_include.c new file mode 100644 index 0000000..5decc7e --- /dev/null +++ b/listati/main_include.c @@ -0,0 +1,6 @@ +#include /* primitive system data types */ +#include /* directory constants and functions */ +#include /* C standard library */ +#include /* unix standard library */ + +#include "Gapil.h" /* local library */ diff --git a/macro.tex b/macro.tex index a012190..85267ea 100644 --- a/macro.tex +++ b/macro.tex @@ -18,7 +18,7 @@ \def\circonf{\char'136} \def\invap{\char'140} -\newcommand{\includecodesnip}[1]{\lstinputlisting[stepnumber=0,frame=]{#1}}{} +\newcommand{\includecodesnip}[1]{\lstinputlisting[stepnumber=0,xleftmargin=\parindent,frame=]{#1}}{} \newcommand{\includestruct}[1]{\lstinputlisting[stepnumber=0]{#1}}{} \newcommand{\includecodesample}[1]{\lstinputlisting{#1}}{} @@ -216,14 +216,12 @@ \newlength{\funcboxwidth} \setlength{\funcboxwidth}{0.85\textwidth} - - % % Nuove macro per diversa formattazione delle definizioni delle funzioni % \newcommand{\fhead}[1]{\texttt{\#include <#1>}\par} \newcommand{\fdecl}[1]{\texttt{#1}\par} -\newcommand{\fdesc}[1]{#1\par} +\newcommand{\fdesc}[1]{\hfill{#1}\par} \newenvironment{funcproto}[2] {% defining what is done by \begin @@ -249,9 +247,9 @@ } {% defining what is done by \end \end{boxedminipage} +\break \normalsize \par -%\break } diff --git a/process.tex b/process.tex index df94444..70ccc61 100644 --- a/process.tex +++ b/process.tex @@ -34,201 +34,529 @@ programma: si possono avere più processi che eseguono lo stesso programma ma ciascun processo vedrà la sua copia del codice (in realtà il kernel fa sì che tutte le parti uguali siano condivise), avrà un suo spazio di indirizzi, variabili proprie e sarà eseguito in maniera completamente indipendente da -tutti gli altri.\footnote{questo non è del tutto vero nel caso di un programma - \textit{multi-thread}, ma la gestione dei \itindex{thread} \textit{thread} - in Linux sarà trattata a parte in cap.~\ref{cha:threads}.} +tutti gli altri. Questo non è del tutto vero nel caso di un programma +\textit{multi-thread}, ma la gestione dei \itindex{thread} \textit{thread} in +Linux sarà trattata a parte in cap.~\ref{cha:threads}. -\subsection{La funzione \func{main}} +\subsection{L'avvio e l'esecuzione di un programma} \label{sec:proc_main} -Quando un programma viene lanciato il kernel esegue un opportuno codice di -avvio, usando il programma \cmd{ld-linux.so}. Questo programma prima carica -le librerie condivise che servono al programma, poi effettua il collegamento -dinamico del codice e alla fine lo esegue. Infatti, a meno di non aver -specificato il flag \texttt{-static} durante la compilazione, tutti i -programmi in Linux sono incompleti e necessitano di essere \textsl{collegati} -alle librerie condivise quando vengono avviati. La procedura è controllata da -alcune variabili di ambiente e dal contenuto di \conffile{/etc/ld.so.conf}. I -dettagli sono riportati nella pagina di manuale di \cmd{ld.so}. - -Il sistema fa partire qualunque programma chiamando la funzione \func{main}; -sta al programmatore chiamare così la funzione principale del programma da cui -si suppone iniziare l'esecuzione; in ogni caso senza questa funzione lo stesso -\textit{linker} (si chiama così il programma che effettua i collegamenti di -cui sopra) darebbe luogo ad errori. Lo standard ISO C specifica che la -funzione \func{main} può non avere argomenti o prendere due argomenti che -rappresentano gli argomenti passati da linea di comando, in sostanza un -prototipo che va sempre bene è il seguente: +\itindbeg{link-loader} + +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 +Windows sono chiamate DLL (\textit{Dinamic Link Library}), e che invece in un +sistema unix-like vengono chiamate \textit{shared objects}. + +Infatti, a meno di non aver specificato il flag \texttt{-static} durante la +compilazione, tutti i programmi in Linux sono compilati facendo riferimento a +librerie condivise, in modo da evitare di duplicare lo stesso codice nei +relativi eseguibili e consentire un uso più efficiente della memoria, dato che +il codice di uno \itindex{shared~objects} \textit{shared objects} viene +caricato in memoria dal kernel 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. + +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 +\texttt{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 +\texttt{LD\_PRELOAD} si può passare direttamente una lista di file di librerie +condivise da usare al posto di quelli di sistema. In questo modo è possibile +effettuare lo sviluppo o il test di nuove librerie senza dover sostituire +quelle di sistema. Ulteriori dettagli sono riportati nella pagina di manuale +di \cmd{ld.so} e per un approfondimento dell'argomento si può consultare +sez.~3.1.2 di \cite{AGL}. + +Una volta completate le operazioni di inizializzazione di \cmd{ld-linux.so}, il +sistema fa partire qualunque programma chiamando la funzione \func{main}. Sta +al programmatore chiamare così la funzione principale del programma da cui si +suppone che inizi l'esecuzione. In ogni caso senza questa funzione lo stesso +\textit{link-loader} darebbe luogo ad errori. Lo standard ISO C specifica che +la funzione \func{main} può non avere argomenti o prendere due argomenti che +rappresentano gli argomenti passati da linea di comando (su cui torneremo in +sez.~\ref{sec:proc_par_format}), in sostanza un prototipo che va sempre bene è +il seguente: \includecodesnip{listati/main_def.c} +\itindend{link-loader} + In realtà nei sistemi Unix esiste un altro modo per definire la funzione \func{main}, che prevede la presenza di un terzo argomento, \code{char - *envp[]}, che fornisce (vedi sez.~\ref{sec:proc_environ}) -l'\textsl{ambiente} del programma; questa forma però non è prevista dallo -standard POSIX.1 per cui se si vogliono scrivere programmi portabili è meglio -evitarla. - - -\subsection{Come chiudere un programma} -\label{sec:proc_conclusion} + *envp[]}, che fornisce l'\textsl{ambiente} del programma; questa forma però +non è prevista dallo standard POSIX.1 per cui se si vogliono scrivere +programmi portabili è meglio evitarla. Per accedere all'ambiente, come vedremo +in sez.~\ref{sec:proc_environ} si usa in genere una variabile globale che +viene sempre definita automaticamente. + +Ogni programma viene fatto partire mettendo in esecuzione il codice contenuto +nella funzione \func{main}, ogni altra funzione usata dal programma, che sia +ottenuta da una libreria condivisa, o che sia direttamente definita nel +codice, dovrà essere invocata a partire dal codice di \func{main}. Nel caso di +funzioni definite nel programma occorre tenere conto che, nel momento stesso +in cui si usano le librerie di sistema (vale a dire la \acr{glibc}) alcuni +nomi sono riservati e non possono essere utilizzati. + +In particolare sono riservati a priori e non possono essere mai ridefiniti in +nessun caso i nomi di tutte le funzioni, le variabili, le macro di +preprocessore, ed i tipi di dati previsti dallo standard ISO C. Lo stesso +varrà per tutti i nomi definiti negli \textit{header file} che si sono +esplicitamente inclusi nel programma (vedi sez.~\ref{sec:proc_syscall}), ma +anche se è possibile riutilizzare nomi definiti in altri \textit{header file} +la pratica è da evitare nella maniera più assoluta per non generare ambiguità. + +Oltre ai nomi delle funzioni di libreria sono poi riservati in maniera +generica tutti i nomi di variabili o funzioni globali che iniziano con il +carattere di sottolineato (``\texttt{\_}''), e qualunque nome che inizi con il +doppio sottolineato (``\texttt{\_\_}'') o con il sottolineato seguito da +lettera maiuscola. Questi identificativi infatti sono utilizzati per i nomi +usati internamente in forma privata dalle librerie, ed evitandone l'uso si +elimina il rischio di conflitti. + +Infine esiste una serie di classi di nomi che sono riservati per un loro +eventuale uso futuro da parte degli standard ISO C e POSIX.1, questi in teoria +possono essere usati senza problemi oggi, ma potrebbero dare un conflitto con +una revisione futura di questi standard, per cui è comunque opportuno +evitarli, in particolare questi sono: +\begin{itemize*} +\item i nomi che iniziano per ``\texttt{E}'' costituiti da lettere maiuscole e + numeri, che potrebbero essere utilizzati per nuovi codici di errore (vedi + sez.~\ref{sec:sys_errors}), +\item i nomi che iniziano con ``\texttt{is}'' o ``\texttt{to}'' e costituiti + da lettere minuscole che potrebbero essere utilizzati da nuove funzioni per + il controllo e la conversione del tipo di caratteri, +\item i nomi che iniziano con ``\texttt{LC\_}'' e costituiti + da lettere maiuscole che possono essere usato per macro attinenti la + localizzazione (vedi sez.~\ref{sec:proc_localization}), +\item nomi che iniziano con ``\texttt{SIG}'' o ``\texttt{SIG\_}'' e costituiti + da lettere maiuscole che potrebbero essere usati per nuovi nomi di segnale + (vedi sez.~\ref{sec:sig_classification}), +\item nomi che iniziano con ``\texttt{str}'', ``\texttt{mem}'', o + ``\texttt{wcs}'' e costituiti da lettere minuscole che possono essere + utilizzati per funzioni attinenti la manipolazione delle stringhe e delle + aree di memoria, +\item nomi che terminano in ``\texttt{\_t}'' che potrebbero essere utilizzati + per la definizione di nuovi tipi di dati di sistema oltre quelli di + tab.~\ref{tab:intro_primitive_types}). +\end{itemize*} -Normalmente un programma finisce quando la funzione \func{main} ritorna, una -modalità equivalente di chiudere il programma è quella di chiamare -direttamente la funzione \func{exit} (che viene comunque chiamata -automaticamente quando \func{main} ritorna). Una forma alternativa è quella -di chiamare direttamente la system call \func{\_exit}, che restituisce il -controllo direttamente alla funzione 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 (tratteremo i segnali in cap.~\ref{cha:signals}) o della chiamata alla -funzione \func{abort}; torneremo su questo in sez.~\ref{sec:proc_termination}. +\subsection{Chiamate a funzioni e \textit{system call}} +\label{sec:proc_syscall} + +Come accennato in sez.~\ref{sec:intro_syscall} un programma può utilizzare le +risorse che il sistema gli mette a disposizione attraverso l'uso delle +opportune \textit{system call}. Abbiamo inoltre appena visto come all'avvio un +programma venga messo in grado di chiamare le funzioni fornite da eventuali +librerie condivise da esso utilizzate. + +Vedremo nel resto della guida quali sono le risorse del sistema accessibili +attraverso le \textit{system call} e tratteremo buona parte delle funzioni +messe a disposizione dalla Libreria Standard del C, in questa sezione però si +forniranno alcune indicazioni generali sul come fare perché un programma possa +utilizzare queste funzioni. + +\itindbeg{header~file} + +In sez.~\ref{sec:intro_standard} abbiamo accennato come le funzioni definite +nei vari standard siano definite in una serie di \textit{header file} (in +italiano \textsl{file di intestazione}). Vengono chiamati in questo modo quei +file, forniti insieme al codice delle librerie, che contengono le +dichiarazioni delle variabili, dei tipi di dati, delle macro di preprocessore +e soprattutto delle funzioni che fanno parte di una libreria. + +Questi file sono necessari al compilatore del linguaggio C per ottenere i +riferimenti ai nomi delle funzioni (e alle altre risorse) definite in una +libreria, per questo quando si vogliono usare le funzioni di una libreria +occorre includere nel proprio codice gli \textit{header file} che le +definiscono con la direttiva \code{\#include}. Dato che le funzioni devono +essere definite prima di poterle usare in genere gli \textit{header file} +vengono inclusi all'inizio del programma. Se inoltre si vogliono utilizzare le +macro di controllo delle funzionalità fornite dai vari standard illustrate in +sez.~\ref{sec:intro_gcc_glibc_std} queste, come accennato, dovranno a loro +volta essere definite prima delle varie inclusioni. + +Ogni libreria fornisce i propri file di intestazione per i quali si deve +consultare la documentazione, ma in tab.~\ref{tab:intro_posix_header} si sono +riportati i principali \textit{header file} definiti nella Libreria Standard +del C (nel caso la \acr{glibc}) che contengono le varie funzioni previste +negli standard POSIX ed ANSI C, e che prevedono la definizione sia delle +funzioni di utilità generica che delle interfacce alle \textit{system call}. In +seguito per ciascuna funzione o \textit{system call} che tratteremo +indicheremo anche quali sono gli \textit{header file} contenenti le necessarie +definizioni. -Il valore di ritorno della funzione \func{main}, o quello usato nelle chiamate -ad \func{exit} e \func{\_exit}, viene chiamato \textsl{stato di uscita} (o -\textit{exit status}) e passato al processo che aveva lanciato il programma -(in genere la shell). In generale si usa questo valore per fornire -informazioni sulla riuscita o il fallimento del programma; l'informazione è -necessariamente generica, ed il valore deve essere compreso fra 0 e 255. - -La convenzione in uso pressoché universale è quella di restituire 0 in caso di -successo e 1 in caso di fallimento; l'unica eccezione è per i programmi che -effettuano dei confronti (come \cmd{diff}), che usano 0 per indicare la -corrispondenza, 1 per indicare la non corrispondenza e 2 per indicare -l'incapacità di effettuare il confronto. È opportuno adottare una di queste -convenzioni a seconda dei casi. Si tenga presente che se si raggiunge la fine -della funzione \func{main} senza ritornare esplicitamente si ha un valore di -uscita indefinito, è pertanto consigliabile di concludere sempre in maniera -esplicita detta funzione. +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|c|c|l|} + \hline + \multirow{2}{*}{\textbf{Header}}& + \multicolumn{2}{|c|}{\textbf{Standard}}& + \multirow{2}{*}{\textbf{Contenuto}} \\ + \cline{2-3} + & ANSI C& POSIX& \\ + \hline + \hline + \file{assert.h}&$\bullet$& -- & Verifica le asserzioni fatte in un + programma.\\ + \file{ctype.h} &$\bullet$& -- & Tipi standard.\\ + \file{dirent.h}& -- &$\bullet$& Manipolazione delle directory.\\ + \file{errno.h} & -- &$\bullet$& Errori di sistema.\\ + \file{fcntl.h} & -- &$\bullet$& Controllo sulle opzioni dei file.\\ + \file{limits.h}& -- &$\bullet$& Limiti e parametri del sistema.\\ + \file{malloc.h}&$\bullet$& -- & Allocazione della memoria.\\ + \file{setjmp.h}&$\bullet$& -- & Salti non locali.\\ + \file{signal.h}& -- &$\bullet$& Gestione dei segnali.\\ + \file{stdarg.h}&$\bullet$& -- & Gestione di funzioni a argomenti + variabili.\\ + \file{stdio.h} &$\bullet$& -- & I/O bufferizzato in standard ANSI C.\\ + \file{stdlib.h}&$\bullet$& -- & Definizioni della libreria standard.\\ + \file{string.h}&$\bullet$& -- & Manipolazione delle stringhe.\\ + \file{time.h} & -- &$\bullet$& Gestione dei tempi.\\ + \file{times.h} &$\bullet$& -- & Gestione dei tempi.\\ + \file{unistd.h}& -- &$\bullet$& Unix standard library.\\ + \file{utmp.h} & -- &$\bullet$& Registro connessioni utenti.\\ + \hline + \end{tabular} + \caption{Elenco dei principali \textit{header file} definiti dagli standard + POSIX e ANSI C.} + \label{tab:intro_posix_header} +\end{table} -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é questa convenzione non sia -universalmente seguita è una buona idea tenerne conto. +Un esempio di inclusione di questi file, preso da uno dei programmi di +esempio, è il seguente, e si noti come gli \textit{header file} possano essere +referenziati con il nome fra parentesi angolari, nel qual caso si indica l'uso +di quelli installati con il sistema,\footnote{in un sistema GNU/Linux che + segue le specifiche del \textit{Filesystem Hierarchy Standard} (per maggiori + informazioni si consulti sez.~1.2.3 di \cite{AGL}) si trovano sotto + \texttt{/usr/include}.} o fra virgolette, nel qual caso si fa riferimento ad +una versione locale, da indicare con un pathname relativo: +\includecodesnip{listati/main_include.c} + +Si tenga presente che oltre ai nomi riservati a livello generale di cui si è +parlato in sez.~\ref{sec:proc_main}, alcuni di questi \textit{header file} +riservano degli ulteriori identificativi, il cui uso sarà da evitare, ad +esempio si avrà che: +\begin{itemize*} +\item in \file{dirent.h} vengono riservati i nomi che iniziano con + ``\texttt{d\_}'' e costituiti da lettere minuscole, +\item in \file{fcntl.h} vengono riservati i nomi che iniziano con + ``\texttt{l\_}'', ``\texttt{F\_}'',``\texttt{O\_}'' e ``\texttt{S\_}'', +\item in \file{limits.h} vengono riservati i nomi che finiscono in + ``\texttt{\_MAX}'', +\item in \file{signal.h} vengono riservati i nomi che iniziano con + ``\texttt{sa\_}'' e ``\texttt{SA\_}'', +\item in \file{sys/stat.h} vengono riservati i nomi che iniziano con + ``\texttt{st\_}'' e ``\texttt{S\_}'', +\item in \file{sys/times.h} vengono riservati i nomi che iniziano con + ``\texttt{tms\_}'', +\item in \file{termios.h} vengono riservati i nomi che iniziano con + ``\texttt{c\_}'', ``\texttt{V}'', ``\texttt{I}'', ``\texttt{O}'' e + ``\texttt{TC}'' e con ``\texttt{B}'' seguito da un numero, +\item in \file{grp.h} vengono riservati i nomi che iniziano con + ``\texttt{gr\_}'', +\item in \file{pwd.h}vengono riservati i nomi che iniziano con + ``\texttt{pw\_}'', +\end{itemize*} -Si tenga presente inoltre che non è una buona idea usare il codice di errore -restituito dalla variabile \var{errno} (per i dettagli si veda -sez.~\ref{sec:sys_errors}) come stato di uscita. In generale infatti una shell -non si cura del valore se non per vedere se è diverso da zero; inoltre il -valore dello stato di uscita è sempre troncato ad 8 bit, per cui si potrebbe -incorrere nel caso in cui restituendo un codice di errore 256, si otterrebbe -uno stato di uscita uguale a zero, che verrebbe interpretato come un successo. +\itindend{header~file} + +Una volta inclusi gli \textit{header file} necessari un programma potrà +richiamare le funzioni di libreria direttamente nel proprio codice ed accedere +ai servizi del kernel; come accennato infatti normalmente ogni \textit{system + call} è associata ad una omonima funzione di libreria, che è quella che si +usa normalmente per invocarla. + +Occorre però tenere presente che anche se dal punto di vista della scrittura +del codice la chiamata di una \textit{system call} non è diversa da quella di +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}. + +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}. + +% +% TODO:trattare qui, quando sarà il momento vsyscall e vDSO, vedi: +% http://davisdoesdownunder.blogspot.com/2011/02/linux-syscall-vsyscall-and-vdso-oh-my.html +% http://www.win.tue.nl/~aeb/linux/lk/lk-4.html +% + +Inoltre alcune \textit{system call} sono state modificate nel corso degli anni +con lo sviluppo del kernel per aggiungere ad esempio funzionalità in forma di +nuovi argomenti, o per consolidare diverse varianti in una interfaccia +generica. Per questo motivo dovendo utilizzare una \textit{system call} è +sempre preferibile usare l'interfaccia fornita dalla \textsl{glibc}, che si +cura di mantenere una uniformità chiamando le versioni più aggiornate. + +Ci sono alcuni casi però in cui può essere necessario evitare questa +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}, è: -In \file{stdlib.h} sono definite, seguendo lo standard POSIX, le due costanti -\const{EXIT\_SUCCESS} e \const{EXIT\_FAILURE}, da usare sempre per specificare -lo stato di uscita di un processo. In Linux esse sono poste rispettivamente ai -valori di tipo \ctyp{int} 0 e 1. +\begin{funcproto}{ + \fhead{unistd.h} + \fhead{sys/syscall.h} + \fdecl{int syscall(int number, ...)} + \fdesc{Esegue la \textit{system call} indicata da \param{number}.} +} +{La funzione ritorna un intero dipendente dalla \textit{system call} invocata, +in generale $0$ indica il successo e un valore negativo un errore.} +\end{funcproto} +La funzione richiede come primo argomento il numero della \textit{system call} +da invocare, seguita dagli argomenti da passare alla stessa, che ovviamente +dipendono da quest'ultima, e restituisce il codice di ritorno della +\textit{system call} invocata. In generale un valore nullo indica il successo +ed un valore negativo è un codice di errore che poi viene memorizzato nella +variabile \var{errno} (sulla gestione degli errori torneremo in dettaglio in +sez.~\ref{sec:sys_errors}). + +Il valore di \param{number} dipende sia dalla versione di kernel che +dall'architettura,\footnote{in genere le vecchie \textit{system call} non + vengono eliminate e se ne aggiungono di nuove con nuovi numeri.} ma +ciascuna \textit{system call} viene in genere identificata da una costante +nella forma \texttt{SYS\_*} dove al prefisso viene aggiunto il nome che spesso +corrisponde anche alla omonima funzione di libreria. Queste costanti sono +definite nel file \texttt{sys/syscall.h}, ma si possono anche usare +direttamente valori numerici. + + +\subsection{La terminazione di un programma} +\label{sec:proc_conclusion} -\subsection{Le funzioni \func{exit} e \func{\_exit}} -\label{sec:proc_exit} +Normalmente un programma conclude la sua esecuzione quando si fa ritornare la +funzione \func{main}, si usa cioè l'istruzione \texttt{return} del 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 \func{main} ritorna, passandogli come argomento il +valore di ritorno (che essendo . -Come accennato le funzioni usate per effettuare un'uscita ``\textit{normale}'' -da un programma sono due, la prima è la funzione \funcd{exit}, che è definita -dallo standard ANSI C ed il cui prototipo è: -\begin{prototype}{stdlib.h}{void exit(int status)} - Causa la conclusione ordinaria del programma. +La funzione \funcd{exit}, che è completamente generale, essendo definita dallo +standard ANSI C, è quella che deve essere invocata per una terminazione +``\textit{normale}'', il suo prototipo è: - \bodydesc{La funzione non ritorna. Il processo viene terminato.} -\end{prototype} +\begin{funcproto}{ + \fhead{unistd.h} + \fdecl{void exit(int status)} + \fdesc{Causa la conclusione ordinaria del programma.} +} +{La funzione non ritorna, il processo viene terminato.} +\end{funcproto} -La funzione \func{exit} è pensata per eseguire una conclusione pulita di un -programma che usi le librerie standard del C; essa esegue tutte le funzioni -che sono state registrate con \func{atexit} e \func{on\_exit} (vedi -sez.~\ref{sec:proc_atexit}), e chiude tutti gli stream effettuando il +La funzione è pensata per eseguire una conclusione pulita di un programma che +usi la Libreria Standard del C; essa esegue tutte le funzioni che sono state +registrate con \func{atexit} e \func{on\_exit} (vedi +sez.~\ref{sec:proc_atexit}), chiude tutti gli stream effettuando il salvataggio dei dati sospesi (chiamando \func{fclose}, vedi -sez.~\ref{sec:file_fopen}), infine passa il controllo al kernel chiamando -\func{\_exit} e restituendo il valore di \param{status} come stato di uscita. - -La system call \funcd{\_exit} restituisce direttamente il controllo al kernel, -concludendo immediatamente il processo; i dati sospesi nei buffer degli stream -non vengono salvati e le eventuali funzioni registrate con \func{atexit} e -\func{on\_exit} non vengono eseguite. Il prototipo della funzione è: -\begin{prototype}{unistd.h}{void \_exit(int status)} - Causa la conclusione immediata del programma. +sez.~\ref{sec:file_fopen}), infine passa il controllo al kernel chiamando la +\textit{system call} \func{\_exit} (che vedremo a breve) che completa la +terminazione del processo. + +\itindbeg{exit~status} + +Il valore dell'argomento \param{status} o il valore di ritorno di \func{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 +informazioni generiche sulla riuscita o il fallimento del programma appena +terminato. + +Anche se l'argomento \param{status} (ed il valore di ritorno di \func{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 \func{main} senza ritornare esplicitamente si ha un valore di +uscita indefinito, è pertanto consigliabile di concludere sempre in maniera +esplicita detta funzione. - \bodydesc{La funzione non ritorna. Il processo viene terminato.} -\end{prototype} +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. + +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}) +come \textit{exit status}. In generale infatti non ci si cura del valore dello +stato di uscita di un processo se non per vedere se è diverso da zero, come +indicazione di un qualche errore. Dato che viene troncato ad 8 bit utilizzare +un intero di valore generico può comportare il rischio, qualora si vada ad +usare un multiplo di 256, di avere uno stato di uscita uguale a zero, che +verrebbe interpretato come un successo. + +Per questo motivo in \file{stdlib.h} sono definite, seguendo lo standard +POSIX, le due costanti \const{EXIT\_SUCCESS} e \const{EXIT\_FAILURE}, da usare +sempre per specificare lo stato di uscita di un processo. Su Linux, ed in +generale in qualunque sistema POSIX, ad esse sono assegnati rispettivamente i +valori 0 e 1. + +\itindend{exit~status} + +Una forma alternativa per effettuare una terminazione esplicita di un +programma è quella di chiamare direttamente la \textit{system call} +\func{\_exit}, 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 + ritorna, il processo viene terminato.} +\end{funcproto} -La funzione chiude tutti i file descriptor appartenenti al processo; si tenga -presente che questo non comporta il salvataggio dei dati bufferizzati degli -stream, (torneremo sulle due interfacce dei file a partire da -cap.~\ref{cha:file_intro}), fa sì che ogni figlio del processo sia adottato da -\cmd{init} (vedi 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}). +La funzione termina immediatamente il processo e le eventuali funzioni +registrate con \func{atexit} e \func{on\_exit} non vengono eseguite. La +funzione chiude tutti i file descriptor appartenenti al processo, cosa che +però non comporta il salvataggio dei dati eventualmente presenti nei buffer +degli stream, (torneremo sulle due interfacce dei file a partire da +cap.~\ref{cha:file_intro}). Infine fa sì che ogni figlio del processo sia +adottato da \cmd{init} (vedi cap.~\ref{cha:process_handling}), manda un +segnale \const{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:proc_wait}). + +Si tenga presente infine che oltre alla conclusione ``\textsl{normale}'' +appena illustrata esiste anche la possibilità di una conclusione +``\textsl{anomala}'' del programma a causa della ricezione di un segnale +(tratteremo i segnali in cap.~\ref{cha:signals}) o della chiamata alla +funzione \func{abort}; torneremo su questo in sez.~\ref{sec:proc_termination}. -\subsection{Le funzioni \func{atexit} e \func{on\_exit}} +\subsection{Esecuzione di funzioni preliminari all'uscita} \label{sec:proc_atexit} -Un'esigenza comune che si incontra nella programmazione è 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. +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. È invece molto meno soggetto ad errori, e completamente trasparente -all'utente, avere la possibilità di effettuare automaticamente la chiamata ad -una funzione che effettui tali operazioni all'uscita dal programma. A questo -scopo lo standard ANSI C prevede la possibilità di registrare un certo numero -di funzioni che verranno eseguite all'uscita dal programma (sia per la -chiamata ad \func{exit} che per il ritorno di \func{main}). La prima funzione -che si può utilizzare a tal fine è \funcd{atexit} il cui prototipo è: -\begin{prototype}{stdlib.h}{void atexit(void (*function)(void))} - Registra la funzione \param{function} per la chiamata all'uscita dal - programma. - - \bodydesc{La funzione restituisce $0$ in caso di successo e $-1$ in caso di - fallimento, \var{errno} non viene modificata.} -\end{prototype} -\noindent la funzione richiede come argomento l'indirizzo di una opportuna -funzione di pulizia da chiamare all'uscita del programma, che non deve -prendere argomenti e non deve ritornare niente (deve essere cioè definita come -\code{void function(void)}). +all'utente, avere la possibilità di fare effettuare automaticamente la +chiamata ad una funzione che effettui tali operazioni all'uscita dal +programma. A questo scopo lo standard ANSI C prevede la possibilità di +registrare un certo numero di funzioni che verranno eseguite all'uscita dal +programma,\footnote{nel caso di \func{atexit} lo standard POSIX.1-2001 + richiede che siano registrabili almeno \const{ATEXIT\_MAX} funzioni (il + valore può essere ottenuto con \func{sysconf}, vedi + sez.~\ref{sec:sys_sysconf}).} sia per la chiamata ad \func{exit} che per il +ritorno di \func{main}. La prima funzione che si può utilizzare a tal fine è +\funcd{atexit}, il cui prototipo è: + +\begin{funcproto}{ \fhead{stdlib.h} \fdecl{void (*function)(void)} + \fdesc{Registra la funzione \param{function} per la chiamata all'uscita + dal programma.} } {La funzione restituisce $0$ in caso di successo e + $-1$ in caso di fallimento, \var{errno} non viene modificata.} +\end{funcproto} + +La funzione richiede come argomento \param{function} l'indirizzo di una +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; il suo prototipo è: -\begin{prototype}{stdlib.h} -{void on\_exit(void (*function)(int , void *), void *arg)} - Registra la funzione \param{function} per la chiamata all'uscita dal - programma. - - \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di - fallimento, \var{errno} non viene modificata.} -\end{prototype} +\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 è: + +\begin{funcproto}{ +\fhead{stdlib.h} +\fdecl{void (*function)(int , void *), void *arg)} +\fdesc{Registra la funzione \param{function} per la chiamata all'uscita dal + programma.} }{La funzione restituisce $0$ in caso di successo e $-1$ in caso +di fallimento, \var{errno} non viene modificata.} +\end{funcproto} 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 -puntatore \param{arg} passato come secondo argomento di \func{on\_exit}. Così -diventa possibile passare dei dati alla funzione di chiusura. +specificati nel prototipo, un intero ed un puntatore; 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 puntatore \param{arg} passato come secondo argomento di +\func{on\_exit}. Così diventa possibile passare dei dati alla funzione di +chiusura. Nella sequenza di chiusura tutte le funzioni registrate verranno chiamate in -ordine inverso rispetto a quello di registrazione (ed una stessa funzione -registrata più volte sarà chiamata più volte); poi verranno chiusi tutti gli -stream aperti, infine verrà chiamata \func{\_exit}. +ordine inverso rispetto a quello di registrazione, ed una stessa funzione +registrata più volte sarà chiamata più volte. Siccome entrambe le funzioni +\func{atexit} e \func{on\_exit} fanno riferimento alla stessa lista, l'ordine +di esecuzione sarà riferito alla registrazione in quanto tale, +indipendentemente dalla funzione usata per farla. + +Una volta completata l'esecuzione di tutte le funzioni registrate verranno +chiusi tutti gli stream aperti ed infine verrà chiamata \func{\_exit} per la +terminazione del programma. Questa è la sequenza ordinaria, eseguita a meno +che una delle funzioni registrate non esegua al suo interno \func{\_exit}, nel +qual caso la terminazione del programma sarà immediata ed anche le successive +funzioni registrate non saranno invocate. + +Se invece all'interno di una delle funzioni registrate si chiama un'altra +volta \func{exit} lo standard POSIX.1-2001 prescrive un comportamento +indefinito, con la possibilità (che su Linux comunque non c'è) di una +ripetizione infinita. Pertanto questa eventualità è da evitare nel modo più +assoluto. Una altro comportamento indefinito si può avere se si termina +l'esecuzione di una delle funzioni registrate con \func{longjmp} (vedi +sez.~\ref{sec:proc_longjmp}). +Si tenga presente infine che in caso di terminazione anomala di un processo +(ad esempio a causa di un segnale) nessuna delle funzioni registrate verrà +eseguita e che se invece si crea un nuovo processo con \func{fork} (vedi +sez.~\ref{sec:proc_fork}) questo manterrà tutte le funzioni già registrate. -\subsection{Conclusioni} + +\subsection{Un riepilogo} \label{sec:proc_term_conclusion} -Data l'importanza dell'argomento è opportuno sottolineare ancora una volta che -in un sistema Unix l'unico modo in cui un programma può essere eseguito dal -kernel è attraverso la chiamata alla system call \func{execve} (o attraverso -una delle funzioni della famiglia \func{exec} che vedremo in +Data l'importanza dell'argomento è opportuno un piccolo riepilogo dei fatti +essenziali relativi alla esecuzione di un programma. Il primo punto da +sottolineare è che in un sistema unix-like l'unico modo in cui un programma +può essere eseguito dal kernel è attraverso la chiamata alla \textit{system + call} \func{execve}, sia direttamente che attraverso una delle funzioni +della famiglia \func{exec} che ne semplificano l'uso (vedi sez.~\ref{sec:proc_exec}). Allo stesso modo l'unico modo in cui un programma può concludere -volontariamente la sua esecuzione è attraverso una chiamata alla system call -\func{\_exit}, o esplicitamente, o in maniera indiretta attraverso l'uso di -\func{exit} o il ritorno di \func{main}. +volontariamente la propria esecuzione è attraverso una chiamata alla +\textit{system call} \func{\_exit}, sia che questa venga fatta esplicitamente, +o in maniera indiretta attraverso l'uso di \func{exit} o il ritorno di +\func{main}. Uno schema riassuntivo che illustra le modalità con cui si avvia e conclude normalmente un programma è riportato in fig.~\ref{fig:proc_prog_start_stop}. @@ -243,7 +571,7 @@ normalmente un programma è riportato in fig.~\ref{fig:proc_prog_start_stop}. \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{exec}}; + \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}}; @@ -287,11 +615,11 @@ loro gestione nel capitolo \ref{cha:signals}. \section{I processi e l'uso della memoria} \label{sec:proc_memory} -Una delle risorse base che ciascun processo ha a disposizione è la memoria, e -la gestione della memoria è appunto uno degli aspetti più complessi di un -sistema unix-like. In questa sezione, dopo una breve introduzione ai concetti -base, esamineremo come la memoria viene vista da parte di un programma in -esecuzione, e le varie funzioni utilizzabili per la sua gestione. +Una delle risorse più importanti che ciascun processo ha a disposizione è la +memoria, e la gestione della memoria è appunto uno degli aspetti più complessi +di un sistema unix-like. In questa sezione, dopo una breve introduzione ai +concetti di base, esamineremo come la memoria viene vista da parte di un +programma in esecuzione, e le varie funzioni utilizzabili per la sua gestione. \subsection{I concetti generali} @@ -307,11 +635,11 @@ 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 anche per macchine a 32 bit.} -Come accennato in cap.~\ref{cha:intro_unix} questo spazio di indirizzi è +Come accennato nel 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 +computer. In generale detto spazio non è neppure continuo, cioè non tutti gli indirizzi possibili sono utilizzabili, e quelli usabili non sono -necessariamente adiacenti). +necessariamente adiacenti. Per la gestione da parte del kernel la memoria viene divisa in pagine di dimensione fissa,\footnote{inizialmente questi erano di 4kb sulle macchine a @@ -321,19 +649,19 @@ dimensione fissa,\footnote{inizialmente questi erano di 4kb sulle macchine a comporta una perdita di prestazioni.} e ciascuna pagina nello spazio di indirizzi virtuale è associata ad un supporto che può essere una pagina di memoria reale o ad un dispositivo di stoccaggio secondario (come lo spazio -disco riservato alla swap, o i file che contengono il codice). Per ciascun -processo il kernel si cura di mantenere un mappa di queste corrispondenze -nella cosiddetta \itindex{page~table} \textit{page table}.\footnote{questa è - una semplificazione brutale, il meccanismo è molto più complesso; una buona - trattazione di come Linux gestisce la memoria virtuale si trova su - \cite{LinVM}.} +disco riservato alla \textit{swap}, o i file che contengono il codice). Per +ciascun processo il kernel si cura di mantenere un mappa di queste +corrispondenze nella cosiddetta \itindex{page~table} \textit{page + table}.\footnote{questa è una semplificazione brutale, il meccanismo è molto + più complesso; una buona trattazione di come Linux gestisce la memoria + virtuale si trova su \cite{LinVM}.} Una stessa pagina di memoria reale può fare da supporto a diverse pagine di -memoria virtuale appartenenti a processi diversi (come accade in genere per le -pagine che contengono il codice delle librerie condivise). Ad esempio il -codice della funzione \func{printf} starà su una 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. +memoria virtuale appartenenti a processi diversi, come accade in genere per le +pagine che contengono il codice delle librerie condivise. Ad esempio il codice +della funzione \func{printf} starà su una 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 \index{memoria~virtuale} memoria virtuale di un processo e quelle della memoria fisica della macchina viene @@ -351,21 +679,22 @@ Quando un processo cerca di accedere ad una pagina che non è nella memoria reale, avviene quello che viene chiamato un \itindex{page~fault} \textit{page fault}; la 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. +RAM la pagina richiesta, effettuando tutte le operazioni necessarie per +reperire lo spazio necessario, per poi restituire il controllo al processo. Dal punto di vista di un processo questo meccanismo è completamente trasparente, e tutto avviene come se tutte le pagine fossero sempre disponibili in memoria. L'unica differenza avvertibile è quella dei tempi di -esecuzione, che passano dai pochi nanosecondi necessari per l'accesso in RAM, -a tempi molto più lunghi, dovuti all'intervento del kernel. +esecuzione, che passano dai pochi nanosecondi necessari per l'accesso in RAM +se la pagina è direttamente disponibile, a tempi estremamente più lunghi, +dovuti all'intervento del kernel, qualora sia necessario reperire pagine +riposte nella \textit{swap}. 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 \index{paginazione} paginazione e mantenere fisse delle pagine in memoria (vedi sez.~\ref{sec:proc_mem_lock}). -Inoltre per certe applicazioni gli algoritmi di gestione della memoria \subsection{La struttura della memoria di un processo} @@ -376,33 +705,31 @@ una parte di essi è effettivamente allocato ed utilizzabile dal processo; il tentativo di accedere ad un indirizzo non allocato è un tipico errore che si commette quando si è manipolato male un puntatore e genera quella che viene chiamata una \itindex{segment~violation} \textit{segment violation}. 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 +tenta cioè di leggere o scrivere con un indirizzo per il quale non esiste +un'associazione nella memoria virtuale, il kernel risponde al relativo \itindex{page~fault} \textit{page fault} mandando un segnale \const{SIGSEGV} al processo, che normalmente ne causa la terminazione immediata. È pertanto importante capire come viene strutturata \index{memoria~virtuale} -\textsl{la memoria virtuale} di un processo. Essa viene divisa in -\textsl{segmenti}, cioè un insieme contiguo di indirizzi virtuali ai quali il -processo può accedere. Solitamente un programma C viene suddiviso nei -seguenti segmenti: +la memoria virtuale di un processo. Essa viene divisa in \textsl{segmenti}, +cioè un insieme contiguo di indirizzi virtuali ai quali il processo può +accedere. Solitamente un programma C viene suddiviso nei seguenti segmenti: \begin{enumerate} \item Il \index{segmento!testo} segmento di testo o \textit{text segment}. Contiene il codice del programma, delle funzioni di librerie da esso utilizzate, e le costanti. Normalmente viene condiviso fra tutti i processi - che eseguono lo stesso programma (e anche da processi che eseguono altri - programmi nel caso delle librerie). Viene marcato in sola lettura per - evitare sovrascritture accidentali (o maliziose) che ne modifichino le - istruzioni. + che eseguono lo stesso programma e nel caso delle librerie anche da processi + che eseguono altri programmi. Viene marcato in sola lettura per evitare + sovrascritture accidentali (o maliziose) che ne modifichino le istruzioni. - Viene allocato da \func{exec} all'avvio del programma e resta invariato + Viene allocato da \func{execve} all'avvio del programma e resta invariato per tutto il tempo dell'esecuzione. \item Il \index{segmento!dati} segmento dei dati o \textit{data segment}. - Contiene le variabili globali (cioè quelle definite al di fuori di tutte le - funzioni che compongono il programma) e le variabili statiche (cioè quelle - dichiarate con l'attributo \ctyp{static}). Di norma è diviso in due parti. + Contiene le variabili globali, cioè quelle definite al di fuori di tutte le + funzioni che compongono il programma, e le variabili statiche, cioè quelle + dichiarate con l'attributo \ctyp{static}. Di norma è diviso in due parti. La prima parte è il segmento dei dati inizializzati, che contiene le variabili il cui valore è stato assegnato esplicitamente. Ad esempio @@ -428,13 +755,13 @@ seguenti segmenti: l'estensione del segmento dati, a cui di solito è posto giusto di seguito. È qui che avviene l'allocazione dinamica della memoria; può essere ridimensionato allocando e disallocando la memoria dinamica con le apposite - funzioni (vedi sez.~\ref{sec:proc_mem_alloc}), ma il suo limite inferiore - (quello adiacente al segmento dati) ha una posizione fissa. + funzioni (vedi sez.~\ref{sec:proc_mem_alloc}), ma il suo limite inferiore, + quello adiacente al segmento dati, ha una posizione fissa. \item Il segmento di \itindex{stack} \textit{stack}, che contiene quello che viene chiamato \textit{stack} del programma. Tutte le volte che si effettua una chiamata ad una funzione è qui che viene salvato l'indirizzo di ritorno - e le informazioni dello stato del chiamante (tipo il contenuto di alcuni + e le informazioni dello stato del chiamante (come il contenuto di alcuni registri della CPU), poi la funzione chiamata alloca qui lo spazio per le sue variabili locali. Tutti questi dati vengono \textit{impilati} (da questo viene il nome \itindex{stack} \textit{stack}) in sequenza uno sull'altro; in @@ -504,7 +831,7 @@ L'\textsl{allocazione statica} è quella con cui sono memorizzate le variabili globali e le variabili statiche, cioè le variabili il cui valore deve essere mantenuto per tutta la durata del programma. Come accennato queste variabili vengono allocate nel \index{segmento!dati} segmento dei dati all'avvio del -programma (come parte delle operazioni svolte da \func{exec}) e lo spazio da +programma come parte delle operazioni svolte da \func{exec}, e lo spazio da loro occupato non viene liberato fino alla sua conclusione. L'\textsl{allocazione automatica} è quella che avviene per gli argomenti di @@ -516,12 +843,11 @@ viene eseguita la funzione e liberato quando si esce dalla medesima. Esiste però un terzo tipo di allocazione, l'\textsl{allocazione dinamica} della memoria, che non è prevista direttamente all'interno del linguaggio C, ma che è necessaria quando il quantitativo di memoria che serve è -determinabile solo durante il corso dell'esecuzione del programma. - -Il C non consente di usare variabili allocate dinamicamente, non è possibile -cioè definire in fase di programmazione una variabile le cui dimensioni -possano essere modificate durante l'esecuzione del programma. Per questo le -librerie del C forniscono una serie opportuna di funzioni per eseguire +determinabile solo durante il corso dell'esecuzione del programma. Il C non +consente di usare variabili allocate dinamicamente, non è possibile cioè +definire in fase di programmazione una variabile le cui dimensioni possano +essere modificate durante l'esecuzione del programma. Per questo la Libreria +Standard del C fornisce una serie opportuna di funzioni per eseguire l'allocazione dinamica di memoria (in genere nello \itindex{heap} \textit{heap}). @@ -531,78 +857,104 @@ usate direttamente come le altre (quelle nello \itindex{stack} attraverso i puntatori alla memoria loro riservata che si sono ottenuti dalle funzioni di allocazione. - Le funzioni previste dallo standard ANSI C per la gestione della memoria sono -quattro: \funcd{malloc}, \funcd{calloc}, \funcd{realloc} e \funcd{free}, i -loro prototipi sono i seguenti: -\begin{functions} -\headdecl{stdlib.h} -\funcdecl{void *calloc(size\_t nmemb, size\_t size)} - Alloca nello \textit{heap} un'area di memoria per un vettore di - \param{nmemb} membri di \param{size} byte di dimensione. La memoria viene - inizializzata a 0. - - La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \val{NULL} in caso di fallimento, nel qual caso - \var{errno} assumerà il valore \errval{ENOMEM}. -\funcdecl{void *malloc(size\_t size)} - Alloca \param{size} byte nello \textit{heap}. La memoria non viene - inizializzata. - - La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \val{NULL} in caso di fallimento, nel qual caso - \var{errno} assumerà il valore \errval{ENOMEM}. -\funcdecl{void *realloc(void *ptr, size\_t size)} - Cambia la dimensione del blocco allocato all'indirizzo \param{ptr} - portandola a \param{size}. - - La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \val{NULL} in caso di fallimento, nel qual caso - \var{errno} assumerà il valore \errval{ENOMEM}. -\funcdecl{void free(void *ptr)} - Disalloca lo spazio di memoria puntato da \param{ptr}. - - La funzione non ritorna nulla e non riporta errori. -\end{functions} -Il puntatore ritornato dalle funzioni di allocazione è garantito essere sempre -allineato correttamente per tutti i tipi di dati; ad esempio sulle macchine a -32 bit in genere è allineato a multipli di 4 byte e sulle macchine a 64 bit a -multipli di 8 byte. - -In genere si usano le funzioni \func{malloc} e \func{calloc} per allocare -dinamicamente la quantità di memoria necessaria al programma indicata da -\param{size},\footnote{queste funzioni presentano un comportamento diverso fra - le \acr{glibc} e le \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 \code{realloc(NULL, 0)}.} e -siccome i puntatori ritornati sono di tipo generico non è necessario -effettuare un cast per assegnarli a puntatori al tipo di variabile per la -quale si effettua l'allocazione. - -La memoria allocata dinamicamente deve essere esplicitamente rilasciata usando -\func{free}\footnote{le glibc provvedono anche una funzione \func{cfree} - definita per compatibilità con SunOS, che è deprecata.} una volta che non -sia più necessaria. Questa funzione vuole come argomento un puntatore -restituito da una precedente chiamata a una qualunque delle funzioni di -allocazione che non sia già stato liberato da un'altra chiamata a \func{free}, -in caso contrario il comportamento della funzione è indefinito. - -La funzione \func{realloc} si usa invece per cambiare (in genere aumentare) la -dimensione di un'area di memoria precedentemente allocata, la funzione vuole -in ingresso il puntatore restituito dalla precedente chiamata ad una -\func{malloc} (se è passato un valore \val{NULL} allora la funzione si -comporta come \func{malloc})\footnote{questo è vero per Linux e - l'implementazione secondo lo standard ANSI C, ma non è vero per alcune - vecchie implementazioni, inoltre alcune versioni delle librerie del C - consentivano di usare \func{realloc} anche per un puntatore liberato con - \func{free} purché non ci fossero state nel frattempo altre chiamate a - funzioni di allocazione, questa funzionalità è totalmente deprecata e non è - consentita sotto Linux.} ad esempio quando si deve far crescere la -dimensione di un vettore. In questo caso se è disponibile dello spazio -adiacente al precedente la funzione lo utilizza, altrimenti rialloca altrove -un blocco della dimensione voluta, copiandoci automaticamente il contenuto; lo -spazio aggiunto non viene inizializzato. +quattro: \func{malloc}, \func{calloc}, \func{realloc} e \func{free}. Le prime +due, \funcd{malloc} e \funcd{calloc} allocano in nuovo spazio di memoria; i +rispettivi prototipi sono: + +\begin{funcproto}{ +\fhead{stdlib.h} +\fdecl{void *calloc(size\_t nmemb, size\_t size)} +\fdesc{Alloca un'area di memoria inizializzata a 0.} +\fdecl{void *malloc(size\_t size)} +\fdesc{Alloca un'area di memoria non inizializzata.} +} +{Entrambe le funzioni restituiscono il puntatore alla zona di memoria allocata +in caso di successo e \val{NULL} in caso di fallimento, nel qual caso + \var{errno} assumerà il valore \errval{ENOMEM}.} +\end{funcproto} + +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} + è 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 + \code{realloc(NULL, 0)}.} Dato che i puntatori ritornati sono di tipo +generico non è necessario effettuare un cast per assegnarli a puntatori al +tipo di variabile per la quale si effettua l'allocazione, inoltre le funzioni +garantiscono che i puntatori siano allineati correttamente per tutti i tipi di +dati; ad esempio sulle macchine a 32 bit in genere è allineato a multipli di 4 +byte e sulle macchine a 64 bit a multipli di 8 byte. + +Nel caso di \func{calloc} l'area di memoria viene allocata nello \textit{heap} +come un vettore di \param{nmemb} membri di \param{size} byte di dimensione, e +preventivamente inizializzata a zero, nel caso di \func{malloc} invece vengono +semplicemente allocati \param{size} byte e l'area di memoria non viene +inizializzata. + +Una volta che non sia più necessaria la memoria allocata dinamicamente deve +essere esplicitamente rilasciata usando la funzione \func{free},\footnote{le + glibc provvedono anche una funzione \func{cfree} definita per compatibilità + con SunOS, che è deprecata.} il suo prototipo è: + +\begin{funcproto}{ +\fhead{stdlib.h} +\fdecl{void free(void *ptr)} +\fdesc{Disalloca un'area di memoria precedentemente allocata.} +} +{La funzione non ritorna nulla e non riporta errori.} +\end{funcproto} + +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 +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 +queste funzioni. + +Dato che questo errore, chiamato in gergo \textit{double free}, è abbastanza +frequente specie quando si manipolano vettori di puntatori, e dato che le +conseguenze possono essere pesanti ed inaspettate, si suggerisce come +soluzione precauzionale di assegnare sempre a \val{NULL} ogni puntatore su cui +sia stata eseguita \func{free} immediatamente dopo l'esecuzione della +funzione. In questo modo, dato che quando l'argomento è un puntatore nullo +\func{free} non esegue nessuna operazione, si evitano i problemi del +\textit{double free}. + +Infine la funzione \funcd{realloc} consente di modificare (in genere +aumentare) la dimensione di un'area di memoria precedentemente allocata, il +suo prototipo è: + +\begin{funcproto}{ +\fhead{stdlib.h} +\fdecl{void *realloc(void *ptr, size\_t size)} +\fdesc{Cambia la dimensione di un'area di memoria precedentemente allocata.} +} {La funzione restituisce il puntatore alla zona di memoria allocata in caso + di successo e \val{NULL} in caso di fallimento, nel qual caso \var{errno} + assumerà il valore \errval{ENOMEM}.} +\end{funcproto} + +La funzione vuole come primo argomento il puntatore restituito da una +precedente chiamata a \func{malloc} o \func{calloc} e come secondo argomento +la nuova dimensione (in byte) che si intende ottenere. Se si passa +per \param{ptr} il valore \val{NULL} allora la funzione si comporta come +\func{malloc}.\footnote{questo è vero per Linux e l'implementazione secondo lo + standard ANSI C, ma non è vero per alcune vecchie implementazioni, inoltre + alcune versioni delle librerie del C consentivano di usare \func{realloc} + anche per un puntatore liberato con \func{free} purché non ci fossero state + nel frattempo altre chiamate a funzioni di allocazione, questa funzionalità + è totalmente deprecata e non è consentita sotto Linux.} + +La funzione si usa ad esempio quando si deve far crescere la dimensione di un +vettore. In questo caso se è disponibile dello spazio adiacente al precedente +la funzione lo utilizza, altrimenti rialloca altrove un blocco della +dimensione voluta, copiandoci automaticamente il contenuto; lo spazio aggiunto +non viene inizializzato. Se la funzione fallisce l'area di memoria originale +non viene assolutamente toccata. Si deve sempre avere ben presente il fatto che il blocco di memoria restituito da \func{realloc} può non essere un'estensione di quello che gli si è passato @@ -611,14 +963,7 @@ in ingresso; per questo si dovrà \emph{sempre} eseguire la riassegnazione di ad un adeguato aggiornamento di tutti gli altri puntatori all'interno del blocco di dati ridimensionato. -Un errore abbastanza frequente (specie se si ha a che fare con vettori di -puntatori) è quello di chiamare \func{free} più di una volta sullo stesso -puntatore; per evitare questo problema una soluzione di ripiego è quella di -assegnare sempre a \val{NULL} ogni puntatore liberato con \func{free}, dato -che, quando l'argomento è un puntatore nullo, \func{free} non esegue nessuna -operazione. - -Le \acr{glibc} hanno un'implementazione delle funzioni di allocazione che è +La \acr{glibc} ha un'implementazione delle funzioni di allocazione che è controllabile dall'utente attraverso alcune variabili di ambiente (vedi sez.~\ref{sec:proc_environ}), in particolare diventa possibile tracciare questo tipo di errori usando la variabile di ambiente \val{MALLOC\_CHECK\_} @@ -699,12 +1044,15 @@ precedenza, è di allocare la memoria nel segmento di \itindex{stack} \textit{stack} della funzione corrente invece che nello \itindex{heap} \textit{heap}, per farlo si può usare la funzione \funcd{alloca}, la cui sintassi è identica a quella di \func{malloc}; il suo prototipo è: -\begin{prototype}{stdlib.h}{void *alloca(size\_t size)} - Alloca \param{size} byte nello \textit{stack}. - - \bodydesc{La funzione restituisce il puntatore alla zona di memoria - allocata.} -\end{prototype} + +\begin{funcproto}{ +\fhead{stdlib.h} +\fdecl{void *alloca(size\_t size)} +\fdesc{Alloca un'area di memoria nello \textit{stack}} +} +{La funzione restituisce il puntatore alla zona di memoria allocata, in caso + di fallimento il comportamento è indefinito.} +\end{funcproto} La funzione alloca la quantità di memoria (non inizializzata) richiesta dall'argomento \param{size} nel segmento di \itindex{stack} \textit{stack} @@ -712,7 +1060,7 @@ della funzione chiamante. Con questa funzione non è più necessario liberare la memoria allocata (e 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 +Come è evidente questa funzione ha alcuni vantaggi, anzitutto permette di evitare alla radice i problemi di \itindex{memory~leak} \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 @@ -2277,9 +2625,12 @@ il valore del confronto delle due variabili. % LocalWords: boundary memptr alignment sizeof overrun mcheck abortfn enum big % LocalWords: mprobe DISABLED HEAD TAIL touch right emacs OSTYPE endianess IBM % LocalWords: endian little endtest Macintosh PowerPC Intel Digital Motorola -% LocalWords: Sun order VME +% LocalWords: Sun order VME loader Windows DLL shared objects PRELOAD termios +% LocalWords: is to LC SIG str mem wcs assert ctype dirent fcntl signal stdio +% LocalWords: times library utmp syscall number Filesystem Hierarchy pathname %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil" %%% End: +% LocalWords: context assembler sysconf fork diff --git a/prochand.tex b/prochand.tex index 6045b5a..7e882ab 100644 --- a/prochand.tex +++ b/prochand.tex @@ -3108,7 +3108,7 @@ di I/O.\footnote{se usate in corrispondenza ad uno scheduler diverso il loro utilizzo non avrà alcun effetto.} Dato che non esiste una interfaccia diretta nelle \acr{glibc} per queste due funzioni occorrerà invocarle tramite la funzione \func{syscall} (come illustrato in -sez.~\ref{sec:intro_syscall}). Le due funzioni sono \funcd{ioprio\_get} ed +sez.~\ref{sec:proc_syscall}). Le due funzioni sono \funcd{ioprio\_get} ed \funcd{ioprio\_set}; i rispettivi prototipi sono: \begin{functions} \headdecl{linux/ioprio.h} @@ -3371,7 +3371,7 @@ Dato che l'uso principale della nuova \textit{system call} è quello relativo alla creazione dei \textit{thread}, le \acr{glibc} definiscono una funzione di libreria con una sintassi diversa, orientata a questo scopo, e la \textit{system call} resta accessibile solo se invocata esplicitamente come -visto in sez.~\ref{sec:intro_syscall}.\footnote{ed inoltre per questa +visto in sez.~\ref{sec:proc_syscall}.\footnote{ed inoltre per questa \textit{system call} non è disponibile la chiamata veloce con \texttt{vsyscall}.} La funzione di libreria si chiama semplicemente \funcd{clone} ed il suo prototipo è: diff --git a/sockctrl.tex b/sockctrl.tex index 51e489c..8d47429 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -4189,9 +4189,9 @@ di manuale accessibile con \texttt{man 7 ip}, sono i seguenti: procedimento del \itindex{Maximum~Transfer~Unit} \textit{Path MTU discovery} fallisce; dato che questo può avvenire a causa di router\footnote{ad esempio se si scartano tutti i pacchetti ICMP, il problema è affrontato - anche in sez.~1.4.4 di \cite{FwGL}.} o interfacce\footnote{ad esempio se i + anche in sez.~3.4.4 di \cite{SGL}.} o interfacce\footnote{ad esempio se i due capi di un collegamento \textit{point-to-point} non si accordano sulla - stessa MTU.} mal configurate è opportuno correggere le configurazioni, + stessa MTU.} mal configurati è opportuno correggere le configurazioni, perché disabilitare globalmente il procedimento con questo parametro ha pesanti ripercussioni in termini di prestazioni di rete. -- 2.30.2