From: Simone Piccardi Date: Tue, 30 Oct 2001 18:17:26 +0000 (+0000) Subject: Revisioni varie, introdotto nuovo capitolo sulle funzioni di gestione X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=commitdiff_plain;h=3e5c39bbc24981125adb422a672fcd3d54493bb6;p=gapil.git Revisioni varie, introdotto nuovo capitolo sulle funzioni di gestione del sistema generiche. --- diff --git a/elemtcp.tex b/elemtcp.tex index c538c3f..a29c3bc 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -36,38 +36,37 @@ creazione di una connessione \item Il server deve essere preparato per accettare le connessioni in arrivo; il procedimento si chiama \textsl{apertura passiva} del socket (in inglese \textit{passive open}); questo viene fatto chiamando la sequenza di funzioni - \texttt{socket}, \texttt{bind} e \texttt{listen}. Completata l'apertura - passiva il server chiama la funzione \texttt{accept} e il processo si blocca - in attesa di connessioni. + \func{socket}, \func{bind} e \func{listen}. Completata l'apertura passiva il + server chiama la funzione \func{accept} e il processo si blocca in attesa di + connessioni. \item Il client richiede l'inizio della connessione usando la funzione - \texttt{connect}, attraverso un procedimento che viene chiamato + \func{connect}, attraverso un procedimento che viene chiamato \textsl{apertura attiva}, dall'inglese \textit{active open}. La chiamata di - \texttt{connect} blocca il processo e causa l'invio da parte del client di - un segmento \texttt{SYN}\footnote{Si ricordi che il segmento è l'unità - elementare di dati trasmessa dal protocollo TCP al livello superiore; - tutti i segmenti hanno un header che contiene le informazioni che servono - allo \textit{stack TCP} (così viene di solito chiamata la parte del kernel - che implementa il protocollo) per realizzare la comunicazione, fra questi - dati ci sono una serie di flag usati per gestire la connessione, come - \texttt{SYN}, \texttt{ACK}, \texttt{URG}, \texttt{FIN}, alcuni di essi, - come \texttt{SYN} (che sta per \textit{syncronize}) corrispondono a - funzioni particolari del protocollo e danno il nome al segmento, (per - maggiori dettagli vedere \capref{cha:tcp_protocol})}, in sostanza viene - inviato al server un pacchetto IP che contiene solo gli header IP e TCP (con - il numero di sequenza iniziale e il flag \texttt{SYN}) e le opzioni di TCP. + \func{connect} blocca il processo e causa l'invio da parte del client di un + segmento SYN\footnote{Si ricordi che il segmento è l'unità elementare di + dati trasmessa dal protocollo TCP al livello superiore; tutti i segmenti + hanno un header che contiene le informazioni che servono allo + \textit{stack TCP} (così viene di solito chiamata la parte del kernel che + implementa il protocollo) per realizzare la comunicazione, fra questi dati + ci sono una serie di flag usati per gestire la connessione, come SYN, ACK, + URG, FIN, alcuni di essi, come SYN (che sta per \textit{syncronize}) + corrispondono a funzioni particolari del protocollo e danno il nome al + segmento, (per maggiori dettagli vedere \capref{cha:tcp_protocol})}, in + sostanza viene inviato al server un pacchetto IP che contiene solo gli + header IP e TCP (con il numero di sequenza iniziale e il flag SYN) e le + opzioni di TCP. -\item il server deve dare ricevuto (l'\textit{acknowledge}) del \texttt{SYN} - del client, inoltre anche il server deve inviare il suo \texttt{SYN} al - client (e trasmettere il suo numero di sequenza iniziale) questo viene fatto - ritrasmettendo un singolo segmento in cui entrambi i flag \texttt{SYN} - \texttt{ACK} e sono settati. +\item il server deve dare ricevuto (l'\textit{acknowledge}) del SYN del + client, inoltre anche il server deve inviare il suo SYN al client (e + trasmettere il suo numero di sequenza iniziale) questo viene fatto + ritrasmettendo un singolo segmento in cui entrambi i flag SYN ACK e sono + settati. \item una volta che il client ha ricevuto l'acknowledge dal server la funzione - \texttt{connect} ritorna, l'ultimo passo è dare dare il ricevuto del - \texttt{SYN} del server inviando un \texttt{ACK}. Alla ricezione di - quest'ultimo la funzione \texttt{accept} del server ritorna e la connessione - è stabilita. + \func{connect} ritorna, l'ultimo passo è dare dare il ricevuto del SYN del + server inviando un ACK. Alla ricezione di quest'ultimo la funzione + \func{accept} del server ritorna e la connessione è stabilita. \end{enumerate} Il procedimento viene chiamato \textit{three way handshake} dato che per @@ -102,7 +101,7 @@ Il numero di sequenza di ciascun segmento viene calcolato a partire da un \textsl{numero di sequenza iniziale} generato in maniera casuale del kernel all'inizio della connessione e trasmesso con il SYN; l'acknowledgement di ciascun segmento viene effettuato dall'altro capo della connessione settando -il flag \texttt{ACK} e restituendo nell'apposito campo dell'header un +il flag ACK e restituendo nell'apposito campo dell'header un \textit{acknowledge number}) pari al numero di sequenza che il ricevente si aspetta di ricevere con il pacchetto successivo; dato che il primo pacchetto SYN consuma un byte, nel \textit{three way handshake} il numero di acknowledge @@ -122,7 +121,7 @@ regolare la connessione. Normalmente vengono usate le seguenti opzioni: questa opzione ciascun capo della connessione annuncia all'altro il massimo ammontare di dati che vorrebbe accettare per ciascun segmento nella connessione corrente. È possibile leggere e scrivere questo valore - attraverso l'opzione del socket \texttt{TCP\_MAXSEG}. + attraverso l'opzione del socket \macro{TCP\_MAXSEG}. \item \textit{window scale option}; come spiegato in \capref{cha:tcp_protocol} il protocollo TCP implementa il controllo di flusso attraverso una @@ -167,7 +166,7 @@ riferimento al codice degli esempi \figref{fig:net_cli_code} e seguente: \begin{enumerate} -\item Un processo ad uno dei due capi chiama la funzione \texttt{close}, dando +\item Un processo ad uno dei due capi chiama la funzione \func{close}, dando l'avvio a quella che viene chiamata \textsl{chiusura attiva} (o \textit{active close}). Questo comporta l'emissione di un segmento FIN, che significa che si è finito con l'invio dei dati sulla connessione. @@ -181,7 +180,7 @@ seguente: riceveranno altri dati sulla connessione. \item Dopo un certo tempo anche il secondo processo chiamerà la funzione - \texttt{close} sul proprio socket, causando l'emissione di un altro segmento + \func{close} sul proprio socket, causando l'emissione di un altro segmento FIN. \item L'altro capo della connessione riceverà il FIN conclusivo e risponderà @@ -213,10 +212,10 @@ Nella sequenza indicata i dati verrebbero persi, dato che si socket dal lato che esegue la chiusura attiva; esistono tuttavia situazioni in cui si vuole poter sfuttare questa possibilità, usando una procedura che è chiamata \textit{half-close}; torneremo su questo aspetto e su come -utilizzarlo più avanti, quando parleremo della funzione \texttt{shutdown}. +utilizzarlo più avanti, quando parleremo della funzione \func{shutdown}. La emissione del FIN avviene quando il socket viene chiuso, questo però non -avviene solo per la chiamata della funzione \texttt{close} (come in +avviene solo per la chiamata della funzione \func{close} (come in \figref{fig:net_serv_code}), ma anche alla terminazione di un processo (come in \figref{fig:net_cli_code}). Questo vuol dire ad esempio che se un processo viene terminato da un segnale tutte le connessioni aperte verranno chiuse. @@ -531,8 +530,8 @@ mettendosi in ascolto sulla porta 22 riservata a questo servizio e che si posto in ascolto per connessioni provenienti da uno qualunque degli indirizzi associati alle interfacce locali; la notazione 0.0.0.0 usata da netstat è equivalente all'asterisco utilizzato per il numero di porta ed indica il -valore generico, e corrisponde al valore \texttt{INADDR\_ANY} definito in -\texttt{arpa/inet.h}. +valore generico, e corrisponde al valore \macro{INADDR\_ANY} definito in +\file{arpa/inet.h}. Inoltre la porta e l'indirizzo di ogni eventuale connessione esterna non sono specificati; in questo caso la \textit{socket pair} associata al socket può @@ -627,7 +626,7 @@ Useremo questo schema anche per l'esempio di reimplementazione del servizio \texttt{daytime} che illustreremo in \secref{sec:TCPel_cunc_serv}. -\subsection{La funzione \texttt{bind}} +\subsection{La funzione \func{bind}} \label{sec:TCPel_func_bind} La funzione \func{bind} assegna un indirizzo locale ad un socket. È usata @@ -706,7 +705,8 @@ assegnazione. Per questo nell'header \file{netinet/in.h} è definita una variabile \type{in6addr\_any} (dichiarata come \type{extern}, ed inizializzata dal sistema al valore \macro{IN6ADRR\_ANY\_INIT}) che permette di effettuare una -assegnazione del tipo: \footnotesize +assegnazione del tipo: +\footnotesize \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} serv_add.sin6_addr = in6addr_any; /* connect from anywhere */ \end{lstlisting} @@ -731,20 +731,20 @@ connessione con un server TCP, il prototipo della funzione di errore la variabile \var{errno} viene settata secondo i seguenti codici di errore: \begin{errlist} - \item \texttt{EISCONN} il socket è già connesso. - \item \texttt{ECONNREFUSED} non c'è nessuno in ascolto sull'indirizzo remoto. - \item \texttt{ETIMEDOUT} si è avuto timeout durante il tentativo di + \item \macro{EISCONN} il socket è già connesso. + \item \macro{ECONNREFUSED} non c'è nessuno in ascolto sull'indirizzo remoto. + \item \macro{ETIMEDOUT} si è avuto timeout durante il tentativo di connessione. - \item \texttt{ENETUNREACH} la rete non è raggiungibile. - \item \texttt{EADDRINUSE} l'indirizzo locale è in uso. - \item \texttt{EINPROGRESS} il socket è non bloccante e la connessione non + \item \macro{ENETUNREACH} la rete non è raggiungibile. + \item \macro{EADDRINUSE} l'indirizzo locale è in uso. + \item \macro{EINPROGRESS} il socket è non bloccante e la connessione non può essere conclusa immediatamente. - \item \texttt{EALREADY} il socket è non bloccante e un tentativo precedente + \item \macro{EALREADY} il socket è non bloccante e un tentativo precedente di connessione non si è ancora concluso. - \item \texttt{EAGAIN} non ci sono più porte locali libere. - \item \texttt{EAFNOSUPPORT} l'indirizzo non ha una famiglia di indirizzi + \item \macro{EAGAIN} non ci sono più porte locali libere. + \item \macro{EAFNOSUPPORT} l'indirizzo non ha una famiglia di indirizzi corretta nel relativo campo. - \item \texttt{EACCESS, EPERM} si è tentato di eseguire una connessione ad un + \item \macro{EACCESS, EPERM} si è tentato di eseguire una connessione ad un indirizzo broadcast senza che il socket fosse stato abilitato per il broadcast. \end{errlist} @@ -757,7 +757,7 @@ numero di porta del server a cui ci si vuole connettere, come mostrato nell'esempio \secref{sec:net_cli_sample} usando le funzioni illustrate in \secref{sec:sock_addr_func}. -Nel caso di socket TCP la funzione \texttt{connect} avvia il \textit{three way +Nel caso di socket TCP la funzione \func{connect} avvia il \textit{three way handshake}, e ritorna solo quando la connessione è stabilita o si è verificato un errore. Le possibili cause di errore sono molteplici (ed i relativi codici riportati sopra), quelle che però dipendono dalla situazione @@ -859,7 +859,7 @@ infatti vengono mantenute due code: sono tutti nello stato \texttt{SYN\_RECV}. \item Una coda delle connessioni complete (\textit{complete connection queue} che contiene un ingresso per ciascun socket per il quale il three way - handshake è stato completato ma ancora \texttt{accept} non è ritornata. + handshake è stato completato ma ancora \func{accept} non è ritornata. Questi socket sono tutti nello stato \texttt{ESTABLISHED}. \end{enumerate} @@ -869,34 +869,34 @@ incomplete, e poi risponde con il SYN$+$ACK. La entrata rester delle connessioni incomplete fino al ricevimento dell'ACK dal client o fino ad un timeout. Nel caso di completamento del three way handshake l'entrata viene sostata nella coda delle connessioni complete. Quando il processo chiama la -funzione \texttt{accept} (vedi \secref{sec:TCPel_func_accept}) la prima +funzione \func{accept} (vedi \secref{sec:TCPel_func_accept}) la prima entrata nella coda delle connessioni complete è passata al programma, o, se la coda è vuota, il processo viene posto in attesa e risvegliato all'arrivo della prima connessione completa. -Storicamente il valore del parametro \texttt{backlog} era corrispondente al +Storicamente il valore del parametro \var{backlog} era corrispondente al massimo valore della somma del numero di entrate possibili per ciascuna di dette code. Stevens riporta che BSD ha sempre applicato un fattore di 1.5 al valore, e provvede una tabella con i risultati ottenuti con vari kernel, compreso Linux 2.0, che mostrano le differenze fra diverse implementazioni. In Linux il significato di questo valore è cambiato a partire dal kernel -2.2 per prevenire l'attacco chiamato \texttt{syn flood}. Questo si basa +2.2 per prevenire l'attacco chiamato \textit{syn flood}. Questo si basa sull'emissione da parte dell'attaccante di un grande numero di pacchetti SYN indirizzati verso una porta forgiati con indirizzo IP fasullo\footnote{con la tecnica che viene detta \textit{ip spoofing}} così che i SYN$+$ACK vanno perduti e la coda delle connessioni incomplete viene saturata, impedendo di fatto ulteriori connessioni. -Per ovviare a questo il significato del \texttt{backlog} è stato cambiato a +Per ovviare a questo il significato del \var{backlog} è stato cambiato a indicare la lunghezza della coda delle connessioni complete. La lunghezza della coda delle connessioni incomplete può essere ancora controllata usando -la \texttt{sysctl} o scrivendola direttamente in -\texttt{/proc/sys/net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la +la \func{sysctl} o scrivendola direttamente in +\file{/proc/sys/net/ipv4/tcp\_max\_syn\_backlog}. Quando si attiva la protezione dei syncookies però (con l'opzione da compilare nel kernel e da -attivare usando \texttt{/proc/sys/net/ipv4/tcp\_syncookies}) questo valore +attivare usando \file{/proc/sys/net/ipv4/tcp\_syncookies}) questo valore viene ignorato e non esiste più un valore massimo. In ogni caso in Linux il -valore di \texttt{backlog} viene troncato ad un massimo di \texttt{SOMAXCONN} +valore di \var{backlog} viene troncato ad un massimo di \macro{SOMAXCONN} se è superiore a detta costante (che di default vale 128). La scelta storica per il valore di questo parametro è di 5, e alcuni vecchi @@ -911,7 +911,7 @@ ricompilazione del server) ma usare piuttosto una variabile di ambiente (vedi Lo Stevens tratta accuratamente questo argomento, con esempi presi da casi reali su web server, ed in particolare evidenzia come non sia più vero che il compito principale della coda sia quello di gestire il caso in cui il server è -occupato fra chiamate successive alla \texttt{accept} (per cui la coda più +occupato fra chiamate successive alla \func{accept} (per cui la coda più occupata sarebbe quella delle connessioni completate), ma piuttosto quello di gestire la presenza di un gran numero di SYN in attesa di concludere il three way handshake. @@ -922,16 +922,16 @@ condizione in cui le code sono piene client ritrasmette il SYN è probabile che passato un po' di tempo possa trovare nella coda lo spazio per una nuova connessione. Se invece si rispondesse con un RST per indicare l'impossibilità di effettuare la -connessione la chiamata a \texttt{connect} nel client ritornerebbe con una +connessione la chiamata a \func{connect} nel client ritornerebbe con una condizione di errore, costringendo a inserire nell'applicazione la gestione dei tentativi di riconnessione che invece può essere effettuata in maniera trasparente dal protocollo TCP. -\subsection{La funzione \texttt{accept}} +\subsection{La funzione \func{accept}} \label{sec:TCPel_func_accept} -La funzione \texttt{accept} è chiamata da un server TCP per gestire la +La funzione \func{accept} è chiamata da un server TCP per gestire la connessione una volta che sia stato completato il three way handshake, la funzione restituisce un nuovo socket descriptor su cui si potrà operare per effettuare la comunicazione. Se non ci sono connessioni completate il processo @@ -939,103 +939,102 @@ viene messo in attesa. Il prototipo della funzione \begin{prototype}{sys/socket.h} {int listen(int sockfd, struct sockaddr *addr, socklen\_t *addrlen)} - La funzione estrae la prima connessione relativa al socket \texttt{sockfd} + La funzione estrae la prima connessione relativa al socket \var{sockfd} in attesa sulla coda delle connessioni complete, che associa ad nuovo socket - con le stesse caratteristiche di \texttt{sockfd} (restituito dalla funzione + con le stesse caratteristiche di \vara{sockfd} (restituito dalla funzione stessa). Il socket originale non viene toccato. Nella struttura - \texttt{addr} e nella variabile \texttt{addrlen} vengono restituiti + \var{addr} e nella variabile \var{addrlen} vengono restituiti indirizzo e relativa lunghezza del client che si è connesso. La funzione restituisce un numero di socket descriptor positivo in caso di - successo e -1 in caso di errore, nel qual caso la variabile \texttt{errno} + successo e -1 in caso di errore, nel qual caso la variabile \var{errno} viene settata ai seguenti valori: \begin{errlist} - \item \texttt{EBADF} l'argomento \texttt{sockfd} non è un file descriptor + \item \macro{EBADF} l'argomento \var{sockfd} non è un file descriptor valido. - \item \texttt{ENOTSOCK} l'argomento \texttt{sockfd} non è un socket. - \item \texttt{EOPNOTSUPP} il socket è di un tipo che non supporta questa + \item \macro{ENOTSOCK} l'argomento \var{sockfd} non è un socket. + \item \macro{EOPNOTSUPP} il socket è di un tipo che non supporta questa operazione. - \item \texttt{EAGAIN} o \item \texttt{EWOULDBLOCK} il socket è stato - settato come non bloccante, e non ci sono connessioni in attesa di essere - accettate. - \item \texttt{EFAULT} l'argomento \texttt{addr} . - \item \texttt{EPERM} Firewall rules forbid connection. + \item \macro{EAGAIN} o \macro{EWOULDBLOCK} il socket è stato settato come + non bloccante, e non ci sono connessioni in attesa di essere accettate. + \item \macro{EFAULT} l'argomento \avr{addr} . + \item \macro{EPERM} Firewall rules forbid connection. - \item \texttt{ENOBUFS, ENOMEM} Not enough free memory. This often means + \item \macro{ENOBUFS, ENOMEM} Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory. Inoltre possono essere restituiti gli errori di rete relativi al nuovo - socket come: \texttt{EMFILE}, \texttt{EINVAL}, \texttt{ENOSR}, - \texttt{ENOBUFS}, \texttt{EPERM}, \texttt{ECONNABORTED}, - \texttt{ESOCKTNOSUPPORT}, \texttt{EPROTONOSUPPORT}, \texttt{ETIMEDOUT}, - \texttt{ERESTARTSYS}. + socket come: \macro{EMFILE}, \macro{EINVAL}, \macro{ENOSR}, + \macro{ENOBUFS}, \macro{EPERM}, \macro{ECONNABORTED}, + \macro{ESOCKTNOSUPPORT}, \macro{EPROTONOSUPPORT}, \macro{ETIMEDOUT}, + \macro{ERESTARTSYS}. \end{errlist} \end{prototype} La funzione può essere usata solo con socket che supportino la connessione -(cioè di tipo \texttt{SOCK\_STREAM}, \texttt{SOCK\_SEQPACKET} o -\texttt{SOCK\_RDM}). Per alcuni protocolli che richiedono una conferma +(cioè di tipo \macro{SOCK\_STREAM}, \macro{SOCK\_SEQPACKET} o +\macro{SOCK\_RDM}). Per alcuni protocolli che richiedono una conferma esplicita della connessione, (attualmente in Linux solo DECnet ha questo comportamento), la funzione opera solo l'estrazione dalla coda delle connessioni, la conferma della connessione viene fatta implicitamente dalla -prima chiamata ad una \texttt{read} o una \texttt{write} mentre il rifiuto -della connessione viene fatto con la funzione \texttt{close}. +prima chiamata ad una \func{read} o una \func{write} mentre il rifiuto +della connessione viene fatto con la funzione \func{close}. È da chiarire che Linux presenta un comportamento diverso nella gestione degli errori rispetto ad altre implementazioni dei socket BSD, infatti la funzione -\texttt{accept} passa gli errori di rete pendenti sul nuovo socket come codici -di errore per \texttt{accept}. Inoltre la funzione non fa ereditare ai nuovi -socket flag come \texttt{O\_NONBLOCK}, che devono essere rispecificati volta +\func{accept} passa gli errori di rete pendenti sul nuovo socket come codici +di errore per \func{accept}. Inoltre la funzione non fa ereditare ai nuovi +socket flag come \macro{O\_NONBLOCK}, che devono essere rispecificati volta volta, questo è un comportamento diverso rispetto a quanto accade con BSD e deve essere tenuto in conto per scrivere programmi portabili. -I due argomenti \texttt{cliaddr} e \texttt{addrlen} (si noti che quest'ultimo +I due argomenti \var{cliaddr} e \var{addrlen} (si noti che quest'ultimo è passato per indirizzo per avere indietro il valore) sono usati per ottenere l'indirizzo del client da cui proviene la connessione. Prima della chiamata -\texttt{addrlen} deve essere inizializzato alle dimensioni della struttura il -cui indirizzo è passato come argomento in \texttt{cliaddr}, al ritorno della -funzione \texttt{addrlen} conterrà il numero di bytes scritti dentro -\texttt{cliaddr}. Se questa informazione non interessa basterà inizializzare a -\texttt{NULL} detti puntatori. +\var{addrlen} deve essere inizializzato alle dimensioni della struttura il +cui indirizzo è passato come argomento in \var{cliaddr}, al ritorno della +funzione \var{addrlen} conterrà il numero di bytes scritti dentro +\var{cliaddr}. Se questa informazione non interessa basterà inizializzare a +\macro{NULL} detti puntatori. Se la funzione ha successo restituisce il descrittore di un nuovo socket creato dal kernel (detto \textit{connected socket}) a cui viene associata la prima connessione completa (estratta dalla relativa coda, vedi \secref{sec:TCPel_func_listen}) che il client TCP ha effettuato verso il -socket \texttt{sockfd}. Quest'ultimo (detto \textit{listening socket}) è -quello creato all'inizio e messo in ascolto con \texttt{listen}, e non viene +socket \var{sockfd}. Quest'ultimo (detto \textit{listening socket}) è +quello creato all'inizio e messo in ascolto con \func{listen}, e non viene toccato dalla funzione. Se non ci sono connessioni pendenti da accettare la funzione mette in attesa il processo\footnote{a meno che non si sia settato il socket per essere - non-bloccante, nel qual caso ritorna con l'errore \texttt{EAGAIN}, + non-bloccante, nel qual caso ritorna con l'errore \func{EAGAIN}, torneremo su questa modalità di operazione in \secref{sec:xxx_sock_noblock}} fintanto che non ne arriva una. -Il meccanismo di funzionamento di \texttt{accept} è essenziale per capire il +Il meccanismo di funzionamento di \func{accept} è essenziale per capire il funzionamento di un server: in generale infatti c'è sempre un solo socket in ascolto, che resta per tutto il tempo nello stato \texttt{LISTEN}, mentre le -connessioni vengono gestite dai nuovi socket ritornati da \texttt{accept} che +connessioni vengono gestite dai nuovi socket ritornati da \func{accept} che si trovano automaticamente nello stato \texttt{ESTABLISHED} e utilizzati fino alla chiusura della connessione che avviene su di essi. Si può riconoscere questo schema anche nell'esempio elementare in \figref{fig:net_serv_code} dove -per ogni connessione il socket creato da \texttt{accept} viene chiuso dopo +per ogni connessione il socket creato da \func{accept} viene chiuso dopo l'invio dei dati. -\subsection{La funzione \texttt{close}} +\subsection{La funzione \func{close}} \label{sec:TCPel_func_close} -La funzione standard unix \texttt{close} (vedi \secref{sec:file_close}) che si +La funzione standard unix \func{close} (vedi \secref{sec:file_close}) che si usa sui file può essere usata con lo stesso effetto anche sui socket descriptor. L'azione standard di questa funzione quando applicata a socket è di marcarlo come chiuso e ritornare immediatamente al processo. Una volta chiamata il socket descriptor non è più utilizzabile dal processo e non può essere usato -come argomento per una \texttt{write} o una \texttt{read} (anche se l'altro +come argomento per una \func{write} o una \func{read} (anche se l'altro capo della connessione non avesse chiuso la sua parte). Il kernel invierà comunque tutti i dati che ha in coda prima di iniziare la sequenza di chiusura. @@ -1051,7 +1050,7 @@ comportamento normale che ci si aspetta in un'applicazione client/server quale quella che illustreremo in \secref{sec:TCPel_cunc_serv}. Per attivare immediatamente l'emissione del FIN e la sequenza di chiusura si -può usare la funzione \texttt{shutdown} su cui torneremo in seguito. +può usare la funzione \func{shutdown} su cui torneremo in seguito. \section{I server concorrenti su TCP} @@ -1065,7 +1064,7 @@ bloccare un server nel servizio di un client per volta; per questo si ricorre alle capacità di multitasking del sistema. Il modo più immediato per creare un server concorrente è allora quello di -usare la funzione \texttt{fork} per far creare al server per ogni richiesta da +usare la funzione \func{fork} per far creare al server per ogni richiesta da parte di un client un processo figlio che si incarichi della gestione della comunicazione. @@ -1081,7 +1080,7 @@ degli indirizzi delle connessioni ricevute. In \nfig\ è mostrato un estratto del codice, in cui si sono tralasciati il trattamento delle opzioni e le parti rimaste invariate rispetto al precedente esempio. Al solito il sorgente completo del server -\texttt{ElemDaytimeTCPCuncServ.c} è allegato nella directory dei sorgenti. +\file{ElemDaytimeTCPCuncServ.c} è allegato nella directory dei sorgenti. \begin{figure}[!htb] \footnotesize @@ -1145,38 +1144,38 @@ int main(int argc, char *argv[]) \end{figure} Come si può vedere (alle linee \texttt{\small 21--25}) la funzione -\texttt{accept} stavolta è chiamata fornendo una struttura di indirizzi in cui +\func{accept} stavolta è chiamata fornendo una struttura di indirizzi in cui saranno ritornati numero IP e porta da cui il client effettua la connessione, che stamperemo, se avremo abilitato il logging, sullo standard output (\texttt{\small 39--43}). -Quando \texttt{accept} ritorna il server chiama la funzione \texttt{fork} +Quando \func{accept} ritorna il server chiama la funzione \func{fork} (\texttt{\small 26--30}) per creare il processo figlio che effettuerà tutte le operazioni relative a quella connessione (\texttt{\small 31--45}), mentre il padre resterà in attesa di ulteriori connessioni. Si noti come il figlio operi solo sul socket connesso, chiudendo -immediatamente il socket \texttt{list\_fd}; mentre il padre continua ad -operare solo sul socket in ascolto chiudendo \texttt{sock\_fd} dopo ciascuna -\texttt{accept}. Per quanto abbiamo detto in \secref{sec:TCPel_func_close} +immediatamente il socket \var{list\_fd}; mentre il padre continua ad operare +solo sul socket in ascolto chiudendo \var{sock\_fd} dopo ciascuna +\func{accept}. Per quanto abbiamo detto in \secref{sec:TCPel_func_close} queste due chiusure non causano l'innesco della sequenza di chiusura perché il numero di riferimenti non si è annullato. -Infatti subito dopo la creazione del socket \texttt{list\_fd} ha una -referenza, e lo stesso vale per \texttt{sock\_fd} dopo il ritorno di -\texttt{accept}, ma dopo la fork i descrittori vengono duplicati nel padre e +Infatti subito dopo la creazione del socket \var{list\_fd} ha una +referenza, e lo stesso vale per \var{sock\_fd} dopo il ritorno di +\func{accept}, ma dopo la fork i descrittori vengono duplicati nel padre e nel figlio per cui entrambi i socket si trovano con due referenze. Questo fa -si che quando il padre chiude \texttt{sock\_fd} esso resta con una referenza +si che quando il padre chiude \var{sock\_fd} esso resta con una referenza da parte del figlio, e sarà definitivamente chiuso solo quando quest'ultimo, -dopo aver completato le sue operazioni, chiamerà la funzione \texttt{close}. +dopo aver completato le sue operazioni, chiamerà la funzione \func{close}. In realtà per il figlio non sarebbero necessarie nessuna delle due chiamate a -\texttt{close} in quanto nella \texttt{exit} tutti i file ed i socket vengono +\func{close} in quanto nella \func{exit} tutti i file ed i socket vengono chiusi, ma si è preferito effettuare la chiusura esplicitamente per avere una maggiore chiarezza del codice ed evitare possibili errori. Si noti come sia essenziale che il padre chiuda ogni volta il socket connesso -dopo la \texttt{accept}; se così non fosse nessuno di questi socket sarebbe +dopo la \func{accept}; se così non fosse nessuno di questi socket sarebbe effettivamente chiuso dato che alla chiusura da parte del figlio resterebbe ancora un riferimento. Si avrebbero così due effetti, il padre potrebbe esaurire i descrittori disponibili (che sono un numero limitato per ogni @@ -1184,7 +1183,7 @@ processo) e soprattutto nessuna delle connessioni con i client verrebbe chiusa. -\subsection{Le funzioni \texttt{getsockname} e \texttt{getpeername}} +\subsection{Le funzioni \func{getsockname} e \func{getpeername}} \label{sec:TCPel_get_names} Queste due funzioni vengono usate per ottenere la socket pair associata ad un @@ -1195,28 +1194,28 @@ remoto. {int getsockname(int sockfd, struct sockaddr * name, socklen\_t * namelen)} La funzione restituisce 0 in caso di successo e -1 in caso di errore. I - codici di errore restituiti in \texttt{errno} sono i seguenti: + codici di errore restituiti in \var{errno} sono i seguenti: \begin{errlist} - \item \texttt{EBADF} l'argomento \texttt{sockfd} non è un file descriptor + \item \macro{EBADF} l'argomento \var{sockfd} non è un file descriptor valido. - \item \texttt{ENOTSOCK} l'argomento \texttt{sockfd} non è un socket. - \item \texttt{ENOBUFS} non ci sono risorse sufficienti nel sistema per + \item \macro{ENOTSOCK} l'argomento \var{sockfd} non è un socket. + \item \macro{ENOBUFS} non ci sono risorse sufficienti nel sistema per eseguire l'operazione. - \item \texttt{EFAULT} l'argomento \texttt{name} punta al di fuori dello + \item \macro{EFAULT} l'argomento \var{name} punta al di fuori dello spazio di indirizzi del processo. \end{errlist} \end{prototype} -La funzione \texttt{getsockname} si usa tutte le volte che si vuole avere +La funzione \func{getsockname} si usa tutte le volte che si vuole avere l'indirizzo locale di un socket; ad esempio può essere usata da un client (che -usualmente non chiama \texttt{bind}) per ottenere numero IP e porta locale -associati al socket restituito da una \texttt{connect}, o da un server che ha -chiamato \texttt{bind} su un socket usando 0 come porta locale per ottenere il +usualmente non chiama \func{bind}) per ottenere numero IP e porta locale +associati al socket restituito da una \func{connect}, o da un server che ha +chiamato \func{bind} su un socket usando 0 come porta locale per ottenere il numero di porta effimera assegnato dal kernel. -Inoltre quando un server esegue una \texttt{bind} su un indirizzo generico, se +Inoltre quando un server esegue una \func{bind} su un indirizzo generico, se chiamata dopo il completamento di una connessione sul socket restituito da -\texttt{accept}, restituisce l'indirizzo locale che il kernel ha assegnato a +\func{accept}, restituisce l'indirizzo locale che il kernel ha assegnato a quella connessione. @@ -1224,49 +1223,50 @@ quella connessione. {int getpeername(int sockfd, struct sockaddr * name, socklen\_t * namelen)} La funzione restituisce 0 in caso di successo e -1 in caso di errore. I - codici di errore restituiti in \texttt{errno} sono i seguenti: + codici di errore restituiti in \var{errno} sono i seguenti: \begin{errlist} - \item \texttt{EBADF} l'argomento \texttt{sockfd} non è un file descriptor + \item \maccro{EBADF} l'argomento \var{sockfd} non è un file descriptor valido. - \item \texttt{ENOTSOCK} l'argomento \texttt{sockfd} non è un socket. - \item \texttt{ENOTCONN} il socket non è connesso. - \item \texttt{ENOBUFS} non ci sono risorse sufficienti nel sistema per + \item \macro{ENOTSOCK} l'argomento \var{sockfd} non è un socket. + \item \macro{ENOTCONN} il socket non è connesso. + \item \macro{ENOBUFS} non ci sono risorse sufficienti nel sistema per eseguire l'operazione. - \item \texttt{EFAULT} l'argomento \texttt{name} punta al di fuori dello + \item \macro{EFAULT} l'argomento \var{name} punta al di fuori dello spazio di indirizzi del processo. \end{errlist} \end{prototype} -La funzione \texttt{getpeername} si usa tutte le volte che si vuole avere +La funzione \func{getpeername} si usa tutte le volte che si vuole avere l'indirizzo remoto di un socket. Ci si può chiedere a cosa serva questa funzione dato che dal lato client -l'indirizzo remoto è sempre noto quando si esegue la \texttt{connect} mentre +l'indirizzo remoto è sempre noto quando si esegue la \func{connect} mentre dal lato server si possono usare, come si è fatto nell'esempio precedente, i -valori di ritorno di \texttt{accept}. +valori di ritorno di \func{accept}. In generale però questa ultima possibilità è sempre possibile. In particolare questo avviene quando il server invece di far gestire la connessione direttamente a un processo figlio, come nell'esempio precedente, lancia un -opportuno programma per ciascuna connessione usando \texttt{exec} (questa ad -esempio è la modailità con cui opera il \textsl{super-server} \texttt{inetd} +opportuno programma per ciascuna connessione usando \func{exec} (questa ad +esempio è la modailità con cui opera il \textsl{super-server} \cmd{inetd} che gestisce tutta una serie di servizi lanciando per ogni connessione l'opportuno server). In questo caso benché il processo figlio abbia una immagine della memoria che è copia di quella del processo padre (e contiene quindi anche la struttura -ritornata da \texttt{accept}), all'esecuzione di \texttt{exec} viene caricata +ritornata da \func{accept}), all'esecuzione di \func{exec} viene caricata in memoria l'immagine del programma eseguito che a questo punto perde ogni riferimento. Il socket descriptor però resta aperto. Allora se una opportuna convenzione è seguita per rendere noto al programma eseguito qual'è il socket -connesso (\texttt{inetd} ad esempio fa sempre in modo che i file descriptor 0, +connesso (\cmd{inetd} ad esempio fa sempre in modo che i file descriptor 0, 1 e 2 corrispondano al socket connesso) quest'ultimo potrà usare la funzione -\texttt{getpeername} per determinare l'indirizzo remoto del client. +\func{getpeername} per determinare l'indirizzo remoto del client. -Infine è da chiarire (si legga la man page) che come per \texttt{accept} il +Infine è da chiarire (si legga la man page) che come per \func{accept} il terzo parametro che è specificato dallo standard POSIX 1003.1g come di tipo -\texttt{socklen\_t *} in realtà deve sempre corrispondere ad un \texttt{int *} +\type{socklen\_t *} in realtà deve sempre corrispondere ad un \type{int *} come prima dello standard perché tutte le implementazioni dei socket BSD fanno questa assunzione. + diff --git a/filedir.tex b/filedir.tex index ad97494..19f347d 100644 --- a/filedir.tex +++ b/filedir.tex @@ -167,19 +167,19 @@ processo (quando tutti i file vengono chiusi). \label{sec:file_remove} Al contrario di quanto avviene con altri unix in Linux non è possibile usare -\func{unlink} sulle directory; per cancellare una directory si può usare la +\func{unlink} sulle directory; per cancellare una directory si può usare la funzione \func{rmdir} (vedi \secref{sec:file_dir_creat_rem}), oppure la funzione \func{remove}. Questa è la funzione prevista dallo standard ANSI C per cancellare un file o una directory (e funziona anche per i sistemi che non -supportano i link diretti), che per i file è identica alla \func{unlink} e per -le directory è identica alla \func{rmdir}: +supportano i link diretti). Per i file è identica a \func{unlink} e per le +directory è identica a \func{rmdir}: \begin{prototype}{stdio.h}{int remove(const char *pathname)} Cancella un nome dal filesystem. Usa \func{unlink} per i file e \func{rmdir} per le directory. La funzione restituisce zero in caso di successo e -1 per un errore, nel - qual caso il file non viene toccato. Per i codici di errori vedi quanto + qual caso il file non viene toccato. Per i codici di errore vedi quanto riportato nelle descrizioni di \func{unlink} e \func{rmdir}. \end{prototype} @@ -211,19 +211,12 @@ nello stesso filesystem) si usa invece la funzione \func{rename}\footnote{la \item \macro{EINVAL} \var{newpath} contiene un prefisso di \var{oldpath} o più in generale si è cercato di creare una directory come sottodirectory di se stessa. - \item \macro{EMLINK} \var{oldpath} ha già il massimo numero di link - consentiti o è una directory e la directory che contiene \var{newpath} ha - già il massimo numero di link. \item \macro{ENOTDIR} Uno dei componenti dei pathname non è una directory o \var{oldpath} è una directory e \var{newpath} esiste e non è una directory. - \item \macro{EPERM} le directory contenenti \var{oldpath} o \var{newpath} - hanno lo sticky bit attivo e i permessi del processo non consentono - rispettivamente la cancellazione e la creazione del file, o il filesystem - non supporta i link. \end{errlist} - ed inoltre \macro{EACCESS}, \macro{ENOENT}, \macro{ENOMEM}, \macro{EROFS}, - \macro{ELOOP} e \macro{ENOSPC}. + ed inoltre \macro{EACCESS}, \macro{EPERM}, \macro{EMLINK}, \macro{ENOENT}, + \macro{ENOMEM}, \macro{EROFS}, \macro{ELOOP} e \macro{ENOSPC}. \end{prototype} Il comportamento della funzione è diverso a seconda che si voglia rinominare @@ -265,7 +258,7 @@ riferimento allo stesso file. Come abbiamo visto in \secref{sec:file_link} la funzione \func{link} crea riferimenti agli inodes, pertanto può funzionare soltanto per file che -risiedono sullo stesso filesysteme solo per un filesystem di tipo unix. +risiedono sullo stesso filesysteme e solo per un filesystem di tipo unix. Inoltre abbiamo visto che in Linux non è consentito eseguire un link diretto ad una directory. @@ -273,16 +266,16 @@ Per ovviare a queste limitazioni i sistemi unix supportano un'altra forma di link (i cosiddetti \textit{soft link} o \textit{symbolic link}), che sono, come avviene in altri sistemi operativi, dei file speciali che contengono il semplicemente il riferimento ad un altro file (o directory). In questo modo è -possibile effettuare link anche attraverso filesystem diversi, in filesystem -che non supportano i link diretti, link alle directory, e pure a file che non -esistono ancora. +possibile effettuare link anche attraverso filesystem diversi, a file posti in +filesystem che non supportano i link diretti, a delle directory, e anche a +file che non esistono ancora. Il sistema funziona in quanto i link simbolici sono contrassegnati come tali al kernel (analogamente a quanto avviene per le directory) per cui per alcune -funzioni di libreria (come \func{open} o \func{stat}) l'indicazione di un link -simbolico l'applicazione della funzione al file da esso specificato. La -funzione che permette di creare un nuovo link simbolico è \func{symlink}; il -suo prototipo è: +funzioni di libreria (come \func{open} o \func{stat}) dare come parametro un +link simbolico comporta l'applicazione della funzione al file da esso +specificato. La funzione che permette di creare un nuovo link simbolico è +\func{symlink}; il suo prototipo è: \begin{prototype}{unistd.h} {int symlink(const char * oldpath, const char * newpath)} @@ -292,50 +285,30 @@ suo prototipo La funzione restituisce zero in caso di successo e -1 per un errore, nel qual caso la variabile \var{errno} restituisce i valori: \begin{errlist} - \item \macro{EEXIST} esiste già un file \var{newpath}. - \item \macro{EROFS} \var{newpath} è su un filesystem montato in sola lettura. + \item \macro{EPERM} il filesystem che contiene \var{newpath} non supporta i + link simbolici. \item \macro{ENOENT} una componente di \var{newpath} non esiste o \func{oldpath} è una stringa vuota. + \item \macro{EEXIST} esiste già un file \var{newpath}. + \item \macro{EROFS} \var{newpath} è su un filesystem montato in sola lettura. \end{errlist} - ed inoltre \macro{ENAMETOOLONG}, \macro{ENOSPC}, \macro{ENOMEM}, - \macro{ELOOP}, \macro{EACCES}, \macro{EIO} e \macro{ENOTDIR}. + ed inoltre \macro{EFAULT}, \macro{EACCES}, \macro{ENAMETOOLONG}, + \macro{ENOTDIR}, \macro{ENOMEM}, \macro{ELOOP}, \macro{ENOSPC} e + \macro{EIO}. \end{prototype} +Si tenga presente che la funzione non effettua nessun controllo sull'esistenza +di un file di nome \var{oldpath}, ma si limita ad inserire quella stringa nel +link simbolico. Pertanto un link simbolico può anche riferirsi ad un file che +non esiste: quello che viene chiamato un \textit{dangling link}, letteralmente +\textsl{link ciondolante}. - - - -Inoltre esistono funzioni apposite, come la \func{readlink} e la \func{lstat} -per accedere alle informazioni del link invece che a quelle del file a cui -esso fa riferimento. Dato che, come indicato in \secref{tab:file_symb_effect}, -la funzione \func{open} segue i link simbolici, è necessaria usare un'altra -funzione quando si vuole leggere il contenuto di un link simbolico, questa -funzione è la: - -\begin{prototype}{unistd.h} -{int readlink(const char * path, char * buff, size\_t size)} - Legge il contenuto del link simbolico indicato da \var{path} nel buffer - \var{buff} di dimensione \var{size}. Non chiude la stringa con un - carattere nullo e la tronca a \var{size} nel caso il buffer sia troppo - piccolo per contenerla. - - La funzione restituisce il numero di caratteri letti dentro \var{buff} o - -1 per un errore, in caso di errore. La variabile \var{errno} viene - settata secondo i codici di errore: - \begin{errlist} - \item \macro{EEXIST} Un file (o una directory) con quel nome esiste di - già. - \item \macro{EROFS} La directory su cui si vuole inserire il nuovo link è - su un filesystem montato in sola lettura. - \item \macro{ENOSPC} La directory o il filesystem in cui si vuole creare il - link è piena e non c'è ulteriore spazio disponibile. - \end{errlist} -\end{prototype} - -In \ntab\ si è riportato un elenco dei comportamenti delle varie funzioni che -operano sui file rispetto ai link simbolici; specificando quali seguono il -link simbolico e quali possono operare direttamente sul suo contenuto. +Come accennato i link simbolici sono risolti automaticamente dal kernel +all'invocazione delle varie system call; in \ntab\ si è riportato un elenco +dei comportamenti delle varie funzioni di libreria che operano sui file nei +confronti della risoluzione dei link simbolici, specificando quali seguono il +link simbolico e quali invece possono operare direttamente sul suo contenuto. \begin{table}[htb] \centering \footnotesize @@ -370,10 +343,43 @@ link simbolico e quali possono operare direttamente sul suo contenuto. \caption{Uso dei link simbolici da parte di alcune funzioni.} \label{tab:file_symb_effect} \end{table} -si noti che non si è specificato il comportamento delle funzioni che operano -con i file descriptor, in quanto la gestione del link simbolico viene in + +Si noti che non si è specificato il comportamento delle funzioni che operano +con i file descriptor, in quanto la risoluzione del link simbolico viene in genere effettuata dalla funzione che restituisce il file descriptor -(normalmente la \func{open}). +(normalmente la \func{open}) e tutte le operazioni seguenti fanno riferimento +solo a quest'ultimo. + +Dato che, come indicato in \tabref{tab:file_symb_effect}, funzioni come la +\func{open} seguono i link simbolici, occorrono funzioni apposite per accedere +alle informazioni del link invece che a quelle del file a cui esso fa +riferimento. Quando si vuole leggere il contenuto di un link simbolico si usa +la funzione \func{readlink}, il cui prototipo è: + +\begin{prototype}{unistd.h} +{int readlink(const char * path, char * buff, size\_t size)} + Legge il contenuto del link simbolico indicato da \var{path} nel buffer + \var{buff} di dimensione \var{size}. + + La funzione restituisce il numero di caratteri letti dentro \var{buff} o -1 + per un errore, nel qual caso la variabile \var{errno} viene settata a: + \begin{errlist} + \item \macro{EINVAL} \var{file} non è un link simbolico o \var{size} non è + positiva. + \item \macro{EROFS} La directory su cui si vuole inserire il nuovo link è + su un filesystem montato in sola lettura. + \item \macro{ENOSPC} La directory o il filesystem in cui si vuole creare il + link è piena e non c'è ulteriore spazio disponibile. + \end{errlist} + ed inoltre \macro{ENOTDIR}, \macro{ENAMETOOLONG}, \macro{ENOENT}, + \macro{EACCES}, \macro{ELOOP}, \macro{EIO}, \macro{EFAULT} e \macro{ENOMEM}. +\end{prototype} + +La funzione apre il link simbolico, ne legge il contenuto, lo scrive nel +buffer, e lo richiude. Si tenga presente che la funzione non termina la +stringa con un carattere nullo e la tronca alla dimensione specificata da +\var{size} per evitare di sovrascrivere oltre le dimensioni del buffer. + \begin{figure}[htb] \centering @@ -397,27 +403,30 @@ Questo pu scansione di una directory senza tener conto dei link simbolici, ad esempio se lanciassimo un comando del tipo \cmd{grep -r linux *}, il loop nella directory porterebbe il comando ad esaminare \file{/boot}, \file/{boot/boot}, -\file/{boot/boot/boot} e così via, fino a generare un errore (che poi è -\macro{ELOOP}) quando viene superato il numero massimo di link simbolici -consentiti (uno dei limiti del sistema, posto proprio per poter uscire da -questo tipo di situazione). - -Un secondo punto da tenere presente è che un link simbolico può essere fatto -anche ad un file che non esiste; ad esempio possiamo creare un file temporaneo -nella nostra directory con un link del tipo: +\file/{boot/boot/boot} e così via. + +Per questo motivo il kernel e le librerie prevedono che nella risoluzione di +un pathname possano essere seguiti un numero limitato di link simbolici, il +cui valore limite è specificato dalla costante \macro{MAXSYMLINKS}; qualora +questo limite venga superato viene generato un errore ed \var{errno} viene +settata al valore \macro{ELOOP}. + +Un punto da tenere sempre presente è il fatto che un link simbolico può fare +riferimento anche ad un file che non esiste; ad esempio possiamo creare un +file temporaneo nella nostra directory con un link del tipo: \begin{verbatim} $ ln -s /tmp/tmp_file temporaneo \end{verbatim}%$ -ma anche se \file{/tmp/tmp\_file} non esiste (quello che viene chiamato un -\textit{dangling link}, letteralemnte \textsl{link ciondolante}). Aprendo in -scrittura \file{temporaneo} questo verrà scritto; ma se cercassimo di -accederlo in sola lettura (ad esempio con \cmd{cat}) otterremmo: +anche se \file{/tmp/tmp\_file} non esiste. Questo può generare confusione, in +quanto aprendo in scrittura \file{temporaneo} verrà creato +\file{/tmp/tmp\_file} e scritto; ma accedendo in sola lettura a +\file{temporaneo}, ad esempio con \cmd{cat}, otterremmo: \begin{verbatim} $ cat temporaneo cat: temporaneo: No such file or directory \end{verbatim}%$ -con un errore che può sembrare sbagliato, dato \cmd{ls} ci mostrerebbe -l'esistenza di \file{temporaneo}. +con un errore che può sembrare sbagliato, dato che invece \cmd{ls} ci +mostrerebbe l'esistenza di \file{temporaneo}. \subsection{Le funzioni \func{mkdir} e \func{rmdir}} @@ -426,7 +435,7 @@ l'esistenza di \file{temporaneo}. Queste due funzioni servono per creare e cancellare delle directory e sono omonime degli analoghi comandi di shell. Per poter accedere ai tipi usati da queste funzioni si deve includere il file \file{sys/types.h}, il -protoripo della prima è: +prototipo della prima è: \begin{prototype}{sys/stat.h} {int mkdir (const char * dirname, mode\_t mode)} @@ -454,12 +463,22 @@ protoripo della prima \macro{EROFS}. \end{prototype} - +La funzione crea una nuova directory vuota (che contiene solo le due voci +standard \file{.} e \file{..}). I permessi di accesso (vedi la trattazione in +\secref{sec:file_access_control}) specificati da \var{mode} (i cui possibili +valori sono riportati in \tabref{tab:file_permission_const}) sono modificati +dalla maschera di creazione dei file (si veda \secref{sec:file_umask}). La +titolarità della nuova directory è settata secondo quanto riportato in +\secref{sec:file_ownership}. + +La seconda funzione serve ad eliminare una directory già vuota (la directory +deve cioè contenere soltanto le due voci standard \file{.} e \file{..}); il +suo prototipo è: \begin{prototype}{sys/stat.h} - {int rmdir (const char * dirname)} - Cancella la directory \var{dirname}, che deve essere vuota. Il nome può - essere indicato con il pathname assoluto o relativo. + {int rmdir (const char * dirname)} Cancella la directory \var{dirname}, che + deve essere vuota. Il nome può essere indicato con il pathname assoluto o + relativo. La funzione restituisce zero in caso di successo e -1 per un errore, nel qual caso \var{errno} assumerà i valori: @@ -479,7 +498,14 @@ protoripo della prima ed inoltre anche \macro{EFAULT}, \macro{ENAMETOOLONG}, \macro{ENOENT}, \macro{ENOTDIR}, \macro{ENOMEM}, \macro{ELOOP}, \macro{EROFS}. \end{prototype} - + +La modalità con cui avviene la cancellazione è analoga a quella di +\func{unlink}, fintanto che il numero di link all'inode della directory non +diventa nullo e nessun processo ha la directory aperta lo spazio occupato su +disco non viene rilasciato. Se un processo ha la directory aperta la funzione +rimuove il link all'inode e nel caso sia l'ultimo, pure le voci standard +\file{.} e \file{..}, ed il kernel non consentirà di creare più nuovi file +nella directory. \subsection{Accesso alle directory} @@ -488,51 +514,49 @@ protoripo della prima Benché le directory siano oggetti del filesystem come tutti gli altri non ha ovviamente senso aprirle come fossero dei file di dati. Può però essere utile poterne leggere il contenuto ad esempio per fare la lista dei file che esse -contengono o ricerche sui medesimi. +contengono o ricerche sui medesimi. Solo il kernel scrivere direttamente in +una directory (onde evitare inconsistenze all'interno del filesystem), i +processi devono creare i file usando le apposite funzioni. Per accedere al contenuto delle directory si usano i cosiddetti \textit{directory streams} (chiamati così per l'analogia con i file stream); la funzione \func{opendir} apre uno di questi stream e la funzione \func{readdir} legge il contenuto della directory, i cui elementi sono le \textit{directory entry} (da distinguersi da quelle della cache di cui -parlavamo in \secref{sec:file_vfs}) in una opportuna struttura -\var{struct dirent}. +parlavamo in \secref{sec:file_vfs}) in una opportuna struttura \var{struct + dirent}. (NdA Il resto va scritto!!! É noioso e lo farò più avanti). - \subsection{La directory di lavoro} \label{sec:file_work_dir} A ciascun processo è associato ad una directory nel filesystem che è chiamata directory corrente o directory di lavoro (\textit{current working directory}) che è quella a cui si fa riferimento quando un filename è espresso in forma -relativa (relativa appunto a questa directory). +relativa, dove il relativa fa riferimento appunto a questa directory. Quando un utente effettua il login questa directory viene settata alla -cosiddetta \textit{home directory} del suo account, il comando \cmd{cd} -della shell consente di cambiarla a piacere, spostandosi da una directory ad -un'altra. Siccome la directory corrente resta la stessa quando viene creato -un processo figlio, la directory corrente della shell diventa anche la +\textit{home directory} del suo account. Il comando \cmd{cd} della shell +consente di cambiarla a piacere, spostandosi da una directory ad un'altra, il +comando \cmd{pwd} la stampa sul terminale. Siccome la directory corrente +resta la stessa quando viene creato un processo figlio (vedi +\secref{sec:proc_fork}), la directory corrente della shell diventa anche la directory corrente di qualunque comando da essa lanciato. -Le funzioni qui descritte servono esaminare e cambiare la directory di lavoro -corrente. +In genere il kernel tiene traccia per ciascun processo dell'inode della +directory di lavoro corrente, per ottenere il pathname occorre usare una +apposita funzione di libreria, \func{getcwd}, il cui prototipo è: \begin{prototype}{unistd.h}{char * getcwd (char * buffer, size\_t size)} Restituisce il filename completo della directory di lavoro corrente nella stringa puntata da \var{buffer}, che deve essere precedentemente - allocata, per una dimensione massima di \var{size}. Si può anche - specificare un puntatore nullo come \var{buffer}, nel qual caso la - stringa sarà allocata automaticamente per una dimensione pari a - \var{size} qualora questa sia diversa da zero, o della lunghezza esatta - del pathname altrimenti. In questo caso si deve ricordare di disallocare la - stringa una volta cessato il suo utilizzo. + allocata, per una dimensione massima di \var{size}. - La funzione restituisce il puntatore \var{buffer} se riesce, - \macro{NULL} se fallisce, in quest'ultimo caso la variabile - \var{errno} è settata con i seguenti codici di errore: + La funzione restituisce il puntatore \var{buffer} se riesce, \macro{NULL} se + fallisce, in quest'ultimo caso la variabile \var{errno} è settata con i + seguenti codici di errore: \begin{errlist} \item \macro{EINVAL} L'argomento \var{size} è zero e \var{buffer} non è nullo. @@ -544,6 +568,16 @@ corrente. \end{errlist} \end{prototype} +Il buffer deve essere sufficientemente lungo da poter contenere il pathname +completo più lo zero di terminazione della stringa. Qualora esso ecceda le +dimensioni specificate con \var{size} la funzione restituiece un errore. Si +può anche specificare un puntatore nullo come \var{buffer}\footnote{questa è + una estensione allo standard POSIX.1, supportata da Linux}, nel qual caso la +stringa sarà allocata automaticamente per una dimensione pari a \var{size} +qualora questa sia diversa da zero, o della lunghezza esatta del pathname +altrimenti. In questo caso ci si deve ricordare di disallocare la stringa una +volta cessato il suo utilizzo. + Di questa funzione esiste una versione \func{char * getwd(char * buffer)} fatta per compatibilità all'indietro con BSD, che non consente di specificare la dimensione del buffer; esso deve essere allocato in precedenza ed avere una @@ -553,33 +587,41 @@ superiore per un pathname, per cui non contenere il nome del file, e questa è la ragione principale per cui questa funzione è deprecata. -Una seconda funzione simile è \func{char * get\_current\_dir\_name(void)} -che è sostanzialmente equivalente ad una \func{getcwd(NULL, 0)}, con la sola -differenza che essa ritorna il valore della variabile di ambiente -\macro{PWD}, che essendo costruita dalla shell può contenere anche dei -riferimenti simbolici. - -Come già detto in unix anche le directory sono file, è possibile pertanto -riferirsi ad esse tramite il file descriptor dell'interfaccia a basso livello, -e non solo tramite il filename; per questo motivo ci sono due diverse funzioni -per cambiare directory di lavoro. - -\begin{prototype}{unistd.h}{int chdir (const char * pathname)} - Come dice il nome (che significa \textit{change directory}) questa funzione - serve a cambiare la directory di lavoro a quella specificata dal pathname - contenuto nella stringa \var{pathname}. -\end{prototype} +Una seconda funzione simile è \func{char * get\_current\_dir\_name(void)} che +è sostanzialmente equivalente ad una \func{getcwd(NULL, 0)}, con la sola +differenza che essa ritorna il valore della variabile di ambiente \macro{PWD}, +che essendo costruita dalla shell può contenere anche dei riferimenti +simbolici; nel caso di \func{getcwd} infatti, essendo il pathname ricavato +risalendo all'indietro l'albero della directory, si perderebbe traccia di ogni +passaggio attraverso eventuali pathname. + +Altre due funzioni, \func{chdir} e \func{fchdir}, vengono usate, come dice il +nome (che deriva da \textit{change directory}), per cambiare la directory di +lavoro corrente. Dato che anche le directory sono file, è possibile riferirsi +ad esse anche tramite il file descriptor dell'interfaccia a basso livello, e +non solo tramite il filename, i prototipi di queste funzioni sono: + +\begin{functions} + \headdecl{unistd.h} + \funcdecl{int chdir (const char * path)} + Cambia la directory di lavoro corrente a quella specificata dal pathname + contenuto nella stringa \var{path}. -\begin{prototype}{unistd.h}{int fchdir (int filedes)} - Analoga alla precedente, ma usa un file descriptor invece del pathname. + \funcdecl{int fchdir (int fd)} Analoga alla precedente, ma + usa un file descriptor invece del pathname. Entrambe le funzioni restituiscono zero in caso di successo e -1 per un - errore, in caso di errore \var{errno} viene settata secondo i codici di - errore standard di accesso ai file (trattati in dettaglio in - \secref{sec:file_access_control}) ai quali si aggiunge il codice - \macro{ENOTDIR} nel caso il \var{filename} indichi un file che non sia una - directory. -\end{prototype} + errore, in caso di errore \var{errno} viene settata per \func{chdir} ai + valori: + \begin{errlist} + \item \macro{ENOTDIR} Uno dei componenti di \var{path} non è una directory. + \item \macro{EACCESS} Manca il permesso di ricerca su uno dei componenti di + \func{path}. + \end{errlist} + ed inoltre \macro{EFAULT}, \macro{ENAMETOOLONG}, \macro{ENOENT}, + \macro{ENOMEM}, \macro{ELOOP} e \macro{EIO}. Per \func{fchdir} invece gli + errori sono \macro{EBADF} e \macro{EACCES}. +\end{functions} @@ -656,7 +698,7 @@ struct stat { \end{lstlisting} \end{minipage} \normalsize - \caption{La struttura \texttt{stat} per la lettura delle informazioni dei + \caption{La struttura \var{stat} per la lettura delle informazioni dei file} \label{fig:file_stat_struct} \end{figure} @@ -1222,7 +1264,7 @@ proprietario del file al posto dell'uid del processo originario. Avere il bit \acr{sgid} settato ha lo stesso effetto sull'\textit{effective group id} del processo. -I bit \textsl{suid} e \textsl{sgid} vengono usati per permettere agli utenti +I bit \acr{suid} e \acr{sgid} vengono usati per permettere agli utenti normali di usare programmi che abbisognano di privilegi speciali; l'esempio classico è il comando \cmd{passwd} che ha la necessità di modificare il file delle password, quest'ultimo ovviamente può essere scritto solo @@ -1494,7 +1536,7 @@ particolare: l'\textit{effective user id} del processo è zero). \end{itemize} -Per alcuni filesystem\footnote{il filesystem \textsl{ext2} supporta questa +Per alcuni filesystem\footnote{il filesystem \acr{ext2} supporta questa caratteristica, che è mutuata da BSD.} è inoltre prevista una ulteriore misura di sicurezza, volta ad scongiurare l'abuso dei bit \acr{suid} e \acr{sgid}; essa consiste nel cancellare automaticamente questi bit qualora un @@ -1503,7 +1545,6 @@ modo anche se un utente malizioso scopre un file \acr{suid} su cui pu scrivere, un eventuale modifica comporterà la perdita di ogni ulteriore privilegio. - \subsection{La funzione \func{umask}} \label{sec:file_umask} diff --git a/gapil.tex b/gapil.tex index 3ecb745..fb8f4e3 100644 --- a/gapil.tex +++ b/gapil.tex @@ -1,4 +1,4 @@ -%% +%% %% GaPiL : Guida alla Programmazione in Linux %% %% S. Piccardi Oct. 2000 diff --git a/intro.tex b/intro.tex index 849fc85..a416328 100644 --- a/intro.tex +++ b/intro.tex @@ -242,65 +242,65 @@ tipi di dati. \subsection{Prototipi e puntatori} \label{sec:intro_function} -\subsection{La misura del tempo in unix} -\label{sec:intro_unix_time} - -Storicamente i sistemi unix-like hanno sempre mantenuto due distinti valori -per i tempi all'interno del sistema, essi sono rispettivamente chiamati -\textit{calendar time} e \textit{process time}, secondo le definizioni: -\begin{itemize} -\item \textit{calendar time}: è il numero di secondi dalla mezzanotte del - primo gennaio 1970, in tempo universale coordinato (o UTC), data che viene - usualmente indicata con 00:00:00 Jan, 1 1970 (UTC) e chiamata \textit{the - Epoch}. Questo tempo viene anche chiamato anche GMT (Greenwich Mean Time) - dato che l'UTC corrisponde all'ora locale di Greenwich. È il tempo su cui - viene mantenuto l'orologio del calcolatore, e viene usato ad esempio per - indicare le date di modifica dei file o quelle di avvio dei processi. Per - memorizzare questo tempo è stato riservato il tipo primitivo \func{time\_t}. -\item \textit{process time}: talvolta anche detto tempo di CPU. Viene misurato - in \textit{clock tick}, corrispondenti al numero di interruzioni effettuate - dal timer di sistema, e che per Linux avvengono ogni centesimo di - secondo\footnote{eccetto per la piattaforma alpha dove avvengono ogni - millesimo di secondo}. Il dato primitivo usato per questo tempo è - \func{clock\_t}, inoltre la costante \macro{HZ} restituisce la frequenza di - operazione del timer, e corrisponde dunque al numero di tick al secondo. Lo - standard POSIX definisce allo stesso modo la costante \macro{CLK\_TCK}); - questo valore può comunque essere ottenuto con \func{sysconf} (vedi - \secref{sec:intro_limits}). -\end{itemize} - -In genere si usa il \textit{calendar time} per tenere le date dei file e le -informazioni analoghe che riguardano i tempi di ``orologio'', usati ad esempio -per i demoni che compiono lavori amministrativi ad ore definite, come -\cmd{cron}. Di solito questo vene convertito automaticamente dal valore in UTC -al tempo locale, utilizzando le opportune informazioni di localizzazione -(specificate in \file{/etc/timezone}). E da tenere presente che questo tempo è -mantenuto dal sistema e non corrisponde all'orologio hardware del calcolatore. - -Il \textit{process time} di solito si esprime in secondi e viene usato appunto -per tenere conto dei tempi di esecuzione dei processi. Per ciascun processo il -kernel tiene tre di questi tempi: -\begin{itemize*} -\item \textit{clock time} -\item \textit{user time} -\item \textit{system time} -\end{itemize*} -il primo è il tempo ``reale'' (viene anche chiamato \textit{wall clock time}) -dall'avvio del processo, e misura il tempo trascorso fino alla sua -conclusione; chiaramente un tale tempo dipende anche dal carico del sistema e -da quanti altri processi stavano girando nello stesso periodo. Il secondo -tempo è quello che la CPU ha speso nell'esecuzione delle istruzioni del -processo in user space. Il terzo è il tempo impiegato dal kernel per eseguire -delle system call per conto del processo medesimo (tipo quello usato per -eseguire una \func{write} su un file). In genere la somma di user e system -time viene chiamato \textit{CPU time}. \subsection{Lo standard ANSI C} \label{sec:intro_ansiC} +Lo standard ANSI C è stato definito nel 1989 dall'\textit{American National + Standard Institute}, come standard del linguaggio C ed è stato +successivamente adottatto dalla \textit{International Standard Organisation} +come standard internazionale con la sigla ISO/IEC 9899:1990. + +Scopo dello standard è quello di garantire la portabilità dei programmi C fra +sistemi operativi diversi, ma oltre alla sintassi e alla semantica del +linguaggio C (operatori, parole chiave, tipi di dati) lo standard prevede +anche una libreria di funzioni standard che devono poter essere implementate +su qualunque sistema operativo. + +Linux, come molti unix moderni, provvede la compatibilità con questo standard, +fornendo le funzioni di libreria da esso previste; queste sono dichiarate in +quindici header files, uno per ciascuna delle quindici aree in cui è stata +suddivisa la libreria. In \ntab\ si sono riportati questi header, insieme a +quelli definiti negli altri standard descritti nelle sezioni successive. + \subsection{Lo standard POSIX} \label{sec:intro_posix} +In realtà POSIX è una famiglia di standard diversi, il nome, suggerito da +Richard Stallman, sta per \textit{Portable Operating System Interface}, ma la +X finale denuncia la sua stretta relazione con i sistemi unix. Esso nasce dal +lavoro dell'IEEE (\textit{Institute of Electrical and Electronics Engeneers}) +che ne ha prodotto un primo una prima versione, nota come IEEE 1003.1-1988, +mirante a standardizzare l'interfaccia con il sistema operativo. + +Ma gli standard POSIX non si limitano alla standardizzazione delle funzioni di +libreria, e in seguito sono stati prodotti anche altri standard per la shell e +le utilities di sistema (1003.2), per le estensioni realtime e per i thread +(1003.1d e 1003.1c) e molti altri. + +Benchè lo standard POSIX sia basato sui sistemi unix esso definisce comunque +una interfaccia e non fa riferimento ad una specifica implementazione (per cui +esiste ad esempio anche una implementazione di questo standard pure sotto +Windows NT). Lo standard si è evoluto nel tempo ed una nuova versione (quella +che viene normalmente denominata POSIX.1) è stata rilasciata come standard +internazionale con la sigla ISO/IEC 9945-1:1990. + + +\subsection{Lo standard X/Open -- XPG3} +\label{sec:intro_xopen} + +Il consorzio X/Open nasce come consorzio di venditori di sistemi unix, che nel +1989 produsse una voluminosa guida chiamata \textit{X/Open Portability Guide, + Issue 3} al cui interno definiva una ulteriore standardizzazione +dell'interfaccia ad un sistema unix. + +Questo standard, detto anche XPG3 dal nome della suddetta guida, è sempre +basato sullo standard POSIX.1, ma prevede una serie di funzionalità +aggiuntive. + +Il consorzio + + \subsection{Valori e limiti del sistema} \label{sec:intro_limits} @@ -309,153 +309,3 @@ time viene chiamato \textit{CPU time}. \label{sec:intro_data_types} - - -\section{La gestione degli errori} -\label{sec:intro_errors} - -La gestione degli errori è in genere una materia complessa. Inoltre il modello -utilizzato dai sistema unix-like è basato sull'architettura a processi, e -presenta una serie di problemi nel caso lo si debba usare con i thread. -Esamineremo in questa sezione le sue caratteristiche principali. - - -\subsection{La variabile \func{errno}} -\label{sec:intro_errno} - -Quasi tutte le funzioni delle librerie del C sono in grado di individuare e -riportare condizioni di errore, ed è una buona norma di programmazione -controllare sempre che le funzioni chiamate si siano concluse correttamente. - -In genere le funzioni di libreria usano un valore speciale per indicare che -c'è stato un errore. Di solito questo valore è -1 o un puntatore nullo o la -costante \macro{EOF} (a seconda della funzione); ma questo valore segnala solo -che c'è stato un errore, non il tipo di errore. - -Per riportare il tipo di errore il sistema usa la variabile globale -\var{errno}\footnote{L'uso di una variabile globale può comportare alcuni - problemi (ad esempio nel caso dei thread) ma lo standard ISO C consente - anche di definire \var{errno} come un \textit{modifiable lvalue}, quindi si - può anche usare una macro, e questo è infatti il modo usato da Linux per - renderla locale ai singoli thread }, definita nell'header \file{errno.h}, la -variabile è in genere definita come \var{volatile} dato che può essere -cambiata in modo asincrono da un segnale (per una descrizione dei segnali si -veda \secref{cha:signals}), ma dato che un manipolatore di segnale scritto -bene salva e ripristina il valore della variabile, di questo non è necessario -preoccuparsi nella programmazione normale. - -I valori che può assumere \var{errno} sono riportati in \capref{cha:errors}, -nell'header \file{errno.h} sono anche definiti i nomi simbolici per le -costanti numeriche che identificano i vari errori; essi iniziano tutti per -\macro{E} e si possono considerare come nomi riservati. In seguito faremo -sempre rifermento a tali valori, quando descriveremo i possibili errori -restituiti dalle funzioni. Il programma di esempio \cmd{errcode} stampa il -codice relativo ad un valore numerico con l'opzione \cmd{-l}. - -Il valore di \var{errno} viene sempre settato a zero all'avvio di un -programma, gran parte delle funzioni di libreria settano \var{errno} ad un -valore diverso da zero in caso di errore. Il valore è invece indefinito in -caso di successo, perché anche se una funzione ha successo, può chiamarne -altre al suo interno che falliscono, modificando così \var{errno}. - -Pertanto un valore non nullo di \var{errno} non è sintomo di errore (potrebbe -essere il risultato di un errore precedente) e non lo si può usare per -determinare quando o se una chiamata a funzione è fallita. La procedura da -seguire è sempre quella di controllare \var{errno} immediatamente dopo aver -verificato il fallimento della funzione attraverso il suo codice di ritorno. - - -\subsection{Le funzioni \func{strerror} e \func{perror}} -\label{sec:intro_strerror} - -Benché gli errori siano identificati univocamente dal valore numerico di -\var{errno} le librerie provvedono alcune funzioni e variabili utili per -riportare in opportuni messaggi le condizioni di errore verificatesi. La -prima funzione che si può usare per ricavare i messaggi di errore è -\func{strerror}, il cui prototipo è: -\begin{prototype}{string.h}{char * strerror(int errnum)} - La funzione ritorna una stringa (statica) che descrive l'errore il cui - codice è passato come parametro. -\end{prototype} - -In generale \func{strerror} viene usata passando \var{errno} come parametro; -nel caso si specifichi un codice sbagliato verrà restituito un messaggio di -errore sconosciuto. La funzione utilizza una stringa statica che non deve -essere modificata dal programma e che è utilizzabile solo fino ad una chiamata -successiva a \func{strerror}; nel caso si usino i thread è -provvista\footnote{questa funzione è una estensione GNU, non fa parte dello - standard POSIX} una versione apposita: -\begin{prototype}{string.h} -{char * strerror\_r(int errnum, char * buff, size\_t size)} - La funzione è analoga a \func{strerror} ma ritorna il messaggio in un buffer - specificato da \var{buff} di lunghezza massima (compreso il terminatore) - \var{size}. -\end{prototype} -che utilizza un buffer che il singolo thread deve allocare, per evitare i -problemi connessi alla condivisione del buffer statico. Infine, per completare -la caratterizzazione dell'errore, si può usare anche la variabile -globale\footnote{anche questa è una estensione GNU} -\var{program\_invocation\_short\_name} che riporta il nome del programma -attualmente in esecuzione. - -Una seconda funzione usata per riportare i codici di errore in maniera -automatizzata sullo standard error (vedi \secref{sec:file_stdfiles}) è -\func{perror}, il cui prototipo è: -\begin{prototype}{stdio.h}{void perror (const char *message)} - La funzione stampa il messaggio di errore relativo al valore corrente di - \var{errno} sullo standard error; preceduto dalla stringa \var{message}. -\end{prototype} -i messaggi di errore stampati sono gli stessi di \func{strerror}, (riportati -in \capref{cha:errors}), e, usando il valore corrente di \var{errno}, si -riferiscono all'ultimo errore avvenuto. La stringa specificata con -\var{message} viene stampato prime del messaggio d'errore, seguita dai due -punti e da uno spazio, il messaggio è terminato con un a capo. - -Il messaggio può essere riportato anche usando altre variabili globali -dichiarate in \file{errno.h}: -\begin{verbatim} - const char *sys_errlist[]; - int sys_nerr; -\end{verbatim} -la prima contiene i puntatori alle stringhe di errore indicizzati da -\var{errno}; la seconda esprime il valore più alto per un codice di errore, -l'utilizzo di questa stringa è sostanzialmente equivalente a quello di -\func{strerror}. - -In \nfig\ è riportata la sezione attinente del codice del programma -\cmd{errcode}, che può essere usato per stampare i messaggi di errore e le -costanti usate per identificare i singoli errori; il sorgente completo del -programma è allegato nel file \file{ErrCode.c} e contiene pure la gestione -delle opzioni e tutte le definizioni necessarie ad associare il valore -numerico alla costante simbolica. In particolare si è riportata la sezione che -converte la stringa passata come parametro in un intero (\texttt{\small - 1--2}), controllando con i valori di ritorno di \func{strtol} che la -conversione sia avvenuta correttamente (\texttt{\small 4--10}), e poi stampa, -a seconda dell'opzione scelta il messaggio di errore (\texttt{\small 11--14}) -o la macro (\texttt{\small 15--17}) associate a quel codice. - -\begin{figure}[!htb] - \footnotesize - \begin{lstlisting}{} - /* convert string to number */ - err = strtol(argv[optind], NULL, 10); - /* testing error condition on conversion */ - if (err==LONG_MIN) { - perror("Underflow on error code"); - return 1; - } else if (err==LONG_MIN) { - perror("Overflow on error code"); - return 1; - } - /* conversion is fine */ - if (message) { - printf("Error message for %d is %s\n", err, strerror(err)); - } - if (label) { - printf("Error label for %d is %s\n", err, err_code[err]); - } - \end{lstlisting} - \caption{Codice per la stampa del messaggio di errore standard.} - \label{fig:intro_err_mess} -\end{figure} - diff --git a/macro.tex b/macro.tex index 7114c2d..6b79556 100644 --- a/macro.tex +++ b/macro.tex @@ -100,7 +100,7 @@ tab.~\thechapter.\theusercount} \footnotesize \par \vspace{4pt} - \centering + \center \ \begin{minipage}[c]{14cm} \begin{description*}{}{} @@ -110,6 +110,7 @@ tab.~\thechapter.\theusercount} \par \end{minipage} \vspace{6pt} + \par \normalsize %\break } diff --git a/process.tex b/process.tex index 5114b01..a096d6a 100644 --- a/process.tex +++ b/process.tex @@ -976,18 +976,36 @@ Nella maggior parte delle funzioni di libreria e delle system call i puntatori vengono usati per scambiare dati (attraverso buffer o strutture) e le variabili semplici vengono usate per specificare parametri; in genere le informazioni a riguardo dei risultati vengono passate alla routine chiamante -attraverso il valore di ritorno. Talvolta però è necessario che la funzione -possa restituire indietro alla funzione chiamate un valore relativo ad uno dei -suoi parametri. Per far questo si usa il cosiddetto \textit{}. Pertanto occorre -tenere conto che quando si vuole restituire un risultato alla funzione -chiamante occorre usare un puntatore. +attraverso il valore di ritorno. È buona norma seguire questa pratica anche +nella programmazione normale. +Talvolta però è necessario che la funzione possa restituire indietro alla +funzione chiamante un valore relativo ad uno dei suoi parametri. Per far +questo si usa il cosiddetto \textit{value result argument}, si passa cioè, +invece di una normale variabile un puntatore; vedremo alcuni esempi di questa +modalità nelle funzioni che gestiscono i socket (in +\secref{sec:TCPel_functions}) in cui, per permettere al kernel di restituire +informazioni sulle dimensioni delle strutture degli indirizzi utilizzate, +viene usato questo meccanismo. + \subsection{Potenziali problemi con le variabili automatiche} \label{sec:proc_auto_var} - +Uno dei possibili problemi che si possono avere con le subroutine è quello di +restituire alla funzione chiamante dei dati che sono contenuti in una +variabile automatica. Ovviamente quando la subroutine ritorna la sezione +dello stack che conteneva la variabile automatica potrà essere riutilizzata da +una nuova funzione, con le conseguenze immaginabili di sovrapposizione. + +Per questo una delle regole fondamentali della programmazione in C è che +all'uscita di una funzione non deve restare nessun riferimento a variabili +locali di quella funzione; qualora necessiti di utilizzare variabili che +possano essere viste anche dalla funzione chiamante queste devono essere +allocate esplicitamente, o in maniera statica (usando variabili di tipo +\type{static} o \type{extern}), o dinamicamente con una delle funzioni della +famiglia \func{malloc}. \subsection{Il controllo di flusso non locale} \label{sec:proc_longjmp} diff --git a/prochand.tex b/prochand.tex index 4a03b94..c2af15d 100644 --- a/prochand.tex +++ b/prochand.tex @@ -537,8 +537,8 @@ sequenza impredicibile. Le modalit \end{enumerate} Oltre ai file aperti i processi figli ereditano dal padre una serie di altre -proprietà comuni; in dettaglio avremo che dopo l'esecuzione di una \func{fork} -padre e figlio avranno in comune: +proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in +comune dopo l'esecuzione di una \func{fork} è la seguente: \begin{itemize*} \item i file aperti (e gli eventuali flag di \textit{close-on-exec} se settati). @@ -558,7 +558,7 @@ padre e figlio avranno in comune: \item i limiti sulle risorse \item le variabili di ambiente (vedi \secref{sec:proc_environ}). \end{itemize*} -le differenze invece sono: +le differenze fra padree figlio dopo la \func{fork} invece sono: \begin{itemize*} \item il valore di ritorno di \func{fork}. \item il \textit{process id}. @@ -760,7 +760,7 @@ di terminare il processo che li ha generati, in modo che \cmd{init} possa adottarli e provvedere a concludere la terminazione. -\subsection{Le funzioni \texttt{wait} e \texttt{waitpid}} +\subsection{Le funzioni \func{wait} e \func{waitpid}} \label{sec:proc_wait} Abbiamo già accennato come uno degli usi possibili delle capacità multitasking @@ -791,6 +791,7 @@ caso di errore; \var{errno} pu \item \macro{EINTR} la funzione è stata interrotta da un segnale. \end{errlist} \end{functions} + è presente fin dalle prime versioni di unix; la funzione ritorna alla conclusione del primo figlio (o immediatamente se un figlio è già uscito). Nel caso un processo abbia più figli il valore di ritorno permette di identificare @@ -963,6 +964,7 @@ accessibili definendo la costante \macro{\_USE\_BSD}, sono: Prima versione, equivalente a \func{wait4(-1, \&status, opt, rusage)} è ormai deprecata in favore di \func{wait4}. \end{functions} +\noindent la struttura \type{rusage} è definita in \file{sys/resource.h}, e viene utilizzata anche dalla funzione \func{getrusage} per ottenere le risorse di sistema usate dal processo; in Linux è definita come: @@ -992,7 +994,7 @@ struct rusage { \end{lstlisting} \end{minipage} \normalsize - \caption{La struttura \texttt{rusage} per la lettura delle informazioni dei + \caption{La struttura \var{rusage} per la lettura delle informazioni dei delle risorse usate da un processo.} \label{fig:proc_rusage_struct} \end{figure} @@ -1004,7 +1006,7 @@ sono mantenuti sono: \var{ru\_utime}, \var{ru\_stime}, \var{ru\_minflt}, \var{ru\_majflt}, e \var{ru\_nswap}. -\subsection{Le funzioni \texttt{exec}} +\subsection{Le funzioni \func{exec}} \label{sec:proc_exec} Abbiamo già detto che una delle modalità principali con cui si utilizzano i @@ -1655,12 +1657,11 @@ amministratore o, per gli altri utenti, se il valore specificato coincide con uno dei \textit{real}, \textit{effective} o \textit{saved id}. - \section{Problematiche di programmazione multitasking} \label{sec:proc_multi_prog} Benché i processi siano strutturati in modo da apparire il più possibile come -independenti l'uno dall'altro, nella programmazione in un sistema multiutente +indipendenti l'uno dall'altro, nella programmazione in un sistema multiutente occorre tenere conto di tutta una serie di problematiche che normalmente non esistono quando si ha a che fare con un sistema in cui viene eseguito un solo programma alla volta. diff --git a/socket.tex b/socket.tex index 9a1018b..cd90c28 100644 --- a/socket.tex +++ b/socket.tex @@ -93,11 +93,11 @@ di interagire con protocolli di comunicazione anche molto diversi fra di loro; in questa sezione vedremo come è possibile creare un socket e come specificare il tipo di comunicazione che esso deve utilizzare. -\subsection{La funzione \texttt{socket}} +\subsection{La funzione \func{socket}} \label{sec:sock_socket} La creazione di un socket avviene attraverso l'uso della funzione -\texttt{socket} questa restituisce un \textit{socket descriptor} (un valore +\func{socket} questa restituisce un \textit{socket descriptor} (un valore intero non negativo) che come gli analoghi file descriptor di file e alle pipe serve come riferimento al socket; in sostanza è l'indice nella tabella dei file che contiene i puntatori alle opportune strutture usate dal kernel ed @@ -113,19 +113,19 @@ socket, per cui viene messo a zero (con l'eccezione dei \textit{raw socket}). \begin{prototype}{sys/socket.h}{int socket(int domain, int type, int protocol)} La funzione restituisce un intero positivo se riesce, e -1 se fallisce, in - quest'ultimo caso la variabile \texttt{errno} è settata con i seguenti + quest'ultimo caso la variabile \var{errno} è settata con i seguenti codici di errore: \begin{errlist} - \item \texttt{EPROTONOSUPPORT} Il tipo di socket o il protocollo scelto non + \item \macro{EPROTONOSUPPORT} Il tipo di socket o il protocollo scelto non sono supportati nel dominio. - \item \texttt{ENFILE} Il kernel non ha memoria sufficiente a creare una + \item \macro{ENFILE} Il kernel non ha memoria sufficiente a creare una nuova struttura per il socket. - \item \texttt{EMFILE} Si è ecceduta la tabella dei file. - \item \texttt{EACCES} Non si hanno privilegi per creare un socket nel + \item \macro{EMFILE} Si è ecceduta la tabella dei file. + \item \macro{EACCES} Non si hanno privilegi per creare un socket nel dominio o con il protocollo specificato. - \item \texttt{EINVAL} Protocollo sconosciuto o dominio non disponibile. - \item \texttt{ENOBUFS} o \texttt{ENOMEM} Non c'è sufficiente memoria per + \item \macro{EINVAL} Protocollo sconosciuto o dominio non disponibile. + \item \macro{ENOBUFS} o \macro{ENOMEM} Non c'è sufficiente memoria per creare il socket. \end{errlist} \end{prototype} @@ -284,7 +284,7 @@ viene effettivamente realizzata. Ogni famiglia di protocolli ha ovviamente una sua forma di indirizzamento e in corrispondenza a questa una sua peculiare struttura degli indirizzi; i nomi di -tutte queste strutture iniziano per \texttt{sockaddr\_}, quelli propri di +tutte queste strutture iniziano per \var{sockaddr\_}, quelli propri di ciascuna famiglia vengono identificati dal suffisso finale, aggiunto al nome precedente. @@ -355,7 +355,7 @@ definiti; la struttura \end{table} In alcuni sistemi la struttura è leggermente diversa e prevede un primo membro -aggiuntivo \texttt{uint8\_t sin\_len} (come riportato da R. Stevens nei suoi +aggiuntivo \var{uint8\_t sin\_len} (come riportato da R. Stevens nei suoi libri). Questo campo non verrebbe usato direttamente dal programmatore e non è richiesto dallo standard POSIX.1g, in Linux pertanto non esiste. Il campo \type{sa\_family\_t} era storicamente un \type{unsigned short}. @@ -363,8 +363,8 @@ richiesto dallo standard POSIX.1g, in Linux pertanto non esiste. Il campo Dal punto di vista del programmatore l'unico uso di questa struttura è quello di fare da riferimento per il casting, per il kernel le cose sono un po' diverse, in quanto esso usa il puntatore per recuperare il campo -\texttt{sa\_family} con cui determinare il tipo di indirizzo; per questo -motivo, anche se l'uso di un puntatore \texttt{void *} sarebbe più immediato +\var{sa\_family} con cui determinare il tipo di indirizzo; per questo +motivo, anche se l'uso di un puntatore \type{void *} sarebbe più immediato per l'utente (che non dovrebbe più eseguire il casting), è stato mantenuto l'uso di questa struttura. @@ -375,7 +375,7 @@ l'uso di questa struttura. I socket di tipo \macro{PF\_INET} vengono usati per la comunicazione attraverso internet; la struttura per gli indirizzi per un socket internet (IPv4) è definita come \type{sockaddr\_in} nell'header file -\texttt{netinet/in.h} e secondo le man page ha la forma mostrata in \nfig, +\file{netinet/in.h} e secondo le man page ha la forma mostrata in \nfig, conforme allo standard POSIX.1g. \begin{figure}[!htb] @@ -392,7 +392,7 @@ struct in_addr { }; \end{lstlisting} \caption{La struttura degli indirizzi dei socket internet (IPv4) - \texttt{sockaddr\_in}.} + \type{sockaddr\_in}.} \label{fig:sock_sa_ipv4_struct} \end{figure} @@ -403,14 +403,14 @@ superiore come TCP e UDP. Questa struttura per RAW che accedono direttamente al livello di IP, nel qual caso il numero della porta viene settato al numero di protocollo. -Il membro \texttt{sin\_family} deve essere sempre settato; \texttt{sin\_port} +Il membro \var{sin\_family} deve essere sempre settato; \var{sin\_port} specifica il numero di porta (vedi \secref{sec:TCPel_port_num}; i numeri di porta sotto il 1024 sono chiamati \textsl{riservati} in quanto utilizzati da servizi standard. Soltanto processi con i privilegi di root (effective uid -uguale a zero) o con la capability \texttt{CAP\_NET\_BIND\_SERVICE} possono +uguale a zero) o con la capability \macro{CAP\_NET\_BIND\_SERVICE} possono usare la funzione \func{bind} su queste porte. -Il membro \texttt{sin\_addr} contiene l'indirizzo internet dell'altro capo +Il membro \var{sin\_addr} contiene l'indirizzo internet dell'altro capo della comunicazione, e viene acceduto sia come struttura (un resto di una implementazione precedente in cui questa era una \texttt{union} usata per accedere alle diverse classi di indirizzi) che come intero. @@ -426,10 +426,10 @@ problema e le relative soluzioni). \subsection{La struttura degli indirizzi IPv6} \label{sec:sock_sa_ipv6} -Essendo IPv6 una estensione di IPv4 i socket di tipo \texttt{PF\_INET6} sono +Essendo IPv6 una estensione di IPv4 i socket di tipo \macro{PF\_INET6} sono sostanzialmente identici ai precedenti; la parte in cui si trovano praticamente tutte le differenze è quella della struttura degli indirizzi. La -struttura degli indirizzi è definita ancora in \texttt{netinet/in.h}. +struttura degli indirizzi è definita ancora in \file{netinet/in.h}. \begin{figure}[!htb] \footnotesize @@ -447,23 +447,23 @@ struct in6_addr { }; \end{lstlisting} \caption{La struttura degli indirizzi dei socket IPv6 - \texttt{sockaddr\_in6}.} + \type{sockaddr\_in6}.} \label{fig:sock_sa_ipv6_struct} \end{figure} -Il campo \texttt{sin6\_family} deve essere sempre settato ad -\texttt{AF\_INET6}, il campo \texttt{sin6\_port} è analogo a quello di IPv4 e -segue le stesse regole; il campo \texttt{sin6\_flowinfo} è a sua volta diviso +Il campo \var{sin6\_family} deve essere sempre settato ad +\macro{AF\_INET6}, il campo \var{sin6\_port} è analogo a quello di IPv4 e +segue le stesse regole; il campo \var{sin6\_flowinfo} è a sua volta diviso in tre parti di cui i 24 bit inferiori indicano l'etichetta di flusso, i successivi 4 bit la priorità e gli ultimi 4 sono riservati; questi valori fanno riferimento ad alcuni campi specifici dell'header dei pacchetti IPv6 (vedi \secref{sec:IP_ipv6head}) ed il loro uso è sperimentale. -Il campo \texttt{sin6\_addr} contiene l'indirizzo a 128 bit usato da IPv6, -infine il campo \texttt{sin6\_scope\_id} è un campo introdotto con il kernel +Il campo \var{sin6\_addr} contiene l'indirizzo a 128 bit usato da IPv6, +infine il campo \var{sin6\_scope\_id} è un campo introdotto con il kernel 2.4 per gestire alcune operazioni riguardanti il multicasting. -Si noti che questa struttura è più grande di una \texttt{sockaddr} generica, +Si noti che questa struttura è più grande di una \var{sockaddr} generica, quindi occorre stare attenti a non avere fatto assunzioni riguardo alla possibilità di contenere i dati nelle dimensioni di quest'ultima. @@ -471,12 +471,12 @@ possibilit \subsection{La struttura degli indirizzi locali} \label{sec:sock_sa_local} -I socket di tipo \texttt{PF\_UNIX} vengono usati per una comunicazione +I socket di tipo \macro{PF\_UNIX} vengono usati per una comunicazione efficiente fra processi che stanno sulla stessa macchina; essi rispetto ai precedenti possono essere anche creati in maniera anonima attraverso la -funzione \texttt{socketpair}. Quando però si vuole fare riferimento esplicito +funzione \func{socketpair}. Quando però si vuole fare riferimento esplicito ad uno di questi socket si deve usare la seguente struttura di indirizzi -definita nel file di header \texttt{sys/un.h}. +definita nel file di header \file{sys/un.h}. \begin{figure}[!htb] \footnotesize @@ -488,16 +488,16 @@ struct sockaddr_un { }; \end{lstlisting} \caption{La struttura degli indirizzi dei socket locali - \texttt{sockaddr\_un}.} + \var{sockaddr\_un}.} \label{fig:sock_sa_local_struct} \end{figure} -In questo caso il campo \texttt{sun\_family} deve essere \texttt{AF\_UNIX}, -mentre il campo \texttt{sun\_path} deve specificare un indirizzo; questo ha +In questo caso il campo \var{sun\_family} deve essere \macro{AF\_UNIX}, +mentre il campo \var{sun\_path} deve specificare un indirizzo; questo ha due forme un file (di tipo socket) nel filesystem o una stringa univoca (tenuta in uno spazio di nomi astratto). Nel primo caso l'indirizzo viene specificato come una stringa (terminata da uno zero) corrispondente al -pathname del file; nel secondo invece \texttt{sun\_path} inizia con uno zero +pathname del file; nel secondo invece \var{sun\_path} inizia con uno zero vengono usati i restanti bytes come stringa (senza terminazione). @@ -694,7 +694,7 @@ seguenti: La funzione restituisce un puntatore non nullo a \var{dest} in caso di successo e un puntatore nullo in caso di fallimento, in quest'ultimo caso - viene settata la variabile \texttt{errno} con il valore \macro{ENOSPC} in + viene settata la variabile \var{errno} con il valore \macro{ENOSPC} in caso le dimensioni dell'indirizzo eccedano la lunghezza specificata da \var{len} o \macro{ENOAFSUPPORT} in caso \var{af} non sia una famiglia di indirizzi valida. @@ -890,7 +890,7 @@ int main(int argc, char *argv[]) \label{fig:net_cli_code} \end{figure} -Il sorgente completo del programma (\texttt{ElemDaytimeTCPClient.c}, che +Il sorgente completo del programma (\file{ElemDaytimeTCPClient.c}, che comprende il trattamento delle opzioni e una funzione per stampare un messaggio di aiuto) è allegato alla guida nella sezione dei codici sorgente e può essere compilato su una qualunque macchina Linux. @@ -913,7 +913,7 @@ il numero della porta del servizio. Il primo passo zero, per poi inserire il tipo di protocollo e la porta (usando per quest'ultima la funzione \func{htons} per convertire il formato dell'intero usato dal computer a quello usato nella rete), infine si utilizza la funzione -\texttt{inet\_pton} per convertire l'indirizzo numerico passato dalla linea di +\func{inet\_pton} per convertire l'indirizzo numerico passato dalla linea di comando. Usando la funzione \func{connect} sul socket creato in precedenza @@ -927,7 +927,7 @@ valore di ritorno negativo implica il fallimento della connessione. Completata con successo la connessione il passo successivo (\texttt{\small 34--40}) è leggere la data dal socket; il server invierà sempre una stringa di 26 caratteri della forma \verb|Wed Apr 4 00:53:00 2001\r\n|, che viene -letta dalla funzione \func{read} e scritta su \texttt{stdout}. +letta dalla funzione \func{read} e scritta su \file{stdout}. Dato il funzionamento di TCP la risposta potrà tornare in un unico pacchetto di 26 byte (come avverrà senz'altro nel caso in questione) ma potrebbe anche @@ -953,8 +953,8 @@ necessario deve provvedere il programma stesso. Dopo aver illustrato il client daremo anche un esempio di un server elementare, in grado di rispondere al precedente client. Il listato è nuovamente mostrato in \nfig, il sorgente completo -(\texttt{ElemDaytimeTCPServer.c}) è allegato insieme agli altri file nella -directory \texttt{sources}. +(\file{ElemDaytimeTCPServer.c}) è allegato insieme agli altri file nella +directory \file{sources}. \begin{figure}[!htbp] \footnotesize diff --git a/system.tex b/system.tex new file mode 100644 index 0000000..56ae1d1 --- /dev/null +++ b/system.tex @@ -0,0 +1,210 @@ +\chapter{La gestione del sistema} +\label{cha:system} + + + + + +\subsection{La misura del tempo in unix} +\label{sec:intro_unix_time} + +Storicamente i sistemi unix-like hanno sempre mantenuto due distinti valori +per i tempi all'interno del sistema, essi sono rispettivamente chiamati +\textit{calendar time} e \textit{process time}, secondo le definizioni: +\begin{itemize} +\item \textit{calendar time}: è il numero di secondi dalla mezzanotte del + primo gennaio 1970, in tempo universale coordinato (o UTC), data che viene + usualmente indicata con 00:00:00 Jan, 1 1970 (UTC) e chiamata \textit{the + Epoch}. Questo tempo viene anche chiamato anche GMT (Greenwich Mean Time) + dato che l'UTC corrisponde all'ora locale di Greenwich. È il tempo su cui + viene mantenuto l'orologio del calcolatore, e viene usato ad esempio per + indicare le date di modifica dei file o quelle di avvio dei processi. Per + memorizzare questo tempo è stato riservato il tipo primitivo \func{time\_t}. +\item \textit{process time}: talvolta anche detto tempo di CPU. Viene misurato + in \textit{clock tick}, corrispondenti al numero di interruzioni effettuate + dal timer di sistema, e che per Linux avvengono ogni centesimo di + secondo\footnote{eccetto per la piattaforma alpha dove avvengono ogni + millesimo di secondo}. Il dato primitivo usato per questo tempo è + \func{clock\_t}, inoltre la costante \macro{HZ} restituisce la frequenza di + operazione del timer, e corrisponde dunque al numero di tick al secondo. Lo + standard POSIX definisce allo stesso modo la costante \macro{CLK\_TCK}); + questo valore può comunque essere ottenuto con \func{sysconf} (vedi + \secref{sec:intro_limits}). +\end{itemize} + +In genere si usa il \textit{calendar time} per tenere le date dei file e le +informazioni analoghe che riguardano i tempi di ``orologio'', usati ad esempio +per i demoni che compiono lavori amministrativi ad ore definite, come +\cmd{cron}. Di solito questo vene convertito automaticamente dal valore in UTC +al tempo locale, utilizzando le opportune informazioni di localizzazione +(specificate in \file{/etc/timezone}). E da tenere presente che questo tempo è +mantenuto dal sistema e non corrisponde all'orologio hardware del calcolatore. + +Il \textit{process time} di solito si esprime in secondi e viene usato appunto +per tenere conto dei tempi di esecuzione dei processi. Per ciascun processo il +kernel tiene tre di questi tempi: +\begin{itemize*} +\item \textit{clock time} +\item \textit{user time} +\item \textit{system time} +\end{itemize*} +il primo è il tempo ``reale'' (viene anche chiamato \textit{wall clock time}) +dall'avvio del processo, e misura il tempo trascorso fino alla sua +conclusione; chiaramente un tale tempo dipende anche dal carico del sistema e +da quanti altri processi stavano girando nello stesso periodo. Il secondo +tempo è quello che la CPU ha speso nell'esecuzione delle istruzioni del +processo in user space. Il terzo è il tempo impiegato dal kernel per eseguire +delle system call per conto del processo medesimo (tipo quello usato per +eseguire una \func{write} su un file). In genere la somma di user e system +time viene chiamato \textit{CPU time}. + + + +\section{La gestione degli errori} +\label{sec:intro_errors} + +La gestione degli errori è in genere una materia complessa. Inoltre il modello +utilizzato dai sistema unix-like è basato sull'architettura a processi, e +presenta una serie di problemi nel caso lo si debba usare con i thread. +Esamineremo in questa sezione le sue caratteristiche principali. + + +\subsection{La variabile \func{errno}} +\label{sec:intro_errno} + +Quasi tutte le funzioni delle librerie del C sono in grado di individuare e +riportare condizioni di errore, ed è una buona norma di programmazione +controllare sempre che le funzioni chiamate si siano concluse correttamente. + +In genere le funzioni di libreria usano un valore speciale per indicare che +c'è stato un errore. Di solito questo valore è -1 o un puntatore nullo o la +costante \macro{EOF} (a seconda della funzione); ma questo valore segnala solo +che c'è stato un errore, non il tipo di errore. + +Per riportare il tipo di errore il sistema usa la variabile globale +\var{errno}\footnote{L'uso di una variabile globale può comportare alcuni + problemi (ad esempio nel caso dei thread) ma lo standard ISO C consente + anche di definire \var{errno} come un \textit{modifiable lvalue}, quindi si + può anche usare una macro, e questo è infatti il modo usato da Linux per + renderla locale ai singoli thread }, definita nell'header \file{errno.h}, la +variabile è in genere definita come \var{volatile} dato che può essere +cambiata in modo asincrono da un segnale (per una descrizione dei segnali si +veda \secref{cha:signals}), ma dato che un manipolatore di segnale scritto +bene salva e ripristina il valore della variabile, di questo non è necessario +preoccuparsi nella programmazione normale. + +I valori che può assumere \var{errno} sono riportati in \capref{cha:errors}, +nell'header \file{errno.h} sono anche definiti i nomi simbolici per le +costanti numeriche che identificano i vari errori; essi iniziano tutti per +\macro{E} e si possono considerare come nomi riservati. In seguito faremo +sempre rifermento a tali valori, quando descriveremo i possibili errori +restituiti dalle funzioni. Il programma di esempio \cmd{errcode} stampa il +codice relativo ad un valore numerico con l'opzione \cmd{-l}. + +Il valore di \var{errno} viene sempre settato a zero all'avvio di un +programma, gran parte delle funzioni di libreria settano \var{errno} ad un +valore diverso da zero in caso di errore. Il valore è invece indefinito in +caso di successo, perché anche se una funzione ha successo, può chiamarne +altre al suo interno che falliscono, modificando così \var{errno}. + +Pertanto un valore non nullo di \var{errno} non è sintomo di errore (potrebbe +essere il risultato di un errore precedente) e non lo si può usare per +determinare quando o se una chiamata a funzione è fallita. La procedura da +seguire è sempre quella di controllare \var{errno} immediatamente dopo aver +verificato il fallimento della funzione attraverso il suo codice di ritorno. + + +\subsection{Le funzioni \func{strerror} e \func{perror}} +\label{sec:intro_strerror} + +Benché gli errori siano identificati univocamente dal valore numerico di +\var{errno} le librerie provvedono alcune funzioni e variabili utili per +riportare in opportuni messaggi le condizioni di errore verificatesi. La +prima funzione che si può usare per ricavare i messaggi di errore è +\func{strerror}, il cui prototipo è: +\begin{prototype}{string.h}{char * strerror(int errnum)} + La funzione ritorna una stringa (statica) che descrive l'errore il cui + codice è passato come parametro. +\end{prototype} + +In generale \func{strerror} viene usata passando \var{errno} come parametro; +nel caso si specifichi un codice sbagliato verrà restituito un messaggio di +errore sconosciuto. La funzione utilizza una stringa statica che non deve +essere modificata dal programma e che è utilizzabile solo fino ad una chiamata +successiva a \func{strerror}; nel caso si usino i thread è +provvista\footnote{questa funzione è una estensione GNU, non fa parte dello + standard POSIX} una versione apposita: +\begin{prototype}{string.h} +{char * strerror\_r(int errnum, char * buff, size\_t size)} + La funzione è analoga a \func{strerror} ma ritorna il messaggio in un buffer + specificato da \var{buff} di lunghezza massima (compreso il terminatore) + \var{size}. +\end{prototype} +che utilizza un buffer che il singolo thread deve allocare, per evitare i +problemi connessi alla condivisione del buffer statico. Infine, per completare +la caratterizzazione dell'errore, si può usare anche la variabile +globale\footnote{anche questa è una estensione GNU} +\var{program\_invocation\_short\_name} che riporta il nome del programma +attualmente in esecuzione. + +Una seconda funzione usata per riportare i codici di errore in maniera +automatizzata sullo standard error (vedi \secref{sec:file_stdfiles}) è +\func{perror}, il cui prototipo è: +\begin{prototype}{stdio.h}{void perror (const char *message)} + La funzione stampa il messaggio di errore relativo al valore corrente di + \var{errno} sullo standard error; preceduto dalla stringa \var{message}. +\end{prototype} +i messaggi di errore stampati sono gli stessi di \func{strerror}, (riportati +in \capref{cha:errors}), e, usando il valore corrente di \var{errno}, si +riferiscono all'ultimo errore avvenuto. La stringa specificata con +\var{message} viene stampato prime del messaggio d'errore, seguita dai due +punti e da uno spazio, il messaggio è terminato con un a capo. + +Il messaggio può essere riportato anche usando altre variabili globali +dichiarate in \file{errno.h}: +\begin{verbatim} + const char *sys_errlist[]; + int sys_nerr; +\end{verbatim} +la prima contiene i puntatori alle stringhe di errore indicizzati da +\var{errno}; la seconda esprime il valore più alto per un codice di errore, +l'utilizzo di questa stringa è sostanzialmente equivalente a quello di +\func{strerror}. + +In \nfig\ è riportata la sezione attinente del codice del programma +\cmd{errcode}, che può essere usato per stampare i messaggi di errore e le +costanti usate per identificare i singoli errori; il sorgente completo del +programma è allegato nel file \file{ErrCode.c} e contiene pure la gestione +delle opzioni e tutte le definizioni necessarie ad associare il valore +numerico alla costante simbolica. In particolare si è riportata la sezione che +converte la stringa passata come parametro in un intero (\texttt{\small + 1--2}), controllando con i valori di ritorno di \func{strtol} che la +conversione sia avvenuta correttamente (\texttt{\small 4--10}), e poi stampa, +a seconda dell'opzione scelta il messaggio di errore (\texttt{\small 11--14}) +o la macro (\texttt{\small 15--17}) associate a quel codice. + +\begin{figure}[!htb] + \footnotesize + \begin{lstlisting}{} + /* convert string to number */ + err = strtol(argv[optind], NULL, 10); + /* testing error condition on conversion */ + if (err==LONG_MIN) { + perror("Underflow on error code"); + return 1; + } else if (err==LONG_MIN) { + perror("Overflow on error code"); + return 1; + } + /* conversion is fine */ + if (message) { + printf("Error message for %d is %s\n", err, strerror(err)); + } + if (label) { + printf("Error label for %d is %s\n", err, err_code[err]); + } + \end{lstlisting} + \caption{Codice per la stampa del messaggio di errore standard.} + \label{fig:intro_err_mess} +\end{figure} +