From 81ff87c3e2a6ecd3e33867798cba0d27576f44d0 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Tue, 9 Jul 2002 17:29:24 +0000 Subject: [PATCH] Sistemati gli indici. --- fileadv.tex | 64 ++++++++++++++++++++++++++++++++++++++++++------ filedir.tex | 20 ++++++++------- fileunix.tex | 69 +++++++++++++++++++++++++++------------------------- intro.tex | 12 ++++----- process.tex | 53 +++++++++++++++++++++------------------- prochand.tex | 33 +++++++++++++------------ signal.tex | 66 +++++++++++++++++++++++++------------------------ system.tex | 23 +++++++++--------- 8 files changed, 201 insertions(+), 139 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 067e1ff..7442379 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -84,11 +84,56 @@ Attende che un certo insieme di file descriptor cambi stato. \end{prototype} La funzione mette il processo in stato di \textit{sleep} (vedi -\ref{tab:proc_proc_states}) +\tabref{tab:proc_proc_states}) fintanto che non viene rilevate dell'attività +sull'insieme dei file descriptor specificati (\param{readfds}, +\param{writefds} e \param{exceptfds}), per un tempo massimo specificato da +\param{timeout}. + +Per specificare quali file descriptor si intende selezionare, la funzione usa +un particolare oggetto, il \textit{file descriptor set}, identificato dal tipo +\type{fd\_set}, che serve ad identificare un insieme di file descriptor, in +maniera analoga a come un \textit{signal set} (vedi \secref{sec:sig_sigset}) +identifica un insieme di segnali. Per la manipolazione di questi \textit{file + descriptor set} si possono usare delle opportune macro di preprocessore: +\begin{functions} + \headdecl{sys/select.h} + \funcdecl{FD\_ZERO(fd\_set *set)} + Inizializza l'insieme (vuoto). + + \funcdecl{FD\_SET(int fd, fd\_set *set)} + Inserisce il file descriptor \param{fd} nell'insieme. + + \funcdecl{FD\_CLR(int fd, fd\_set *set)} + Rimuove il file descriptor \param{fd} nell'insieme. + + \funcdecl{FD\_ISSET(int fd, fd\_set *set)} + Controlla se il file descriptor \param{fd} è nell'insieme. +\end{functions} +In genere un \textit{file descriptor set} può contenere fino ad un massimo di +\macro{FD\_SETSIZE} file descriptor. Questo a seconda del sistema può essere +il limite del numero massimo di file aperti\footnote{ad esempio in Linux, fino + alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma quando, +come nelle versioni più recenti del kernel, questo limite non c'è un massimo, +esso indica le dimensioni in munero di bit utilizzabili per l'insieme. -il cui prototipo è: +La funzione richiede di specificare tre insiemi distinti di file descriptor; +il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di +input in lettura, il secondo, \param{writefds} per verificare la possibilità +di scrivere ed il terzo, \param{exceptfds}, per verificare l'esistenza di +eccezioni. I corrispondenti valori dei \textit{file descriptor set} saranno +modificati di conseguenza per mostrare quale dei file descriptor ha cambiato +stato. + + + + + +Come accennato l'interfaccia di \func{select} è una estensione aggiunta BSD, e +poi entrata a far parte di POSIX; allo stesso tempo System V aveva introdotto +una interfaccia alternativa, basata sulla funzione \func{poll}, il cui +prototipo è: \begin{prototype}{sys/poll.h} {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)} @@ -97,7 +142,9 @@ specificati da \param{ufds}. \bodydesc{La funzione restituisce il numero di file descriptor con attività in caso di successo, o 0 se c'è stato un timeout; in caso di errore viene - restituito -1 ed \var{errno} viene .} + restituito -1 ed \var{errno} viene settata ai valori: + +.} \end{prototype} @@ -145,11 +192,12 @@ diversi. In quell'occasione si in \textit{append mode}, quando più processi scrivono contemporaneamente sullo stesso file non è possibile determinare la sequenza in cui essi opereranno. -Questo causa la possibilità di race condition; in generale le situazioni più -comuni sono due: l'interazione fra un processo che scrive e altri che leggono, -in cui questi ultimi possono leggere informazioni scritte solo in maniera -parziale o incompleta; o quella in cui diversi processi scrivono, mescolando -in maniera imprevedebile il loro output sul file. +Questo causa la possibilità di race condition\index{race condition}; in +generale le situazioni più comuni sono due: l'interazione fra un processo che +scrive e altri che leggono, in cui questi ultimi possono leggere informazioni +scritte solo in maniera parziale o incompleta; o quella in cui diversi +processi scrivono, mescolando in maniera imprevedebile il loro output sul +file. In tutti questi casi il \textit{file locking} è la tecnica che permette di evitare le race condition, attraverso una serie di funzioni che permettono di diff --git a/filedir.tex b/filedir.tex index 66b1f99..c389a7d 100644 --- a/filedir.tex +++ b/filedir.tex @@ -793,7 +793,8 @@ POSIX definisce la funzione \func{tempfile}, il cui prototipo automaticamente cancellato alla sua chiusura o all'uscita dal programma. Lo standard non specifica in quale directory verrà aperto il file, ma le \acr{glibc} prima tentano con \macro{P\_tmpdir} e poi con \file{/tmp}. Questa -funzione è rientrante e non soffre di problemi di \textit{race condition}. +funzione è rientrante e non soffre di problemi di \textit{race + condition}\index{race condition}. Alcune versioni meno recenti di Unix non supportano queste funzioni; in questo caso si possono usare le vecchie funzioni \func{mktemp} e \func{mkstemp} che @@ -814,12 +815,13 @@ il suo prototipo \end{prototype} \noindent dato che \param{template} deve poter essere modificata dalla funzione non si può usare una stringa costante. Tutte le avvertenze riguardo -alle possibili \textit{race condition} date per \func{tmpnam} continuano a -valere; inoltre in alcune vecchie implementazioni il valore di usato per -sostituire le \code{XXXXXX} viene formato con il \acr{pid} del processo più -una lettera, il che mette a disposizione solo 26 possibilità diverse per il -nome del file, e rende il nome temporaneo facile da indovinare. Per tutti -questi motivi la funzione è deprecata e non dovrebbe mai essere usata. +alle possibili \textit{race condition}\index{race condition} date per +\func{tmpnam} continuano a valere; inoltre in alcune vecchie implementazioni +il valore di usato per sostituire le \code{XXXXXX} viene formato con il +\acr{pid} del processo più una lettera, il che mette a disposizione solo 26 +possibilità diverse per il nome del file, e rende il nome temporaneo facile da +indovinare. Per tutti questi motivi la funzione è deprecata e non dovrebbe mai +essere usata. @@ -865,8 +867,8 @@ In OpenBSD \end{prototype} \noindent la directory è creata con permessi \code{0700} (al solito si veda \capref{cha:file_unix_interface} per i dettagli); dato che la creazione della -directory è sempre esclusiva i precedenti problemi di \textit{race condition} -non si pongono. +directory è sempre esclusiva i precedenti problemi di \textit{race + condition}\index{race condition} non si pongono. \section{La manipolazione delle caratteristiche dei files} diff --git a/fileunix.tex b/fileunix.tex index 56e7043..37b0906 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -3,11 +3,12 @@ Esamineremo in questo capitolo la prima delle due interfacce di programmazione -per i file, quella dei \textit{file descriptor}, nativa di Unix. Questa è -l'interfaccia di basso livello provvista direttamente dalle system call, che -non prevede funzionalità evolute come la bufferizzazione o funzioni di lettura -o scrittura formattata, e sulla quale è costruita anche l'interfaccia definita -dallo standard ANSI C che affronteremo al \capref{cha:files_std_interface}. +per i file, quella dei \textit{file descriptor}\index{file descriptor}, +nativa di Unix. Questa è l'interfaccia di basso livello provvista direttamente +dalle system call, che non prevede funzionalità evolute come la +bufferizzazione o funzioni di lettura o scrittura formattata, e sulla quale è +costruita anche l'interfaccia definita dallo standard ANSI C che affronteremo +al \capref{cha:files_std_interface}. @@ -33,10 +34,10 @@ terminate le operazioni, il file dovr canale di comunicazione impedendo ogni ulteriore operazione. All'interno di ogni processo i file aperti sono identificati da un intero non -negativo, chiamato appunto \textit{file descriptor}. Quando un file viene -aperto la funzione \func{open} restituisce questo numero, tutte le ulteriori -operazioni saranno compiute specificando questo stesso valore come argomento -alle varie funzioni dell'interfaccia. +negativo, chiamato appunto \textit{file descriptor}\index{file descriptor}. +Quando un file viene aperto la funzione \func{open} restituisce questo numero, +tutte le ulteriori operazioni saranno compiute specificando questo stesso +valore come argomento alle varie funzioni dell'interfaccia. Per capire come funziona il meccanismo occorre spiegare a grandi linee come è che il kernel gestisce l'interazione fra processi e file. Il kernel mantiene @@ -56,8 +57,8 @@ particolare: \item una tabella che contiene un puntatore alla relativa voce nella \textit{file table} per ogni file aperto. \end{itemize*} -il \textit{file descriptor} in sostanza è l'intero positivo che indicizza -quest'ultima tabella. +il \textit{file descriptor}\index{file descriptor} in sostanza è l'intero +positivo che indicizza quest'ultima tabella. La \textit{file table} è una tabella che contiene una voce per ciascun file che è stato aperto nel sistema. In Linux è costituita da strutture di tipo @@ -87,23 +88,23 @@ varie strutture di dati sulla quale essa \end{figure} Ritorneremo su questo schema più volte, dato che esso è fondamentale per capire i dettagli del funzionamento dell'interfaccia dei \textit{file - descriptor}. + descriptor}\index{file descriptor}. \subsection{I file standard} \label{sec:file_std_descr} -Come accennato i \textit{file descriptor} non sono altro che un indice nella -tabella dei file aperti di ciascun processo; per questo motivo essi vengono -assegnati in successione tutte le volte che si apre un nuovo file (se non ne è -stato chiuso nessuno in precedenza). +Come accennato i \textit{file descriptor}\index{file descriptor} non sono +altro che un indice nella tabella dei file aperti di ciascun processo; per +questo motivo essi vengono assegnati in successione tutte le volte che si apre +un nuovo file (se non ne è stato chiuso nessuno in precedenza). In tutti i sistemi unix-like esiste una convenzione generale per cui ogni processo viene lanciato con almeno tre file aperti. Questi, per quanto appena -detto, avranno come \textit{file descriptor} i valori 0, 1 e 2. Benché questa -sia soltanto una convenzione, essa è seguita dalla gran parte delle -applicazioni, e non aderirvi potrebbe portare a gravi problemi di -interoperabilità. +detto, avranno come \textit{file descriptor}\index{file descriptor} i valori +0, 1 e 2. Benché questa sia soltanto una convenzione, essa è seguita dalla +gran parte delle applicazioni, e non aderirvi potrebbe portare a gravi +problemi di interoperabilità. Il primo file è sempre associato a quello che viene chiamato \textit{standard input}. È cioè il file da cui il processo si aspetta di ricevere i dati in @@ -292,9 +293,9 @@ sempre il file descriptor con il valore pi \footnotetext[2]{la man page di \func{open} segnala che questa opzione è difettosa su NFS, e che i programmi che la usano per stabilire un file di - lock possono incorrere in una race condition. Si consiglia come alternativa - di usare un file con un nome univoco e la funzione \func{link} per - verificarne l'esistenza.} + lock possono incorrere in una race condition\index{race condition}. Si + consiglia come alternativa di usare un file con un nome univoco e la + funzione \func{link} per verificarne l'esistenza.} \footnotetext[3]{\textit{Denial of Service}, si chiamano così attacchi miranti ad impedire un servizio causando una qualche forma di carico eccessivo per @@ -477,8 +478,8 @@ Si tenga presente inoltre che usare \macro{SEEK\_END} non assicura affatto che successiva scrittura avvenga alla fine del file, infatti se questo è stato aperto anche da un altro processo che vi ha scritto, la fine del file può essersi spostata, ma noi scriveremo alla posizione settata in precedenza. -(questa è una potenziale sorgente di \textit{race condition}, vedi -\secref{sec:file_atomic}). +(questa è una potenziale sorgente di +\textit{race condition}\index{race condition}, vedi \secref{sec:file_atomic}). Non tutti i file supportano la capacità di eseguire una \func{lseek}, in questo caso la funzione ritorna l'errore \macro{EPIPE}. Questo, oltre che per @@ -764,12 +765,14 @@ utilizzare meccanismi di sincronizzazione pi Un caso tipico di necessità di accesso condiviso in scrittura è quello in cui vari processi devono scrivere alla fine di un file (ad esempio un file di log). Come accennato in \secref{sec:file_lseek} settare la posizione alla fine -del file e poi scrivere può condurre ad una \textit{race condition}: infatti -può succedere che un secondo processo scriva alla fine del file fra la -\func{lseek} e la \func{write}; in questo caso, come abbiamo appena visto, il -file sarà esteso, ma il nostro primo processo avrà ancora la posizione -corrente settata con la \func{lseek} che non corrisponde più alla fine del -file, e la successiva \func{write} sovrascriverà i dati del secondo processo. +del file e poi scrivere può condurre ad una +\textit{race condition}\index{race condition}: +infatti può succedere che un secondo processo scriva alla fine +del file fra la \func{lseek} e la \func{write}; in questo caso, come abbiamo +appena visto, il file sarà esteso, ma il nostro primo processo avrà ancora la +posizione corrente settata con la \func{lseek} che non corrisponde più alla +fine del file, e la successiva \func{write} sovrascriverà i dati del secondo +processo. Il problema è che usare due system call in successione non è un'operazione atomica; il problema è stato risolto introducendo la modalità @@ -783,8 +786,8 @@ Un altro caso tipico in cui creare un file di lock, bloccandosi se il file esiste. In questo caso la sequenza logica porterebbe a verificare prima l'esistenza del file con una \func{stat} per poi crearlo con una \func{creat}; di nuovo avremmo la -possibilità di una race condition da parte di un altro processo che crea lo -stesso file fra il controllo e la creazione. +possibilità di una race condition\index{race condition} da parte di un altro +processo che crea lo stesso file fra il controllo e la creazione. Per questo motivo sono stati introdotti pe \func{open} i due flag \macro{O\_CREAT} e \macro{O\_EXCL}. In questo modo l'operazione di controllo diff --git a/intro.tex b/intro.tex index 6b7be35..73f6761 100644 --- a/intro.tex +++ b/intro.tex @@ -61,12 +61,12 @@ soltanto attraverso delle opportune chiamate al sistema che restituiranno il controllo al kernel. La memoria viene sempre gestita dal kernel attraverso il meccanismo della -\textsl{memoria virtuale}, che consente di assegnare a ciascun processo uno -spazio di indirizzi ``virtuale'' (vedi \secref{sec:proc_memory}) che il kernel -stesso, con l'ausilio della unità di gestione della memoria, si incaricherà di -rimappare automaticamente sulla memoria disponibile, salvando su disco quando -necessario (nella cosiddetta area di \textit{swap}) le pagine di memoria in -eccedenza. +\textsl{memoria virtuale}\index{memoria virtuale}, che consente di assegnare a +ciascun processo uno spazio di indirizzi ``virtuale'' (vedi +\secref{sec:proc_memory}) che il kernel stesso, con l'ausilio della unità di +gestione della memoria, si incaricherà di rimappare automaticamente sulla +memoria disponibile, salvando su disco quando necessario (nella cosiddetta +area di \textit{swap}) le pagine di memoria in eccedenza. Le periferiche infine vengono viste in genere attraverso un'interfaccia astratta che permette di trattarle come fossero file, secondo il concetto per diff --git a/process.tex b/process.tex index e3a4774..b6afd42 100644 --- a/process.tex +++ b/process.tex @@ -291,8 +291,9 @@ servono. Questo meccanismo \textit{paging}), ed è uno dei compiti principali del kernel. Quando un processo cerca di accedere ad una pagina che non è nella memoria -reale, avviene quello che viene chiamato un \textit{page fault}\index{page - fault}; l'hardware di gestione della memoria genera un'interruzione e passa +reale, avviene quello che viene chiamato un +\textit{page fault}\index{page fault}; +l'hardware di gestione della memoria genera un'interruzione e passa il controllo al kernel il quale sospende il processo e si incarica di mettere in RAM la pagina richiesta (effettuando tutte le operazioni necessarie per reperire lo spazio necessario), per poi restituire il controllo al processo. @@ -319,14 +320,15 @@ tentativo di accedere ad un indirizzo non allocato commette quando si è manipolato male un puntatore e genera quello che viene chiamato un \textit{segmentation fault}. Se si tenta cioè di leggere o scrivere da un indirizzo per il quale non esiste un'associazione della pagina -virtuale, il kernel risponde al relativo \textit{page fault} +virtuale, il kernel risponde al relativo \textit{page fault}\index{page fault} mandando un segnale \macro{SIGSEGV} al processo, che normalmente ne causa la terminazione immediata. -È pertanto importante capire come viene strutturata la memoria virtuale di un -processo. Essa viene divisa in \textsl{segmenti}, cioè un insieme contiguo di -indirizzi virtuali ai quali il processo può accedere. Solitamente un -programma C viene suddiviso nei seguenti segmenti: +È pertanto importante capire come viene strutturata \textsl{la memoria + virtuale}\index{page fault} di un processo. Essa viene divisa in +\textsl{segmenti}, cioè un insieme contiguo di indirizzi virtuali ai quali il +processo può accedere. Solitamente un programma C viene suddiviso nei +seguenti segmenti: \begin{enumerate} \item Il segmento di testo o \textit{text segment}. Contiene il codice del @@ -654,13 +656,13 @@ implementare una sua versione delle routine di allocazione. % \label{sec:proc_mem_malloc_custom} -\subsection{Il controllo della memoria virtuale} +\subsection{Il controllo della memoria virtuale\index{memoria virtuale}} \label{sec:proc_mem_lock} -Come spiegato in \secref{sec:proc_mem_gen} il kernel gestisce la memoria in -maniera trasparente ai processi, decidendo quando rimuovere pagine dalla -memoria per metterle nello swap, sulla base dell'utilizzo corrente da parte -dei vari processi. +Come spiegato in \secref{sec:proc_mem_gen} il kernel gestisce la memoria +virtuale in maniera trasparente ai processi, decidendo quando rimuovere pagine +dalla memoria per metterle nello swap, sulla base dell'utilizzo corrente da +parte dei vari processi. Nell'uso comune un processo non deve preoccuparsi di tutto ciò, in quanto il meccanismo della paginazione\index{paginazione} riporta in RAM, ed in maniera @@ -708,14 +710,14 @@ comporta anche la fine dell'uso della sua memoria virtuale, e quindi anche di tutti i suoi \textit{memory lock}. I \textit{memory lock} non sono ereditati dai processi figli.\footnote{ma - siccome Linux usa il \textit{copy on write} (vedi \secref{sec:proc_fork}) - gli indirizzi virtuali del figlio sono mantenuti sullo stesso segmento di - RAM del padre, quindi fintanto che un figlio non scrive su un segmento, può - usufruire del memory lock del padre.} Siccome la presenza di un -\textit{memory lock} riduce la memoria disponibile al sistema, con un impatto -su tutti gli altri processi, solo l'amministratore ha la capacità di bloccare -una pagina. Ogni processo può però sbloccare le pagine relative alla propria -memoria. + siccome Linux usa il \textit{copy on write}\index{copy on write} (vedi + \secref{sec:proc_fork}) gli indirizzi virtuali del figlio sono mantenuti + sullo stesso segmento di RAM del padre, quindi fintanto che un figlio non + scrive su un segmento, può usufruire del memory lock del padre.} Siccome la +presenza di un \textit{memory lock} riduce la memoria disponibile al sistema, +con un impatto su tutti gli altri processi, solo l'amministratore ha la +capacità di bloccare una pagina. Ogni processo può però sbloccare le pagine +relative alla propria memoria. Il sistema pone dei limiti all'ammontare di memoria di un processo che può essere bloccata e al totale di memoria fisica che può dedicare a questo, lo @@ -787,11 +789,12 @@ esempio limitandosi a tutte le pagine allocate a partire da un certo momento. In ogni caso un processo real-time che deve entrare in una sezione critica deve provvedere a riservare memoria sufficiente prima dell'ingresso, per -scongiurare in partenza un eventuale page fault causato dal meccanismo di -\textit{copy on write}. Infatti se nella sezione critica si va ad utilizzare -memoria che non è ancora stata riportata in RAM si potrebbe avere un page -fault durante l'esecuzione della stessa, con conseguente rallentamento -(probabilmente inaccettabile) dei tempi di esecuzione. +scongiurare in partenza un eventuale page fault\index{page fault} causato dal +meccanismo di \textit{copy on write}\index{copy on write}. Infatti se nella +sezione critica si va ad utilizzare memoria che non è ancora stata riportata +in RAM si potrebbe avere un page fault durante l'esecuzione della stessa, con +conseguente rallentamento (probabilmente inaccettabile) dei tempi di +esecuzione. In genere si ovvia a questa problematica chiamando una funzione che ha allocato una quantità sufficientemente ampia di variabili automatiche, in modo diff --git a/prochand.tex b/prochand.tex index 1d805c0..e57e8ca 100644 --- a/prochand.tex +++ b/prochand.tex @@ -416,8 +416,8 @@ degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione descrizione delle opzioni); il codice completo, compresa la parte che gestisce le opzioni a riga di comando, è disponibile nel file \file{ForkTest.c}, distribuito insieme agli altri sorgenti degli esempi su -\href{http://firenze.linux.it/~piccardi/gapil_source.tgz} -{\texttt{http://firenze.linux.it/\~~\hspace{-2.0mm}piccardi/gapil\_source.tgz}}. +\href{http://gapil.firenze.linux.it/gapil_source.tgz} +{\texttt{http://gapil.firenze.linux.it/gapil\_source.tgz}}. Decifrato il numero di figli da creare, il ciclo principale del programma (\texttt{\small 24--40}) esegue in successione la creazione dei processi figli @@ -478,8 +478,9 @@ Pertanto non si pu istruzioni del codice fra padre e figli, né sull'ordine in cui questi potranno essere messi in esecuzione. Se è necessaria una qualche forma di precedenza occorrerà provvedere ad espliciti meccanismi di sincronizzazione, pena il -rischio di incorrere nelle cosiddette \textit{race condition} \index{race - condition} (vedi \secref{sec:proc_race_cond}. +rischio di incorrere nelle cosiddette +\textit{race condition}\index{race condition} +(vedi \secref{sec:proc_race_cond}). Si noti inoltre che essendo i segmenti di memoria utilizzati dai singoli processi completamente separati, le modifiche delle variabili nei processi @@ -649,10 +650,10 @@ padre, che costituiva un inutile appesantimento in tutti quei casi in cui la \func{fork} veniva fatta solo per poi eseguire una \func{exec}. La funzione venne introdotta in BSD per migliorare le prestazioni. -Dato che Linux supporta il \textit{copy on write} la perdita di prestazioni è -assolutamente trascurabile, e l'uso di questa funzione (che resta un caso -speciale della funzione \func{clone}), è deprecato; per questo eviteremo di -trattarla ulteriormente. +Dato che Linux supporta il \textit{copy on write}\index{copy on write} la +perdita di prestazioni è assolutamente trascurabile, e l'uso di questa +funzione (che resta un caso speciale della funzione \func{clone}), è +deprecato; per questo eviteremo di trattarla ulteriormente. \subsection{La conclusione di un processo.} @@ -2103,11 +2104,11 @@ processo qualsiasi sia la sua priorit interrupt vengono intercettati dall'interfaccia real-time, e gestiti direttamente qualora ci sia la necessità di avere un processo con priorità più elevata di un \textit{interrupt handler}.} mentre con l'incorrere in un -page fault si possono avere ritardi non previsti. Se l'ultimo problema può -essere aggirato attraverso l'uso delle funzioni di controllo della memoria -virtuale (vedi \secref{sec:proc_mem_lock}), il primo non è superabile e può -comportare ritardi non prevedibili riguardo ai tempi di esecuzione di -qualunque processo. +page fault\index{page fault} si possono avere ritardi non previsti. Se +l'ultimo problema può essere aggirato attraverso l'uso delle funzioni di +controllo della memoria virtuale (vedi \secref{sec:proc_mem_lock}), il primo +non è superabile e può comportare ritardi non prevedibili riguardo ai tempi di +esecuzione di qualunque processo. In ogni caso occorre usare le priorità assolute con molta attenzione: se si dà ad un processo una priorità assoluta e questo finisce in un loop infinito, @@ -2355,7 +2356,8 @@ di interruzione in una fase intermedia. In un ambiente multitasking il concetto è essenziale, dato che un processo può essere interrotto in qualunque momento dal kernel che mette in esecuzione un altro processo o dalla ricezione di un segnale; occorre pertanto essere -accorti nei confronti delle possibili \textit{race condition} (vedi +accorti nei confronti delle possibili +\textit{race condition}\index{race condition} (vedi \secref{sec:proc_race_cond}) derivanti da operazioni interrotte in una fase in cui non erano ancora state completate. @@ -2389,7 +2391,8 @@ condiviso, onde evitare problemi con le ottimizzazioni del codice. -\subsection{Le \textit{race condition} e i \textit{deadlock}} +\subsection{Le \textit{race condition}\index{race condition} e i + \textit{deadlock}} \label{sec:proc_race_cond} Si definiscono \textit{race condition} tutte quelle situazioni in cui processi diff --git a/signal.tex b/signal.tex index 246c308..1ab3bd6 100644 --- a/signal.tex +++ b/signal.tex @@ -120,8 +120,8 @@ int sig_handler() Questa è la ragione per cui l'implementazione dei segnali secondo questa semantica viene chiamata \textsl{inaffidabile}; infatti la ricezione del segnale e la reinstallazione del suo manipolatore non sono operazioni -atomiche, e sono sempre possibili delle race condition (sull'argomento vedi -quanto detto in \secref{sec:proc_multi_prog}). +atomiche, e sono sempre possibili delle race condition\index{race condition} +(sull'argomento vedi quanto detto in \secref{sec:proc_multi_prog}). Un'altro problema è che in questa semantica non esiste un modo per bloccare i segnali quando non si vuole che arrivino; i processi possono ignorare il @@ -242,7 +242,8 @@ Un programma pu \secref{sec:sig_sigaction}). Se si è installato un manipolatore sarà quest'ultimo ad essere eseguito alla notifica del segnale. Inoltre il sistema farà si che mentre viene eseguito il manipolatore di un segnale, quest'ultimo -venga automaticamente bloccato (così si possono evitare race condition). +venga automaticamente bloccato (così si possono evitare race +condition\index{race condition}). Nel caso non sia stata specificata un'azione, viene utilizzata l'azione standard che (come vedremo in \secref{sec:sig_standard}) è propria di ciascun @@ -411,9 +412,9 @@ tipologia, verr \label{sec:sig_prog_error} Questi segnali sono generati quando il sistema, o in certi casi direttamente -l'hardware (come per i page fault non validi) rileva un qualche errore -insanabile nel programma in esecuzione. In generale la generazione di questi -segnali significa che il programma ha dei gravi problemi (ad esempio ha +l'hardware (come per i \textit{page fault} non validi) rileva un qualche +errore insanabile nel programma in esecuzione. In generale la generazione di +questi segnali significa che il programma ha dei gravi problemi (ad esempio ha dereferenziato un puntatore non valido o ha eseguito una operazione aritmetica proibita) e l'esecuzione non può essere proseguita. @@ -1472,8 +1473,9 @@ tutti gli stati di terminazione sono stati ricevuti. Le funzioni esaminate finora fanno riferimento ad alle modalità più elementari della gestione dei segnali; non si sono pertanto ancora prese in -considerazione le tematiche più complesse, collegate alle varie race condition -che i segnali possono generare e alla natura asincrona degli stessi. +considerazione le tematiche più complesse, collegate alle varie race +condition\index{race condition} che i segnali possono generare e alla natura +asincrona degli stessi. Affronteremo queste problematiche in questa sezione, partendo da un esempio che le evidenzi, per poi prendere in esame le varie funzioni che permettono di @@ -1540,13 +1542,13 @@ unsigned int sleep(unsigned int seconds) Questo codice però, a parte il non gestire il caso in cui si è avuta una precedente chiamata a \func{alarm} (che si è tralasciato per brevità), -presenta una pericolosa race condition. Infatti se il processo viene -interrotto fra la chiamata di \func{alarm} e \func{pause} può capitare (ad -esempio se il sistema è molto carico) che il tempo di attesa scada prima -dell'esecuzione quest'ultima, cosicché essa sarebbe eseguita dopo l'arrivo di -\macro{SIGALRM}. In questo caso ci si troverebbe di fronte ad un deadlock, in -quanto \func{pause} non verrebbe mai più interrotta (se non in caso di un -altro segnale). +presenta una pericolosa race condition\index{race condition}. Infatti se il +processo viene interrotto fra la chiamata di \func{alarm} e \func{pause} può +capitare (ad esempio se il sistema è molto carico) che il tempo di attesa +scada prima dell'esecuzione quest'ultima, cosicché essa sarebbe eseguita dopo +l'arrivo di \macro{SIGALRM}. In questo caso ci si troverebbe di fronte ad un +deadlock, in quanto \func{pause} non verrebbe mai più interrotta (se non in +caso di un altro segnale). Questo problema può essere risolto (ed è la modalità con cui veniva fatto in SVr2) usando la funzione \func{longjmp} (vedi \secref{sec:proc_longjmp}) per @@ -1650,10 +1652,10 @@ quale potr segnale, e prendere le relative azioni conseguenti (\texttt{\small 6-11}). Questo è il tipico esempio di caso, già citato in \secref{sec:proc_race_cond}, -in cui si genera una race condition; se infatti il segnale arriva -immediatamente dopo l'esecuzione del controllo (\texttt{\small 6}) ma prima -della cancellazione del flag (\texttt{\small 7}), la sua occorrenza sarà -perduta. +in cui si genera una race condition\index{race condition}; se infatti il +segnale arriva immediatamente dopo l'esecuzione del controllo (\texttt{\small + 6}) ma prima della cancellazione del flag (\texttt{\small 7}), la sua +occorrenza sarà perduta. Questi esempi ci mostrano che per una gestione effettiva dei segnali occorrono funzioni più sofisticate di quelle illustrate finora, che hanno origine dalla @@ -2048,15 +2050,15 @@ occorre ricordare che qualunque modifica alla maschera dei segnali viene perduta alla conclusione del terminatore. Benché con l'uso di \func{sigprocmask} si possano risolvere la maggior parte -dei casi di race condition restano aperte alcune possibilità legate all'uso di -\func{pause}; il caso è simile a quello del problema illustrato nell'esempio -di \secref{fig:sig_sleep_incomplete}, e cioè la possibilità che il processo -riceva il segnale che si intende usare per uscire dallo stato di attesa -invocato con \func{pause} immediatamente prima dell'esecuzione di -quest'ultima. Per poter effettuare atomicamente la modifica della maschera dei -segnali (di solito attivandone uno specifico) insieme alla sospensione del -processo lo standard POSIX ha previsto la funzione \func{sigsuspend}, il cui -prototipo è: +dei casi di race condition\index{race condition} restano aperte alcune +possibilità legate all'uso di \func{pause}; il caso è simile a quello del +problema illustrato nell'esempio di \secref{fig:sig_sleep_incomplete}, e cioè +la possibilità che il processo riceva il segnale che si intende usare per +uscire dallo stato di attesa invocato con \func{pause} immediatamente prima +dell'esecuzione di quest'ultima. Per poter effettuare atomicamente la modifica +della maschera dei segnali (di solito attivandone uno specifico) insieme alla +sospensione del processo lo standard POSIX ha previsto la funzione +\func{sigsuspend}, il cui prototipo è: \begin{prototype}{signal.h} {int sigsuspend(const sigset\_t *mask)} @@ -2146,10 +2148,10 @@ fine (\texttt{\small 27}), e al contempo si prepara la maschera dei segnali \var{sleep\_mask} per riattivare \macro{SIGALRM} all'esecuzione di \func{sigsuspend}. -In questo modo non sono più possibili race condition dato che \macro{SIGALRM} -viene disabilitato con \func{sigprocmask} fino alla chiamata di -\func{sigsuspend}. Questo metodo è assolutamente generale e può essere -applicato a qualunque altra situazione in cui si deve attendere per un +In questo modo non sono più possibili race condition\index{race conditionx} +dato che \macro{SIGALRM} viene disabilitato con \func{sigprocmask} fino alla +chiamata di \func{sigsuspend}. Questo metodo è assolutamente generale e può +essere applicato a qualunque altra situazione in cui si deve attendere per un segnale, i passi sono sempre i seguenti: \begin{enumerate} \item Leggere la maschera dei segnali corrente e bloccare il segnale voluto diff --git a/system.tex b/system.tex index ced0465..04d92db 100644 --- a/system.tex +++ b/system.tex @@ -1395,12 +1395,13 @@ rispettivamente il tempo impiegato dal processo nell'eseguire le istruzioni in user space, e quello impiegato dal kernel nelle system call eseguite per conto del processo. -Gli altri tre campi servono a quantificare l'uso della memoria virtuale e -corrispondono rispettivamente al numero di \textit{page fault}\index{page - fault} (vedi \secref{sec:proc_mem_gen}) avvenuti senza richiedere I/O (i -cosiddetti \textit{minor page fault}), a quelli che invece han richiesto I/O -(detti invece \textit{major page fault}) ed al numero di volte che il processo -è stato completamente tolto dalla memoria per essere inserito nello swap. +Gli altri tre campi servono a quantificare l'uso della memoria +virtuale\index{memoria virtuale} e corrispondono rispettivamente al numero di +\textit{page fault}\index{page fault} (vedi \secref{sec:proc_mem_gen}) +avvenuti senza richiedere I/O (i cosiddetti \textit{minor page fault}), a +quelli che invece han richiesto I/O (detti invece \textit{major page fault}) +ed al numero di volte che il processo è stato completamente tolto dalla +memoria per essere inserito nello swap. In genere includere esplicitamente \file{} non è più necessario, ma aumenta la portabilità, e serve comunque quando, come nella maggior parte @@ -1568,12 +1569,12 @@ mantenuti attraverso una \func{exec} (vedi \secref{sec:proc_exec}). La gestione della memoria è già stata affrontata in dettaglio in \secref{sec:proc_memory}; abbiamo visto allora che il kernel provvede il -meccanismo della memoria virtuale attraverso la divisione della memoria fisica -in pagine. +meccanismo della memoria virtuale\index{memoria virtuale} attraverso la +divisione della memoria fisica in pagine. In genere questo è del tutto trasparente al singolo processo, ma in certi -casi, come per l'I/O mappato in memoria (vedi \ref{sec:file_memory_map}) che -usa lo stesso meccanismo per accedere ai file, è necessario conoscere le +casi, come per l'I/O mappato in memoria (vedi \secref{sec:file_memory_map}) +che usa lo stesso meccanismo per accedere ai file, è necessario conoscere le dimensioni delle pagine usate dal kernel. Lo stesso vale quando si vuole gestire in maniera ottimale l'interazione della memoria allocata con il meccanismo della paginazione. @@ -1586,7 +1587,7 @@ dover fornire binari diversi per ogni possibile modello, utilizzare una funzione. In genere questa dimensione può essere ottenuta attraverso una chiamata a -\func{sysconf} come \func{sysconf(\_SC\_PAGESIZE)}, ma in BSD 4.2 è stata +\func{sysconf} come \code{sysconf(\_SC\_PAGESIZE)}, ma in BSD 4.2 è stata introdotta una apposita funzione, \func{getpagesize}, che restituisce la dimensione delle pagine di memoria; il suo prototipo è: \begin{prototype}{unistd.h}{int getpagesize(void)} -- 2.30.2