From beece18eba2dcc2a9b915dab61277df8685a3da6 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 18 Jan 2014 16:28:58 +0000 Subject: [PATCH] Materuale vario, correzioni e aggiornamenti sulla code di messaggi posix finiti. --- fileadv.tex | 160 +++++++++++++------------- gapil.tex | 2 +- ipc.tex | 308 ++++++++++++++++++++++++++++----------------------- prochand.tex | 4 +- tcpsock.tex | 94 ++++++++-------- 5 files changed, 303 insertions(+), 265 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 94bad88..8b979cb 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -914,13 +914,13 @@ I/O. \label{sec:file_noblocking} Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra -\textit{fast} e \textit{slow} system call,\index{system~call~lente} che in -certi casi le funzioni di I/O possono bloccarsi indefinitamente.\footnote{si - ricordi però che questo può accadere solo per le pipe, i socket ed alcuni - file di dispositivo\index{file!di~dispositivo}; sui file normali le funzioni - di lettura e scrittura ritornano sempre subito.} Ad esempio le operazioni -di lettura possono bloccarsi quando non ci sono dati disponibili sul -descrittore su cui si sta operando. +\textit{fast} e \textit{slow} \textit{system call},\index{system~call~lente} +che in certi casi le funzioni di I/O possono bloccarsi +indefinitamente.\footnote{si ricordi però che questo può accadere solo per le + pipe, i socket ed alcuni file di dispositivo\index{file!di~dispositivo}; sui + file normali le funzioni di lettura e scrittura ritornano sempre subito.} +Ad esempio le operazioni di lettura possono bloccarsi quando non ci sono dati +disponibili sul descrittore su cui si sta operando. Questo comportamento causa uno dei problemi più comuni che ci si trova ad affrontare nelle operazioni di I/O, che si verifica quando si deve operare con @@ -945,8 +945,8 @@ modalità di I/O permette di risolvere il problema controllando a turno i vari file descriptor, in un ciclo in cui si ripete l'accesso fintanto che esso non viene garantito. Ovviamente questa tecnica, detta \itindex{polling} \textit{polling}, è estremamente inefficiente: si tiene costantemente -impiegata la CPU solo per eseguire in continuazione delle system call che -nella gran parte dei casi falliranno. +impiegata la CPU solo per eseguire in continuazione delle \textit{system call} +che nella gran parte dei casi falliranno. Per superare questo problema è stato introdotto il concetto di \textit{I/O multiplexing}, una nuova modalità di operazioni che consente di tenere sotto @@ -1161,15 +1161,16 @@ precedenti, ed inoltre aggiunge a \func{select} una nuova funzione La funzione è sostanzialmente identica a \func{select}, solo che usa una struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timespec_struct}) per indicare con maggiore precisione il timeout e non ne aggiorna il valore in -caso di interruzione.\footnote{in realtà la system call di Linux aggiorna il - valore al tempo rimanente, ma la funzione fornita dalle \acr{glibc} modifica - questo comportamento passando alla system call una variabile locale, in modo - da mantenere l'aderenza allo standard POSIX che richiede che il valore di - \param{timeout} non sia modificato.} Inoltre prende un argomento aggiuntivo -\param{sigmask} che è il puntatore ad una \index{maschera~dei~segnali} -maschera di segnali (si veda sez.~\ref{sec:sig_sigmask}). La maschera -corrente viene sostituita da questa immediatamente prima di eseguire l'attesa, -e ripristinata al ritorno della funzione. +caso di interruzione.\footnote{in realtà la \textit{system call} di Linux + aggiorna il valore al tempo rimanente, ma la funzione fornita dalle + \acr{glibc} modifica questo comportamento passando alla \textit{system call} + una variabile locale, in modo da mantenere l'aderenza allo standard POSIX + che richiede che il valore di \param{timeout} non sia modificato.} Inoltre +prende un argomento aggiuntivo \param{sigmask} che è il puntatore ad una +\index{maschera~dei~segnali} maschera di segnali (si veda +sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa +immediatamente prima di eseguire l'attesa, e ripristinata al ritorno della +funzione. L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili \textit{race condition} \itindex{race~condition} quando ci si deve porre in @@ -1196,18 +1197,19 @@ interrotta, e la ricezione del segnale non sarà rilevata. Per questo è stata introdotta \func{pselect} che attraverso l'argomento \param{sigmask} permette di riabilitare la ricezione il segnale contestualmente all'esecuzione della funzione,\footnote{in Linux però, fino al - kernel 2.6.16, non era presente la relativa system call, e la funzione era - implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man - select\_tut}) per cui la possibilità di \itindex{race~condition} - \textit{race condition} permaneva; in tale situazione si può ricorrere ad una - soluzione alternativa, chiamata \itindex{self-pipe trick} \textit{self-pipe - trick}, che consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes}) - ed usare \func{select} sul capo in lettura della stessa; si può indicare - l'arrivo di un segnale scrivendo sul capo in scrittura all'interno del - gestore dello stesso; in questo modo anche se il segnale va perso prima - della chiamata di \func{select} questa lo riconoscerà comunque dalla - presenza di dati sulla pipe.} ribloccandolo non appena essa ritorna, così -che il precedente codice potrebbe essere riscritto nel seguente modo: + kernel 2.6.16, non era presente la relativa \textit{system call}, e la + funzione era implementata nelle \acr{glibc} attraverso \func{select} (vedi + \texttt{man select\_tut}) per cui la possibilità di \itindex{race~condition} + \textit{race condition} permaneva; in tale situazione si può ricorrere ad + una soluzione alternativa, chiamata \itindex{self-pipe trick} + \textit{self-pipe trick}, che consiste nell'aprire una pipe (vedi + sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in lettura della + stessa; si può indicare l'arrivo di un segnale scrivendo sul capo in + scrittura all'interno del gestore dello stesso; in questo modo anche se il + segnale va perso prima della chiamata di \func{select} questa lo riconoscerà + comunque dalla presenza di dati sulla pipe.} ribloccandolo non appena essa +ritorna, così che il precedente codice potrebbe essere riscritto nel seguente +modo: \includecodesnip{listati/pselect_norace.c} in questo caso utilizzando \var{oldmask} durante l'esecuzione di \func{pselect} la ricezione del segnale sarà abilitata, ed in caso di @@ -1903,8 +1905,8 @@ interruzioni delle funzioni di attesa sincrone, ed evitare possibili \itindex{race~condition} \textit{race conditions}.\footnote{in sostanza se non fossero per i segnali non ci sarebbe da doversi preoccupare, fintanto che si effettuano operazioni all'interno di un processo, della non atomicità delle - \index{system~call~lente} system call lente che vengono interrotte e devono - essere riavviate.} + \index{system~call~lente} \textit{system call} lente che vengono interrotte + e devono essere riavviate.} Abbiamo visto però in sez.~\ref{sec:sig_real_time} che insieme ai segnali \textit{real-time} sono state introdotte anche delle interfacce di gestione @@ -3237,8 +3239,8 @@ approssimativamente 512 eventi.\footnote{si ricordi che la quantità di dati del nome del file restituito insieme a \struct{inotify\_event}.} In caso di errore di lettura (\texttt{\small 35--40}) il programma esce con un messaggio di errore (\texttt{\small 37--39}), a meno che non si tratti di una -interruzione della system call, nel qual caso (\texttt{\small 36}) si ripete la -lettura. +interruzione della \textit{system call}, nel qual caso (\texttt{\small 36}) si +ripete la lettura. Se la lettura è andata a buon fine invece si esegue un ciclo (\texttt{\small 43--52}) per leggere tutti gli eventi restituiti, al solito si inizializza @@ -3336,12 +3338,12 @@ effettuare in contemporanea le operazioni di calcolo e quelle di I/O. Benché la modalità di apertura asincrona di un file possa risultare utile in varie occasioni (in particolar modo con i socket e gli altri file per i quali -le funzioni di I/O sono \index{system~call~lente} system call lente), essa è -comunque limitata alla notifica della disponibilità del file descriptor per le -operazioni di I/O, e non ad uno svolgimento asincrono delle medesime. Lo -standard POSIX.1b definisce una interfaccia apposita per l'I/O asincrono vero -e proprio, che prevede un insieme di funzioni dedicate per la lettura e la -scrittura dei file, completamente separate rispetto a quelle usate +le funzioni di I/O sono \index{system~call~lente} \textit{system call} lente), +essa è comunque limitata alla notifica della disponibilità del file descriptor +per le operazioni di I/O, e non ad uno svolgimento asincrono delle medesime. +Lo standard POSIX.1b definisce una interfaccia apposita per l'I/O asincrono +vero e proprio, che prevede un insieme di funzioni dedicate per la lettura e +la scrittura dei file, completamente separate rispetto a quelle usate normalmente. In generale questa interfaccia è completamente astratta e può essere @@ -3473,8 +3475,8 @@ verificatosi, ed esegue la corrispondente impostazione di \var{errno}. Il codice può essere sia \errcode{EINVAL} ed \errcode{EBADF}, dovuti ad un valore errato per \param{aiocbp}, che uno degli errori possibili durante l'esecuzione dell'operazione di I/O richiesta, nel qual caso saranno restituiti, a seconda -del caso, i codici di errore delle system call \func{read}, \func{write} e -\func{fsync}. +del caso, i codici di errore delle \textit{system call} \func{read}, +\func{write} e \func{fsync}. Una volta che si sia certi che le operazioni siano state concluse (cioè dopo che una chiamata ad \func{aio\_error} non ha restituito @@ -3498,10 +3500,10 @@ l'operazione cui \param{aiocbp} fa riferimento si è completata. Una chiamata precedente il completamento delle operazioni darebbe risultati indeterminati. La funzione restituisce il valore di ritorno relativo all'operazione eseguita, -così come ricavato dalla sottostante system call (il numero di byte letti, -scritti o il valore di ritorno di \func{fsync}). É importante chiamare sempre -questa funzione, altrimenti le risorse disponibili per le operazioni di I/O -asincrono non verrebbero liberate, rischiando di arrivare ad un loro +così come ricavato dalla sottostante \textit{system call} (il numero di byte +letti, scritti o il valore di ritorno di \func{fsync}). É importante chiamare +sempre questa funzione, altrimenti le risorse disponibili per le operazioni di +I/O asincrono non verrebbero liberate, rischiando di arrivare ad un loro esaurimento. Oltre alle operazioni di lettura e scrittura l'interfaccia POSIX.1b mette a @@ -4227,7 +4229,7 @@ unix-like. Diventa così possibile utilizzare una sola mappatura iniziale\footnote{e quindi una sola \textit{virtual memory area} nella \itindex{page~table} \textit{page table} del processo.} e poi rimappare a piacere all'interno di questa i dati del file. Ciò è possibile grazie ad una -nuova system call, \funcd{remap\_file\_pages}, il cui prototipo è: +nuova \textit{system call}, \funcd{remap\_file\_pages}, il cui prototipo è: \begin{functions} \headdecl{sys/mman.h} @@ -4442,13 +4444,13 @@ l'operazione sia facilmente eseguibile attraverso una serie multipla di chiamate a \func{read} e \func{write}, ci sono casi in cui si vuole poter contare sulla atomicità delle operazioni. -Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove system call -che permettessero di effettuare con una sola chiamata una serie di letture o -scritture su una serie di buffer, con quello che viene normalmente chiamato -\textsl{I/O vettorizzato}. Queste funzioni sono \funcd{readv} e -\funcd{writev},\footnote{in Linux le due funzioni sono riprese da BSD4.4, esse - sono previste anche dallo standard POSIX.1-2001.} ed i relativi prototipi -sono: +Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove +\textit{system call} che permettessero di effettuare con una sola chiamata una +serie di letture o scritture su una serie di buffer, con quello che viene +normalmente chiamato \textsl{I/O vettorizzato}. Queste funzioni sono +\funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese + da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} ed i +relativi prototipi sono: \begin{functions} \headdecl{sys/uio.h} @@ -4512,10 +4514,10 @@ stesso valore deve essere ottenibile in esecuzione tramite la funzione sez.~\ref{sec:sys_limits}). Nel caso di Linux il limite di sistema è di 1024, però se si usano le -\acr{glibc} queste forniscono un \textit{wrapper} per le system call che si -accorge se una operazione supererà il precedente limite, in tal caso i dati -verranno letti o scritti con le usuali \func{read} e \func{write} usando un -buffer di dimensioni sufficienti appositamente allocato e sufficiente a +\acr{glibc} queste forniscono un \textit{wrapper} per le \textit{system call} +che si accorge se una operazione supererà il precedente limite, in tal caso i +dati verranno letti o scritti con le usuali \func{read} e \func{write} usando +un buffer di dimensioni sufficienti appositamente allocato e sufficiente a contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo ma si perderà l'atomicità del trasferimento da e verso la destinazione finale. @@ -4695,16 +4697,16 @@ semplicemente un ``\textsl{dimezzamento}'' di \func{sendfile}.\footnote{nel senso che un trasferimento di dati fra due file con \func{sendfile} non sarebbe altro che la lettura degli stessi su un buffer seguita dalla relativa scrittura, cosa che in questo caso si dovrebbe eseguire con due - chiamate a \func{splice}.} In realtà le due system call sono profondamente -diverse nel loro meccanismo di funzionamento;\footnote{questo fino al kernel - 2.6.23, dove \func{sendfile} è stata reimplementata in termini di - \func{splice}, pur mantenendo disponibile la stessa interfaccia verso l'user - space.} \func{sendfile} infatti, come accennato, non necessita di avere a -disposizione un buffer interno, perché esegue un trasferimento diretto di -dati; questo la rende in generale più efficiente, ma anche limitata nelle sue -applicazioni, dato che questo tipo di trasferimento è possibile solo in casi -specifici.\footnote{e nel caso di Linux questi sono anche solo quelli in cui - essa può essere effettivamente utilizzata.} + chiamate a \func{splice}.} In realtà le due \textit{system call} sono +profondamente diverse nel loro meccanismo di funzionamento;\footnote{questo + fino al kernel 2.6.23, dove \func{sendfile} è stata reimplementata in + termini di \func{splice}, pur mantenendo disponibile la stessa interfaccia + verso l'user space.} \func{sendfile} infatti, come accennato, non necessita +di avere a disposizione un buffer interno, perché esegue un trasferimento +diretto di dati; questo la rende in generale più efficiente, ma anche limitata +nelle sue applicazioni, dato che questo tipo di trasferimento è possibile solo +in casi specifici.\footnote{e nel caso di Linux questi sono anche solo quelli + in cui essa può essere effettivamente utilizzata.} Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in realtà la proposta originale di Larry Mc Voy non differisce poi tanto negli @@ -5103,16 +5105,16 @@ fig.~\ref{fig:splice_example}). Infine una nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}: occorre sottolineare che benché finora si sia parlato di trasferimenti o copie -di dati in realtà nella implementazione di queste system call non è affatto -detto che i dati vengono effettivamente spostati o copiati, il kernel infatti -realizza le \textit{pipe} come un insieme di puntatori\footnote{per essere - precisi si tratta di un semplice buffer circolare, un buon articolo sul tema - si trova su \url{http://lwn.net/Articles/118750/}.} alle pagine di memoria -interna che contengono i dati, per questo una volta che i dati sono presenti -nella memoria del kernel tutto quello che viene fatto è creare i suddetti -puntatori ed aumentare il numero di referenze; questo significa che anche con -\func{tee} non viene mai copiato nessun byte, vengono semplicemente copiati i -puntatori. +di dati in realtà nella implementazione di queste \textit{system call} non è +affatto detto che i dati vengono effettivamente spostati o copiati, il kernel +infatti realizza le \textit{pipe} come un insieme di puntatori\footnote{per + essere precisi si tratta di un semplice buffer circolare, un buon articolo + sul tema si trova su \url{http://lwn.net/Articles/118750/}.} alle pagine di +memoria interna che contengono i dati, per questo una volta che i dati sono +presenti nella memoria del kernel tutto quello che viene fatto è creare i +suddetti puntatori ed aumentare il numero di referenze; questo significa che +anche con \func{tee} non viene mai copiato nessun byte, vengono semplicemente +copiati i puntatori. % TODO?? dal 2.6.25 splice ha ottenuto il supporto per la ricezione su rete @@ -5427,7 +5429,7 @@ livello di kernel. % http://lwn.net/Articles/432757/ -% LocalWords: dell'I locking multiplexing cap dell' sez system call socket BSD +% LocalWords: dell'I locking multiplexing cap sez system call socket BSD % LocalWords: descriptor client deadlock NONBLOCK EAGAIN polling select kernel % LocalWords: pselect like sys unistd int fd readfds writefds exceptfds struct % LocalWords: timeval errno EBADF EINTR EINVAL ENOMEM sleep tab signal void of diff --git a/gapil.tex b/gapil.tex index 85598e1..d73b1b0 100644 --- a/gapil.tex +++ b/gapil.tex @@ -121,7 +121,7 @@ hyperfootnotes=false]{hyperref} \begin{quote} - Copyright \copyright\ 2000-2011 Simone Piccardi. Permission is granted to + Copyright \copyright\ 2000-2014 Simone Piccardi. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being ``Un preambolo'' diff --git a/ipc.tex b/ipc.tex index ff6e69c..48cc7b8 100644 --- a/ipc.tex +++ b/ipc.tex @@ -200,9 +200,9 @@ Un programma che deve essere eseguito come \textit{CGI} deve rispondere a delle caratteristiche specifiche, esso infatti non viene lanciato da una shell, ma dallo stesso web server, alla richiesta di una specifica URL, che di solito ha la forma: -\begin{Verbatim} +\begin{Example} http://www.sito.it/cgi-bin/programma?argomento -\end{Verbatim} +\end{Example} ed il risultato dell'elaborazione deve essere presentato (con una intestazione che ne descrive il \textit{mime-type}) sullo \textit{standard output}, in modo che il server web possa reinviarlo al browser che ha effettuato la richiesta, @@ -3610,13 +3610,13 @@ quella iniziale. Il vantaggio degli oggetti di IPC POSIX è comunque che essi vengono inseriti nell'albero dei file, e possono essere maneggiati con le usuali funzioni e -comandi di accesso ai file, che funzionano come su dei file normali; questo +comandi di accesso ai file, che funzionano come su dei file normali. Questo però è vero nel caso di Linux, che usa una implementazione che lo consente, non è detto che altrettanto valga per altri kernel. In particolare, come si -può facilmente verificare con uno \cmd{strace}, sia per la memoria condivisa -che per le code di messaggi varie \textit{system call} utilizzate da Linux -corrispondono in realtà a quelle ordinarie dei file, essendo detti oggetti -realizzati come tali in appositi filesystem. +può facilmente verificare con il comando \cmd{strace}, sia per la memoria +condivisa che per le code di messaggi varie \textit{system call} utilizzate da +Linux corrispondono in realtà a quelle ordinarie dei file, essendo detti +oggetti realizzati come tali in appositi filesystem. In particolare i permessi associati agli oggetti di IPC POSIX sono identici ai permessi dei file, ed il controllo di accesso segue esattamente la stessa @@ -3650,9 +3650,9 @@ disponibili a partire dalla versione 2.3.4 delle medesime. La libreria inoltre richiede la presenza dell'apposito filesystem di tipo \texttt{mqueue} montato sulla directory \file{/dev/mqueue}; questo può essere fatto aggiungendo ad \conffile{/etc/fstab} una riga come: -\begin{Example} +\begin{FileExample}[label=/etc/fstab] mqueue /dev/mqueue mqueue defaults 0 0 -\end{Example} +\end{FileExample} ed esso sarà utilizzato come radice sulla quale vengono risolti i nomi delle code di messaggi che iniziano con una ``\texttt{/}''. Le opzioni di mount accettate sono \texttt{uid}, \texttt{gid} e \texttt{mode} che permettono @@ -3794,8 +3794,8 @@ dei limiti sono: superiore per il valore di \var{attr->mq\_msgsize} in \func{mq\_open}. Il suo valore di default è 8192. Il valore massimo è 1048576 ed il valore minimo 128 (ma per i kernel precedenti il 2.6.28 detti limiti erano - rispettivamente \const{INT\_MAX} e 8192). Questo valore viene ignorato dai - processi con privilegi amministrativi (la \itindex{capability} + rispettivamente \const{INT\_MAX} e 8192). Questo limite viene ignorato dai + processi con privilegi amministrativi (con la \itindex{capability} \textit{capability} \const{CAP\_SYS\_RESOURCE}). \item[\sysctlfile{fs/mqueue/queues\_max}] Indica il numero massimo di code di @@ -3808,9 +3808,9 @@ dei limiti sono: Infine sulle code di messaggi si applica il limite imposto sulla risorsa \const{RLIMIT\_MSGQUEUE} (vedi sez.~\ref{sec:sys_resource_limit}) che indica -lo spazio massimo (in byte) occupabile da tutte le code di messaggi -appartenenti ai processi di uno stesso utente, identificato dal loro -\textit{real user ID}. +lo spazio massimo (in byte) occupabile dall'insieme di tutte le code di +messaggi appartenenti ai processi di uno stesso utente, che viene identificato +in base al \textit{real user ID} degli stessi. Quando l'accesso alla coda non è più necessario si può chiudere il relativo descrittore con la funzione \funcd{mq\_close}, il cui prototipo è: @@ -3851,7 +3851,12 @@ funzione di sistema \funcd{mq\_unlink}, il cui prototipo è: } {La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual - caso \var{errno} assumerà gli stessi valori riportati da \func{unlink}. + caso \var{errno} assumerà gli uno dei valori: + \begin{errlist} + \item[\errcode{EACCES}] non si hanno i permessi per cancellare la coda. + \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. + \item[\errcode{ENOENT}] non esiste una coda con il nome indicato. + \end{errlist} } \end{funcproto} @@ -3870,7 +3875,7 @@ di essa. Allo stesso modo una coda ed i suoi contenuti resteranno disponibili all'interno del sistema anche quando quest'ultima non è aperta da nessun processo (questa è una delle differenze più rilevanti nei confronti di \textit{pipe} e \textit{fifo}). La sola differenza fra code di messaggi POSIX -e file normali è che, essendo il filesystem delle code di messaggi virtuale e +e file normali è che, essendo il filesystem delle code di messaggi virtuale, e basato su oggetti interni al kernel, il suo contenuto viene perduto con il riavvio del sistema. @@ -3894,33 +3899,44 @@ funzioni \funcd{mq\_getattr} e \funcd{mq\_setattr}, i cui prototipi sono: \end{funcproto} La funzione \func{mq\_getattr} legge i valori correnti degli attributi della -coda nella struttura puntata da \param{mqstat}; di questi l'unico relativo -allo stato corrente della coda è \var{mq\_curmsgs} che indica il numero di -messaggi da essa contenuti, gli altri indicano le caratteristiche generali -della stessa. +coda \param{mqdes} nella struttura \struct{mq\_attr} puntata +da \param{mqstat}; di questi l'unico relativo allo stato corrente della coda è +\var{mq\_curmsgs} che indica il numero di messaggi da essa contenuti, gli +altri indicano le caratteristiche generali della stessa impostate in fase di +apertura. La funzione \func{mq\_setattr} permette di modificare gli attributi di una -coda tramite i valori contenuti nella struttura puntata da \param{mqstat}, ma -può essere modificato solo il campo \var{mq\_flags}, gli altri campi vengono -ignorati. In particolare i valori di \var{mq\_maxmsg} e \var{mq\_msgsize} -possono essere specificati solo in fase ci creazione della coda. Inoltre i -soli valori possibili per \var{mq\_flags} sono 0 e \const{O\_NONBLOCK}, per -cui alla fine la funzione può essere utilizzata solo per abilitare o -disabilitare la modalità non bloccante. L'argomento \param{omqstat} viene -usato, quando diverso da \val{NULL}, per specificare l'indirizzo di una -struttura su cui salvare i valori degli attributi precedenti alla chiamata -della funzione. - -Per inserire messaggi su di una coda sono previste due funzioni, -\funcd{mq\_send} e \funcd{mq\_timedsend}, i cui prototipi sono: +coda (indicata da \param{mqdes}) tramite i valori contenuti nella struttura +\struct{mq\_attr} puntata da \param{mqstat}, ma può essere modificato solo il +campo \var{mq\_flags}, gli altri campi vengono comunque ignorati. + +In particolare i valori di \var{mq\_maxmsg} e \var{mq\_msgsize} possono essere +specificati solo in fase ci creazione della coda. Inoltre i soli valori +possibili per \var{mq\_flags} sono 0 e \const{O\_NONBLOCK}, per cui alla fine +la funzione può essere utilizzata solo per abilitare o disabilitare la +modalità non bloccante. L'argomento \param{omqstat} viene usato, quando +diverso da \val{NULL}, per specificare l'indirizzo di una struttura su cui +salvare i valori degli attributi precedenti alla chiamata della funzione. + +Per inserire messaggi su di una coda sono previste due funzioni di sistema, +\funcd{mq\_send} e \funcd{mq\_timedsend}. In realtà su Linux la \textit{system + call} è soltanto \func{mq\_timedsend}, mentre \func{mq\_send} viene +implementata come funzione di libreria che si appoggia alla +precedente. Inoltre \func{mq\_timedsend} richiede che sia definita la macro +\macro{\_XOPEN\_SOURCE} ad un valore pari ad almeno \texttt{600} o la macro +\macro{\_POSIX\_C\_SOURCE} ad un valore uguale o maggiore di \texttt{200112L}. +I rispettivi prototipi sono: \begin{funcproto}{ \fhead{mqueue.h} \fdecl{int mq\_send(mqd\_t mqdes, const char *msg\_ptr, size\_t msg\_len, unsigned int msg\_prio)} \fdesc{Esegue l'inserimento di un messaggio su una coda.} +\fhead{mqueue.h} +\fhead{time.h} \fdecl{int mq\_timedsend(mqd\_t mqdes, const char *msg\_ptr, size\_t - msg\_len, unsigned msg\_prio, const struct timespec *abs\_timeout)} + msg\_len, unsigned int msg\_prio,\\ +\phantom{int mq\_timedsend(}const struct timespec *abs\_timeout)} \fdesc{Esegue l'inserimento di un messaggio su una coda entro un tempo specificato} } @@ -3930,83 +3946,92 @@ Per inserire messaggi su di una coda sono previste due funzioni, \begin{errlist} \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la coda è piena. - \item[\errcode{EMSGSIZE}] la lunghezza del messaggio \param{msg\_len} - eccede il limite impostato per la coda. + \item[\errcode{EBADF}] si specificato un file descriptor non valido. \item[\errcode{EINVAL}] si è specificato un valore nullo per \param{msg\_len}, o un valore di \param{msg\_prio} fuori dai limiti, o un valore non valido per \param{abs\_timeout}. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio \param{msg\_len} + eccede il limite impostato per la coda. \item[\errcode{ETIMEDOUT}] l'inserimento del messaggio non è stato - effettuato entro il tempo stabilito. + effettuato entro il tempo stabilito (solo \func{mq\_timedsend}). \end{errlist} - ed inoltre \errval{EBADF}, \errval{ENOMEM} ed \errval{EINTR} nel loro - significato generico. -} + ed inoltre \errval{EBADF}, \errval{EINTR} nel suo significato generico. } \end{funcproto} -Entrambe le funzioni richiedono un puntatore al testo del messaggio -nell'argomento \param{msg\_ptr} e la relativa lunghezza in \param{msg\_len}. +Entrambe le funzioni richiedono un puntatore ad un buffer in memoria +contenente il testo del messaggio da inserire nella coda \param{mqdes} +nell'argomento \param{msg\_ptr}, e la relativa lunghezza in \param{msg\_len}. Se quest'ultima eccede la dimensione massima specificata da \var{mq\_msgsize} le funzioni ritornano immediatamente con un errore di \errcode{EMSGSIZE}. -L'argomento \param{msg\_prio} indica la priorità dell'argomento; i messaggi di -priorità maggiore vengono inseriti davanti a quelli di priorità inferiore (e -quindi saranno riletti per primi). A parità del valore della priorità il -messaggio sarà inserito in coda a tutti quelli con la stessa priorità. Il -valore della priorità non può eccedere il limite di sistema -\const{MQ\_PRIO\_MAX}, che nel caso è pari a 32768. +L'argomento \param{msg\_prio} indica la priorità dell'argomento, che, essendo +definito come \ctyp{unsigned int}, è sempre un intero positivo. I messaggi di +priorità maggiore vengono inseriti davanti a quelli di priorità inferiore, e +quindi saranno riletti per primi. A parità del valore della priorità il +messaggio sarà inserito in coda a tutti quelli che hanno la stessa priorità +che quindi saranno letti con la politica di una \textit{fifo}. Il valore della +priorità non può eccedere il limite di sistema \const{MQ\_PRIO\_MAX}, che al +momento è pari a 32768. Qualora la coda sia piena, entrambe le funzioni si bloccano, a meno che non -sia stata selezionata in fase di apertura la modalità non -bloccante,\footnote{o si sia impostato il flag \const{O\_NONBLOCK} sul file - descriptor della coda.} nel qual caso entrambe ritornano \errcode{EAGAIN}. -La sola differenza fra le due funzioni è che la seconda, passato il tempo -massimo impostato con l'argomento \param{abs\_timeout},\footnote{deve essere - specificato un tempo assoluto tramite una struttura \struct{timespec} (vedi - fig.~\ref{fig:sys_timespec_struct}) indicato in numero di secondi e - nanosecondi a partire dal 1 gennaio 1970.} ritorna comunque con un errore di -\errcode{ETIMEDOUT}, se invece il tempo è già scaduto al momento della -chiamata e la coda è vuota la funzione ritorna immediatamente. +sia stata selezionata in fase di apertura della stessa la modalità non +bloccante o non si sia impostato il flag \const{O\_NONBLOCK} sul file +descriptor della coda, nel qual caso entrambe ritornano con un codice di +errore di \errcode{EAGAIN}. + +La sola differenza fra le due funzioni è che \func{mq\_timedsend}, passato il +tempo massimo impostato con l'argomento \param{abs\_timeout}, ritorna comunque +con un errore di \errcode{ETIMEDOUT}, se invece il tempo è già scaduto al +momento della chiamata e la coda è piena la funzione ritorna +immediatamente. Il valore di \param{abs\_timeout} deve essere specificato come +tempo assoluto tramite una struttura \struct{timespec} (vedi +fig.~\ref{fig:sys_timespec_struct}) indicato in numero di secondi e +nanosecondi a partire dal 1 gennaio 1970. Come per l'inserimento, anche per l'estrazione dei messaggi da una coda sono -previste due funzioni, \funcd{mq\_receive} e \funcd{mq\_timedreceive}, i cui -prototipi sono: -\begin{functions} - \headdecl{mqueue.h} - - \funcdecl{ssize\_t mq\_receive(mqd\_t mqdes, char *msg\_ptr, size\_t - msg\_len, unsigned int *msg\_prio)} - Effettua la ricezione di un messaggio da una coda. - - \funcdecl{ssize\_t mq\_timedreceive(mqd\_t mqdes, char *msg\_ptr, size\_t - msg\_len, unsigned int *msg\_prio, const struct timespec *abs\_timeout)} - Effettua la ricezione di un messaggio da una coda entro il tempo - \param{abs\_timeout}. - - \bodydesc{Le funzioni restituiscono il numero di byte del messaggio in caso - di successo e -1 in caso di errore; nel quel caso \var{errno} assumerà i - valori: - \begin{errlist} +previste due funzioni di sistema, \funcd{mq\_receive} e +\funcd{mq\_timedreceive}. Anche in questo caso su Linux soltanto +\func{mq\_timedreceive} è effettivamente, una \textit{system call} e per +usarla devono essere definite le opportune macro come per +\func{mq\_timedsend}. I rispettivi prototipi sono: + +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{ssize\_t mq\_receive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len, unsigned int *msg\_prio)} +\fdesc{Effettua la ricezione di un messaggio da una coda.} +\fhead{mqueue.h} +\fhead{time.h} +\fdecl{ssize\_t mq\_timedreceive(mqd\_t mqdes, char *msg\_ptr, size\_t + msg\_len,\\ +\phantom{ssize\_t mq\_timedreceive(}unsigned int *msg\_prio, const struct timespec +*abs\_timeout)} +\fdesc{Riceve un messaggio da una coda entro un limite di tempo.} +} +{Entrambe le funzioni ritornano il numero di byte del messaggio in caso di + successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei + valori: + \begin{errlist} \item[\errcode{EAGAIN}] si è aperta la coda con \const{O\_NONBLOCK}, e la coda è vuota. - \item[\errcode{EMSGSIZE}] la lunghezza del messaggio sulla coda eccede il - valore \param{msg\_len} specificato per la ricezione. \item[\errcode{EINVAL}] si è specificato un valore nullo per \param{msg\_ptr}, o un valore non valido per \param{abs\_timeout}. + \item[\errcode{EMSGSIZE}] la lunghezza del messaggio sulla coda eccede il + valore \param{msg\_len} specificato per la ricezione. \item[\errcode{ETIMEDOUT}] la ricezione del messaggio non è stata effettuata entro il tempo stabilito. - \end{errlist} - ed inoltre \errval{EBADF}, \errval{EINTR}, \errval{ENOMEM}, o - \errval{EINVAL}.} -\end{functions} + \end{errlist} + ed inoltre \errval{EBADF} o \errval{EINTR} nel loro significato generico. } +\end{funcproto} -La funzione estrae dalla coda il messaggio a priorità più alta, o il più -vecchio fra quelli della stessa priorità. Una volta ricevuto il messaggio -viene tolto dalla coda e la sua dimensione viene restituita come valore di -ritorno.\footnote{si tenga presente che 0 è una dimensione valida e che la - condizione di errore è restituita dal valore -1; Stevens in \cite{UNP2} fa - notare che questo è uno dei casi in cui vale ciò che lo standard - \textsl{non} dice, una dimensione nulla infatti, pur non essendo citata, non - viene proibita.} +La funzione estrae dalla coda \param{mqdes} il messaggio a priorità più alta, +o il più vecchio fra quelli della stessa priorità. Una volta ricevuto il +messaggio viene tolto dalla coda e la sua dimensione viene restituita come +valore di ritorno; si tenga presente che 0 è una dimensione valida e che la +condizione di errore è indicata soltanto da un valore di +$-1$.\footnote{Stevens in \cite{UNP2} fa notare che questo è uno dei casi in + cui vale ciò che lo standard \textsl{non} dice, una dimensione nulla + infatti, pur non essendo citata, non viene proibita.} Se la dimensione specificata da \param{msg\_len} non è sufficiente a contenere il messaggio, entrambe le funzioni, al contrario di quanto avveniva nelle code @@ -4046,39 +4071,48 @@ superare in parte questo problema. Una caratteristica specifica delle code di messaggi POSIX è la possibilità di usufruire di un meccanismo di notifica asincrono; questo può essere attivato usando la funzione \funcd{mq\_notify}, il cui prototipo è: -\begin{prototype}{mqueue.h} -{int mq\_notify(mqd\_t mqdes, const struct sigevent *notification)} -Attiva il meccanismo di notifica per la coda \param{mqdes}. - -\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore; nel quel caso \var{errno} assumerà i valori: - \begin{errlist} - \item[\errcode{EBUSY}] c'è già un processo registrato per la notifica. +\begin{funcproto}{ +\fhead{mqueue.h} +\fdecl{int mq\_notify(mqd\_t mqdes, const struct sigevent *notification)} + +\fdesc{Attiva il meccanismo di notifica per una coda.} +} + +{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual + caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EBADF}] il descrittore non fa riferimento ad una coda di messaggi. - \end{errlist}} -\end{prototype} + \item[\errcode{EBUSY}] c'è già un processo registrato per la notifica. + \item[\errcode{EINVAL}] si è richiesto un meccanismo di notifica invalido + o specificato nella notifica con i segnali il valore di un segnale non + esistente. + \end{errlist} + ed inoltre \errval{ENOMEM} nel suo significato generico. + +} +\end{funcproto} Il meccanismo di notifica permette di segnalare in maniera asincrona ad un -processo la presenza di dati sulla coda, in modo da evitare la necessità di -bloccarsi nell'attesa. Per far questo un processo deve registrarsi con la -funzione \func{mq\_notify}, ed il meccanismo è disponibile per un solo -processo alla volta per ciascuna coda. +processo la presenza di dati sulla coda indicata da \param{mqdes}, in modo da +evitare la necessità di bloccarsi nell'attesa. Per far questo un processo deve +registrarsi con la funzione \func{mq\_notify}, ed il meccanismo è disponibile +per un solo processo alla volta per ciascuna coda. -Il comportamento di \func{mq\_notify} dipende dal valore dell'argomento -\param{notification}, che è un puntatore ad una apposita struttura +Il comportamento di \func{mq\_notify} dipende dai valori passati con +l'argomento \param{notification}, che è un puntatore ad una apposita struttura \struct{sigevent}, (definita in fig.~\ref{fig:struct_sigevent}) introdotta dallo standard POSIX.1b per gestire la notifica di eventi; per altri dettagli -si può vedere quanto detto in sez.~\ref{sec:sig_timer_adv} a proposito -dell'uso della stessa struttura per la notifica delle scadenze dei +su di essa si può rivedere quanto detto in sez.~\ref{sec:sig_timer_adv} a +proposito dell'uso della stessa struttura per la notifica delle scadenze dei \textit{timer}. Attraverso questa struttura si possono impostare le modalità con cui viene effettuata la notifica nel campo \var{sigev\_notify}, che può assumere i -valori di tab.~\ref{tab:sigevent_sigev_notify}.\footnote{la pagina di manuale - riporta soltanto i primi tre (inizialmente era possibile solo - \const{SIGEV\_SIGNAL}).} Il metodo consigliato è quello di usare +valori di tab.~\ref{tab:sigevent_sigev_notify}; fra questi la pagina di +manuale riporta soltanto i primi tre, ed inizialmente era possibile solo +\const{SIGEV\_SIGNAL}. Il metodo consigliato è quello di usare \const{SIGEV\_SIGNAL} usando il campo \var{sigev\_signo} per indicare il quale segnale deve essere inviato al processo. Inoltre il campo \var{sigev\_value} è un puntatore ad una struttura \struct{sigval} (definita in @@ -4092,12 +4126,12 @@ La funzione registra il processo chiamante per la notifica se \param{notification} punta ad una struttura \struct{sigevent} opportunamente inizializzata, o cancella una precedente registrazione se è \val{NULL}. Dato che un solo processo alla volta può essere registrato, la funzione fallisce -con \errcode{EBUSY} se c'è un altro processo già registrato.\footnote{questo - significa anche che se si registra una notifica con \const{SIGEV\_NONE} il - processo non la riceverà, ma impedirà anche che altri possano registrarsi - per poterlo fare.} Si tenga presente inoltre che alla chiusura del -descrittore associato alla coda (e quindi anche all'uscita del processo) ogni -eventuale registrazione di notifica presente viene cancellata. +con \errcode{EBUSY} se c'è un altro processo già registrato. Questo significa +anche che se si registra una notifica con \const{SIGEV\_NONE} il processo non +la riceverà, ma impedirà anche che altri possano registrarsi per poterlo fare. +Si tenga presente inoltre che alla chiusura del descrittore associato alla +coda (e quindi anche all'uscita del processo) ogni eventuale registrazione di +notifica presente viene cancellata. La notifica del segnale avviene all'arrivo di un messaggio in una coda vuota (cioè solo se sulla coda non ci sono messaggi) e se non c'è nessun processo @@ -4112,8 +4146,8 @@ la coda diventa disponibile per una ulteriore registrazione. Questo comporta che se si vuole mantenere il meccanismo di notifica occorre ripetere la registrazione chiamando nuovamente \func{mq\_notify} all'interno del gestore del segnale di notifica. A differenza della situazione simile che si aveva con -i segnali non affidabili,\footnote{l'argomento è stato affrontato in - \ref{sec:sig_semantics}.} questa caratteristica non configura una +i segnali non affidabili (l'argomento è stato affrontato in +\ref{sec:sig_semantics}) questa caratteristica non configura una \itindex{race~condition} \textit{race condition} perché l'invio di un segnale avviene solo se la coda è vuota; pertanto se si vuole evitare di correre il rischio di perdere eventuali ulteriori segnali inviati nel lasso di tempo che @@ -4124,13 +4158,12 @@ L'invio del segnale di notifica avvalora alcuni campi di informazione restituiti al gestore attraverso la struttura \struct{siginfo\_t} (definita in fig.~\ref{fig:sig_siginfo_t}). In particolare \var{si\_pid} viene impostato al valore del \ids{PID} del processo che ha emesso il segnale, \var{si\_uid} -all'userid effettivo, \var{si\_code} a \const{SI\_MESGQ}, e \var{si\_errno} a -0. Questo ci dice che, se si effettua la ricezione dei messaggi usando -esclusivamente il meccanismo di notifica, è possibile ottenere le informazioni -sul processo che ha inserito un messaggio usando un gestore per il segnale in -forma estesa.\footnote{di nuovo si faccia riferimento a quanto detto al - proposito in sez.~\ref{sec:sig_sigaction} e sez.~\ref{sec:sig_real_time}.} - +all'\textsl{user-ID} effettivo, \var{si\_code} a \const{SI\_MESGQ}, e +\var{si\_errno} a 0. Questo ci dice che, se si effettua la ricezione dei +messaggi usando esclusivamente il meccanismo di notifica, è possibile ottenere +le informazioni sul processo che ha inserito un messaggio usando un gestore +per il segnale in forma estesa, di nuovo si faccia riferimento a quanto detto +al proposito in sez.~\ref{sec:sig_sigaction} e sez.~\ref{sec:sig_real_time}. \subsection{Memoria condivisa} @@ -4143,18 +4176,19 @@ suoi contenuti in memoria, che viene attivato abilitando l'opzione \texttt{CONFIG\_TMPFS} in fase di compilazione del kernel. Per potere utilizzare l'interfaccia POSIX per la memoria condivisa la -\acr{glibc}\footnote{le funzioni sono state introdotte con la versione 2.2.} -richiede di compilare i programmi con l'opzione \code{-lrt}; inoltre è -necessario che in \file{/dev/shm} sia montato un filesystem \texttt{tmpfs}; -questo di norma viene fatto aggiungendo una riga del tipo di: -\begin{verbatim} +\acr{glibc} (le funzioni sono state introdotte con la versione 2.2) richiede +di compilare i programmi con l'opzione \code{-lrt}; inoltre è necessario che +in \file{/dev/shm} sia montato un filesystem \texttt{tmpfs}; questo di norma +viene fatto aggiungendo una riga del tipo di: +\begin{FileExample}[label=/etc/fstab] tmpfs /dev/shm tmpfs defaults 0 0 -\end{verbatim} -ad \conffile{/etc/fstab}. In realtà si può montare un filesystem \texttt{tmpfs} -dove si vuole, per usarlo come RAM disk, con un comando del tipo: -\begin{verbatim} +\end{FileExample} +ad \conffile{/etc/fstab}. In realtà si può montare un filesystem +\texttt{tmpfs} dove si vuole, per usarlo come RAM disk, con un comando del +tipo: +\begin{Example} mount -t tmpfs -o size=128M,nr_inodes=10k,mode=700 tmpfs /mytmpfs -\end{verbatim} +\end{Example} Il filesystem riconosce, oltre quelle mostrate, le opzioni \texttt{uid} e \texttt{gid} che identificano rispettivamente utente e gruppo cui assegnarne @@ -4982,10 +5016,10 @@ testo alla terminazione di quest'ultimo. % LocalWords: violation dell'I SIGINT setter Fri Dec Sleeping seconds ECHILD % LocalWords: SysV capability short RESOURCE INFO UNDEFINED EFBIG semtimedop % LocalWords: scan HUGETLB huge page NORESERVE copy RLIMIT MEMLOCK REMAP +% LocalWords: readmon Hierarchy defaults queues MSGQUEUE %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil" %%% End: -% LocalWords: readmon Hierarchy defaults queues MSGQUEUE diff --git a/prochand.tex b/prochand.tex index fe46b23..d13d4c6 100644 --- a/prochand.tex +++ b/prochand.tex @@ -602,7 +602,7 @@ comune dopo l'esecuzione di una \func{fork} è la seguente: sez.~\ref{sec:file_fcntl_ioctl}); \item gli identificatori per il controllo di accesso: l'\textsl{user-ID reale}, il \textsl{group-ID reale}, l'\textsl{user-ID effettivo}, il - \textsl{group-ID effettivo} ed i \textit{group-ID supplementari} (vedi + \textsl{group-ID effettivo} ed i \textsl{group-ID supplementari} (vedi sez.~\ref{sec:proc_access_id}); \item gli identificatori per il controllo di sessione: il \itindex{process~group} \textit{process group-ID} e il \textit{session id} @@ -1537,7 +1537,7 @@ seguente: \begin{itemize*} \item il \textit{process id} (\ids{PID}) ed il \textit{parent process id} (\ids{PPID}); -\item l'\textsl{user-ID reale}, il \textit{group-ID reale} ed i +\item l'\textsl{user-ID reale}, il \textsl{group-ID reale} ed i \textsl{group-ID supplementari} (vedi sez.~\ref{sec:proc_access_id}); \item la directory radice e la \index{directory~di~lavoro} directory di lavoro corrente (vedi sez.~\ref{sec:file_work_dir}); diff --git a/tcpsock.tex b/tcpsock.tex index dfa8540..8118b1e 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1325,9 +1325,10 @@ disponibile fra i sorgenti allegati alla guida nei file \file{FullRead.c} e Come si può notare le due funzioni ripetono la lettura/scrittura in un ciclo fino all'esaurimento del numero di byte richiesti, in caso di errore viene -controllato se questo è \errcode{EINTR} (cioè un'interruzione della system -call dovuta ad un segnale), nel qual caso l'accesso viene ripetuto, altrimenti -l'errore viene ritornato al programma chiamante, interrompendo il ciclo. +controllato se questo è \errcode{EINTR} (cioè un'interruzione della +\textit{system call} dovuta ad un segnale), nel qual caso l'accesso viene +ripetuto, altrimenti l'errore viene ritornato al programma chiamante, +interrompendo il ciclo. Nel caso della lettura, se il numero di byte letti è zero, significa che si è arrivati alla fine del file (per i socket questo significa in genere che @@ -2062,11 +2063,11 @@ all'esempio illustrato in fig.~\ref{fig:TCP_echo_server_first_code}. In questo modo però si introduce un altro problema. Si ricordi infatti che, come spiegato in sez.~\ref{sec:sig_gen_beha}, quando un programma si trova in -stato di \texttt{sleep} durante l'esecuzione di una system call, questa viene -interrotta alla ricezione di un segnale. Per questo motivo, alla fine -dell'esecuzione del gestore del segnale, se questo ritorna, il programma -riprenderà l'esecuzione ritornando dalla system call interrotta con un errore -di \errcode{EINTR}. +stato di \texttt{sleep} durante l'esecuzione di una \textit{system call}, +questa viene interrotta alla ricezione di un segnale. Per questo motivo, alla +fine dell'esecuzione del gestore del segnale, se questo ritorna, il programma +riprenderà l'esecuzione ritornando dalla \textit{system call} interrotta con +un errore di \errcode{EINTR}. Vediamo allora cosa comporta tutto questo nel nostro caso: quando si chiude il client, il processo figlio che gestisce la connessione terminerà, ed il padre, @@ -2083,13 +2084,13 @@ accept error: Interrupted system call \end{verbatim}%# Come accennato in sez.~\ref{sec:sig_gen_beha} le conseguenze di questo -comportamento delle system call possono essere superate in due modi diversi, -il più semplice è quello di modificare il codice di \func{Signal} per -richiedere il riavvio automatico delle system call interrotte secondo la -semantica di BSD, usando l'opzione \const{SA\_RESTART} di \func{sigaction}; -rispetto a quanto visto in fig.~\ref{fig:sig_Signal_code}. Definiremo allora la -nuova funzione \func{SignalRestart}\footnote{anche questa è definita, insieme - alle altre funzioni riguardanti la gestione dei segnali, nel file +comportamento delle \textit{system call} possono essere superate in due modi +diversi, il più semplice è quello di modificare il codice di \func{Signal} per +richiedere il riavvio automatico delle \textit{system call} interrotte secondo +la semantica di BSD, usando l'opzione \const{SA\_RESTART} di \func{sigaction}; +rispetto a quanto visto in fig.~\ref{fig:sig_Signal_code}. Definiremo allora +la nuova funzione \func{SignalRestart}\footnote{anche questa è definita, + insieme alle altre funzioni riguardanti la gestione dei segnali, nel file \file{SigHand.c}, il cui contento completo può essere trovato negli esempi allegati.} come mostrato in fig.~\ref{fig:sig_SignalRestart_code}, ed installeremo il gestore usando quest'ultima. @@ -2101,30 +2102,30 @@ installeremo il gestore usando quest'ultima. \end{minipage} \normalsize \caption{La funzione \func{SignalRestart}, che installa un gestore di - segnali in semantica BSD per il riavvio automatico delle system call - interrotte.} + segnali in semantica BSD per il riavvio automatico delle \textit{system + call} interrotte.} \label{fig:sig_SignalRestart_code} \end{figure} Come si può notare questa funzione è identica alla precedente \func{Signal}, -illustrata in fig.~\ref{fig:sig_Signal_code}, solo che in questo caso invece di -inizializzare a zero il campo \var{sa\_flags} di \struct{sigaction}, lo si +illustrata in fig.~\ref{fig:sig_Signal_code}, solo che in questo caso invece +di inizializzare a zero il campo \var{sa\_flags} di \struct{sigaction}, lo si inizializza (\texttt{\small 5}) al valore \const{SA\_RESTART}. Usando questa funzione al posto di \func{Signal} nel server non è necessaria nessuna altra -modifica: le system call interrotte saranno automaticamente riavviate, e -l'errore \errcode{EINTR} non si manifesterà più. +modifica: le \textit{system call} interrotte saranno automaticamente +riavviate, e l'errore \errcode{EINTR} non si manifesterà più. La seconda soluzione è più invasiva e richiede di controllare tutte le volte -l'errore restituito dalle varie system call, ripetendo la chiamata qualora -questo corrisponda ad \errcode{EINTR}. Questa soluzione ha però il pregio -della portabilità, infatti lo standard POSIX dice che la funzionalità di -riavvio automatico delle system call, fornita da \const{SA\_RESTART}, è -opzionale, per cui non è detto che essa sia disponibile su qualunque sistema. -Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} accenna che la maggior - parte degli Unix derivati da BSD non fanno ripartire \func{select}; altri - non riavviano neanche \func{accept} e \func{recvfrom}, cosa che invece nel - caso di Linux viene sempre fatta.} anche quando questa è presente, non è -detto possa essere usata con \func{accept}. +l'errore restituito dalle varie \textit{system call}, ripetendo la chiamata +qualora questo corrisponda ad \errcode{EINTR}. Questa soluzione ha però il +pregio della portabilità, infatti lo standard POSIX dice che la funzionalità +di riavvio automatico delle \textit{system call}, fornita da +\const{SA\_RESTART}, è opzionale, per cui non è detto che essa sia disponibile +su qualunque sistema. Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} + accenna che la maggior parte degli Unix derivati da BSD non fanno ripartire + \func{select}; altri non riavviano neanche \func{accept} e \func{recvfrom}, + cosa che invece nel caso di Linux viene sempre fatta.} anche quando questa è +presente, non è detto possa essere usata con \func{accept}. La portabilità nella gestione dei segnali però viene al costo di una @@ -2160,7 +2161,7 @@ programma. \normalsize \caption{La sezione nel codice della seconda versione del server per il servizio \textit{echo} modificata per tener conto dell'interruzione - delle system call.} + delle \textit{system call}.} \label{fig:TCP_echo_server_code_second} \end{figure} @@ -2182,12 +2183,12 @@ eventuale pausa con una condizione (\texttt{\small 21}) sulla variabile numero di secondi da aspettare (il valore preimpostato è nullo). Si è potuto lasciare inalterata tutta la sezione di creazione del socket -perché nel server l'unica chiamata ad una system call lenta, che può essere -interrotta dall'arrivo di \signal{SIGCHLD}, è quella ad \func{accept}, che è -l'unica funzione che può mettere il processo padre in stato di sleep nel +perché nel server l'unica chiamata ad una \textit{system call} lenta, che può +essere interrotta dall'arrivo di \signal{SIGCHLD}, è quella ad \func{accept}, +che è l'unica funzione che può mettere il processo padre in stato di sleep nel periodo in cui un figlio può terminare; si noti infatti come le altre -\index{system~call~lente} \textit{slow system call}\footnote{si ricordi la - distinzione fatta in sez.~\ref{sec:sig_gen_beha}.} o sono chiamate prima di +\index{system~call~lente} \textit{system call} lente (si ricordi la +distinzione fatta in sez.~\ref{sec:sig_gen_beha}) o sono chiamate prima di entrare nel ciclo principale, quando ancora non esistono processi figli, o sono chiamate dai figli stessi e non risentono di \signal{SIGCHLD}. @@ -2467,15 +2468,16 @@ avanti in sez.~\ref{sec:TCP_shutdown} la chiusura di un solo capo di un socket è una operazione lecita, per cui la nostra scrittura avrà comunque successo (come si può constatare lanciando usando \cmd{strace}\footnote{il comando \cmd{strace} è un comando di debug molto utile che prende come argomento un - altro comando e ne stampa a video tutte le invocazioni di una system call, - coi relativi argomenti e valori di ritorno, per cui usandolo in questo - contesto potremo verificare che effettivamente la \func{write} ha scritto la - riga, che in effetti è stata pure trasmessa via rete.}), in quanto il nostro -programma non ha a questo punto alcun modo di sapere che dall'altra parte non -c'è più nessuno processo in grado di leggere quanto scriverà. Questo sarà -chiaro solo dopo il tentativo di scrittura, e la ricezione del segmento RST di -risposta che indica che dall'altra parte non si è semplicemente chiuso un capo -del socket, ma è completamente terminato il programma. + altro comando e ne stampa a video tutte le invocazioni di una \textit{system + call}, coi relativi argomenti e valori di ritorno, per cui usandolo in + questo contesto potremo verificare che effettivamente la \func{write} ha + scritto la riga, che in effetti è stata pure trasmessa via rete.}), in +quanto il nostro programma non ha a questo punto alcun modo di sapere che +dall'altra parte non c'è più nessuno processo in grado di leggere quanto +scriverà. Questo sarà chiaro solo dopo il tentativo di scrittura, e la +ricezione del segmento RST di risposta che indica che dall'altra parte non si +è semplicemente chiuso un capo del socket, ma è completamente terminato il +programma. Per questo motivo il nostro client proseguirà leggendo dal socket, e dato che questo è stato chiuso avremo che, come spiegato in -- 2.30.2