X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=signal.tex;h=c2345f01e1c49dc0ba19659c9775ca2a2d6d81f6;hp=3bc1af18268fc02f28356d0a52e9988d0fd3d5f5;hb=0c63d62732e14ab73d5f1f0a0e5d70f3873bc334;hpb=52c484e2b0db3aba63c356136bea66f9aec48ef5 diff --git a/signal.tex b/signal.tex index 3bc1af1..c2345f0 100644 --- a/signal.tex +++ b/signal.tex @@ -2,7 +2,7 @@ \label{cha:signals} I segnali sono il primo e più semplice meccanismo di comunicazione nei -confronti dei processi. Non portano con se nessuna informazione che non sia il +confronti dei processi. Non portano con sé nessuna informazione che non sia il loro tipo; si tratta in sostanza di un'interruzione software portata ad un processo. @@ -15,7 +15,7 @@ esempio vengono usati per il controllo di sessione), per notificare eventi In questo capitolo esamineremo i vari aspetti della gestione dei segnali, partendo da una introduzione relativa ai concetti base con cui essi vengono realizzati, per poi affrontarne la classificazione a secondo di uso e modalità -di generazionem fino ad esaminare in dettaglio funzioni e le metodologie di +di generazione fino ad esaminare in dettaglio funzioni e le metodologie di gestione. @@ -243,7 +243,7 @@ non \var{task\_struct} del processo; si dice così che il segnale diventa \textsl{pendente} (o \textit{pending}), e rimane tale fino al momento in cui verrà notificato al processo (o verrà specificata come azione di default -quella di ingorarlo). +quella di ignorarlo). Normalmente l'invio al processo che deve ricevere il segnale è immediato ed avviene non appena questo viene rimesso in esecuzione dallo scheduler che @@ -372,7 +372,7 @@ anche a seconda dell'architettura hardware. Per questo motivo ad ogni segnale viene associato un nome, definendo con una macro di preprocessore una costante uguale al suddetto numero. Sono questi -nomi, che sono standardizzati e sostanzialemnte uniformi rispetto alle varie +nomi, che sono standardizzati e sostanzialmente uniformi rispetto alle varie implementazioni, che si devono usare nei programmi. Tutti i nomi e le funzioni che concernono i segnali sono definiti nell'header di sistema \file{signal.h}. @@ -819,20 +819,20 @@ eseguire un manipolatore non comporta nessun inconveniente. Esistono però dei casi in cui questo non è possibile perché renderebbe impossibile una risposta pronta al segnale. In generale questo avviene tutte le volte che si ha a che fare con system call che possono bloccarsi -indenfinitamente, che per questo vengono chiamate \textsl{lente}. Un elenco +indefinitamente, che per questo vengono chiamate \textsl{lente}. Un elenco dei casi in cui si presenta questa situazione è il seguente: \begin{itemize*} \item lettura da file che possono bloccarsi in attesa di dati non ancora presenti (come per certi file di dispositivo, la rete o le pipe). \item scrittura sugli stessi file, nel caso in cui dati non possano essere accettati immediatamente. -\item apertura di un file di dipositivo che richiede operazioni non immediate +\item apertura di un file di dispositivo che richiede operazioni non immediate per una una risposta. \item operazioni eseguite con \func{ioctl} che non è detto possano essere eseguite immediatamente. \item le funzioni di intercomunicazione che si bloccano in attesa di risposte da altri processi. -\item la funzione \func{pause} (usata appunto per attendere l'arrivo di un +\item la funzione \func{pause} (usata appunto per attendere l'-arrivo di un segnale). \item la funzione \func{wait} (se nessun processo figlio è ancora terminato). \end{itemize*} @@ -945,7 +945,7 @@ un ciclo infinito. \label{sec:sig_kill_raise} Come accennato in \secref{sec:sig_types}, un segnale può essere generato -direttamente da un processo. L'invio di un sengale generico può essere +direttamente da un processo. L'invio di un segnale generico può essere effettuato attraverso delle funzioni \func{kill} e \func{raise}. La prima serve per inviare un segnale al processo corrente, ed il suo prototipo è: \begin{prototype}{signal.h}{int raise(int sig)} @@ -1180,7 +1180,7 @@ Si deve comunque tenere presente che la precisione di queste funzioni limitata da quella del timer di sistema (in genere 10~ms). Il sistema assicura comunque che il segnale non sarà mai generato prima della scadenza programmata (l'arrotondamento cioè è sempre effettuato per eccesso). Una seconda causa di -potenziali ritardi è che il segnale viene generato alla scandenza del timer, +potenziali ritardi è che il segnale viene generato alla scadenza del timer, ma poi deve essere consegnato; se il processo è attivo (questo è sempre vero per \macro{ITIMER\_VIRT}) la consegna è immediata, altrimenti può esserci un ulteriore ritardo che può variare a seconda del carico del sistema. @@ -1236,16 +1236,94 @@ segnale Pone il processo in stato di sleep fino al ritorno di un manipolatore. \bodydesc{La funzione ritorna solo dopo che un segnale è stato ricevuto ed - il relativo manipilatore è ritornato, nel qual caso restituisce -1 e setta + il relativo manipolatore è ritornato, nel qual caso restituisce -1 e setta \var{errno} a \macro{EINTR}.} \end{prototype} +La funzione segnala sempre una condizione di errore (il successo sarebbe +quello di aspettare indefinitamente). In genere si usa questa funzione quando +si vuole mettere un processo in attesa di un qualche evento specifico che non +è sotto il suo diretto controllo (ad esempio la si può usare per far reagire +il processo ad un segnale inviato da un altro processo). + + +Se invece si vuole fare attendere un processo per un determinato intervallo di +tempo lo standard POSIX.1 definisce la funzione \func{sleep}, il cui prototipo +è: +\begin{prototype}{unistd.h}{unsigned int sleep(unsigned int seconds)} + + Pone il processo in stato di sleep per \param{seconds} secondi. + + \bodydesc{La funzione restituisce zero se l'attesa viene completata, o il + numero di secondi restanti se viene interrotta da un segnale.} +\end{prototype} + +La funzione attende per il tempo specificato, a meno di non essere interrotta +da un segnale. In questo caso non è una buona idea ripetere la chiamata per il +tempo rimanente, in quanto la riattivazione del processo può avvenire in un +qualunque momento, ma il valore restituito sarà sempre arrotondato al secondo, +con la conseguenza che, se la successione dei segnali è particolarmente +sfortunata, si potranno avere ritardi anche di parecchi secondi. In genere la +scelta più sicura è quella di stabilire un termine per l'attesa, e ricalcolare +tutte le volte il numero di secondi da aspettare. + +In alcune implementazioni inoltre l'uso di \func{sleep} può avere conflitti +con quello di \macro{SIGALRM}, dato che la funzione può essere realizzata +attraverso \func{pause} e \func{alarm} (in maniera analoga all'esempio che +vedremo fra poco). In tal caso mescolare chiamata di \func{alarm} e +\func{sleep} o modificare l'azione di \macro{SIGALRM}, può causare risultati +indefiniti. Nel caso delle \acr{glibc} è stata usata una implementazione +completamente indipendente e questi problemi non ci sono. + +La granularità di \func{sleep} permette di specificare attese in secondi, per +questo sia sotto BSD4.3 che in SUSv2 è stata definita la funzione +\func{usleep} (dove la \texttt{u} è intesa come sostituzione di $\mu$); i due +standard hanno delle definizioni diverse, ma le \acr{glibc} +seguono\footnote{secondo la man page almeno dalla versione 2.2.2.} seguono +quella di SUSv2 che prevede il seguente prototipo: +\begin{prototype}{unistd.h}{int usleep(unsigned long usec)} + + Pone il processo in stato di sleep per \param{usec} microsecondi. + + \bodydesc{La funzione restituisce zero se l'attesa viene completata, o -1 in + caso di errore, nel qual caso \var{errno} è settata a \macro{EINTR}.} + +\end{prototype} + +Anche questa funzione può presentare problemi nell'interazione con +\func{alarm} e \macro{SIGALRM}, ed è pertanto deprecata in favore di +\func{nanosleep}, definita dallo standard POSIX1.b, il cui prototipo è: +\begin{prototype}{unistd.h}{int nanosleep(const struct timespec *req, struct + timespec *rem)} + + Pone il processo in stato di sleep per il tempo specificato da \param{req}. + + \bodydesc{La funzione restituisce zero se l'attesa viene completata, o -1 in + caso di errore, nel qual caso \var{errno} è settata a \macro{EINVAL} o + \macro{EINTR}.} +\end{prototype} -\subsection{Le semantiche di \macro{SIGCHLD}} + + +\subsection{La gestione di \macro{SIGCHLD}} \label{sec:sig_sigchld} +Un semplice esempio per illustrare il funzionamento di un manipolatore di +segnale è quello della gestione di \macro{SIGCHLD}. Abbiamo visto in +\secref{sec:proc_termination} che una delle azioni eseguite dal kernel alla +conclusione di un processo è quella di inviare questo segnale al +padre;\footnote{in realtà in SRV4 eredita la semantica di System V, in cui il + segnale si chiama \macro{SIGCLD} e viene trattato in maniera speciale; se si + setta esplicitamente l'azione a \macro{SIG\_IGN} il segnale non viene + generato ed il sistema non genera zombie (lo stato di terminazione viene + scartato senza dover chiamare una wait), l'azione di default è sempre quella + di ignorare il segnale, ma non attiva questo comportamento. Linux, come BSD + e POSIX, non supporta questa semantica ed usa il nome di \macro{SIGCLD} come + sinonimo di \macro{SIGCHLD}.} è pertanto naturale completare qui la +trattazione della terminazione dei processi illustrando le modalità per +gestire questo segnale. @@ -1253,6 +1331,12 @@ segnale \label{sec:sig_control} +\subsection{Un esempio di problema} +\label{sec:sig_example} + +Come accennato è possibile implementare \func{sleep} a partire da dall'uso di +\func{pause} e \func{alarm}; + \subsection{Le funzioni \func{sigprocmask} e \func{sigpending}}