From: Simone Piccardi Date: Wed, 22 Aug 2001 17:24:14 +0000 (+0000) Subject: Riorganizzate varie parti, rimetto in linea la nuova versione X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=429f6e0da8fc282eb6611b6fe83fdf58ae8da611 Riorganizzate varie parti, rimetto in linea la nuova versione --- diff --git a/elemtcp.tex b/elemtcp.tex index 9cfcb35..3c4b216 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -13,7 +13,7 @@ connessione TCP. \label{sec:TCPel_connession} Prima di entrare nei dettagli delle funzioni usate nelle applicazioni che -utilizzano i sokcet TCP, è fondamentale spiegare alcune basi del funzionamento +utilizzano i socket TCP, è fondamentale spiegare alcune basi del funzionamento del TCP; la conoscenza del funzionamento del protocollo è infatti essenziale per capire il modello di programmazione ed il funzionamento delle API. @@ -681,18 +681,21 @@ indirizzo di origine l'indirizzo di destinazione specificato dal SYN del client. Per specificare un indirizzo generico con IPv4 si usa il valore -\texttt{INADDR\_ANY}, il cui valore, come visto anche negli esempi precedenti +\macro{INADDR\_ANY}, il cui valore, come visto anche negli esempi precedenti è pari a zero, nell'esempio \figref{fig:net_serv_code} si è usata un'assegnazione immediata del tipo: -\begin{verbatim} - serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ -\end{verbatim} -Si noti che si è usato \texttt{htonl} per assegnare il valore -\texttt{INADDR\_ANY}; benché essendo questo pari a zero il riordinamento sia -inutile; ma dato che tutte le costanti \texttt{INADDR\_} sono definite +\footnotesize +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} + serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ +\end{lstlisting} +\normalsize + +Si noti che si è usato \func{htonl} per assegnare il valore +\macro{INADDR\_ANY}; benché essendo questo pari a zero il riordinamento sia +inutile; ma dato che tutte le costanti \macro{INADDR\_} sono definite secondo l'ordinamento della macchina è buona norma usare sempre la funzione -\texttt{htonl}. +\macro{htonl}. L'esempio precedete funziona con IPv4 dato che l'indirizzo è rappresentabile anche con un intero a 32 bit; non si può usare lo stesso metodo con IPv6, @@ -702,12 +705,14 @@ assegnazione. Per questo nell'header \texttt{netinet/in.h} variabile \texttt{in6addr\_any} (dichiarata come \texttt{extern}, ed inizializzata dal sistema al valore \texttt{IN6ADRR\_ANY\_INIT}) che permette di effettuare una assegnazione del tipo: -\begin{verbatim} +\footnotesize +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} serv_add.sin6_addr = in6addr_any; /* connect from anywhere */ -\end{verbatim} +\end{lstlisting} +\normalsize -\subsection{La funzione \texttt{connect}} +\subsection{La funzione \func{connect}} \label{sec:TCPel_func_connect} La funzione \texttt{connect} è usata da un client TCP per stabilire la diff --git a/filedir.tex b/filedir.tex index cbd718b..1f0edcd 100644 --- a/filedir.tex +++ b/filedir.tex @@ -34,7 +34,7 @@ controllo di accesso molto pi Ad ogni file unix associa sempre l'utente che ne è proprietario (il cosiddetto \textit{owner}) e il gruppo di appartenenza, secondo il meccanismo degli -identificatori di utenti e gruppi (\textsl{uid} e \textsl{gid}). Questi valori +identificatori di utenti e gruppi (\acr{uid} e \acr{gid}). Questi valori sono accessibili da programma tramite i campi \var{st\_uid} e \var{st\_gid} della struttura \var{stat} (si veda \secref{sec:file_stat}). Ad ogni file viene inoltre associato un insieme di permessi che sono divisi in tre classi, @@ -47,7 +47,7 @@ sono espressi da un numero di 12 bit; di questi i nove meno significativi sono usati a gruppi di tre per indicare i permessi base di lettura, scrittura ed esecuzione (indicati nei comandi di sistema con le lettere \cmd{w}, \cmd{r} e \cmd{x}) ed applicabili rispettivamente al proprietario, al gruppo, a tutti -gli altri. I restanti tre bit (\textsl{suid}, \textsl{sgid}, e +gli altri. I restanti tre bit (\acr{suid}, \acr{sgid}, e \textsl{sticky}) sono usati per indicare alcune caratteristiche più complesse su cui torneremo in seguito (vedi \secref{sec:file_suid_sgid} e \secref{sec:file_sticky}). @@ -149,22 +149,9 @@ del processo. Per una spiegazione dettagliata degli identificatori associati ai processi si veda \secref{sec:proc_perms}; normalmente, a parte quanto vedremo in \secref{sec:file_suid_sgid}, l'\textit{effective user id} e -l'\textit{effective group id} corrispondono a uid e gid dell'utente che ha -lanciato il processo, mentre i \textit{supplementary group id} sono quelli dei -gruppi cui l'utente appartiene. - -% Quando un processo cerca l'accesso al file esso controlla i propri uid e gid -% confrontandoli con quelli del file e se l'operazione richiesta è compatibile -% con i permessi associati al file essa viene eseguita, altrimenti viene -% bloccata ed è restituito un errore di \texttt{EPERM}. Questo procedimento non -% viene eseguito per l'amministratore di sistema (il cui uid è zero) il quale -% a -% pertanto accesso senza restrizione a qualunque file del sistema. - -% In realtà il procedimento è più complesso di quanto descritto in maniera -% elementare qui; inoltre ad un processo sono associati diversi identificatori, -% torneremo su questo in maggiori dettagli in seguito in -% \secref{sec:proc_perms}. +l'\textit{effective group id} corrispondono a \acr{uid} e \acr{gid} +dell'utente che ha lanciato il processo, mentre i \textit{supplementary group + id} sono quelli dei gruppi cui l'utente appartiene. I passi attraverso i quali viene stabilito se il processo possiede il diritto di accesso sono i seguenti: @@ -184,15 +171,15 @@ di accesso sono i seguenti: \item altrimenti l'accesso è negato \end{itemize} \item Se l'\textit{effective group id} del processo o uno dei - \textit{supplementary group id} dei processi corrispondono al gid del file - allora: + \textit{supplementary group id} dei processi corrispondono al \acr{gid} del + file allora: \begin{itemize} \item se il bit dei permessi d'accesso del gruppo è settato, l'accesso è consentito, \item altrimenti l'accesso è negato \end{itemize} \item se il bit dei permessi d'accesso per tutti gli altri è settato, - l'accesso è consentito, altrimenti l'accesso è negato. + l'accesso è consentito, altrimenti l'accesso è negato. \end{itemize} Si tenga presente che questi passi vengono eseguiti esattamente in @@ -203,22 +190,22 @@ processo appartiene ad un gruppo appropriato, in questo caso i permessi per tutti gli altri non vengono controllati. -\subsection{I bit \textsl{suid} e \textsl{sgid}} +\subsection{I bit \acr{suid} e \acr{sgid}} \label{sec:file_suid_sgid} Come si è accennato (in \secref{sec:file_perm_overview}) nei dodici bit del campo \var{st\_mode} usati per il controllo di accesso oltre ai bit dei permessi veri e propri, ci sono altri tre bit che vengono usati per indicare alcune proprietà speciali dei file. Due di questi sono i bit detti -\textsl{suid} (o \textit{set-user-ID bit}) e \textsl{sgid} (o +\acr{suid} (o \textit{set-user-ID bit}) e \acr{sgid} (o \textit{set-group-ID bit}) che sono identificati dalle constanti \macro{S\_ISUID} e \macro{S\_ISGID}. Come spiegato in dettaglio in \secref{sec:proc_exec}, quando si lancia un programma il comportamendo normale del kernel è quello di settare l'\textit{effective user id} e l'\textit{effective group id} del nuovo -processo all'uid e al gid del processo corrente, che normalmente corrispondono -dell'utente con cui si è entrati nel sistema. +processo all'\acr{uid} e al \acr{gid} del processo corrente, che normalmente +corrispondono dell'utente con cui si è entrati nel sistema. Se però il file del programma\footnote{per motivi di sicurezza il kernel ignora i bit \acr{suid} e \acr{sgid} per gli script eseguibili} (che @@ -258,8 +245,8 @@ con questi bit l'uso della semantica BSD nella creazione di nuovi file (si veda \secref{sec:file_ownership} per una spiegazione dettagliata al proposito). -Infine Linux utilizza il bit \textsl{sgid} per una ulteriore estensione -mutuata da SVR4. Il caso in cui il file abbia il bit \textsl{sgid} settato ma +Infine Linux utilizza il bit \acr{sgid} per una ulteriore estensione +mutuata da SVR4. Il caso in cui il file abbia il bit \acr{sgid} settato ma non il corrispondente bit di esecuzione viene utilizzato per attivare per quel file il \textit{mandatory locking} (argomento che affronteremo nei dettagli in \secref{sec:xxx_mandatory_lock}). @@ -322,26 +309,28 @@ stesso problema di presenta per la creazione di nuove directory (procedimento descritto in \secref{sec:file_dir_creat_rem}). Lo standard POSIX prescrive che l'uid del nuovo file corrisponda -all'\textit{effective user id} del processo che lo crea; per il gid invece -prevede due diverse possibilità: +all'\textit{effective user id} del processo che lo crea; per il \acr{gid} +invece prevede due diverse possibilità: \begin{itemize} -\item il gid del file corrisponde all'\textit{effective group id} del processo. -\item il gid del file corrisponde al gid della directory in cui esso è creato. +\item il \acr{gid} del file corrisponde all'\textit{effective group id} del + processo. +\item il \acr{gid} del file corrisponde al gid della directory in cui esso è + creato. \end{itemize} in genere BSD usa sempre la seconda possibilità, che viene per questo chiamata semantica BSD. Linux invece segue quella che viene chiamata semantica SVR4; di -norma cioè il nuovo file viene creato, seguendo la prima opzione, con il gid -del processo, se però la directory in cui viene creato il file ha il bit -\textsl{sgid} settato allora viene usata la seconda opzione.. +norma cioè il nuovo file viene creato, seguendo la prima opzione, con il +\acr{gid} del processo, se però la directory in cui viene creato il file ha il +bit \acr{sgid} settato allora viene usata la seconda opzione.. -Usare la semantica BSD ha il vantaggio che il gid viene sempre automaticamente -propagato, restando coerente a quello della directory di partenza, in tutte le -sottodirectory. La semantica SVR4 offre una maggiore possibilità di scelta, ma -per ottenere lo stesso risultato necessita che per le nuove directory venga -anche propagato anche il bit sgid. Questo è comunque il comportamento di -default di \func{mkdir}, ed é in questo modo ad esempio che Debian assicura -che le sottodirectory create nelle home di un utente restino sempre con il gid -del gruppo primario dello stesso. +Usare la semantica BSD ha il vantaggio che il \acr{gid} viene sempre +automaticamente propagato, restando coerente a quello della directory di +partenza, in tutte le sottodirectory. La semantica SVR4 offre una maggiore +possibilità di scelta, ma per ottenere lo stesso risultato necessita che per +le nuove directory venga anche propagato anche il bit \acr{sgid}. Questo è +comunque il comportamento di default di \func{mkdir}, ed é in questo modo ad +esempio che Debian assicura che le sottodirectory create nelle home di un +utente restino sempre con il \acr{gid} del gruppo primario dello stesso. \subsection{La funzione \texttt{access}} @@ -489,7 +478,7 @@ particolare: \item per via della semantica SVR4 nella creazione dei nuovi file, si può avere il caso in cui il file creato da un processo è assegnato a un gruppo per il quale il processo non ha privilegi. Per evitare che si possa - assegnare il bit \textsl{sgid} ad un file appartenente a un gruppo per cui + assegnare il bit \acr{sgid} ad un file appartenente a un gruppo per cui non si hanno diritti, questo viene automaticamente cancellato (senza notifica di errore) da \var{mode} qualora il gruppo del file non corrisponda a quelli associati al processo (la cosa non avviene quando @@ -591,8 +580,8 @@ Un'altra estensione rispetto allo standard POSIX valore per \var{owner} e \var{group} i valori restano immutati. Quando queste funzioni sono chiamate con successo da un processo senza i -privilegi di root entrambi i bit \textsl{suid} e \textsl{sgid} vengono -cancellati. Questo non avviene per il bit \textsl{sgid} nel caso in cui esso +privilegi di root entrambi i bit \acr{suid} e \acr{sgid} vengono +cancellati. Questo non avviene per il bit \acr{sgid} nel caso in cui esso sia usato (in assenza del corrispondente permesso di esecuzione) per indicare che per il file è attivo il \textit{mandatory locking}. diff --git a/fileintro.tex b/fileintro.tex index d4d4785..4fc1e38 100644 --- a/fileintro.tex +++ b/fileintro.tex @@ -56,17 +56,17 @@ percorso che si deve fare per accedere al file. Dopo la fase di inizializzazione il kernel riceve dal boot loader l'indicazione di quale dispositivo contiene il filesystem da usare come punto di partenza e questo viene montato come radice dell'albero (cioè nella -directory \texttt{/}); tutti gli ulteriori dischi devono poi essere inseriti +directory \file{/}); tutti gli ulteriori dischi devono poi essere inseriti nell'albero utilizzando opportune subdirectory. -Alcuni filesystem speciali (come \texttt{/proc} che contiene un'interfaccia ad +Alcuni filesystem speciali (come \file{/proc} che contiene un'interfaccia ad alcune strutture interne del kernel) sono generati automaticamente dal kernel stesso, ma anche essi devono essere montati all'interno dell'albero. All'interno dello stesso albero si potranno poi inserire anche gli altri oggetti visti attraverso l'interfaccia che manipola i files come le FIFO, i link, i socket e gli stessi i file di dispositivo (questi ultimi, per -convenzione, sono inseriti nella directory \texttt{/dev}). +convenzione, sono inseriti nella directory \file{/dev}). L'organizzazione dei nomi dei file deriva direttamente dall'organizzazione dei medesimi nell'albero descritto in precedenza; una directory comunque, come già @@ -79,21 +79,22 @@ che contiene le informazioni che associano un nome al contenuto. I manuale delle glibc chiama i nomi contenuti nelle directory \textsl{componenti} (in inglese \textit{file name components}), noi li -chiameremo più semplicemente nomi. Un file può essere indicato rispetto alla -directory corrente semplicemente specificando il nome da essa contenuto. Una -directory contiene semplicemente un elenco di questi nomi, che possono -corrispondere a un qualunque oggetto del filesystem, compresa un'altra +chiameremo più semplicemente \textit{nomi}. Un file può essere indicato +rispetto alla directory corrente semplicemente specificando il nome da essa +contenuto. Una directory contiene semplicemente un elenco di questi nomi, che +possono corrispondere a un qualunque oggetto del filesystem, compresa un'altra directory; l'albero viene appunto creato inserendo directory in altre directory. -Il nome completo di file generico è composto da una serie di questi -\textsl{componenti} separati da una \texttt{/} (in Linux più \texttt{/} -consecutive sono considerate equivalenti ad una sola). Il nome completo di un -file viene usualmente chiamato \textit{pathname}, e anche se il manuale della -glibc depreca questo nome (poiché genererebbe confusione, dato che con -\textit{path} si indica anche un insieme di directory su cui effettuare una -ricerca, come quello in cui si cercano i comandi); l'uso è ormai così comune -che è senz'altro più chiaro dell'alternativa proposta. +Il nome completo di file generico è composto da una serie di nomi separati da +una \texttt{/} (in Linux più \texttt{/} consecutive sono considerate +equivalenti ad una sola). Il nome completo di un file viene usualmente +chiamato \textit{pathname}, e anche se il manuale della glibc depreca questo +nome (poiché genererebbe confusione, dato che con \textit{path} si indica +anche un insieme di directory su cui effettuare una ricerca, come quello in +cui si cercano i comandi); non seguiremo questa scelta dato che l'uso della +parola \textit{pathname} è ormai così comune che è senz'altro più chiaro +dell'alternativa proposta. Il processo con cui si associa ad un pathname uno specifico file è chiamato risoluzione del nome (\textit{file name resolution} o \textit{pathname @@ -112,11 +113,11 @@ equivale alla directory radice dell'albero (come descritto in cui torneremo più avanti in \secref{sec:file_work_dir}) ed il pathname è detto \textsl{relativo}. -I nomi \texttt{.} e \texttt{..} hanno un significato speciale e vengono -inseriti in ogni directory, il primo fa riferimento alla directory corrente e -il secondo alla directory \textsl{genitore} (\textit{parent directory}) cioè -la directory che contiene il riferimento alla directory corrente; nel caso -questa sia la directory radice allora il riferimento è a se stessa. +I nomi \file{.} e \file{..} hanno un significato speciale e vengono inseriti +in ogni directory, il primo fa riferimento alla directory corrente e il +secondo alla directory \textsl{genitrice} (\textit{parent directory}) cioè la +directory che contiene il riferimento alla directory corrente; nel caso questa +sia la directory radice allora il riferimento è a se stessa. \subsection{I tipi di files} @@ -195,8 +196,8 @@ bufferizzato in quanto la lettura e la scrittura vengono eseguite chiamando direttamente le system call del kernel (in realtà il kernel effettua al suo interno alcune bufferizzazioni per aumentare l'efficienza nell'accesso ai dispositivi); i file descriptors sono rappresentati da numeri interi (cioè -semplici variabili di tipo \texttt{int}). L'interfaccia è definita -nell'header \texttt{unistd.h}. +semplici variabili di tipo \type{int}). L'interfaccia è definita +nell'header \file{unistd.h}. La seconda interfaccia è quella che il manuale della glibc chiama degli \textit{stream}, essa provvede funzioni più evolute e un accesso bufferizzato @@ -207,10 +208,10 @@ Questa anche su tutti i sistemi non unix. Gli stream sono oggetti complessi e sono rappresentati da puntatori ad un opportuna struttura definita dalle librerie del C, si accede ad essi sempre in maniera indiretta utilizzando il tipo -\texttt{FILE *}. L'interfaccia è definita nell'header \texttt{stdio.h}. +\type{FILE *}. L'interfaccia è definita nell'header \type{stdio.h}. Entrambe le interfacce possono essere usate per l'accesso ai file come agli -altri oggetti del VFS (pipes, socket, device), ma per poter accedere alle +altri oggetti del VFS (pipe, socket, device), ma per poter accedere alle operazioni di controllo sul particolare tipo di oggetto del VFS scelto occorre usare l'interfaccia standard di unix coi file descriptors. Allo stesso modo devono essere usati i file descriptor se si vuole ricorrere a modalità @@ -305,7 +306,7 @@ tracciavamo al \capref{cha:intro_unix}. In questa sezione esamineremo come viene implementato l'accesso ai files in Linux, come il kernel può gestire diversi tipi di filesystem, descrivendo poi in maniera un po' più dettagliata il filesystem standard di Linux, -l'\texttt{ext2}, come esempio di un filesystem unix-like. +l'\acr{ext2}, come esempio di un filesystem unix-like. % in particolare si riprenderà, approfondendolo sul piano @@ -415,18 +416,18 @@ files, ovviamente per non riempire tutta la memoria questa vista richiesto l'accesso), quando si vuole risolvere un nuovo pathname il VFS deve creare una nuova dentry e caricare l'inode corrispondente in memoria. -Questo procedimento viene eseguito dal metodo \texttt{lookup()} dell'inode +Questo procedimento viene eseguito dal metodo \func{lookup()} dell'inode della directory che contiene il file; questo viene installato nelle relative strutture in memoria quando si effettua il montaggio lo specifico filesystem su cui l'inode va a vivere. Una volta che il VFS ha a disposizione la dentry (ed il relativo inode) diventa possibile accedere alle varie operazioni sul file come la -\texttt{open} per aprire il file o la \texttt{stat} per leggere i dati +\func{open} per aprire il file o la \func{stat} per leggere i dati dell'inode e passarli in user space. L'apertura di un file richiede comunque un'altra operazione, l'allocazione di -una struttura di tipo \texttt{file} in cui viene inserito un puntatore alla +una struttura di tipo \var{file} in cui viene inserito un puntatore alla dentry e una struttura \verb|f_ops| che contiene i puntatori ai metodi che implementano le operazioni disponibili sul file. In questo modo i processi in user space possono accedere alle operazioni attraverso detti metodi, che @@ -527,8 +528,8 @@ torneremo in seguitp; in particolare \item L'\textit{inode} contiene tutte le informazioni riguardanti il file: il tipo di file, i permessi di accesso, le dimensioni, i puntatori ai blocchi fisici che contengono i dati e così via; le informazioni che la funzione - \texttt{stat} fornisce provengono dall'\textit{inode}; dentro una directory - si troverà solo il nome del file e il numero dell'\textit{inode} ad esso + \func{stat} fornisce provengono dall'\textit{inode}; dentro una directory si + troverà solo il nome del file e il numero dell'\textit{inode} ad esso associato, cioè quella che da qui in poi chiameremo una \textsl{voce} (traduzione approssimata dell'inglese \textit{directory entry}, che non useremo anche per evitare confusione con le \textit{dentries} del kernel di @@ -559,7 +560,7 @@ torneremo in seguitp; in particolare Infine è bene avere presente che essendo file pure loro, esiste un numero di riferimenti anche per le directories; per cui se ad esempio a partire dalla -situazione mostrata in \curfig\ creiamo una nuova directory \texttt{img} nella +situazione mostrata in \curfig\ creiamo una nuova directory \file{img} nella directory \file{gapil}: avremo una situazione come quella in \nfig, dove per chiarezza abbiamo aggiunto dei numeri di inode. @@ -587,7 +588,7 @@ caratteristiche di un filesystem standard unix, filenames lunghi (256 caratteri, estendibili a 1012), una dimensione fino a 4~Tb. -Oltre alle caratteristiche standard \textsl{ext2} fornisce alcune estensioni +Oltre alle caratteristiche standard \acr{ext2} fornisce alcune estensioni che non sono presenti sugli altri filesystem unix, le cui principali sono le seguenti: \begin{itemize} @@ -602,7 +603,7 @@ seguenti: gruppo primario del processo, eccetto il caso in cui la directory ha il bit di sgid settato (per una descrizione dettagliata del significato di questi termini si veda \secref{sec:file_access_control}), nel qual caso file e - sottodirectory ereditano sia il group id che il sgid. + sottodirectory ereditano sia il \acr{gid} che lo \acr{sgid}. \item l'amministratore può scegliere la dimensione dei blocchi del filesystem in fase di creazione, a seconda delle sue esigenze (blocchi più grandi permettono un accesso più veloce, ma sprecano più spazio disco). @@ -617,7 +618,7 @@ seguenti: log). \end{itemize} -La struttura di \textsl{ext2} è stata ispirata a quella del filesystem di BSD, +La struttura di \acr{ext2} è stata ispirata a quella del filesystem di BSD, un filesystem è composto da un insieme di blocchi, la struttura generale è quella riportata in \figref{fig:file_filesys_detail}, in cui la partizione è divisa in gruppi di blocchi. diff --git a/filestd.tex b/filestd.tex index aa55878..5036909 100644 --- a/filestd.tex +++ b/filestd.tex @@ -4,7 +4,7 @@ % % Questa va per ultima. Va bene che e` la più usata, ma è basata sulle altre % -\section{I file stream e gli oggetti \texttt{FILE}} +\section{I file stream e gli oggetti \type{FILE}} \label{sec:file_stream} Esamineremo in questa sezione l'interfaccia per i file stream, le modalità per @@ -13,13 +13,13 @@ operazioni connesse all'uso dei file. L'interfaccia l'header file \texttt{stdio.h}. Per ragioni storiche la struttura di dati che rappresenta un stream è stata -chiamata \texttt{FILE}, questi oggetti sono creati dalle funzioni di libreria -e contengono tutte le informazioni necessarie a gestire le operazioni sugli +chiamata \type{FILE}, questi oggetti sono creati dalle funzioni di libreria e +contengono tutte le informazioni necessarie a gestire le operazioni sugli stream, come la posizione corrente, lo stato del buffer e degli indicatori di stato e di fine del file. Per questo motivo gli utenti non devono mai utilizzare direttamente o allocare queste strutture, ma usare sempre puntatori -del tipo \texttt{FILE *} (tanto che in certi caso il termine di puntatore a -file è diventato sinonimo di stream). +del tipo \type{FILE *} (tanto che in certi caso il termine di puntatore a file +è diventato sinonimo di stream). \subsection{Gli stream standard} \label{sec:file_stdfiles} @@ -27,7 +27,7 @@ file Quando un programma viene lanciato il processo ha sempre tre stream predefiniti aperti, che rappresentano i canali standard di input/output prestabiliti per il processo; anche questi tre stream sono definiti -nell'header \texttt{stdio.h} e sono: +nell'header \file{stdio.h} e sono: \begin{itemize} \item \texttt{FILE * stdin} Lo \textit{standard input} cioè lo stream da cui diff --git a/network.tex b/network.tex index 879f970..de8376f 100644 --- a/network.tex +++ b/network.tex @@ -2,15 +2,15 @@ \label{cha:network} In questo capitolo sarà fatta un'introduzione ai concetti generali che servono -come prerequisiti per capire la programmazione di rete, per evitare un -capitolo puramente teorico partiremo con due semplici esempi per poi passare -ad un esame a grandi linee dei protocolli di rete e di come questi sono -organizzati e interagiscono. +come prerequisiti per capire la programmazione di rete, non tratteremo quindi +aspetti specifici ma faremo una breve introduzione al modello più comune usato +nella programmazione di rete, per poi passare ad un esame a grandi linee dei +protocolli di rete e di come questi sono organizzati e interagiscono. In particolare, avendo assunto l'ottica di un'introduzione mirata alla programmazione, ci concentreremo sul protocollo più diffuso, il TCP/IP, che è -quello che sta alla base di internet, con un'ottica improntata a sottolineare -i concetti più importanti da conoscere ai fini della programmazione. +quello che sta alla base di internet, avendo cura di sottolineare i concetti +più importanti da conoscere per la scrittura dei programmi. \section{Il modello client-server} \label{sec:net_cliserv} @@ -49,261 +49,10 @@ soddisfatte contemporaneamente; una volta che il processo figlio ha concluso il suo lavoro viene terminato, mentre il server originale resta sempre attivo. -\subsection{Un primo esempio di client} -\label{sec:net_cli_sample} - -Per evitare di rendere l'esposizione dei concetti generali sulla rete -puramente teorica iniziamo con il mostrare un esempio di un client TCP -elementare. Scopo di questo esempio è fornire un primo approccio alla -programmazione di rete, tutto questo sarà esaminato in dettaglio nei capitoli -successivo; qui ci limiteremo a introdurre la nomenclatura senza fornire -definizioni precise e dettagli di funzionamento che saranno trattati -estensivamente più avanti. - -In \nfig\ è riportata la sezione principale del codice del nostro client -elementare per il servizio \textit{daytime}, un servizio standard che -restituisce l'ora locale della macchina a cui si effettua la richiesta. - - -\begin{figure}[!htb] - \footnotesize - \begin{lstlisting}{} -#include /* predefined types */ -#include /* include unix standard library */ -#include /* IP addresses conversion utiliites */ -#include /* socket library */ -#include /* include standard I/O library */ - -int main(int argc, char *argv[]) -{ - int sock_fd; - int i, nread; - struct sockaddr_in serv_add; - char buffer[MAXLINE]; - ... - /* create socket */ - if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("Socket creation error"); - return -1; - } - /* initialize address */ - memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */ - serv_add.sin_family = AF_INET; /* address type is INET */ - serv_add.sin_port = htons(13); /* daytime post is 13 */ - /* build address using inet_pton */ - if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) { - perror("Address creation error"); - return -1; - } - /* extablish connection */ - if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { - perror("Connection error"); - return -1; - } - /* read daytime from server */ - while ( (nread = read(sock_fd, buffer, MAXLINE)) > 0) { - buffer[nread]=0; - if (fputs(buffer, stdout) == EOF) { /* write daytime */ - perror("fputs error"); - return -1; - } - } - /* error on read */ - if (nread < 0) { - perror("Read error"); - return -1; - } - /* normal exit */ - return 0; -} - \end{lstlisting} - \caption{Esempio di codice di un client elementare per il servizio daytime.} - \label{fig:net_cli_code} -\end{figure} - -Il sorgente completo del programma (\texttt{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. - -Il programma anzitutto include gli header necessari (\texttt{\small 1--5}); -dopo la dichiarazione delle variabili (\texttt{\small 9--12}) si è omessa -tutta la parte relativa al trattamento degli argomenti passati dalla linea di -comando (effettuata con le apposite routines illustrate in -\capref{sec:proc_opt_handling}). - -Il primo passo (\texttt{\small 14--18}) è creare un \textit{socket} IPv4 -(\texttt{AF\_INET}), di tipo TCP \texttt{SOCK\_STREAM} (in sostanza un canale -di comunicazione attraverso internet, questi termini verranno spiegati con -precisione più avanti). La funzione \texttt{socket} ritorna un descrittore, -analogo a quello dei file, che viene usato per identificare il socket in tutte -le chiamate successive. Nel caso la chiamata fallisca si stampa un errore con -la relativa routine e si esce. - -Il passo seguente (\texttt{\small 19--27}) è quello di costruire una apposita -struttura \texttt{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed -il numero della porta del servizio. Il primo passo è inizializzare tutto a -zero, per poi inserire il tipo di protocollo e la porta (usando per -quest'ultima la funzione \texttt{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 -comando. - -Usando la funzione \texttt{connect} sul socket creato in precedenza -(\texttt{\small 28--32}) si provvede poi a stabilire la connessione con il -server specificato dall'indirizzo immesso nella struttura passata come secondo -argomento, il terzo argomento è la dimensione di detta struttura. Dato che -esistono diversi tipi di socket, si è dovuto effettuare un cast della -struttura inizializzata in precedenza, che è specifica per i socket IPv4. Un -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 \texttt{read} e scritta su \texttt{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 -arrivare in 26 pacchetti di un byte. Per questo nel caso generale non si può -mai assumere che tutti i dati arrivino con una singola lettura, pertanto -quest'ultima deve essere effettuata in un loop in cui si continui a leggere -fintanto che la funzione \texttt{read} non ritorni uno zero (che significa che -l'altro capo ha chiuso la connessione) o un numero minore di zero (che -significa un errore nella connessione). - -Si noti come in questo caso la fine dei dati sia specificata dal server che -chiude la connessione; questa è una delle tecniche possibili (è quella usata -pure dal protocollo HTTP), ma ce ne possono essere altre, ad esempio FTP marca -la conclusione di un blocco di dati con la sequenza ASCII \verb|\r\n| -(carriage return e line feed), mentre il DNS mette la lunghezza in testa ad -ogni blocco che trasmette. Il punto essenziale è che TCP non provvede nessuna -indicazione che permetta di marcare dei blocchi di dati, per cui se questo è -necessario deve provvedere il programma stesso. - -\subsection{Un primo esempio di server} -\label{sec:net_serv_sample} - -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}. - -\begin{figure}[!htbp] - \footnotesize - \begin{lstlisting}{} -#include /* predefined types */ -#include /* include unix standard library */ -#include /* IP addresses conversion utiliites */ -#include /* socket library */ -#include /* include standard I/O library */ -#include -#define MAXLINE 80 -#define BACKLOG 10 -int main(int argc, char *argv[]) -{ -/* - * Variables definition - */ - int list_fd, conn_fd; - int i; - struct sockaddr_in serv_add; - char buffer[MAXLINE]; - time_t timeval; - ... - /* create socket */ - if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("Socket creation error"); - exit(-1); - } - /* initialize address */ - memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */ - serv_add.sin_family = AF_INET; /* address type is INET */ - serv_add.sin_port = htons(13); /* daytime port is 13 */ - serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ - /* bind socket */ - if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { - perror("bind error"); - exit(-1); - } - /* listen on socket */ - if (listen(list_fd, BACKLOG) < 0 ) { - perror("listen error"); - exit(-1); - } - /* write daytime to client */ - while (1) { - if ( (conn_fd = accept(list_fd, (struct sockaddr *) NULL, NULL)) <0 ) { - perror("accept error"); - exit(-1); - } - timeval = time(NULL); - snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval)); - if ( (write(conn_fd, buffer, strlen(buffer))) < 0 ) { - perror("write error"); - exit(-1); - } - close(conn_fd); - } - /* normal exit */ - exit(0); -} - \end{lstlisting} - \caption{Esempio di codice di un semplice server per il servizio daytime.} - \label{fig:net_serv_code} -\end{figure} - -Come per il client si includono gli header necessari a cui è aggiunto quello -per trattare i tempi, e si definiscono alcune costanti e le variabili -necessarie in seguito (\texttt{\small 1--18}), come nel caso precedente si -sono omesse le parti relative al trattamento delle opzioni da riga di comando. - -La creazione del socket (\texttt{\small 22--26}) è analoga al caso precedente, -come pure l'inizializzazione della struttura \texttt{sockaddr\_in}, anche in -questo caso si usa la porta standard del servizio daytime, ma come indirizzo -IP si il valore predefinito \texttt{INET\_ANY} che corrisponde ad un indirizzo -generico (\texttt{\small 27--31}). - -Si effettua poi (\texttt{\small 32--36}) la chiamata alla funzione -\texttt{bind} che permette di associare la precedente struttura al socket, in -modo che quest'ultimo possa essere usato per accettare connessioni su una -qualunque delle interfacce di rete locali. - -Il passo successivo (\texttt{\small 37--41}) è mettere ``in ascolto'' il -socket, questo viene effettuato con la funzione \texttt{listen} che dice al -kernel di accettare connessioni per il socket specificato, la funzione indica -inoltre, con il secondo parametro, il numero massimo di connessioni che il -kernel accetterà di mettere in coda per il suddetto socket. - -Questa ultima chiamata completa la preparazione del socket per l'ascolto (che -viene chiamato anche \textit{listening descriptor}) a questo punto il processo -è mandato in sleep (\texttt{\small 44--47}) con la successiva chiamata alla -funzione \texttt{accept}, fin quando non arriva e viene accettata una -connessione da un client. - -Quando questo avviene \texttt{accept} ritorna un secondo descrittore di -socket, che viene chiamato \textit{connected descriptor} che è quello che -viene usato dalla successiva chiamata alla \texttt{write} per scrivere la -risposta al client, una volta che si è opportunamente (\texttt{\small 48--49}) -costruita la stringa con la data da trasmettere. Completata la trasmissione il -nuovo socket viene chiuso (\texttt{\small 54}). -Il tutto è inserito in un loop infinito (\texttt{\small 42--55}) in modo da -poter ripetere l'invio della data ad una successiva connessione. - -È importante notare che questo server è estremamente elementare, infatti a -parte il fatto di essere dipendente da IPv4, esso è in grado di servire solo -un client alla volta, è cioè un \textsl{server iterativo}, inoltre esso è -scritto per essere lanciato da linea di comando, se lo si volesse utilizzare -come demone di sistema (che è in esecuzione anche quando non c'è nessuna shell -attiva e il terminale da cui lo si è lanciato è stato sconnesso), -occorrerebbero delle opportune modifiche. \section{I protocolli di rete} \label{sec:net_protocols} -Visto un primo esempio di programmazione, passiamo ora ad una introduzione più -dettagliata del funzionamento delle reti e dei relativi protocolli. - Parlando di reti di computer si parla in genere di un insieme molto vasto ed eterogeneo di mezzi di comunicazione che vanno dal cavo telefonico, alla fibra ottica, alle comunicazioni via satellite; per rendere possibile la @@ -553,7 +302,7 @@ I vari protocolli mostrati in figura sono i seguenti: se ICMP può venire usato direttamente da alcuni programmi come \texttt{ping}. A volte ci si riferisce ad esso come ICPMv4 per distinguerlo da ICMPv6. -\item \textsl{ICMP} \textit{Internet Group Management Protocol}. É un +\item \textsl{IGMP} \textit{Internet Group Management Protocol}. É un protocollo usato per il \textit{multicasting} (vedi \secref{sec:xxx_multicast}), che è opzionale in IPv4. \item \textsl{ARP} \textit{Address Resolution Protocol}. È il protocollo che @@ -838,7 +587,7 @@ conoscere il \textit{path MTU}. Infine TCP definisce una \textit{maximum segment size} MSS che annuncia -all'altro capo la dimensione massima del segmento di dati +all'altro capo la dimensione massima del segmento di dati. \subsection{Il passaggio dei dati in TCP} diff --git a/process.tex b/process.tex index dafdef8..776a025 100644 --- a/process.tex +++ b/process.tex @@ -9,7 +9,7 @@ richiedere servizi al sistema, su cosa deve fare quando ha finito la sua esecuzione. In genere un programma viene eseguito quando un processo lo fa partire -eseguendo una funzione della famiglia \texttt{exec}; torneremo su questo e +eseguendo una funzione della famiglia \func{exec}; torneremo su questo e sulla la creazione e gestione dei processi nel prossimo capitolo, in questo affronteremo l'avvio e il funzionamento di un singolo processo partendo dal punto di vista del programma posto in esecuzione. @@ -32,25 +32,25 @@ posto in esecuzione esso apparir discorso dei \textit{thread} comunque in Linux necessita di una trattazione a parte per la peculiarità dell'implementazione). -\section{La funzione \texttt{main}} +\subsection{La funzione \func{main}} \label{sec:proc_main} Quando un programma viene lanciato il kernel esegue una opportuna routine di -avvio, usando il programma \texttt{ld-linux.so}, è questo programma che prima +avvio, usando il programma \cmd{ld-linux.so}, è questo programma che prima carica le librerie condivise che servono al programma, effettua il link dinamico del codice e poi alla fine lo esegue. Infatti, a meno di non aver specificato il flag \texttt{-static} durante la compilazione, tutti i programmi in Linux sono incompleti e necessitano di essere linkati alle librerie condivise quando vengono avviati. La procedura è controllata da -alcune variabili di ambiente e dal contenuto di \texttt{/etc/ld.so.conf}, i -dettagli sono riportati nella man page di \texttt{ld.so}. +alcune variabili di ambiente e dal contenuto di \file{/etc/ld.so.conf}, i +dettagli sono riportati nella man page di \cmd{ld.so}. -Il sistema fa partire qualunque programma chiamando la funzione \texttt{main}; +Il sistema fa partire qualunque programma chiamando la funzione \func{main}; sta al programmatore chiamare così la funzione principale del programma da cui si suppone iniziale l'esecuzione; in ogni caso senza questa funzione lo stesso linker darebbe luogo ad errori. -Lo standard ISO C specifica che la funzione \texttt{main} può non avere +Lo standard ISO C specifica che la funzione \func{main} può non avere argomenti o prendere due argomenti che rappresentano gli argomenti passati da linea di comando, in sostanza un prototipo che va sempre bene è il seguente: \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} @@ -58,7 +58,7 @@ linea di comando, in sostanza un prototipo che va sempre bene \end{lstlisting} In realtà nei sistemi unix esiste un'altro modo per definire la funzione -\texttt{main}, che prevede la presenza di un terzo parametro, \texttt{char +\func{main}, che prevede la presenza di un terzo parametro, \var{char *envp[]}, che fornisce l'\textsl{ambiente} (vedi \secref{sec:proc_environ}) del programma; questa forma però non è prevista dallo standard POSIX.1 per cui se si vogliono scrivere programmi portabili è meglio evitarla. @@ -67,20 +67,20 @@ se si vogliono scrivere programmi portabili \subsection{Come chiudere un programma} \label{sec:proc_termination} -La via normale per la quale un programma finisce è quando la funzione main -ritorna, una modalità equivalente di conclusione è quella di chiamare -direttamente la funzione \texttt{exit} (che viene comunque chiamata dalla -routine di avvio del programma quando la funzione main ritorna). Una forma -alternativa è quella di chiamare direttamente la system call \texttt{\_exit} -che passa il controllo direttamente al kernel. +La via normale per la quale un programma finisce è quando la funzione +\func{main} ritorna, una modalità equivalente di conclusione è quella di +chiamare direttamente la funzione \func{exit} (che viene comunque chiamata +dalla routine di avvio del programma quando la funzione \func{main} ritorna). +Una forma alternativa è quella di chiamare direttamente la system call +\func{\_exit} che passa il controllo direttamente al kernel. Oltre alla conclusione ``normale'' esiste anche la possibilità di una conclusione ``anomala'' del programma a causa di segnali o della chiamata alla -funzione \texttt{abort} (che comunque genera un segnale che termina il +funzione \func{abort} (che comunque genera un segnale che termina il programma); torneremo su questo in \secref{sec:sig_prog_error}. Il valore di ritorno della funzione main, o quello usato nelle chiamate ad -\texttt{exit} e \texttt{\_exit}, viene chiamato \textit{exit status} e passato +\func{exit} e \func{\_exit}, viene chiamato \textit{exit status} e passato al processo padre che aveva lanciato il programma (in genere la shell). In generale si usa questo valore per fornire un'informazione generica sulla riuscita o il fallimento del programma; l'informazione è necessariamente @@ -88,11 +88,11 @@ generica, ed il valore deve essere compreso fra 0 e 255. In generale si usa la convenzione di restituire 0 in caso di successo e 1 in caso di fallimento, i programmi che effettuano dei confronti (come -\texttt{diff}) usano invece una notazione leggermente diversa, usando 0 per +\cmd{diff}) usano invece una notazione leggermente diversa, usando 0 per indicare la corrispondenza, 1 per indicare la non corrispondenza e 2 per indicare l'incapacità di effettuare il confronto. È opportuno adottare una di queste convenzioni a seconda dei casi. Si tenga presente che se si raggiunge -la fine della funzione \texttt{main} senza ritornare esplicitamente si ha un +la fine della funzione \func{main} senza ritornare esplicitamente si ha un valore di uscita indefinito, è pertanto consigliabile di concludere sempre in maniera esplicita detta funzione. @@ -102,13 +102,13 @@ programma in un sottoprocesso. Bench universalmente seguita è una buona idea tenerne conto. Si tenga presente inoltre che non è una buona idea usare il valore dell'errore -restituito dalla variabile \texttt{errno} come stato di uscita, in generale +restituito dalla variabile \var{errno} come stato di uscita, in generale una shell non si cura di tutto questo e comunque il valore dello stato di uscita è sempre troncato ad 8 bit, per cui si potrebbe incorrere nel caso in cui l'errore 256, diventando zero, verrebbe interpretato come un successo. In -\texttt{stdlib.h} sono definite due macro \texttt{EXIT\_SUCCESS} e -\texttt{EXIT\_FAILURE}, che in Linux sono poste rispettivamente ai valori 0 e -1 (di tipo \texttt{int}), seguendo lo standard POSIX. +\file{stdlib.h} sono definite due macro \macro{EXIT\_SUCCESS} e +\macro{EXIT\_FAILURE}, che in Linux sono poste rispettivamente ai valori 0 e +1 (di tipo \type{int}), seguendo lo standard POSIX. Infine occorre distinguere fra lo stato di uscita di un programma (l'\textit{exit status}) e lo stato di conclusione di un processo (il @@ -119,51 +119,51 @@ programma per processo (vedi \secref{sec:proc_xxx}). -\subsection{Le funzioni \texttt{exit} e \texttt{\_exit}} +\subsection{Le funzioni \func{exit} e \func{\_exit}} \label{sec:proc_exit} Come accennato funzioni per l'uscita ``normale'' da un programma sono due, la -prima è la funzione \texttt{exit} che è definita dallo standard ANSI C; il +prima è la funzione \func{exit} che è definita dallo standard ANSI C; il prototipo della funzione è il seguente: \begin{prototype}{stdlib.h}{void exit(int status)} Causa la conclusione ordinaria del programma restituendo il valore - \texttt{status} al processo padre. + \var{status} al processo padre. La funzione non ritorna. Il processo viene terminato \end{prototype} -La funzione \texttt{exit} è pensata per una conclusione pulita di un programma +La funzione \func{exit} è pensata per una conclusione pulita di un programma che usa le librerie standard del C; essa esegue tutte le funzioni che sono -state registrate con \texttt{atexit} e \texttt{on\_exit} (vedi +state registrate con \func{atexit} e \func{on\_exit} (vedi \secref{sec:proc_atexit}), e chiude tutti gli stream di I/O effettuando il -salvataggio dei dati sospesi (chiamando \texttt{fclose}, vedi +salvataggio dei dati sospesi (chiamando \func{fclose}, vedi \secref{sec:file_fclose}), infine ripassa il controllo al kernel chiamando -\texttt{\_exit} e passando il valore \texttt{status} come stato di uscita. +\func{\_exit} e passando il valore \var{status} come stato di uscita. -La system call \texttt{\_exit} restituisce direttamente il controllo al +La system call \func{\_exit} restituisce direttamente il controllo al kernel, concludendo immediatamente il processo, le eventuali funzioni -registrate con \texttt{atexit} e \texttt{on\_exit} non vengono eseguite. Il +registrate con \func{atexit} e \func{on\_exit} non vengono eseguite. Il prototipo della funzione è il seguente: \begin{prototype}{unistd.h}{void \_exit(int status)} Causa la conclusione immediata del programma restituendo il valore - \texttt{status} al processo padre. + \var{status} al processo padre. La funzione non ritorna. Il processo viene terminato. \end{prototype} La funzione chiude tutti i file descriptor appartenenti al processo (sui tenga presente che questo non comporta il salvataggio dei dati bufferizzati degli -stream), fa si che ogni figlio del processo sia ereditato da \texttt{init} -(vedi \secref{cha:process_handling}), manda un segnale \texttt{SIGCHLD} al +stream), fa si che ogni figlio del processo sia ereditato da \cmd{init} +(vedi \secref{cha:process_handling}), manda un segnale \macro{SIGCHLD} al processo padre (vedi \ref{sec:sig_job_control}) ed infine ritorna lo stato di -uscita specificato in \texttt{status} che può essere raccolto usando la -funzione \texttt{wait} (vedi \secref{sec:proc_wait}). +uscita specificato in \var{status} che può essere raccolto usando la +funzione \func{wait} (vedi \secref{sec:proc_wait}). -\subsection{Le funzioni \texttt{atexit} e \texttt{on\_exit}} +\subsection{Le funzioni \func{atexit} e \func{on\_exit}} \label{sec:proc_atexit} -Come accennato l'uso di \texttt{exit} al posto della \texttt{\_exit} è fatto +Come accennato l'uso di \func{exit} al posto della \func{\_exit} è fatto principalmente per permettere una uscita pulita dalle funzioni delle librerie standard del C (in particolare per quel che riguarda la chiusura degli stream). @@ -177,10 +177,10 @@ di una funzione che effettui tali operazioni all'uscita dal programma. A questo scopo lo standard ANSI C prevede la possibilità di registrare un certo numero funzioni che verranno eseguite all'uscita dal programma (sia per -la chiamata ad \textit{exit} che per il ritorno di \texttt{main}). La prima +la chiamata ad \func{exit} che per il ritorno di \func{main}). La prima funzione che si può utilizzare a tal fine è: \begin{prototype}{stdlib.h}{void atexit(void (*function)(void))} - Registra la funzione \texttt{function} per essere chiamata all'uscita dal + Registra la funzione \var{function} per essere chiamata all'uscita dal programma. La funzione restituisce 0 in caso di successo e -1 in caso di fallimento, @@ -189,28 +189,28 @@ funzione che si pu La funzione richiede come argomento l'indirizzo della opportuna da chiamare all'uscita che non deve prendere argomenti e non deve ritornare niente. Una -estensione di \texttt{atexit} è la funzione \texttt{on\_exit} (che la glibc +estensione di \func{atexit} è la funzione \func{on\_exit} (che la glibc include per compatibilità con SunOS e che non è detto sia definita su altri sistemi), il cui prototipo è: \begin{prototype}{stdlib.h} {void on\_exit(void (*function)(int status, void *arg), void *arg)} - Registra la funzione \texttt{function} per essere chiamata all'uscita dal + Registra la funzione \var{function} per essere chiamata all'uscita dal programma. Tutte le funzioni registrate vengono chiamate in ordine inverso rispetto a quello di registrazione. La funzione restituisce 0 in caso di successo e -1 in caso di fallimento, - \texttt{errno} non viene settata. + \var{errno} non viene settata. \end{prototype} In questo caso la funzione da chiamare prende due parametri, il primo dei quali sarà inizializzato allo stato di uscita con cui è stata chiamata -\texttt{exit} ed il secondo al puntatore generico specificato come secondo -argomento nella chiamata di \texttt{on\_exit}. +\func{exit} ed il secondo al puntatore generico specificato come secondo +argomento nella chiamata di \func{on\_exit}. Nella sequenza di chiusura tutte le funzioni registrate verranno chiamate in ordine inverso rispetto a quello di registrazione (ed una stessa funzione registrata più volte sarà chiamata più volte); poi verranno chiusi tutti gli -stream aperti, infine verrà chiamata \texttt{\_exit}. +stream aperti, infine verrà chiamata \func{\_exit}. \subsection{Conclusioni} @@ -218,14 +218,14 @@ stream aperti, infine verr Data l'importanza dell'argomento è opportuno sottolineare ancora una volta che in un sistema unix l'unico modo in cui un programma può essere eseguito dal -kernel è attraverso la chiamata alla system call \texttt{execve} (in genere -attraverso una delle funzioni \texttt{exec} che vedremo in +kernel è attraverso la chiamata alla system call \func{execve} (in genere +attraverso una delle funzioni \func{exec} che vedremo in \secref{sec:proc_exec}). Allo stesso modo l'unico modo in cui un programma può concludere volontariamente la sua esecuzione è attraverso una chiamata alla system call -\texttt{\_exit} sia esplicitamente o che in maniera indiretta attraverso l'uso -di \texttt{exit} o il ritorno della funzione \texttt{main}. +\func{\_exit} sia esplicitamente o che in maniera indiretta attraverso l'uso +di \func{exit} o il ritorno della funzione \func{main}. Lo schema delle modalità con cui si avvia e conclude normalmente un programma è riportato in \nfig. @@ -279,7 +279,7 @@ spazio disco riservato alla swap, o i file che contengono il codice). Lo stesso pezzo di memoria reale (o di spazio disco) può fare da supporto a diverse pagine di memoria virtuale appartenenti a processi diversi (come accade in genere per le pagine che contengono il codice delle librerie -condivise). Ad esempio il codice della funzione \texttt{printf} starà su una +condivise). Ad esempio il codice della funzione \func{printf} starà su una sola pagina di memoria reale che farà da supporto a tutte le pagine di memoria virtuale di tutti i processi hanno detta funzione nel loro codice. @@ -322,7 +322,7 @@ commette quando si chiamato un \textit{segmentation fault}. Se si tenta cioè di leggere o scrivere da un indirizzo per il quale non esiste una associazione della pagina virtuale il kernel risponde al relativo \textit{page fault}, mandando un -segnale \texttt{SIGSEGV} al processo, che normalmente ne causa la terminazione +segnale \macro{SIGSEGV} al processo, che normalmente ne causa la terminazione immediata. È pertanto importante capire come viene strutturata la memoria virtuale di un @@ -337,7 +337,7 @@ programma C viene suddiviso nei seguenti segmenti: utilizzarlo, e viene marcato in sola lettura per evitare sovrascritture accidentali (o maliziose) che ne modifichino le istruzioni. - Viene allocato da \texttt{exec} all'avvio del programma e resta invariato + Viene allocato da \func{exec} all'avvio del programma e resta invariato per tutto il tempo dell'esecuzione. \item Il segmento dei dati (\textit{data segment}). Contiene le variabili @@ -351,7 +351,7 @@ programma C viene suddiviso nei seguenti segmenti: double pi = 3.14; \end{lstlisting} questo valore sarà immagazzinato in questo segmento. La memoria di questo - segmento viene preallocato dalla \texttt{exec} e inizializzata ai valori + segmento viene preallocato dalla \func{exec} e inizializzata ai valori specificati. La seconda parte è il segmento dei dati non inizializzati, che contiene le @@ -362,7 +362,7 @@ programma C viene suddiviso nei seguenti segmenti: \end{lstlisting} questo valore sarà immagazzinato in questo segmento. Anch'esso viene allocato all'avvio, e tutte le variabili vengono inizializzate a - zero (ed i puntatori a \texttt{NULL}). + zero (ed i puntatori a \macro{NULL}). Storicamente questo segmento viene chiamato BBS (da \textit{block started by symbol}. La sua dimensione è fissa. @@ -394,7 +394,7 @@ programma C viene suddiviso nei seguenti segmenti: \end{figure} Una disposizione tipica di questi segmenti è riportata in \nfig. Usando il -comando \texttt{size} su un programma se ne può stampare le dimensioni dei +comando \cmd{size} su un programma se ne può stampare le dimensioni dei segmenti di testo e di dati (inizializzati e BSS); il BSS però non è mai salvato sul file, in quanto viene inizializzato a zero al caricamento del programma. @@ -407,7 +407,7 @@ Il C supporta due tipi di allocazione della memoria, l'allocazione statica quella in cui vanno le variabili globali e le variabili statiche (e viene effettuata nel segmento dei dati), lo spazio per queste variabili viene allocati all'avvio del programma (come parte delle operazioni svolte da -\texttt{exec}) e non viene liberato fino alla sua conclusione. +\func{exec}) e non viene liberato fino alla sua conclusione. L'allocazione automatica è quella che avviene per le cosiddette variabili automatiche, cioè gli argomenti delle funzioni o le variabili locali. Lo @@ -425,12 +425,12 @@ cio possano essere modificate durante l'esecuzione del programma; però le librerie del C forniscono una serie opportuna di funzioni per permettere l'allocazione dinamica di spazio in memoria (in genere nello heap, usando la system call -\texttt{sbrk}), solo che a questo punto detto spazio sarà accessibile solo in +\func{sbrk}), solo che a questo punto detto spazio sarà accessibile solo in maniera indiretta attraverso dei puntatori. -\subsection{Le funzioni \texttt{malloc}, \texttt{calloc}, \texttt{realloc} e - \texttt{free}} +\subsection{Le funzioni \func{malloc}, \func{calloc}, \func{realloc} e + \func{free}} \label{sec:proc_mem_malloc} Le funzioni previste dallo standard ANSI C per la gestione della memoria sono @@ -438,26 +438,26 @@ quattro, i prototipi sono i seguenti: \begin{functions} \headdecl{stdlib.h} \funcdecl{void *calloc(size\_t size)} - Alloca \texttt{size} bytes nello heap. La memoria viene inizializzata a 0. + Alloca \var{size} bytes nello heap. La memoria viene inizializzata a 0. La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \texttt{NULL} in caso di fallimento, nel qual caso - \texttt{errno} viene settata a \texttt{ENOMEM}. + di successo e \macro{NULL} in caso di fallimento, nel qual caso + \var{errno} viene settata a \macro{ENOMEM}. \funcdecl{void *malloc(size\_t size)} - Alloca \texttt{size} bytes nello heap. La memoria non viene inizializzata. + Alloca \var{size} bytes nello heap. La memoria non viene inizializzata. La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \texttt{NULL} in caso di fallimento, nel qual caso - \texttt{errno} viene settata a \texttt{ENOMEM}. + di successo e \macro{NULL} in caso di fallimento, nel qual caso + \var{errno} viene settata a \macro{ENOMEM}. \funcdecl{void *realloc(void *ptr, size\_t size)} - Cambia la dimensione del blocco allocato all'indirizzo \texttt{ptr} - portandola a \texttt{size}. + Cambia la dimensione del blocco allocato all'indirizzo \var{ptr} + portandola a \var{size}. La funzione restituisce il puntatore alla zona di memoria allocata in caso - di successo e \texttt{NULL} in caso di fallimento, nel qual caso - \texttt{errno} viene settata a \texttt{ENOMEM}. + di successo e \macro{NULL} in caso di fallimento, nel qual caso + \var{errno} viene settata a \macro{ENOMEM}. \funcdecl{void free(void *ptr)} - Disalloca lo spazio di memoria puntato da \texttt{ptr}. + Disalloca lo spazio di memoria puntato da \var{ptr}. La funzione non ritorna nulla. \end{functions} @@ -466,29 +466,29 @@ sempre correttamente allineato per tutti i tipi di dati; ad esempio sulle macchine a 32 bit in genere è allineato a multipli di 4 byte e sulle macchine a 64 bit a multipli di 8 byte. -In genere su usano le funzioni \texttt{malloc} e \texttt{calloc} per allocare +In genere su usano le funzioni \func{malloc} e \func{calloc} per allocare dinamicamente la memoria necessaria al programma, siccome i puntatori ritornati sono di tipo generico non è necessario effettuare un cast per assegnarli a puntatori al tipo di variabile per la quale si effettua la allocazione. La memoria allocata dinamicamente deve essere esplicitamente rilasciata usando -\texttt{free}\footnote{le glibc provvedono anche una funzione \texttt{cfree} +\func{free}\footnote{le glibc provvedono anche una funzione \func{cfree} defininita per compatibilità con SunOS, che è deprecata} una volta che non sia più necessaria. Questa funzione vuole come parametro un puntatore restituito da una precedente chiamata a una qualunque delle funzioni di allocazione e che non sia già stato liberato da un'altra chiamata a -\texttt{free}, in caso contrario il comportamento della funzione è indefinito. +\func{free}, in caso contrario il comportamento della funzione è indefinito. -La funzione \texttt{realloc} si usa invece per cambiare (in genere aumentare) +La funzione \func{realloc} si usa invece per cambiare (in genere aumentare) la dimensione di un'area di memoria precedentemente allocata, la funzione vuole in ingresso il puntatore restituito dalla precedente chiamata ad una -\texttt{malloc} (se è passato un valore \texttt{NULL} allora la funzione si -comporta come \texttt{malloc}\footnote{questo è vero per linux e +\func{malloc} (se è passato un valore \macro{NULL} allora la funzione si +comporta come \func{malloc}\footnote{questo è vero per linux e l'implementazione secondo lo standard ANSI C, ma non è vero per alcune vecchie implementazioni, inoltre alcune versioni delle librerie del C - consentivano di usare \texttt{realloc} anche per un puntatore liberato con - \texttt{free} purché non ci fossero state altre chiamate a funzioni di + consentivano di usare \func{realloc} anche per un puntatore liberato con + \func{free} purché non ci fossero state altre chiamate a funzioni di allocazione, questa funzionalità è totalmente deprecata e non è consentita sotto linux}), ad esempio quando si deve far crescere la dimensione di un vettore; in questo caso se è disponibile dello spazio adiacente al precedente @@ -503,20 +503,26 @@ essere altri puntatori che puntino all'interno di un'area che si vuole ridimensionare. Uno degli errori più comuni (specie se si ha a che fare con array di -puntatori) è infatti quello di chiamare \texttt{free} più di una volta sullo +puntatori) è infatti quello di chiamare \func{free} più di una volta sullo stesso puntatore; per evitare questo problema una soluzione di ripiego è -quella di assegnare sempre a \texttt{NULL} ogni puntatore liberato con -\texttt{free}, dato che, quando il parametro è un puntatore nullo, -\texttt{free} non esegue nessuna operazione. +quella di assegnare sempre a \macro{NULL} ogni puntatore liberato con +\func{free}, dato che, quando il parametro è un puntatore nullo, +\func{free} non esegue nessuna operazione. Linux e le glibc hanno una implementazione delle routine di allocazione che è controllabile dall'utente attraverso alcune variabili di ambiente, in particolare diventa possibile tracciare questo tipo di errori usando la -variabile \texttt{MALLOC\_CHECK\_} che quando viene settata mette in uso una +variabile \macro{MALLOC\_CHECK\_} che quando viene settata mette in uso una versione meno efficiente delle funzioni, che però è più tollerante nei -confronti di piccoli errori come quello di chiamate doppie a \texttt{free}; in -particolare se la variabile è posta a zero gli errori vengono ignorati, se è -posta ad 1 viene stampato un avviso sullo standard error e se +confronti di piccoli errori come quello di chiamate doppie a \func{free}; in +particolare: +\begin{itemize} +\item se la variabile è posta a zero gli errori vengono ignorati. +\item se è posta ad 1 viene stampato un avviso sullo \textit{standard error} + (vedi \secref{sec:file_stdfiles}). +\item se è posta a 2 viene chiamata \func{abort}, che in genere causa + l'immediata conclusione del programma. +\end{itemize} Il problema più comune e più difficile da tracciare che si incontra con l'allocazione della memoria è però quando la memoria non più utilizzata non @@ -698,7 +704,7 @@ annidate occorre usare la funzione \func{longjump}. Il passaggio dei parametri e delle variabili di ambiente dalla riga di comando al singolo programma quando viene lanciato è effettuato attraverso le -variabili \texttt{argc}, \texttt{argv} che vengono passate al programma +variabili \var{argc}, \var{argv} che vengono passate al programma come argomenti della funzione principale. \subsection{Il formato dei parametri} @@ -710,9 +716,9 @@ ciascuna delle quali viene considerata un parametro; di default per individuare le parole viene usato come separatore lo spazio (comportamento modificabile attraverso il settaggio della variabile di ambiente IFS). -Nella scansione viene costruito il vettore di puntatori \texttt{argv} inserendo +Nella scansione viene costruito il vettore di puntatori \var{argv} inserendo in successione il puntatore alla stringa costituente l'$n$-simo parametro; la -variabile \texttt{argc} viene inizializzata al numero di parametri trovati, in +variabile \var{argc} viene inizializzata al numero di parametri trovati, in questo modo il primo parametro è sempre il nome del programma (vedi \nfig). \subsection{La gestione delle opzioni} @@ -720,7 +726,7 @@ questo modo il primo parametro In generale un programma unix riceve da linea di comando sia i parametri che le opzioni, queste ultime sono standardizzate per essere riconosciute come -tali: un elemento di \texttt{argv} che inizia con \texttt{-} e che non sia un +tali: un elemento di \var{argv} che inizia con \texttt{-} e che non sia un singolo \texttt{-} o \texttt{--} viene considerato un'opzione. In in genere le opzioni sono costituite da una lettera preceduta dal meno e possono avere o no un parametro associato; un comando tipico può essere cioè qualcosa del @@ -730,20 +736,26 @@ touch -r riferimento.txt -m questofile.txt \end{verbatim} ed in questo caso le opzioni sono \texttt{m} ed \texttt{r}. -Per gestire le opzioni all'interno dei parametri passati in \texttt{argv} le -librerie standard del C forniscono la funzione \texttt{getopt} (accessibile -includendo \texttt{unistd.h}), che ha il prototipo: -\begin{verbatim} -int getopt(int argc, char * const argv[], const char * optstring); -\end{verbatim} +Per gestire le opzioni all'interno dei parametri passati in \func{argv} le +librerie standard del C forniscono la funzione \func{getopt} che ha il +prototipo: +\begin{prototype}{unistd.h} +{int getopt(int argc, char * const argv[], const char * optstring)} +La funzione esegue il parsing degli argomenti passati da linea di comando +riconoscendo le possibili opzioni segnalate con \var{optstring}. + +Ritorna il carattere che segue l'opzione, \cmd{:} se manca un paramatro +all'opzione, \cmd{?} se l'opzione è sconosciuta, e -1 se non esistono altre +opzioni. +\end{prototype} -Questa funzione prende come argomenti le due variabili \texttt{argc} e -\texttt{argv} ed una stringa che indica quali sono le opzioni valide; la +Questa funzione prende come argomenti le due variabili \var{argc} e +\var{argv} ed una stringa che indica quali sono le opzioni valide; la funzione effettua la scansione della lista dei parametri ricercando ogni -stringa che comincia con \texttt{-} e ritorna ogni volta che trova una opzione +stringa che comincia con \cmd{-} e ritorna ogni volta che trova una opzione valida. -La stringa \texttt{optstring} indica quali sono le opzioni riconosciute ed è +La stringa \var{optstring} indica quali sono le opzioni riconosciute ed è costituita da tutti i caratteri usati per identificare le singole opzioni, se l'opzione ha un parametro al carattere deve essere fatto seguire un segno di due punti \texttt{:} nel caso appena accennato ad esempio la stringa di @@ -755,7 +767,8 @@ all'interno di un ciclo di while fintanto che essa non ritorna il valore un'opzione non dichiarata in \texttt{optstring} viene ritornato un \texttt{?} mentre se l'opzione non è seguita da un parametro viene ritornato un \texttt{:} infine se viene incontrato il valore \texttt{--} la scansione viene -considerata conclusa. +considerata conclusa, anche se vi sono altri parametri che cominciano con +\texttt{-}. Quando la funzione trova un'opzione essa ritorna il valore numerico del carattere, in questo modo si possono prendere le azioni relative usando un @@ -770,8 +783,7 @@ case; la funzione inizializza inoltre alcune variabili globali: \item \texttt{int optopt} contiene il carattere dell'opzione non riconosciuta. \end{itemize} -In \nfig\ è mostrato un programma di esempio, - +In \nfig\ è mostrato un programma di esempio: \begin{figure}[htbp] \footnotesize \begin{lstlisting}{} @@ -823,8 +835,9 @@ In \nfig\ Un'estensione di questo schema è costituito dalle cosiddette \textit{long-options} espresse nella forma \texttt{--option=parameter}, anche la gestione di queste ultime è stata standardizzata attraverso l'uso di una -versione estesa di \texttt{getopt}. +versione estesa di \func{getopt}. +(NdA: da finire). \subsection{Le variabili di ambiente} \label{sec:proc_environ} @@ -878,4 +891,3 @@ comuni), come riportato in \ntab. GNU/Linux le supporta tutte e ne definisce anche altre per una lista parziale si può controllare \cmd{man environ} - diff --git a/prochand.tex b/prochand.tex index 1dddba1..b2f7b9a 100644 --- a/prochand.tex +++ b/prochand.tex @@ -70,9 +70,9 @@ processo \cmd{init} che I processi vengono creati dalla funzione \func{fork}; in molti unix questa è una system call, Linux però usa un'altra nomenclatura, e la funzione fork è -basata a sua volta sulla system call \func{clone}, che viene usata anche per -generare i \textit{thread}. Il processo figlio creato dalla \func{fork} è una -copia identica del processo processo padre, ma ha nuovo \acr{pid} e viene +basata a sua volta sulla system call \func{\_\_clone}, che viene usata anche +per generare i \textit{thread}. Il processo figlio creato dalla \func{fork} è +una copia identica del processo processo padre, ma ha nuovo \acr{pid} e viene eseguito in maniera indipendente (le differenze fra padre e figlio sono affrontate in dettaglio in \secref{sec:proc_fork}). @@ -157,6 +157,12 @@ cui diventa possibile garantire l'unicit generare un pathname univoco, che non potrà essere replicato da un'altro processo che usi la stessa funzione. +Tutti i processi figli dello stesso processo padre sono detti +\textit{sibling}, questa è un'altra delle relazioni usate nel controllo di +sessione, in cui si raggruppano tutti i processi creati su uno stesso +terminale una volta che si è effettuato il login. Torneremo su questo +argomento in \secref{cap:terminal}, dove esamineremo tutti gli altri +identificativi associati ad un processo. \subsection{Utente e gruppo di un processo} \label{sec:proc_user_group} @@ -164,18 +170,14 @@ processo che usi la stessa funzione. Come accennato in \secref{sec:intro_multiuser} ad ogni utente ed gruppo sono associati due identificatori univoci, lo \acr{uid} e il \acr{gid} che li contraddistinguono nei confonti del kernel. Questi identificatori stanno alla -base del sistema di permessi e protezioni di un sistema unix. - - a ciascun -processo venfon - - -Come accennato in \secref{sec:file_perm_overview} a processo viene associato -un certo numero di identificatori (riportati in \ntab) che vengono usati sia -per il controllo di accesso ai file che per la gestione dei privilegi -associati ai processi stessi. - - +base del sistema di permessi e protezioni di un sistema unix, e vengono usati +anche nella gestione dei privilegi di accesso dei processi. + +Abbiamo già accennato in \secref{sec:file_perm_overview} che ad ogni processo +associato un certo numero di identificatori (riportati \ntab) che fanno +riferimento all'utente che ha lanciato il processo (attraverso i valori di +\acr{uid} e \acr{gid}), e vengono usati sia per il controllo di accesso ai +file che per la gestione dei privilegi associati ai processi stessi. \begin{table}[htb] \centering \begin{tabular}[c]{|c|l|l|} @@ -222,23 +224,25 @@ della funzione \headdecl{unistd.h} \funcdecl{pid\_t fork(void)} - - Le funzioni restituiscono zero in caso di successo e -1 per un errore, in - caso di errore \texttt{errno} può assumere i valori: + + Restituisce zero al padre e il \acr{pid} al figlio in caso di successo, + ritorna -1 al padre (senza creare il figlio) in caso di errore; + \texttt{errno} può assumere i valori: \begin{errlist} - \item \macro{EAGAIN} - \item \macro{ENOMEM} + \item \macro{EAGAIN} non ci sono risorse sufficienti per creare un'altro + processo (per allocare la tabella delle pagine e le strutture del task) o + si è esaurito il numero di processi disponibili. + \item \macro{ENOMEM} non è stato possibile allocare la memoria per le + strutture necessarie al kernel per creare il nuovo processo. \end{errlist} \end{functions} - -Dopo l'esecuzione di una fork sia il processo padre che il processo figlio -continuano ad essere eseguiti normalmente, ed il processo figlio esegue +Dopo l'esecuzione di una \func{fork} sia il processo padre che il processo +figlio continuano ad essere eseguiti normalmente, ed il processo figlio esegue esattamente lo stesso codice del padre. La sola differenza è che nel processo -padre il valore di ritorno della funzione fork è il pid del processo figlio, -mentre nel figlio è zero; in questo modo il programma può identificare se -viene eseguito dal padre o dal figlio. - +padre il valore di ritorno della funzione fork è il \acr{pid} del processo +figlio, mentre nel figlio è zero; in questo modo il programma può identificare +se viene eseguito dal padre o dal figlio. @@ -255,37 +259,6 @@ viene eseguito dal padre o dal figlio. \label{sec:proc_perms} - - - -Come accennato in \secref{sec:file_perm_overview} ciascun processo porta con -se un gruppo di identificatori (riportati in \ntab) utilizzati per i controllo -degli accessi, - - -\begin{table}[htb] - \centering - \begin{tabular}[c]{|c|l|l|} - \hline - Sigla & Significato & Utilizzo \\ - \hline - \hline - \acr{ruid} & \textit{real user id} & indica l'utente reale \\ - \acr{rgid} & \textit{real group id} & indica il gruppo reale \\ - \acr{euid} & \textit{effective user id} & indica l'utente reale \\ - \acr{egid} & \textit{effective group id} & indica il gruppo reale \\ - & \textit{supplementaru group id} & indica il gruppo \\ - \acr{suid} & \textit{saved user id} & indica l'utente reale \\ - \acr{sgid} & \textit{daved group id} & indica il gruppo reale \\ - \acr{fsuid} & \textit{real user id} & indica l'utente reale \\ - \acr{fsgid} & \textit{real group id} & indica il gruppo reale \\ - \hline - \end{tabular} - \caption{Identificatori di utente e gruppo associati a ciascun processo.} - \label{tab:proc_uid_gid} -\end{table} - - \subsection{Le funzioni \texttt{setuid} e \texttt{setgid}} \label{sec:proc_setuid} diff --git a/signal.tex b/signal.tex index b362ad8..cc8e1af 100644 --- a/signal.tex +++ b/signal.tex @@ -346,7 +346,11 @@ stato dello stack e delle variabili al momento della ricezione del segnale. \begin{table}[htb] \centering - \begin{tabular}[c]{c p{10cm}} + \begin{tabular}[c]{|c|p{8cm}|} + \hline + Sigla & Significato \\ + \hline + \hline A & L'azione di default è terminare il processo. \\ B & L'azione di default è ignorare il segnale. \\ C & L'azione di default è terminare il processo e scrivere un \textit{core @@ -354,13 +358,15 @@ stato dello stack e delle variabili al momento della ricezione del segnale. D & L'azione di default è fermare il processo. \\ E & Il segnale non può essere intercettato. \\ F & Il segnale non può essere ignorato.\\ + \hline \end{tabular} \caption{Legenda delle caratteristiche dei segnali riportate in \tabref{tab:sig_signal_list}. } \label{tab:sig_action_leg} \end{table} -la descrizione dettagliata del significato dei vari segnali, raggruppati per -tipologia, è a seguire. + +La descrizione dettagliata del significato dei vari segnali, raggruppati per +tipologia, verrà affrontate nel seguito. \subsection{Segnali di errore di programma} \label{sec:sig_prog_error} @@ -383,13 +389,13 @@ non ci fosse stato. L'azione di default per tutti questi segnali è causare la terminazione del processo che li ha causati. In genere oltre a questo il segnale provoca pure la registrazione su disco di un file di \textit{core dump} che viene scritto -in un file \texttt{core} nella directory corrente del processo al momento +in un file \file{core} nella directory corrente del processo al momento dell'errore, che il debugger può usare per ricostruire lo stato del programma al momento della terminazione. Questi segnali sono: \begin{description} -\item \texttt{SIGFPE} Riporta un errore aritmetico fatale. Benché il nome +\item \macro{SIGFPE} Riporta un errore aritmetico fatale. Benché il nome derivi da \textit{floating point exception} si applica a tutti gli errori aritmetici compresa la divisione per zero e l'overflow. @@ -398,7 +404,7 @@ Questi segnali sono: % standard IEEE per le operazioni in virgola mobile definisce vaire eccezioni % aritmetiche e richiede che esse siano notificate. -\item \texttt{SIGILL} Il nome deriva da \textit{illegal instruction}, +\item \macro{SIGILL} Il nome deriva da \textit{illegal instruction}, significa che il programma sta cercando di eseguire una istruzione privilegiata o inesistente, in generale del codice illegale. Poiché il compilatore del C genera del codice valido si ottiene questo segnale se il @@ -408,7 +414,7 @@ Questi segnali sono: una variabile locale, andando a corrompere lo stack. Lo stesso segnale viene generato in caso di overflow dello stack o di problemi nell'esecuzione di di un signal handler. -\item \texttt{SIGSEGV} Il nome deriva da \textit{segment violation}, e +\item \macro{SIGSEGV} Il nome deriva da \textit{segment violation}, e significa che il programma sta cercando di leggere o scrivere in una zona di memoria protetta al di fuori di quella che gli è stata riservata dal sistema. In genere è il meccanismo della protezione della memoria che si @@ -416,20 +422,20 @@ Questi segnali sono: È tipico ottenere questo segnale dereferenziando un puntatore nullo o non inizializzato leggendo al di la della fine di un vettore. -\item \texttt{SIGBUS} Il nome deriva da \textit{bus error}. Come - \texttt{SIGSEGV} questo è un segnale che viene generato di solito quando si - dereferenzia un puntatore non inzializzato, la differenza è - che\texttt{SIGSEGV} indica un accesso non permesso su un indirizzo esistente - (tipo fuori dallo heap o dallo stack), mentre \texttt{SIGBUS} indica +\item \macro{SIGBUS} Il nome deriva da \textit{bus error}. Come + \macro{SIGSEGV} questo è un segnale che viene generato di solito quando si + dereferenzia un puntatore non inzializzato, la differenza è che + \macro{SIGSEGV} indica un accesso non permesso su un indirizzo esistente + (tipo fuori dallo heap o dallo stack), mentre \macro{SIGBUS} indica l'accesso ad un indirizzo non valido, come nel caso di un puntatore non allineato. -\item \texttt{SIGABRT} Il nome deriva da \textit{abort}. Il segnale indica che +\item \macro{SIGABRT} Il nome deriva da \textit{abort}. Il segnale indica che il programma stesso ha rilevato un errore che viene riportato chiamando la - funzione \texttt{abort} che genera questo segnale. -\item \texttt{SIGTRAP} È il segnale generato da un'istruzione di breakpoint o + funzione \func{abort} che genera questo segnale. +\item \macro{SIGTRAP} È il segnale generato da un'istruzione di breakpoint o dall'attivazione del tracciamento per il processo. È usato dai programmi per il debugging e se un programma normale non dovrebbe ricevere questo segnale. -\item \texttt{SIGSYS} Sta ad indicare che si è eseguita una istruzione che +\item \macro{SIGSYS} Sta ad indicare che si è eseguita una istruzione che richiede l'esecuzione di una system call, ma si è fornito un codice sbagliato per quest'ultima. \end{description} @@ -505,13 +511,13 @@ di default segnali la scelta di default è irrilevante, in quanto il loro uso presuppone sempre la necessità di un manipolatore. Questi segnali sono: \begin{description} -\item \texttt{SIGALRM} Il nome sta per \textit{alarm}. Segnale la scadenza di +\item \macro{SIGALRM} Il nome sta per \textit{alarm}. Segnale la scadenza di un timer misurato sul tempo reale o sull'orologio di sistema. È normalmente usato dalla funzione \func{alarm}. -\item \texttt{SIGVTALRM} Il nome sta per \textit{virtual alarm}. È analogo al +\item \macro{SIGVTALRM} Il nome sta per \textit{virtual alarm}. È analogo al precedente ma segnala la scadenza di un timer sul tempo di CPU usato dal processo. -\item \texttt{SIGPROF} Il nome sta per \textit{profiling}. Indica la scadenza +\item \macro{SIGPROF} Il nome sta per \textit{profiling}. Indica la scadenza di un timer che misura sia il tempo di CPU speso direttamente dal processo che quello che il sistema ha speso per conto di quest'ultimo. In genere viene usato dai tool che servono a fare il profilo d'uso della CPU da parte @@ -528,14 +534,14 @@ generare questi segnali. L'azione di default è di essere ignorati. Questi segnali sono: \begin{description} -\item \texttt{SIGIO} Questo segnale viene inviato quando un file descriptor è +\item \macro{SIGIO} Questo segnale viene inviato quando un file descriptor è pronto per eseguire dell'input/output. In molti sistemi solo i socket e i terminali possono generare questo segnale, in Linux questo può essere usato anche per i file, posto che la \func{fcntl} abbia avuto successo. -\item \texttt{SIGURG} Questo segnale è inviato quando arrivano dei dati +\item \macro{SIGURG} Questo segnale è inviato quando arrivano dei dati urgenti o \textit{out of band} su di un socket; per maggiori dettagli al proposito si veda \secref{sec:xxx_urgent_data}. -\item \texttt{SIGPOLL} Questo segnale è equivalente a \macro{SIGIO}, è +\item \macro{SIGPOLL} Questo segnale è equivalente a \macro{SIGIO}, è definito solo per compatibilità con i sistemi System V. \end{description} @@ -590,16 +596,16 @@ resto del sistema. L'azione di default di questi segnali è di terminare il processo, questi segnali sono: \begin{description} -\item \texttt{SIGPIPE} Sta per \textit{Broken pipe}. Se si usano delle pipes o +\item \macro{SIGPIPE} Sta per \textit{Broken pipe}. Se si usano delle pipe o delle FIFO è necessario che, prima che un processo inizi a scrivere su di essa, un'altro abbia aperto la pipe in lettura (si veda \secref{sec:ipc_pipes}). Se il processo in lettura non è partito o è terminato inavvertitamente alla scrittura sulla pipe il kernel genera questo segnale. Se il segnale è bloccato, intercettato o ignorato la chiamata che lo ha causato fallisce restituendo l'errore \macro{EPIPE} -\item \texttt{SIGLOST} Sta per \textit{Resource lost}. -\item \texttt{SIGXCPU} Sta per \textit{CPU time limit exceeded}. -\item \texttt{SIGXFSZ} Sta per \textit{File size limit exceeded}. +\item \macro{SIGLOST} Sta per \textit{Resource lost}. +\item \macro{SIGXCPU} Sta per \textit{CPU time limit exceeded}. +\item \macro{SIGXFSZ} Sta per \textit{File size limit exceeded}. \end{description} @@ -609,17 +615,17 @@ segnali sono: Raccogliamo qui infine usa serie di segnali che hanno scopi differenti non classificabili in maniera omogenea. Questi segnali sono: \begin{description} -\item \texttt{SIGUSR1} e \texttt{SIGUSR2} Sono due segnali a disposizione +\item \macro{SIGUSR1} e \macro{SIGUSR2} Sono due segnali a disposizione dell'utente che li può usare per quello che vuole. Possono essere utili per implementare una comunicazione elementare fra processi diversi, o per eseguire a richiesta una operazione utilizzando un manipolatore. L'azione di - default è terminare il processo. -\item \texttt{SIGWINCH} Il nome sta per \textit{window (size) change} ed è + default è terminare il processo. +\item \macro{SIGWINCH} Il nome sta per \textit{window (size) change} ed è generato da molti sistemi (GNU/Linux compreso) quando le dimensioni (in righe e colonne) di un terminale vengono cambiate. Viene usato da alcuni programmi testuali per riformattare l'uscita su schermo quando si cambia dimensione a quest'ultimo. L'azione di default è di essere ignorato. -\item \texttt{SIGINFO} Il segnale indica una richiesta di informazioni. È +\item \macro{SIGINFO} Il segnale indica una richiesta di informazioni. È usato con il controllo di sessione, causa la stampa di informazioni da parte del processo leader del gruppo associato al terminale di controllo, gli altri processi lo ignorano. @@ -646,7 +652,7 @@ contenuto, che resta valido solo fino alla successiva chiamata di necessario copiarlo. La seconda funzione deriva da BSD ed è analoga alla funzione \func{perror} -descritta in \secref{} +descritta in \secref{sec:intro_strerror}. \section{La gestione dei segnali} diff --git a/socket.tex b/socket.tex index e20c388..e0057fd 100644 --- a/socket.tex +++ b/socket.tex @@ -1,14 +1,35 @@ \chapter{Introduzione ai socket} \label{cha:socket_intro} -Il \textit{socket} (traducibile liberamente come \textsl{manicotto}) è uno dei -principali meccanismi di comunicazione fra programmi utilizzato in ambito unix -(e non solo). Il socket costituisce in sostanza un canale di comunicazione fra -due processi su cui si possono leggere e scrivere dati analogo a quello di una -pipe ma a differenza di questa e degli altri meccanismi esaminati nel capitolo -\capref{cha:IPC} i socket non sono limitati alla comunicazione fra processi -che girano sulla stessa macchina ma possono effettuare la comunicazione anche -attraverso la rete. +In questo capitolo inizieremo a spiegare le caratteristiche principali della +principale interfaccia per la programmazione di rete, quella dei +\textit{socket}, che pur essendo nata in unix è usata ormai da tutti i sistemi +operativi. + +Dopo una breve panoramica sulle caratteristiche di questa interfaccia vedremo +come creare un socket e come collegarlo allo specifico protocollo di rete che +utilizzerà per la comunicazione. Per evitare una introduzione puramente teorica +concluderemo il capitolo con un primo esempio di applicazione. + +\section{Una panoramica} +\label{sec:sock_overview} + +Iniziamo con una descrizione essenziale di cosa sono i \textit{socket} e di +quali sono i concetti fondamentali da tenere presente quando si ha a che fare +con essi. + +\subsection{I \textit{socket}} +\label{sec:sock_socket_def} + +Il \textit{socket}\footnote{una traduzione letterale potrebbe essere + \textsl{manicotto}, ma essendo universalmente noti come socket utilizzeremo + sempre la parola inglese} è uno dei principali meccanismi di comunicazione +fra programmi utilizzato in ambito unix. Il socket costituisce in sostanza un +canale di comunicazione fra due processi su cui si possono leggere e scrivere +dati analogo a quello di una pipe ma a differenza di questa e degli altri +meccanismi esaminati nel capitolo \capref{cha:IPC} i socket non sono limitati +alla comunicazione fra processi che girano sulla stessa macchina ma possono +effettuare la comunicazione anche attraverso la rete. Quella dei socket costituisce infatti la principale API (\textit{Application Program Interface}) usata nella programmazione di rete. La loro origine @@ -25,7 +46,7 @@ solo con la suite dei protocolli TCP/IP, che sar tratteremo in maniera più estesa. -\section{Concetti base} +\subsection{Concetti base} \label{sec:sock_gen} Per capire il funzionamento dei socket occorre avere presente il funzionamento @@ -64,13 +85,21 @@ la comunicazione, ad esempio se gestire la perdita o il rimescolamento dei dati. -\section{La funzione \texttt{socket}} +\section{La creazione di un \textit{socket}} +\label{sec:sock_creation} + +Come accennato l'interfaccia dei socket è estremamente flessibile e permette +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}} \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 -intero non negativo) che come gli analoghi file descriptor di files e alle -pipes serve come riferimento al socket; in sostanza è l'indice nella tabella +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 allocate per ogni processo, (la stessa usata per i files e le pipes [NdA verificare!]). @@ -137,7 +166,9 @@ protocolli disponibili sono riportate in \ntab. \footnotesize \centering \begin{tabular}[c]{lll} - Nome & Utilizzo & Man page \\ + \hline + \textsl{Nome} & \textsl{Utilizzo} &\textsl{Man page} \\ + \hline \hline PF\_UNIX,PF\_LOCAL & Local communication & unix(7) \\ PF\_INET & IPv4 Internet protocols & ip(7) \\ @@ -149,15 +180,16 @@ protocolli disponibili sono riportate in \ntab. PF\_ATMPVC & Access to raw ATM PVCs & \\ PF\_APPLETALK & Appletalk & ddp(7) \\ PF\_PACKET & Low level packet interface & packet(7) \\ + \hline \end{tabular} \caption{Famiglie di protocolli definiti in Linux} \label{tab:net_pf_names} \end{table} Non tutte le famiglie di protocolli sono accessibili dall'utente generico, ad -esempio in generale tutti i socket di tipo \texttt{SOCK\_RAW} possono essere +esempio in generale tutti i socket di tipo \macro{SOCK\_RAW} possono essere creati solo da processi che hanno i privilegi di root (cioè effective uid -uguale a zero) o la capability \texttt{CAP\_NET\_RAW}. +uguale a zero) o la capability \macro{CAP\_NET\_RAW}. \subsection{Il tipo, o stile} @@ -168,26 +200,26 @@ comunicazione, questo infatti viene a dipendere dal protocollo che si andr utilizzare fra quelli disponibili nella famiglia scelta. Le API permettono di scegliere lo stile di comunicazione indicando il tipo di socket; Linux e le glibc mettono a disposizione i seguenti tipi di socket (che il manuale della -glibc chiama \textit{styles}) definiti come \texttt{int} in \texttt{socket.h}: +glibc chiama \textit{styles}) definiti come \type{int} in \file{socket.h}: \begin{list}{}{} -\item \texttt{SOCK\_STREAM} Provvede un canale di trasmissione dati +\item \macro{SOCK\_STREAM} Provvede un canale di trasmissione dati bidirezionale, sequenziale e affidabile. Opera su una connessione con un altro socket. I dati vengono ricevuti e trasmessi come un flusso continuo di byte (da cui il nome \textit{stream}). -\item \texttt{SOCK\_DGRAM} Viene usato per mandare pacchetti di lunghezza +\item \macro{SOCK\_DGRAM} Viene usato per mandare pacchetti di lunghezza massima fissata (\textit{datagram}) indirizzati singolarmente, senza connessione e in maniera non affidabile. È l'opposto del precedente. -\item \texttt{SOCK\_SEQPACKET} Provvede un canale di trasmissione di dati +\item \macro{SOCK\_SEQPACKET} Provvede un canale di trasmissione di dati bidirezionale, sequenziale e affidabile. Opera su una connessione con un altro socket. I dati possono solo essere trasmessi e letti per pacchetti (di dimensione massima fissata). -\item \texttt{SOCK\_RAW} Provvede l'accesso a basso livello ai protocolli di +\item \macro{SOCK\_RAW} Provvede l'accesso a basso livello ai protocolli di rete e alle varie interfacce. I normali programmi di comunicazione non devono usarlo. -\item \texttt{SOCK\_RDM} Provvede un canale di trasmissione di pacchetti +\item \macro{SOCK\_RDM} Provvede un canale di trasmissione di pacchetti affidabile ma in cui non è garantito l'ordine di arrivo dei pacchetti. -\item \texttt{SOCK\_PACKET} Obsoleto, non deve essere usato. +\item \macro{SOCK\_PACKET} Obsoleto, non deve essere usato. \end{list} Si tenga presente che non tutte le combinazioni di famiglia di protocolli e @@ -199,34 +231,35 @@ tabella che mostra le combinazioni valide \footnotesize \centering \begin{tabular}{l|c|c|c|c|c|} - \multicolumn{1}{c}{} &\multicolumn{1}{c}{\texttt{SOCK\_STREAM}}& - \multicolumn{1}{c}{\texttt{SOCK\_DGRAM}} & - \multicolumn{1}{c}{\texttt{SOCK\_RAW}} & - \multicolumn{1}{c}{\texttt{SOCK\_PACKET}}& - \multicolumn{1}{c}{\texttt{SOCK\_SEQPACKET}} \\ + \multicolumn{1}{c}{} &\multicolumn{1}{c}{\macro{SOCK\_STREAM}}& + \multicolumn{1}{c}{\macro{SOCK\_DGRAM}} & + \multicolumn{1}{c}{\macro{SOCK\_RAW}} & + \multicolumn{1}{c}{\macro{SOCK\_PACKET}}& + \multicolumn{1}{c}{\macro{SOCK\_SEQPACKET}} \\ \cline{2-6} - \texttt{PF\_UNIX} & si & si & & & \\ + \macro{PF\_UNIX} & si & si & & & \\ \cline{2-6} - \texttt{PF\_INET} & TCP & UDP & IPv4 & & \\ + \macro{PF\_INET} & TCP & UDP & IPv4 & & \\ \cline{2-6} - \texttt{PF\_INET6} & TCP & UDP & IPv6 & & \\ + \macro{PF\_INET6} & TCP & UDP & IPv6 & & \\ \cline{2-6} - \texttt{PF\_IPX} & & & & & \\ + \macro{PF\_IPX} & & & & & \\ \cline{2-6} - \texttt{PF\_NETLINK} & & si & si & & \\ + \macro{PF\_NETLINK} & & si & si & & \\ \cline{2-6} - \texttt{PF\_X25} & & & & & si \\ + \macro{PF\_X25} & & & & & si \\ \cline{2-6} - \texttt{PF\_AX25} & & & & & \\ + \macro{PF\_AX25} & & & & & \\ \cline{2-6} - \texttt{PF\_ATMPVC} & & & & & \\ + \macro{PF\_ATMPVC} & & & & & \\ \cline{2-6} - \texttt{PF\_APPLETALK} & & si & si & & \\ + \macro{PF\_APPLETALK} & & si & si & & \\ \cline{2-6} - \texttt{PF\_PACKET} & & si & si & & \\ + \macro{PF\_PACKET} & & si & si & & \\ \cline{2-6} \end{tabular} - \caption{Combinazioni valide di dominio e tipo di protocollo per la funzione \texttt{socket}.} + \caption{Combinazioni valide di dominio e tipo di protocollo per la + funzione \func{socket}.} \label{tab:sock_sock_valid_combinations} \end{table} @@ -234,6 +267,8 @@ Dove per ogni combinazione valida si parola \textsl{si} qualora non il protocollo non abbia un nome definito, mentre si sono lasciate vuote le caselle per le combinazioni non supportate. + + \section{Le strutture degli indirizzi dei socket} \label{sec:sock_sockaddr} @@ -253,6 +288,7 @@ tutte queste strutture iniziano per \texttt{sockaddr\_}, quelli propri di ciascuna famiglia vengono identificati dal suffisso finale, aggiunto al nome precedente. + \subsection{La struttura generica} \label{sec:sock_sa_gen} @@ -261,12 +297,12 @@ attraverso puntatori (cio maneggiare puntatori a strutture relative a tutti gli indirizzi possibili nelle varie famiglie di protocolli; questo pone il problema di come passare questi puntatori, il C ANSI risolve questo problema coi i puntatori generici -(i \texttt{void *}), ma l'interfaccia dei socket è antecendente alla +(i \type{void *}), ma l'interfaccia dei socket è antecendente alla definizione dello standard ANSI, e per questo nel 1982 fu scelto di definire -una struttura generica \texttt{sockaddr} per gli indirizzi dei socket mostrata +una struttura generica \type{sockaddr} per gli indirizzi dei socket mostrata in \nfig: -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} struct sockaddr { @@ -274,7 +310,7 @@ struct sockaddr { char sa_data[14]; /* address (protocol-specific) */ }; \end{lstlisting} - \caption{La struttura generica degli indirizzi dei socket \texttt{sockaddr}} + \caption{La struttura generica degli indirizzi dei socket \type{sockaddr}} \label{fig:sock_sa_gen_struct} \end{figure} @@ -286,9 +322,9 @@ occorrer I tipi di dati che compongono la struttura sono stabiliti dallo standard POSIX.1g, riassunti in \ntab\ con i rispettivi file di include in cui sono definiti; la struttura è invece definita nell'include file -\texttt{sys/socket.h} +\file{sys/socket.h} -\begin{table}[!htbp] +\begin{table}[!htb] \centering \begin{tabular}{|l|l|l|} \hline @@ -321,8 +357,8 @@ definiti; la struttura 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 libri). Questo campo non verrebbe usato direttamente dal programmatore e non è -richiesto dallo standard POSIX.1g, in Linux pertanto non sussiste. Il campo -\texttt{sa\_family\_t} era storicamente un \texttt{unsigned short}. +richiesto dallo standard POSIX.1g, in Linux pertanto non esiste. Il campo +\type{sa\_family\_t} era storicamente un \type{unsigned short}. 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' @@ -336,14 +372,13 @@ l'uso di questa struttura. \subsection{La struttura degli indirizzi IPv4} \label{sec:sock_sa_ipv4} -I socket di tipo \texttt{PF\_INET} vengono usati per la comunicazione +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 \texttt{sockaddr\_in} nell'header file +(IPv4) è definita come \type{sockaddr\_in} nell'header file \texttt{netinet/in.h} e secondo le man page ha la forma mostrata in \nfig, conforme allo standard POSIX.1g. - -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} struct sockaddr_in { @@ -373,12 +408,12 @@ 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 -usare la funzione \texttt{bind} su queste porte. +usare la funzione \func{bind} su queste porte. Il membro \texttt{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. +implementazione precedente in cui questa era una \texttt{union} usata per +accedere alle diverse classi di indirizzi) che come intero. Infine è da sottolineare che sia gli indirizzi che i numeri di porta devono essere specificati in quello che viene chiamato \textit{network order}, cioè @@ -387,6 +422,7 @@ necessit portabilità del codice (vedi \secref{sec:sock_addr_func} per i dettagli del problema e le relative soluzioni). + \subsection{La struttura degli indirizzi IPv6} \label{sec:sock_sa_ipv6} @@ -395,7 +431,7 @@ 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}. -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} struct sockaddr_in6 { @@ -442,7 +478,7 @@ funzione \texttt{socketpair}. Quando per ad uno di questi socket si deve usare la seguente struttura di indirizzi definita nel file di header \texttt{sys/un.h}. -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} #define UNIX_PATH_MAX 108 @@ -483,6 +519,7 @@ vengono usati i restanti bytes come stringa (senza terminazione). % \texttt{getpeername} invece ricevono i valori del kernel + \section{Le funzioni di conversione degli indirizzi} \label{sec:sock_addr_func} @@ -541,29 +578,29 @@ sul computer e quello che viene usato nelle trasmissione sulla rete; queste funzioni sono: \begin{prototype}{netinet/in.h} {unsigned long int htonl(unsigned long int hostlong)} - Converte l'intero a 32 bit \texttt{hostlong} dal formato della macchina a + Converte l'intero a 32 bit \var{hostlong} dal formato della macchina a quello della rete. \end{prototype} \begin{prototype}{netinet/in.h} {unsigned short int htons(unsigned short int hostshort)} - Converte l'intero a 16 bit \texttt{hostshort} dal formato della macchina a + Converte l'intero a 16 bit \var{hostshort} dal formato della macchina a quello della rete. \end{prototype} \begin{prototype}{netinet/in.h} {unsigned long int ntonl(unsigned long int netlong)} - Converte l'intero a 32 bit \texttt{netlong} dal formato della rete a quello + Converte l'intero a 32 bit \var{netlong} dal formato della rete a quello della macchina. \end{prototype} \begin{prototype}{netinet/in.h} {unsigned sort int ntons(unsigned short int netshort)} - Converte l'intero a 16 bit \texttt{netshort} dal formato della rete a quello + Converte l'intero a 16 bit \var{netshort} dal formato della rete a quello della macchina. \end{prototype} I nomi sono assegnati usando la lettera $n$ come mnemonico per indicare l'ordinamento usato sulla rete (da \textit{network order}) e la lettera $h$ come mnemonico per l'ordinamento usato sulla macchina locale (da \textit{host order}), mentre le lettere $s$ e $l$ stanno ad indicare i tipi di dato -(\texttt{long} o \texttt{short}, riportati anche dai prototipi). +(\type{long} o \type{short}, riportati anche dai prototipi). Usando queste funzioni si ha la conversione automatica (nel caso pure la macchina sia in big endian queste funzioni sono definite come macro che non @@ -571,8 +608,8 @@ fanno nulla); esse vanno sempre utilizzate per assicurare la portabilit codice su tutte le architetture. -\subsection{Le funzioni \texttt{inet\_aton}, \texttt{inet\_addr} e - \texttt{inet\_ntoa}} +\subsection{Le funzioni \func{inet\_aton}, \func{inet\_addr} e + \func{inet\_ntoa}} \label{sec:sock_func_ipv4} Un secondo insieme di funzioni di manipolazione serve per passare dal formato @@ -587,17 +624,17 @@ cosiddetta notazione \textit{dotted-decimal}, (cio indicare la stringa. Dette funzioni sono: \begin{prototype}{arpa/inet.h} {int inet\_aton(const char *src, struct in\_addr *dest)} Converte la stringa - puntata da \texttt{src} nell'indirizzo binario da memorizzare all'indirizzo - puntato da \texttt{dest}, restituendo 0 in caso di successo e 1 in caso di + puntata da \var{src} nell'indirizzo binario da memorizzare all'indirizzo + puntato da \var{dest}, restituendo 0 in caso di successo e 1 in caso di fallimento (è espressa in questa forma in modo da poterla usare direttamente con il puntatore usato per passare la struttura degli indirizzi). Se usata - con \texttt{dest} inizializzato a \texttt{NULL} effettua la validazione + con \var{dest} inizializzato a \macro{NULL} effettua la validazione dell'indirizzo. \end{prototype} \begin{prototype}{arpa/inet.h}{in\_addr\_t inet\_addr(const char *strptr)} Restituisce l'indirizzo a 32 bit in network order a partire dalla stringa passata come parametro, in caso di errore restituisce il valore - \texttt{INADDR\_NONE} che tipicamente sono trentadue bit a uno; questo + \macro{INADDR\_NONE} che tipicamente sono trentadue bit a uno; questo comporta che la stringa \texttt{255.255.255.255}, che pure è un indirizzo valido, non può essere usata con questa funzione; per questo motivo essa è generalmente deprecata in favore della precedente. @@ -610,12 +647,12 @@ indicare la stringa. Dette funzioni sono: \end{prototype} -\subsection{Le funzioni \texttt{inet\_pton} e \texttt{inet\_ntop}} +\subsection{Le funzioni \func{inet\_pton} e \func{inet\_ntop}} \label{sec:sock_conv_func_gen} Le tre funzioni precedenti sono limitate solo ad indirizzi IPv4, per questo -motivo è preferibile usare le due nuove funzioni \texttt{inet\_pton} e -\texttt{inet\_ntop} che possono convertire anche gli indirizzi IPv6. Anche in +motivo è preferibile usare le due nuove funzioni \func{inet\_pton} e +\func{inet\_ntop} che possono convertire anche gli indirizzi IPv6. Anche in questo caso le lettere $n$ e $p$ sono degli mnemonici per ricordare il tipo di conversione effettuata e stanno per \textit{presentation} e \textit{numeric}. @@ -635,8 +672,8 @@ al valore \texttt{EAFNOSUPPORT}. I prototipi delle suddette funzioni sono i seguenti: \begin{prototype}{sys/socket.h} {int inet\_pton(int af, const char *src, void *addr\_ptr)} Converte la - stringa puntata da \texttt{src} nell'indirizzo IP da memorizzare - all'indirizzo puntato da \texttt{addr\_ptr}, la funzione restituisce un + stringa puntata da \var{src} nell'indirizzo IP da memorizzare + all'indirizzo puntato da \var{addr\_ptr}, la funzione restituisce un valore positivo in caso di successo, e zero se la stringa non rappresenta un indirizzo valido, e negativo se \var{af} specifica una famiglia di indirizzi non valida. @@ -644,18 +681,18 @@ seguenti: \begin{prototype}{sys/socket.h} {char *inet\_ntop(int af, const void *addr\_ptr, char *dest, size\_t len)} - Converte la struttura dell'indirizzo puntata da \texttt{addr\_ptr} in una - stringa che viene copiata nel buffer puntato dall'indirizzo \texttt{dest}; + Converte la struttura dell'indirizzo puntata da \var{addr\_ptr} in una + stringa che viene copiata nel buffer puntato dall'indirizzo \var{dest}; questo deve essere preallocato dall'utente e la lunghezza deve essere almeno - \texttt{INET\_ADDRSTRLEN} in caso di indirizzi IPv4 e - \texttt{INET6\_ADDRSTRLEN} per indirizzi IPv6; la lunghezza del buffer deve - comunque venire specificata attraverso il parametro \texttt{len}. + \macro{INET\_ADDRSTRLEN} in caso di indirizzi IPv4 e + \macro{INET6\_ADDRSTRLEN} per indirizzi IPv6; la lunghezza del buffer deve + comunque venire specificata attraverso il parametro \var{len}. - La funzione restituisce un puntatore non nullo a \texttt{dest} in caso di + 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 \texttt{ENOSPC} in + viene settata la variabile \texttt{errno} con il valore \macro{ENOSPC} in caso le dimensioni dell'indirizzo eccedano la lunghezza specificata da - \texttt{len} o \macro{ENOAFSUPPORT} in caso \var{af} non sia una famiglia di + \var{len} o \macro{ENOAFSUPPORT} in caso \var{af} non sia una famiglia di indirizzi valida. \end{prototype} @@ -669,7 +706,17 @@ Il formato usato per gli indirizzi in formato di presentazione \textit{dotted decimal} per IPv4 e quella descritta in \secref{sec:IP_ipv6_notation} per IPv6. -\section{Il comportamento delle funzioni di I/O} + +\section{Un esempio di applicazione} +\label{sec:sock_appplication} + +Per evitare di rendere questa introduzione ai socket puramente teorica +iniziamo con il mostrare un esempio di un client TCP elementare. Prima di +passare agli esempi del client e del server, esamimeremo una caratteristica +delle funzioni di I/O sui socket che ci tornerà utile anche in seguito. + + +\subsection{Il comportamento delle funzioni di I/O} \label{sec:sock_io_behav} Una cosa di cui non sempre si è consapevoli quando si ha a che fare con i @@ -677,8 +724,8 @@ socket comportamento che avrebbero con i normali files (in particolare questo accade per i socket di tipo stream). -Infatti con i socket può accadere che funzioni come \texttt{read} o -\texttt{write} possano restituire in input o scrivere in output un numero di +Infatti con i socket può accadere che funzioni come \func{read} o +\func{write} possano restituire in input o scrivere in output un numero di bytes minore di quello richiesto. Questo è un comportamento normale e non un errore, e succede perché si eccede in lettura o scrittura il limite di buffer del kernel. @@ -767,3 +814,249 @@ Nel caso della lettura se il numero di bytes letti arrivati alla fine del file e pertanto si ritorna senza aver concluso la lettura di tutti i bytes richiesti. + + +\subsection{Un primo esempio di client} +\label{sec:net_cli_sample} + +Lo scopo di questo esempio è fornire un primo approccio alla programmazione di +rete e vedere come si usano le funzioni descritte in precedenza, alcune delle +funzioni usate nell'esempio saranno trattate in dettaglio nel capitolo +successivo; qui ci limiteremo a introdurre la nomenclatura senza fornire +definizioni precise e dettagli di funzionamento che saranno trattati +estensivamente più avanti. + +In \nfig\ è riportata la sezione principale del codice del nostro client +elementare per il servizio \textit{daytime}, un servizio standard che +restituisce l'ora locale della macchina a cui si effettua la richiesta. + +\begin{figure}[!htbp] + \footnotesize + \begin{lstlisting}{} +#include /* predefined types */ +#include /* include unix standard library */ +#include /* IP addresses conversion utiliites */ +#include /* socket library */ +#include /* include standard I/O library */ + +int main(int argc, char *argv[]) +{ + int sock_fd; + int i, nread; + struct sockaddr_in serv_add; + char buffer[MAXLINE]; + ... + /* create socket */ + if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket creation error"); + return -1; + } + /* initialize address */ + memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */ + serv_add.sin_family = AF_INET; /* address type is INET */ + serv_add.sin_port = htons(13); /* daytime post is 13 */ + /* build address using inet_pton */ + if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) { + perror("Address creation error"); + return -1; + } + /* extablish connection */ + if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { + perror("Connection error"); + return -1; + } + /* read daytime from server */ + while ( (nread = read(sock_fd, buffer, MAXLINE)) > 0) { + buffer[nread]=0; + if (fputs(buffer, stdout) == EOF) { /* write daytime */ + perror("fputs error"); + return -1; + } + } + /* error on read */ + if (nread < 0) { + perror("Read error"); + return -1; + } + /* normal exit */ + return 0; +} + \end{lstlisting} + \caption{Esempio di codice di un client elementare per il servizio daytime.} + \label{fig:net_cli_code} +\end{figure} + +Il sorgente completo del programma (\texttt{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. + +Il programma anzitutto include gli header necessari (\texttt{\small 1--5}); +dopo la dichiarazione delle variabili (\texttt{\small 9--12}) si è omessa +tutta la parte relativa al trattamento degli argomenti passati dalla linea di +comando (effettuata con le apposite routines illustrate in +\capref{sec:proc_opt_handling}). + +Il primo passo (\texttt{\small 14--18}) è creare un \textit{socket} IPv4 +(\macro{AF\_INET}), di tipo TCP \macro{SOCK\_STREAM}. La funzione +\macro{socket} ritorna il descrittore che viene usato per identificare il +socket in tutte le chiamate successive. Nel caso la chiamata fallisca si +stampa un errore con la relativa routine e si esce. + +Il passo seguente (\texttt{\small 19--27}) è quello di costruire una apposita +struttura \type{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed +il numero della porta del servizio. Il primo passo è inizializzare tutto a +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 +comando. + +Usando la funzione \func{connect} sul socket creato in precedenza +(\texttt{\small 28--32}) si provvede poi a stabilire la connessione con il +server specificato dall'indirizzo immesso nella struttura passata come secondo +argomento, il terzo argomento è la dimensione di detta struttura. Dato che +esistono diversi tipi di socket, si è dovuto effettuare un cast della +struttura inizializzata in precedenza, che è specifica per i socket IPv4. Un +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}. + +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 +arrivare in 26 pacchetti di un byte. Per questo nel caso generale non si può +mai assumere che tutti i dati arrivino con una singola lettura, pertanto +quest'ultima deve essere effettuata in un loop in cui si continui a leggere +fintanto che la funzione \func{read} non ritorni uno zero (che significa che +l'altro capo ha chiuso la connessione) o un numero minore di zero (che +significa un errore nella connessione). + +Si noti come in questo caso la fine dei dati sia specificata dal server che +chiude la connessione; questa è una delle tecniche possibili (è quella usata +pure dal protocollo HTTP), ma ce ne possono essere altre, ad esempio FTP marca +la conclusione di un blocco di dati con la sequenza ASCII \verb|\r\n| +(carriage return e line feed), mentre il DNS mette la lunghezza in testa ad +ogni blocco che trasmette. Il punto essenziale è che TCP non provvede nessuna +indicazione che permetta di marcare dei blocchi di dati, per cui se questo è +necessario deve provvedere il programma stesso. + +\subsection{Un primo esempio di server} +\label{sec:net_serv_sample} + +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}. + +\begin{figure}[!htbp] + \footnotesize + \begin{lstlisting}{} +#include /* predefined types */ +#include /* include unix standard library */ +#include /* IP addresses conversion utiliites */ +#include /* socket library */ +#include /* include standard I/O library */ +#include +#define MAXLINE 80 +#define BACKLOG 10 +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int i; + struct sockaddr_in serv_add; + char buffer[MAXLINE]; + time_t timeval; + ... + /* create socket */ + if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket creation error"); + exit(-1); + } + /* initialize address */ + memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */ + serv_add.sin_family = AF_INET; /* address type is INET */ + serv_add.sin_port = htons(13); /* daytime port is 13 */ + serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ + /* bind socket */ + if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { + perror("bind error"); + exit(-1); + } + /* listen on socket */ + if (listen(list_fd, BACKLOG) < 0 ) { + perror("listen error"); + exit(-1); + } + /* write daytime to client */ + while (1) { + if ( (conn_fd = accept(list_fd, (struct sockaddr *) NULL, NULL)) <0 ) { + perror("accept error"); + exit(-1); + } + timeval = time(NULL); + snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval)); + if ( (write(conn_fd, buffer, strlen(buffer))) < 0 ) { + perror("write error"); + exit(-1); + } + close(conn_fd); + } + /* normal exit */ + exit(0); +} + \end{lstlisting} + \caption{Esempio di codice di un semplice server per il servizio daytime.} + \label{fig:net_serv_code} +\end{figure} + +Come per il client si includono gli header necessari a cui è aggiunto quello +per trattare i tempi, e si definiscono alcune costanti e le variabili +necessarie in seguito (\texttt{\small 1--18}), come nel caso precedente si +sono omesse le parti relative al trattamento delle opzioni da riga di comando. + +La creazione del socket (\texttt{\small 22--26}) è analoga al caso precedente, +come pure l'inizializzazione della struttura \type{sockaddr\_in}, anche in +questo caso si usa la porta standard del servizio daytime, ma come indirizzo +IP si il valore predefinito \macro{INET\_ANY} che corrisponde ad un indirizzo +generico (\texttt{\small 27--31}). + +Si effettua poi (\texttt{\small 32--36}) la chiamata alla funzione +\func{bind} che permette di associare la precedente struttura al socket, in +modo che quest'ultimo possa essere usato per accettare connessioni su una +qualunque delle interfacce di rete locali. + +Il passo successivo (\texttt{\small 37--41}) è mettere ``in ascolto'' il +socket, questo viene effettuato con la funzione \func{listen} che dice al +kernel di accettare connessioni per il socket specificato, la funzione indica +inoltre, con il secondo parametro, il numero massimo di connessioni che il +kernel accetterà di mettere in coda per il suddetto socket. + +Questa ultima chiamata completa la preparazione del socket per l'ascolto (che +viene chiamato anche \textit{listening descriptor}) a questo punto il processo +è mandato in sleep (\texttt{\small 44--47}) con la successiva chiamata alla +funzione \func{accept}, fin quando non arriva e viene accettata una +connessione da un client. + +Quando questo avviene \func{accept} ritorna un secondo descrittore di socket, +che viene chiamato \textit{connected descriptor} che è quello che viene usato +dalla successiva chiamata alla \func{write} per scrivere la risposta al +client, una volta che si è opportunamente (\texttt{\small 48--49}) costruita +la stringa con la data da trasmettere. Completata la trasmissione il nuovo +socket viene chiuso (\texttt{\small 54}). Il tutto è inserito in un loop +infinito (\texttt{\small 42--55}) in modo da poter ripetere l'invio della data +ad una successiva connessione. + +È importante notare che questo server è estremamente elementare, infatti a +parte il fatto di essere dipendente da IPv4, esso è in grado di servire solo +un client alla volta, è cioè un \textsl{server iterativo}, inoltre esso è +scritto per essere lanciato da linea di comando, se lo si volesse utilizzare +come demone di sistema (che è in esecuzione anche quando non c'è nessuna shell +attiva e il terminale da cui lo si è lanciato è stato sconnesso), +occorrerebbero delle opportune modifiche.