X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsock.tex;h=8a7b5e794a5c8ceae62757c8b360e13320c8c3a7;hp=d6b5a945f216475483d1b44356e1004d708a5aa8;hb=fbaf3a4f4f6fe16f6f2233f7165bbfa77557e32e;hpb=de137402ded9a730854f908315767d73c5308d9d diff --git a/tcpsock.tex b/tcpsock.tex index d6b5a94..8a7b5e7 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -3414,8 +3414,8 @@ disponibile coi sorgenti allegati nel file \texttt{select\_echod.c}. \includecodesample{listati/select_echod.c} \end{minipage} \normalsize - \caption{La sezione principale del codice della nuova versione di server - \textit{echo} basati sull'uso della funzione \func{select}.} + \caption{La sezione principale della nuova versione di server + \textit{echo} basato sull'uso della funzione \func{select}.} \label{fig:TCP_SelectEchod} \end{figure} @@ -3427,7 +3427,7 @@ tabella dei socket connessi mantenuta nel vettore \var{fd\_open} dimensionato al valore di \const{FD\_SETSIZE}, ed una variabile \var{max\_fd} per registrare il valore più alto dei file descriptor aperti. -Prima di entrare nel ciclo principale (\texttt{\small 6--56}) la nostra +Prima di entrare nel ciclo principale (\texttt{\small 5--53}) la nostra tabella viene inizializzata (\texttt{\small 2}) a zero (valore che utilizzeremo come indicazione del fatto che il relativo file descriptor non è aperto), mentre il valore massimo (\texttt{\small 3}) per i file descriptor @@ -3435,23 +3435,23 @@ aperti viene impostato a quello del socket in ascolto, in quanto esso è l'unico file aperto, oltre i tre standard, e pertanto avrà il valore più alto, che verrà anche (\texttt{\small 4}) inserito nella tabella. -La prima sezione (\texttt{\small 7--10}) del ciclo principale esegue la +La prima sezione (\texttt{\small 6--8}) del ciclo principale esegue la costruzione del \textit{file descriptor set} \var{fset} in base ai socket connessi in un certo momento; all'inizio ci sarà soltanto il socket in ascolto ma nel prosieguo delle operazioni verranno utilizzati anche tutti i socket connessi registrati nella tabella \var{fd\_open}. Dato che la chiamata di \func{select} modifica il valore del \textit{file descriptor set} è necessario -ripetere (\texttt{\small 7}) ogni volta il suo azzeramento per poi procedere -con il ciclo (\texttt{\small 8--10}) in cui si impostano i socket trovati +ripetere (\texttt{\small 6}) ogni volta il suo azzeramento per poi procedere +con il ciclo (\texttt{\small 7--8}) in cui si impostano i socket trovati attivi. Per far questo si usa la caratteristica dei file descriptor, descritta in sez.~\ref{sec:file_open_close}, per cui il kernel associa sempre ad ogni nuovo file il file descriptor con il valore più basso disponibile. Questo fa sì che -si possa eseguire il ciclo (\texttt{\small 8}) a partire da un valore minimo, +si possa eseguire il ciclo (\texttt{\small 7}) a partire da un valore minimo, che sarà sempre quello del socket in ascolto, mantenuto in \var{list\_fd}, fino al valore massimo di \var{max\_fd} che dovremo aver cura di tenere -aggiornato. Dopo di che basterà controllare (\texttt{\small 9}) nella nostra +aggiornato. Dopo di che basterà controllare (\texttt{\small 8}) nella nostra tabella se il file descriptor è in uso o meno,\footnote{si tenga presente che benché il kernel assegni sempre il primo valore libero, si potranno sempre avere dei \textsl{buchi} nella nostra tabella dato che nelle operazioni i @@ -3462,64 +3462,65 @@ Una volta inizializzato con i socket aperti il nostro \textit{file descriptor set} potremo chiamare \func{select} per fargli osservare lo stato degli stessi (in lettura, presumendo che la scrittura sia sempre consentita). Come per il precedente esempio di sez.~\ref{sec:TCP_child_hand}, essendo questa -l'unica funzione che può bloccarsi, ed essere interrotta da un segnale, la -eseguiremo (\texttt{\small 11--12}) all'interno di un ciclo di \code{while} +l'unica funzione che può bloccarsi ed essere interrotta da un segnale, la +eseguiremo (\texttt{\small 9--10}) all'interno di un ciclo di \code{while}, che la ripete indefinitamente qualora esca con un errore di \errcode{EINTR}. -Nel caso invece di un errore normale si provvede (\texttt{\small 13--16}) ad -uscire stampando un messaggio di errore. - -Se invece la funzione ritorna normalmente avremo in \var{n} il numero di -socket da controllare. Nello specifico si danno due possibili casi diversi per -cui \func{select} può essere ritornata: o si è ricevuta una nuova connessione -ed è pronto il socket in ascolto, sul quale si può eseguire \func{accept}, o -c'è attività su uno dei socket connessi, sui quali si può eseguire -\func{read}. - -Il primo caso viene trattato immediatamente (\texttt{\small 17--26}): si -controlla (\texttt{\small 17}) che il socket in ascolto sia fra quelli attivi, -nel qual caso anzitutto (\texttt{\small 18}) se ne decrementa il numero in -\var{n}. Poi, inizializzata (\texttt{\small 19}) la lunghezza della struttura -degli indirizzi, si esegue \func{accept} per ottenere il nuovo socket connesso -controllando che non ci siano errori (\texttt{\small 20--23}). In questo caso -non c'è più la necessità di controllare per interruzioni dovute a segnali, in -quanto siamo sicuri che \func{accept} non si bloccherà. Per completare la -trattazione occorre a questo punto aggiungere (\texttt{\small 24}) il nuovo -file descriptor alla tabella di quelli connessi, ed inoltre, se è il caso, -aggiornare (\texttt{\small 25}) il valore massimo in \var{max\_fd}. +Nel caso invece di un errore normale si provvede (\texttt{\small 11--14}) ad +uscire dal programma stampando un messaggio di errore. + +Infine quando la funzione ritorna normalmente avremo in \var{n} il numero di +socket da controllare. Nello specifico si danno due casi per cui \func{select} +può ritornare: o si è ricevuta una nuova connessione ed è pronto il socket in +ascolto, sul quale si può eseguire \func{accept}, o c'è attività su uno dei +socket connessi, sui quali si può eseguire \func{read}. + +Il primo caso viene trattato immediatamente (\texttt{\small 15--24}): si +controlla (\texttt{\small 15}) che il socket in ascolto sia fra quelli attivi, +nel qual caso anzitutto (\texttt{\small 16}) se ne decrementa il numero +mantenuto nella variabile \var{n}. Poi, inizializzata (\texttt{\small 17}) la +lunghezza della struttura degli indirizzi, si esegue \func{accept} per +ottenere il nuovo socket connesso, controllando che non ci siano errori +(\texttt{\small 18--21}). In questo caso non c'è più la necessità di +controllare per interruzioni dovute a segnali, in quanto siamo sicuri che +\func{accept} non si bloccherà. Per completare la trattazione occorre a questo +punto aggiungere (\texttt{\small 22}) il nuovo file descriptor alla tabella di +quelli connessi, ed inoltre, se è il caso, aggiornare (\texttt{\small 23}) il +valore massimo in \var{max\_fd}. Una volta controllato l'arrivo di nuove connessioni si passa a verificare se ci sono dati sui socket connessi, per questo si ripete un ciclo -(\texttt{\small 29--55}) fintanto che il numero di socket attivi \var{n} resta -diverso da zero; in questo modo se l'unico socket con attività era quello -connesso, avendo opportunamente decrementato il contatore, il ciclo verrà -saltato e si ritornerà immediatamente, ripetuta l'inizializzazione del -\textit{file descriptor set} con i nuovi valori nella tabella, alla chiamata -di \func{accept}. +(\texttt{\small 26--52}) fintanto che il numero di socket attivi indicato +dalla variabile \var{n} resta diverso da zero. In questo modo, se l'unico +socket con attività era quello connesso, avendola opportunamente decrementata +in precedenza, essa risulterà nulla, pertanto il ciclo di verifica verrà +saltato e si ritornerà all'inzizio del ciclo principale, ripetendo, dopo +l'inizializzazione del \textit{file descriptor set} con i nuovi valori nella +tabella, la chiamata di \func{select}. Se il socket attivo non è quello in ascolto, o ce ne sono comunque anche altri, il valore di \var{n} non sarà nullo ed il controllo sarà -eseguito. Prima di entrare nel ciclo comunque si inizializza (\texttt{\small - 28}) il valore della variabile \var{i}, che useremo come indice nella -tabella, \var{fd\_open} al valore minimo, corrispondente al file descriptor -del socket in ascolto. +eseguito. Prima di entrare nel ciclo di veridica comunque si inizializza +(\texttt{\small 25}) il valore della variabile \var{i}, che useremo come +indice nella tabella \var{fd\_open}, al valore minimo, corrispondente al file +descriptor del socket in ascolto. -Il primo passo (\texttt{\small 30}) nella verifica è incrementare il valore +Il primo passo (\texttt{\small 27}) nella verifica è incrementare il valore dell'indice \var{i} per posizionarsi sul primo valore possibile per un file descriptor associato ad un eventuale socket connesso, dopo di che si controlla -(\texttt{\small 31}) se questo è nella tabella dei socket connessi, chiedendo +(\texttt{\small 28}) se questo è nella tabella dei socket connessi, chiedendo la ripetizione del ciclo in caso contrario. Altrimenti si passa a verificare -(\texttt{\small 32}) se il file descriptor corrisponde ad uno di quelli -attivi, e nel caso si esegue (\texttt{\small 33}) una lettura, uscendo con un -messaggio in caso di errore (\texttt{\small 34--38}). - -Se (\texttt{\small 39}) il numero di byte letti \var{nread} è nullo si è in -presenza di un \textit{end-of-file}, indice che una connessione che si è -chiusa, che deve essere trattato (\texttt{\small 39--48}) opportunamente. Il -primo passo è chiudere (\texttt{\small 40}) anche il proprio capo del socket e -rimuovere (\texttt{\small 41}) il file descriptor dalla tabella di quelli -aperti, inoltre occorre verificare (\texttt{\small 42}) se il file descriptor +(\texttt{\small 29}) se il file descriptor corrisponde ad uno di quelli +attivi, e nel caso si esegue (\texttt{\small 30}) una lettura, uscendo con un +messaggio in caso di errore (\texttt{\small 31--35}). + +Se (\texttt{\small 36}) il numero di byte letti \var{nread} è nullo si è in +presenza di una \textit{end-of-file}, indice che una connessione che si è +chiusa, che deve essere trattata (\texttt{\small 36--45}) opportunamente. Il +primo passo è chiudere (\texttt{\small 37}) anche il proprio capo del socket e +rimuovere (\texttt{\small 38}) il file descriptor dalla tabella di quelli +aperti, inoltre occorre verificare (\texttt{\small 39}) se il file descriptor chiuso è quello con il valore più alto, nel qual caso occorre trovare -(\texttt{\small 42--46}) il nuovo massimo, altrimenti (\texttt{\small 47}) si +(\texttt{\small 39--43}) il nuovo massimo, altrimenti (\texttt{\small 44}) si può ripetere il ciclo da capo per esaminare (se ne restano) ulteriori file descriptor attivi. @@ -3527,15 +3528,15 @@ Se però è stato chiuso il file descriptor più alto, dato che la scansione dei file descriptor attivi viene fatta a partire dal valore più basso, questo significa che siamo anche arrivati alla fine della scansione, per questo possiamo utilizzare direttamente il valore dell'indice \var{i} con un ciclo -all'indietro (\texttt{\small 43}) che trova il primo valore per cui la tabella -presenta un file descriptor aperto, e lo imposta (\texttt{\small 44}) come -nuovo massimo, per poi tornare (\texttt{\small 44}) al ciclo principale con un +all'indietro (\texttt{\small 40}) che trova il primo valore per cui la tabella +presenta un file descriptor aperto, e lo imposta (\texttt{\small 41}) come +nuovo massimo, per poi tornare (\texttt{\small 42}) al ciclo principale con un \code{break}, e rieseguire \func{select}. Se infine si sono effettivamente letti dei dati dal socket (ultimo caso -rimasto) si potrà invocare immediatamente (\texttt{\small 49}) +rimasto) si potrà invocare immediatamente (\texttt{\small 46}) \func{FullWrite} per riscriverli indietro sul socket stesso, avendo cura di -uscire con un messaggio in caso di errore (\texttt{\small 50--53}). Si noti +uscire con un messaggio in caso di errore (\texttt{\small 47--50}). Si noti che nel ciclo si esegue una sola lettura, contrariamente a quanto fatto con la precedente versione (si riveda il codice di fig.~\ref{fig:TCP_ServEcho_second}) in cui si continuava a leggere fintanto che non si riceveva un @@ -3623,8 +3624,8 @@ struttura del programma resta sostanzialmente la stessa. \includecodesample{listati/poll_echod.c} \end{minipage} \normalsize - \caption{La sezione principale del codice della nuova versione di server - \textit{echo} basati sull'uso della funzione \func{poll}.} + \caption{La sezione principale della nuova versione di server + \textit{echo} basato sull'uso della funzione \func{poll}.} \label{fig:TCP_PollEchod} \end{figure} @@ -3646,8 +3647,8 @@ l'opzione \texttt{-n} ed ha un valore di default di 256. Dopo di che si preimposta (\texttt{\small 3}) il valore \var{max\_fd} del file descriptor aperto con valore più alto a quello del socket in ascolto (al momento l'unico), e si provvede (\texttt{\small 4--7}) ad inizializzare le -strutture, disabilitando (\texttt{\small 5}) l'osservazione con un valore -negativo del campo \var{fd} ma predisponendo (\texttt{\small 6}) il campo +strutture, disabilitando l'osservazione (\texttt{\small 5}) con un valore +negativo del campo \var{fd}, ma predisponendo (\texttt{\small 6}) il campo \var{events} per l'osservazione dei dati normali con \const{POLLRDNORM}. Infine (\texttt{\small 8}) si attiva l'osservazione del socket in ascolto inizializzando la corrispondente struttura. Questo metodo comporta, in @@ -3657,59 +3658,59 @@ che non vengono mai utilizzate in quanto la prima è sempre quella relativa al socket in ascolto. Una volta completata l'inizializzazione tutto il lavoro viene svolto -all'interno del ciclo principale \texttt{\small 10--55}) che ha una struttura +all'interno del ciclo principale \texttt{\small 9--53}) che ha una struttura sostanzialmente identica a quello usato per il precedente esempio basato su -\func{select}. La prima istruzione (\texttt{\small 11--12}) è quella di -eseguire \func{poll} all'interno di un ciclo che la ripete qualora venisse -interrotta da un segnale, da cui si esce soltanto quando la funzione ritorna, -restituendo nella variabile \var{n} il numero di file descriptor trovati -attivi. Qualora invece si sia ottenuto un errore si procede (\texttt{\small - 13--16}) alla terminazione immediata del processo provvedendo a stampare una -descrizione dello stesso. +\func{select}. La prima istruzione (\texttt{\small 10}) è quella di eseguire +\func{poll} all'interno di un ciclo che la ripete qualora venisse interrotta +da un segnale, da cui si esce soltanto quando la funzione ritorna restituendo +nella variabile \var{n} il numero di file descriptor trovati attivi. Qualora +invece si sia ottenuto un errore si procede (\texttt{\small 11--14}) alla +terminazione immediata del processo provvedendo a stampare una descrizione +dello stesso. Una volta ottenuta dell'attività su un file descriptor si hanno di nuovo due -possibilità. La prima possibilità è che ci sia attività sul socket in ascolto, -indice di una nuova connessione, nel qual caso si controlla (\texttt{\small - 17}) se il campo \var{revents} della relativa struttura è attivo; se è così -si provvede (\texttt{\small 18}) a decrementare la variabile \var{n} (che -assume il significato di numero di file descriptor attivi rimasti da -controllare) per poi (\texttt{\small 19--23}) effettuare la chiamata ad -\func{accept}, terminando il processo in caso di errore. Se la chiamata ad -\func{accept} ha successo si procede attivando (\texttt{\small 24}) la -struttura relativa al nuovo file descriptor da essa ottenuto, modificando -(\texttt{\small 24}) infine quando necessario il valore massimo dei file -descriptor aperti mantenuto in \var{max\_fd}. +possibilità. La prima è che ci sia attività sul socket in ascolto, indice di +una nuova connessione, nel qual caso si controlla (\texttt{\small 17}) se il +campo \var{revents} della relativa struttura è attivo; se è così si provvede +(\texttt{\small 16}) a decrementare la variabile \var{n} (che assume il +significato di numero di file descriptor attivi rimasti da controllare) per +poi (\texttt{\small 17--21}) effettuare la chiamata ad \func{accept}, +terminando il processo in caso di errore. Se la chiamata ad \func{accept} ha +successo si procede attivando (\texttt{\small 22}) la struttura relativa al +nuovo file descriptor da essa ottenuto, modificando (\texttt{\small 23}) +infine quando necessario il valore massimo dei file descriptor aperti +mantenuto in \var{max\_fd}. La seconda possibilità è che vi sia dell'attività su uno dei socket aperti in -precedenza, nel qual caso si inizializza (\texttt{\small 27}) l'indice \var{i} +precedenza, nel qual caso si inizializza (\texttt{\small 25}) l'indice \var{i} del vettore delle strutture \struct{pollfd} al valore del socket in ascolto, dato che gli ulteriori socket aperti avranno comunque un valore superiore. Il -ciclo (\texttt{\small 28--54}) prosegue fintanto che il numero di file +ciclo (\texttt{\small 26--52}) prosegue fintanto che il numero di file descriptor attivi, mantenuto nella variabile \var{n}, è diverso da zero. Se pertanto ci sono ancora socket attivi da individuare si comincia con -l'incrementare (\texttt{\small 30}) l'indice e controllare (\texttt{\small - 31}) se corrisponde ad un file descriptor in uso analizzando il valore del +l'incrementare (\texttt{\small 27}) l'indice e controllare (\texttt{\small + 28}) se corrisponde ad un file descriptor in uso analizzando il valore del campo \var{fd} della relativa struttura e chiudendo immediatamente il ciclo qualora non lo sia. Se invece il file descriptor è in uso si verifica -(\texttt{\small 31}) se c'è stata attività controllando il campo -\var{revents}. +(\texttt{\small 29}) se c'è stata attività controllando il campo +\var{revents}. Di nuovo se non si verifica la presenza di attività il ciclo si chiude subito, -altrimenti si provvederà (\texttt{\small 32}) a decrementare il numero \var{n} -di file descriptor attivi da controllare e ad eseguire (\texttt{\small 33}) la -lettura, ed in caso di errore (\texttt{\small 34--37}) al solito lo si +altrimenti si provvederà (\texttt{\small 30}) a decrementare il numero \var{n} +di file descriptor attivi da controllare e ad eseguire (\texttt{\small 31}) la +lettura, ed in caso di errore (\texttt{\small 32--35}) al solito lo si notificherà uscendo immediatamente. Qualora invece si ottenga una condizione -di \textit{end-of-file} (\texttt{\small 38--47}) si provvederà a chiudere -(\texttt{\small 39}) anche il nostro capo del socket e a marcarlo -(\texttt{\small 40}) nella struttura ad esso associata come inutilizzato. -Infine dovrà essere ricalcolato (\texttt{\small 41--45}) un eventuale nuovo -valore di \var{max\_fd}. L'ultimo passo è (\texttt{\small 46}) chiudere il +di \textit{end-of-file} (\texttt{\small 36--45}) si provvederà a chiudere +(\texttt{\small 37}) anche il nostro capo del socket e a marcarlo +(\texttt{\small 38}) come inutilizzato nella struttura ad esso associata. +Infine dovrà essere ricalcolato (\texttt{\small 39--43}) un eventuale nuovo +valore di \var{max\_fd}. L'ultimo passo è chiudere (\texttt{\small 44}) il ciclo in quanto in questo caso non c'è più niente da riscrivere all'indietro sul socket. -Se invece si sono letti dei dati si provvede (\texttt{\small 48}) ad +Se invece si sono letti dei dati si provvede (\texttt{\small 46}) ad effettuarne la riscrittura all'indietro, con il solito controllo ed eventuale -uscita e notifica in caso di errore (\texttt{\small 49--52}). +uscita e notifica in caso di errore (\texttt{\small 47--51}). Come si può notare la logica del programma è identica a quella vista in fig.~\ref{fig:TCP_SelectEchod} per l'analogo server basato su \func{select}; @@ -3719,10 +3720,10 @@ dai dati in ingresso. Si applicano comunque anche a questo server le considerazioni finali di sez.~\ref{sec:TCP_serv_select}. -\subsection{\textit{I/O multiplexing} con \textit{epoll}} -\label{sec:TCP_serv_epoll} +%\subsection{\textit{I/O multiplexing} con \textit{epoll}} +%\label{sec:TCP_serv_epoll} -Da fare. +%Da fare. % TODO fare esempio con epoll