\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.
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,
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
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,
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}).
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:
\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
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
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}).
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}}
\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
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}.
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à
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
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}
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
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à
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
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
\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
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.
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}
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).
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.
%
% 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
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}
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
\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}
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 <sys/types.h> /* predefined types */
-#include <unistd.h> /* include unix standard library */
-#include <arpa/inet.h> /* IP addresses conversion utiliites */
-#include <sys/socket.h> /* socket library */
-#include <stdio.h> /* 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 <sys/types.h> /* predefined types */
-#include <unistd.h> /* include unix standard library */
-#include <arpa/inet.h> /* IP addresses conversion utiliites */
-#include <sys/socket.h> /* socket library */
-#include <stdio.h> /* include standard I/O library */
-#include <time.h>
-#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
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
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}
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.
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]{}
\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.
\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
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.
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
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).
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,
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}
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.
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.
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
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
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
\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.
\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.
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
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
\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}
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
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
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}
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}
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
\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
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
\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}{}
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}
anche altre per una lista parziale si può controllare \cmd{man environ}
-
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}).
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}
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|}
\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.
\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}
\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
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}
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.
% 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
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
È 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}
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
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}
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}
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.
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}
\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
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
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!]).
\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) \\
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}
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
\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}
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}
ciascuna famiglia vengono identificati dal suffisso finale, aggiunto al nome
precedente.
+
\subsection{La struttura generica}
\label{sec:sock_sa_gen}
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 {
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}
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
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'
\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 {
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è
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}
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 {
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
% \texttt{getpeername} invece ricevono i valori del kernel
+
\section{Le funzioni di conversione degli indirizzi}
\label{sec:sock_addr_func}
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
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
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.
\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}.
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.
\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}
\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
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.
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 <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* 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 <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+#include <time.h>
+#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.