From 419951b29856965957fe4cacfb61de49e140bb9b Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 10 Feb 2007 14:18:45 +0000 Subject: [PATCH] Sezione sulle limitazioni dei gestori di segnali, e avanti sui semafori POSIX --- ipc.tex | 212 +++++++++++++++++++++++++++++++++++++++++++-------- signal.tex | 97 ++++++++++++++++++++++- sockctrl.tex | 2 +- 3 files changed, 274 insertions(+), 37 deletions(-) diff --git a/ipc.tex b/ipc.tex index b7ccdd8..12cd3c0 100644 --- a/ipc.tex +++ b/ipc.tex @@ -4074,13 +4074,14 @@ successo e proseguire. Si tenga presente che la funzione può sempre essere interrotta da un segnale (nel qual caso si avrà un errore di \const{EINTR}) e che questo avverrà -comunque, anche se si è installato il relativo gestore con \const{SA\_RESTART} -(vedi sez.~\ref{sec:sig_sigaction}) per riavviare le system call interrotte. +comunque, anche se si è richiesta la semantica BSD installando il relativo +gestore con \const{SA\_RESTART} (vedi sez.~\ref{sec:sig_sigaction}) per +riavviare le system call interrotte. Della funzione \func{sem\_wait} esistono due varianti che consentono di gestire diversamente le modalità di attesa in caso di risorsa occupata, la -prima di queste è \funcd{sem\_trywait} che serve ad effettuare un tentativo di -acquisizione senza bloccarsi; il suo prototipo è: +prima di queste è \funcd{sem\_trywait}, che serve ad effettuare un tentativo +di acquisizione senza bloccarsi; il suo prototipo è: \begin{functions} \headdecl{semaphore.h} @@ -4098,14 +4099,17 @@ acquisizione senza bloccarsi; il suo prototipo } \end{functions} -La funzione è identica a \func{sem\_wait} ed ha lo stesso effetto (vale a dire -che in caso di risorsa disponibile questa viene immediatamente acquisita), la -differenza è che nel caso in cui il semaforo è occupato essa non si blocca e -ritorna invece immediatamente, con un errore di \errval{EAGAIN}. - -La seconda variante è una estensione che può essere utilizzata soltanto se si -definisce la macro \macro{\_XOPEN\_SOURCE} ad un valore di 600 prima di -includere \texttt{semaphore.h}, è \func{sem\_timedwait}, il cui prototipo è: +La funzione è identica a \func{sem\_wait} ed se la risorsa è libera ha lo +stesso effetto, vale a dire che in caso di semaforo diverso da zero la +funzione lo decrementa e ritorna immediatamente; la differenza è che nel caso +in cui il semaforo è occupato essa non si blocca e di nuovo ritorna +immediatamente, restituendo però un errore di \errval{EAGAIN}, così che il +programma possa proseguire. + +La seconda variante di \func{sem\_wait} è una estensione specifica che può +essere utilizzata soltanto se viene definita la macro \macro{\_XOPEN\_SOURCE} +ad un valore di 600 prima di includere \texttt{semaphore.h}, la funzione è +\func{sem\_timedwait}, ed il suo prototipo è: \begin{functions} \headdecl{semaphore.h} @@ -4119,18 +4123,23 @@ includere \texttt{semaphore.h}, \begin{errlist} \item[\errcode{ETIMEDOUT}] è scaduto il tempo massimo di attesa. \item[\errcode{EINVAL}] il semaforo \param{sem} non esiste. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. \end{errlist} } \end{functions} Anche in questo caso il comportamento della funzione è identico a quello di -\func{sem\_wait}, solo che in questo caso è possibile impostare, con -l'argomento \param{abs\_timeout}, un tempo limite scaduto il quale la funzione -ritorna comunque anche se non è possibile acquisire il semaforo, riportando un -errore di \errval{ETIMEDOUT}. - -La seconda funzione di gestione dei semafori è \funcd{sem\_post}, che viene -utilizzata per rilasciare un semaforo occupato; il suo prototipo è: +\func{sem\_wait}, la sola differenza consiste nel fatto che con questa +funzione è possibile impostare tramite l'argomento \param{abs\_timeout} un +tempo limite per l'attesa, scaduto il quale la funzione ritorna comunque, +anche se non è possibile acquisire il semaforo. In tal caso la funzione +fallirà, riportando un errore di \errval{ETIMEDOUT}. + +La seconda funzione principale utilizzata per l'uso dei semafori è +\funcd{sem\_post}, che viene usata per rilasciare un semaforo occupato o, in +generale, per aumentare di una unità il valore dello stesso anche qualora non +fosse occupato;\footnote{si ricordi che in generale un semaforo viene usato + come indicatore di un numero di risorse disponibili.} il suo prototipo è: \begin{functions} \headdecl{semaphore.h} @@ -4146,24 +4155,161 @@ utilizzata per rilasciare un semaforo occupato; il suo prototipo } \end{functions} -La funzione incrementa il valore del semaforo puntato dall'argomento -\param{sem}, se questo era nullo la relativa risorsa risulterà sbloccata, -cosicché un altro processo (o thread) bloccato in una \func{sem\_wait} sul -suddetto semaforo potrà essere svegliato e rimesso in esecuzione. Si tenga -presente che la funzione è è sicura per l'uso all'interno di un gestore di -segnali. +La funzione incrementa di uno il valore corrente del semaforo indicato +dall'argomento \param{sem}, se questo era nullo la relativa risorsa risulterà +sbloccata, cosicché un altro processo (o thread) eventualmente bloccato in una +\func{sem\_wait} sul semaforo potrà essere svegliato e rimesso in esecuzione. +Si tenga presente che la funzione è sicura \index{funzioni~sicure} per l'uso +all'interno di un gestore di segnali (si ricordi quanto detto in +sez.~\ref{sec:sig_signal_handler}). + +Se invece di operare su un semaforo se ne vuole solamente leggere il valore, +si può usare la funzione \funcd{sem\_getvalue}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_getvalue(sem\_t *sem, int *sval)} + + Richiede il valore del semaforo \param{sem}. + + \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{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} + +La funzione legge il valore del semaforo indicato dall'argomento \param{sem} e +lo restituisce nella variabile intera puntata dall'argomento +\param{sval}. Qualora ci siano uno o più processi bloccati in attesa sul +semaforo lo standard prevede che la funzione possa restituire un valore nullo +oppure il numero di processi bloccati in una \func{sem\_wait} sul suddetto +semaforo; nel caso di Linux vale la prima opzione. + +Questa funzione può essere utilizzata per avere un suggerimento sullo stato di +un semaforo, ovviamente non si può prendere il risultato riportato in +\param{sval} che come indicazione, il valore del semaforo infatti potrebbe +essere già stato modificato al ritorno della funzione. + +% TODO verificare comportamento sem_getvalue +Una volta che non ci sia più la necessità di operare su un semaforo se ne può +terminare l'uso con la funzione \funcd{sem\_close}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_close(sem\_t *sem)} + + Chiude il semaforo \param{sem}. + + \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{EINVAL}] il semaforo \param{sem} non esiste. + \end{errlist} +} +\end{functions} +La funzione chiude il semaforo indicato dall'argomento \param{sem}; questo +comporta che tutte le risorse che il sistema può avere assegnato al processo +nell'uso dello stesso vengono rilasciate. Questo significa che un altro +processo bloccato sul semaforo a causa della acquisizione da parte del +processo che chiama \func{sem\_close} potrà essere riavviato. + +Si tenga presente poi che come per i file all'uscita di un processo tutti i +semafori che questo aveva aperto vengono automaticamente chiusi; questo +comportamento risolve il problema che si aveva con i semafori del \textit{SysV + IPC} (di cui si è parlato in sez.~\ref{sec:ipc_sysv_sem}) per i quali le +risorse possono restare bloccate. Si tenga poi presente che, a differenza di +quanto avviene per i file, in caso di una chiamata ad \func{execve} tutti i +semafori vengono chiusi automaticamente. + +Come per i semafori del \textit{SysV IPC} anche quelli POSIX hanno una +persistenza di sistema; questo significa che una volta che si è creato un +semaforo con \func{sem\_open} questo continuerà ad esistere fintanto che il +kernel resta attivo (vale a dire fino ad un successivo riavvio) a meno che non +lo si cancelli esplicitamente. Per far questo si può utilizzare la funzione +\funcd{sem\_unlink}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_unlink(const char *name)} + + Rimuove il semaforo \param{name}. + + \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{EACCESS}] non si hanno i permessi necessari a cancellare il + semaforo. + \item[\errcode{ENAMETOOLONG}] il nome indicato è troppo lungo. + \item[\errcode{ENOENT}] il semaforo \param{name} non esiste. + \end{errlist} +} +\end{functions} +La funzione rimuove il semaforo indicato dall'argomento \param{name}, che +prende un valore identico a quello usato per creare il semaforo stesso con +\func{sem\_open}. Il semaforo viene rimosso dal filesystem immediatamente; ma +il semaforo viene effettivamente cancellato dal sistema soltanto quando tutti +i processi che lo avevano aperto lo chiudono. Si segue cioè la stessa +semantica usata con \func{unlink} per i file, trattata in dettaglio in +sez.~\ref{sec:file_link}. Una delle caratteristiche peculiari dei semafori POSIX è che questi possono -anche essere utilizzati in forma anonima. In questo caso si dovrà porre la -variabile che contiene l'indirizzo del semaforo in un tratto di memoria che -sia accessibile a tutti i processi in gioco. Questo può essere una variabile -globale nel caso si usino i thread (nel qual caso si parla di -\textit{thread-shared semaphore}), o un tratto di memoria condivisa nel caso -si usino o processo (nel qual caso si parla di \textit{process-shared - semaphore}). +anche essere utilizzati anche in forma anonima, senza necessità di fare +ricorso ad un nome sul filesystem o ad altri indicativi. In questo caso si +dovrà porre la variabile che contiene l'indirizzo del semaforo in un tratto di +memoria che sia accessibile a tutti i processi in gioco. La funzione che +consente di inizializzare un semaforo anonimo è \funcd{sem\_init}, il cui +prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_init(sem\_t *sem, int pshared, unsigned int value)} + + Inizializza il semaforo anonimo \param{sem}. + + \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{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \item[\errcode{ENOSYS}] il valore di \param{pshared} non è nullo ed il + sistema non supporta i semafori per i processi. + \end{errlist} +} +\end{functions} + +La funzione inizializza un semaforo all'indirizzo puntato dall'argomento +\param{sem}, e come per \func{sem\_open} consente di impostare un valore +iniziale con \param{value}. L'argomento \param{pshared} serve ad indicare se +il semaforo deve essere utilizzato dai \itindex{thread} thread di uno stesso +processo (con un valore nullo) o condiviso fra processi diversi (con un valore +non nullo). + +Qualora il semaforo debba essere condiviso dai \itindex{thread} thread di uno +stesso processo (nel qual caso si parla di \textit{thread-shared semaphore}), +occorrerà che \param{sem} sia l'indirizzo di una variabile visibile da tutti i +\itindex{thread} thread, si dovrà usare cioè una variabile globale o una +variabile allocata dinamicamente nello \itindex{heap} heap. + +Qualora il semaforo debba essere condiviso fra più processi (nel qual caso si +parla di \textit{process-shared semaphore}) la sola scelta possibile per +renderlo visibile a tutti è di porlo in un tratto di memoria condivisa. In +tal caso occorrerà che tutti i processi abbiano un genitore comune che ha +allocato, con uno dei metodi possibili visti con \func{shm\_open} +(sez.~\ref{sec:ipc_posix_shm}), \func{mmap} (sez.~\ref{sec:file_memory_map}) o +\func{shmget} (sez.~\ref{sec:ipc_sysv_shm}) la memoria condivisa su cui si va +a creare il semaforo,\footnote{si ricordi che i tratti di memoria condivisa + vengono mantenuti nei processi figli attraverso la funzione \func{fork}.} a +cui essi poi potranno accedere. + +Una volta inizializzato il semaforo anonimo con \func{sem\_init} lo si potrà +utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e +\func{sem\_post}. Si tenga presente però che inizializzare due volte lo stesso +semaforo può dar luogo ad un comportamento indefinito. @@ -4218,7 +4364,7 @@ si usino o processo (nel qual caso si parla di \textit{process-shared % LocalWords: lrt blocks PAGECACHE TRUNC CLOEXEC mmap ftruncate munmap FindShm % LocalWords: CreateShm RemoveShm LIBRARY Library libmqueue FAILED EACCESS % LocalWords: ENAMETOOLONG qualchenome RESTART trywait XOPEN SOURCE timedwait -% LocalWords: process +% LocalWords: process getvalue sval execve pshared ENOSYS heap %%% Local Variables: diff --git a/signal.tex b/signal.tex index 5b28acd..7d3751b 100644 --- a/signal.tex +++ b/signal.tex @@ -2227,11 +2227,88 @@ parte l'uso di \type{sigjmp\_buf} per \param{env}, \func{longjmp}. +\subsection{Criteri di programmazione per i gestori dei segnali} +\label{sec:sig_signal_handler} + +Abbiamo finora parlato dei gestori dei segnali come funzioni chiamate in +corrispondenza della consegna di un segnale. In realtà un gestore non può +essere una funzione qualunque, in quanto esso può essere eseguito in +corrispondenza all'interruzione in un punto qualunque del programma principale, +ed ad esempio può essere problematico chiamare all'interno di un gestore di +segnali la stessa funzione che dal segnale è stata interrotta. + +\index{funzioni~sicure|(} + +Il concetto è comunque più generale e porta ad una distinzione fra quelle che +che POSIX chiama \textsl{funzioni insicure} (\textit{n'Usane function}) e +\textsl{funzioni sicure} (\textit{safe function}); quando un segnale +interrompe una funzione insicura ed il gestore chiama al suo interno una +funzione insicura il sistema può dare luogo ad un comportamento indefinito. + +Tutto questo significa che un gestore di segnale deve essere programmato con +molta cura per evitare questa evenienza, pertanto è non è possibile chiamare +al suo interno una funzione qualunque, e si può ricorrere soltanto all'uso di +funzioni sicure. + +L'elenco delle funzioni sicure varia a secondo dello standard a cui si fa +riferimento, secondo quanto riportato dallo standard POSIX 1003.1 nella +revisione del 2003, le ``\textit{signal safe function}'' che possono essere +chiamate anche all'interno di un gestore di segnali sono quelle della lista +riportata in fig.~\ref{fig:sig_safe_functions}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \func{\_exit}, \func{abort}, \func{accept}, \func{access}, + \func{aio\_error} \func{aio\_return}, \func{aio\_suspend}, \func{alarm}, + \func{bind}, \func{cfgetispeed}, \func{cfgetospeed}, \func{cfsetispeed}, + \func{cfsetospeed}, \func{chdir}, \func{chmod}, \func{chown}, + \func{clock\_gettime}, \func{close}, \func{connect}, \func{creat}, + \func{dup}, \func{dup2}, \func{execle}, \func{execve}, \func{fchmod}, + \func{fchown}, \func{fcntl}, \func{fdatasync}, \func{fork}, + \func{fpathconf}, \func{fstat}, \func{fsync}, \func{ftruncate}, + \func{getegid}, \func{geteuid}, \func{getgid}, \func{getgroups}, + \func{getpeername}, \func{getpgrp}, \func{getpid}, \func{getppid}, + \func{getsockname}, \func{getsockopt}, \func{getuid}, \func{kill}, + \func{link}, \func{listen}, \func{lseek}, \func{lstat}, \func{mkdir}, + \func{mkfifo}, \func{open}, \func{pathconf}, \func{pause}, \func{pipe}, + \func{poll}, \func{posix\_trace\_event}, \func{pselect}, \func{raise}, + \func{read}, \func{readlink}, \func{recv}, \func{recvfrom}, + \func{recvmsg}, \func{rename}, \func{rmdir}, \func{select}, + \func{sem\_post}, \func{send}, \func{sendmsg}, \func{sendto}, + \func{setgid}, \func{setpgid}, \func{setsid}, \func{setsockopt}, + \func{setuid}, \func{shutdown}, \func{sigaction}, \func{sigaddset}, + \func{sigdelset}, \func{sigemptyset}, \func{sigfillset}, + \func{sigismember}, \func{signal}, \func{sigpause}, \func{sigpending}, + \func{sigprocmask}, \func{sigqueue}, \func{sigset}, \func{sigsuspend}, + \func{sleep}, \func{socket}, \func{socketpair}, \func{stat}, + \func{symlink}, \func{sysconf}, \func{tcdrain}, \func{tcflow}, + \func{tcflush}, \func{tcgetattr}, \func{tcgetgrp}, \func{tcsendbreak}, + \func{tcsetattr}, \func{tcsetpgrp}, \func{time}, \func{timer\_getoverrun}, + \func{timer\_gettime}, \func{timer\_settime}, \func{times}, \func{umask}, + \func{uname}, \func{unlink}, \func{utime}, \func{wait}, \func{waitpid}, + \func{write}. + \end{minipage} + \normalsize + \caption{Elenco delle funzioni sicure secondo lo standard POSIX + 1003.1-2003.} + \label{fig:sig_safe_functions} +\end{figure} + +\index{funzioni~sicure|)} + +Per questo motivo è opportuno mantenere al minimo indispensabile le operazioni +effettuate all'interno di un gestore di segnali, qualora si debbano compiere +operazioni complesse è sempre preferibile utilizzare la tecnica in cui si usa +il gestore per impostare il valore di una qualche variabile globale, e poi si +eseguono le operazioni complesse nel programma verificando (con tutti gli +accorgimenti visti in precedenza) il valore di questa variabile tutte le volte +che si è rilevata una interruzione dovuta ad un segnale. + \subsection{I segnali real-time} \label{sec:sig_real_time} - Lo standard POSIX.1b, nel definire una serie di nuove interfacce per i servizi real-time, ha introdotto una estensione del modello classico dei segnali che presenta dei significativi miglioramenti,\footnote{questa estensione è stata @@ -2465,6 +2542,8 @@ questa maniera devono essere mascherati per tutti i thread, compreso quello dedicato alla gestione, che potrebbe riceverlo fra due chiamate successive. + + % LocalWords: kernel POSIX timer shell control ctrl kill raise signal handler % LocalWords: reliable unreliable fig race condition sez struct process table % LocalWords: delivered pending scheduler sigpending l'I suspend SIGKILL wait @@ -2495,8 +2574,20 @@ dedicato alla gestione, che potrebbe riceverlo fra due chiamate successive. % LocalWords: how oldset BLOCK UNBLOCK SETMASK sigsuspend sigaltstack malloc % LocalWords: SIGSTKSZ MINSIGSTKSZ ss oss ENOMEM flags DISABLE sp setrlimit LB % LocalWords: RLIMIT rlim sigsetjmp siglongjmp sigjmp buf env savesigs jmp ptr -% LocalWords: SIGRTMIN SIGRTMAX sigval sigevent sigqueue EAGAIN sysctl -% LocalWords: QUEUE thread sigwait sigwaitinfo sigtimedwait info DEF SLB +% LocalWords: SIGRTMIN SIGRTMAX sigval sigevent sigqueue EAGAIN sysctl safe +% LocalWords: QUEUE thread sigwait sigwaitinfo sigtimedwait info DEF SLB bind +% LocalWords: function accept return cfgetispeed cfgetospeed cfsetispeed chdir +% LocalWords: cfsetospeed chmod chown gettime close connect creat dup execle +% LocalWords: execve fchmod fchown fdatasync fpathconf fstat fsync ftruncate +% LocalWords: getegid geteuid getgid getgroups getpeername getpgrp getppid sem +% LocalWords: getsockname getsockopt getuid listen lseek lstat mkdir mkfifo +% LocalWords: pathconf poll posix pselect read readlink recv recvfrom recvmsg +% LocalWords: rename rmdir select send sendmsg sendto setgid setpgid setsid +% LocalWords: setsockopt setuid shutdown sigpause socketpair stat symlink +% LocalWords: sysconf tcdrain tcflow tcflush tcgetattr tcgetgrp tcsendbreak +% LocalWords: tcsetattr tcsetpgrp getoverrun times umask uname unlink utime +% LocalWords: write sival + %%% Local Variables: %%% mode: latex diff --git a/sockctrl.tex b/sockctrl.tex index 5212f07..9c8c95f 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -3594,7 +3594,7 @@ sono le seguenti: dell'interfaccia, e restituisce il relativo nome in \var{ifr\_name}. Il kernel infatti assegna ad ogni interfaccia un numero progressivo, detto - appunto \index{interface index} \textit{interface index}, che è quello che + appunto \itindex{interface~index} \textit{interface index}, che è quello che effettivamente la identifica nelle operazioni a basso livello, il nome dell'interfaccia è soltanto una etichetta associata a detto \textsl{indice}, che permette di rendere più comprensibile l'indicazione dell'interfaccia -- 2.30.2