X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=97b9c5e7d6d35521cf0adb178b303a200e2ccc4a;hp=4a03b94525138dd4d3206c2fadea8c3c2fa972b8;hb=33a26fb169d5a3e459b816cb79eb7956e8434ffb;hpb=0726315ca76d6655e232a0fafd82d524d133187d diff --git a/prochand.tex b/prochand.tex index 4a03b94..97b9c5e 100644 --- a/prochand.tex +++ b/prochand.tex @@ -500,10 +500,9 @@ Quello che succede lo stesso avviene anche per tutti i figli; la funzione \func{fork} infatti ha la caratteristica di duplicare (allo stesso modo in cui lo fa la funzione \func{dup}, trattata in \secref{sec:file_dup}) nei figli tutti i file -descriptor aperti nel padre, il che comporta che padre e figli condividono -le stesse voci della file table (per la spiegazione di questi termini si veda -\secref{sec:file_sharing} e referenza a figura da fare) e quindi anche -l'offset corrente nel file. +descriptor aperti nel padre, il che comporta che padre e figli condividono le +stesse voci della file table (per la spiegazione di questi termini si veda +\secref{sec:file_sharing}) e quindi anche l'offset corrente nel file. In questo modo se un processo scrive sul file aggiornerà l'offset sulla file table, e tutti gli altri processi che condividono la file table vedranno il @@ -537,11 +536,10 @@ sequenza impredicibile. Le modalit \end{enumerate} Oltre ai file aperti i processi figli ereditano dal padre una serie di altre -proprietà comuni; in dettaglio avremo che dopo l'esecuzione di una \func{fork} -padre e figlio avranno in comune: +proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in +comune dopo l'esecuzione di una \func{fork} è la seguente: \begin{itemize*} -\item i file aperti (e gli eventuali flag di \textit{close-on-exec} se - settati). +\item i file aperti e gli eventuali flag di \textit{close-on-exec} se settati. \item gli identificatori per il controllo di accesso: il \textit{real user id}, il \textit{real group id}, l'\textit{effective user id}, l'\textit{effective group id} e i \textit{supplementary group id} (vedi @@ -553,12 +551,12 @@ padre e figlio avranno in comune: \item la directory di lavoro e la directory radice (vedi \secref{sec:file_work_dir}). \item la maschera dei permessi di creazione (vedi \secref{sec:file_umask}). -\item la maschera dei segnali. +\item la maschera dei segnali bloccati e le azioni installate. \item i segmenti di memoria condivisa agganciati al processo. -\item i limiti sulle risorse +\item i limiti sulle risorse. \item le variabili di ambiente (vedi \secref{sec:proc_environ}). \end{itemize*} -le differenze invece sono: +le differenze fra padre e figlio dopo la \func{fork} invece sono: \begin{itemize*} \item il valore di ritorno di \func{fork}. \item il \textit{process id}. @@ -567,7 +565,7 @@ le differenze invece sono: \item i valori dei tempi di esecuzione (\var{tms\_utime}, \var{tms\_stime}, \var{tms\_cutime}, \var{tms\_uetime}) che nel figlio sono posti a zero. \item i \textit{file lock}, che non vengono ereditati dal figlio. -\item gli allarmi pendenti, che per il figlio vengono cancellati. +\item gli allarmi ed i segnali pendenti, che per il figlio vengono cancellati. \end{itemize*} @@ -760,7 +758,7 @@ di terminare il processo che li ha generati, in modo che \cmd{init} possa adottarli e provvedere a concludere la terminazione. -\subsection{Le funzioni \texttt{wait} e \texttt{waitpid}} +\subsection{Le funzioni \func{wait} e \func{waitpid}} \label{sec:proc_wait} Abbiamo già accennato come uno degli usi possibili delle capacità multitasking @@ -791,6 +789,7 @@ caso di errore; \var{errno} pu \item \macro{EINTR} la funzione è stata interrotta da un segnale. \end{errlist} \end{functions} + è presente fin dalle prime versioni di unix; la funzione ritorna alla conclusione del primo figlio (o immediatamente se un figlio è già uscito). Nel caso un processo abbia più figli il valore di ritorno permette di identificare @@ -963,6 +962,7 @@ accessibili definendo la costante \macro{\_USE\_BSD}, sono: Prima versione, equivalente a \func{wait4(-1, \&status, opt, rusage)} è ormai deprecata in favore di \func{wait4}. \end{functions} +\noindent la struttura \type{rusage} è definita in \file{sys/resource.h}, e viene utilizzata anche dalla funzione \func{getrusage} per ottenere le risorse di sistema usate dal processo; in Linux è definita come: @@ -992,7 +992,7 @@ struct rusage { \end{lstlisting} \end{minipage} \normalsize - \caption{La struttura \texttt{rusage} per la lettura delle informazioni dei + \caption{La struttura \var{rusage} per la lettura delle informazioni dei delle risorse usate da un processo.} \label{fig:proc_rusage_struct} \end{figure} @@ -1004,7 +1004,7 @@ sono mantenuti sono: \var{ru\_utime}, \var{ru\_stime}, \var{ru\_minflt}, \var{ru\_majflt}, e \var{ru\_nswap}. -\subsection{Le funzioni \texttt{exec}} +\subsection{Le funzioni \func{exec}} \label{sec:proc_exec} Abbiamo già detto che una delle modalità principali con cui si utilizzano i @@ -1655,43 +1655,113 @@ amministratore o, per gli altri utenti, se il valore specificato coincide con uno dei \textit{real}, \textit{effective} o \textit{saved id}. - \section{Problematiche di programmazione multitasking} \label{sec:proc_multi_prog} Benché i processi siano strutturati in modo da apparire il più possibile come -independenti l'uno dall'altro, nella programmazione in un sistema multiutente +indipendenti l'uno dall'altro, nella programmazione in un sistema multiutente occorre tenere conto di tutta una serie di problematiche che normalmente non esistono quando si ha a che fare con un sistema in cui viene eseguito un solo programma alla volta. Pur non essendo tutto questo direttamente legato alla modalità specifica in -cui il multitasking è implementato in un sistema unix-like, siccome la -gestione dei processi è stata affrontata in questo capitolo, tratteremo in -questa sezione conclusiva anche queste problematiche, esaminandone le -caratteristiche fondamentali e le modalità con cui si affrontano. - - -\subsection{Le funzioni rientranti} -\label{sec:proc_reentrant} +cui il multitasking è implementato in un sistema unix-like, né al solo +concetto di multitasking (le stesse problematiche si presentano ad esempio +nella gestione degli interrupt hardware), in questa sezione conclusiva del +capitolo in cui abbiamo affrontato la gestione dei processi, introdurremo +sinteticamente queste problematiche, che ritroveremo a più riprese in capitoli +successivi, con una breve definizione della terminologia e delle loro +caratteristiche di fondo. \subsection{Le operazioni atomiche} \label{sec:proc_atom_oper} +La nozione di \textsl{operazione atomica} deriva dal significato greco della +parola atomo, cioè indivisibile; si dice infatti che una operazione è atomica +quando si ha la certezza che, qualora essa venga effettuata, tutti i passaggi +che devono essere compiuti per realizzarla verranno eseguiti senza possibilità +di interruzione in una fase intermedia. + +In un ambiente multitasking il concetto è esseziale, 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 +\secref{sec:proc_race_cond}) derivanti da operazioni interrotte in una fase in +cui non erano ancora state completate. + +Nel caso dell'interazione fra processi la situazione è molto più semplice, ed +occorre preoccuparsi della atomicità delle operazioni solo quando si ha a che +fare con meccanismi di intercomunicazione (che esamineremo in dettaglio in +\capref{cha:ipc}) o nella operazioni con i file (vedremo alcuni esempi in +\secref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate +funzioni di libreria per compiere le operazioni necessarie è garanzia +sufficiente di atomicità in quanto le system call con cui esse sono realizzate +non possono essere interrotte (o subire interferenze pericolose) da altri +processi. -\subsection{Le \textit{race condition}} +Nel caso dei segnali invece la situazione è molto più delicata, in quanto lo +stesso processo può essere interrotto in qualunque momento, e le operazioni di +un eventuale \textit{signal handler} saranno compiute nello stesso spazio di +indirizzi. Per questo anche solo il solo accesso o l'assegnazione di una +variabile possono non essere più operazioni atomiche (torneremo su questi +aspetti in \secref{sec:sign_xxx}). + +In questo caso il sistema provvede un tipo di dato, il \type{sig\_atomic\_t}, +il cui accesso è assicurato essere atomico. In pratica comunque si può +assumere che in ogni piattaforma su cui è implementato Linux il tipo +\type{int} (e gli altri interi di dimensione inferiore) ed i puntatori sono +atomici. Non è affatto detto che lo stesso valga per interi di dimensioni +maggiori (in cui l'accesso può comportare più istruzioni in assembler) o per +le strutture. In questi casi è anche opportuno marcare come \type{volatile} le +variabili che possono essere interessate ad accesso condiviso, onde evitare +problemi con le ottimizzazioni del codice. + + +\subsection{Le \textit{race condition} e i \textit{deadlock}} \label{sec:proc_race_cond} Si definisce una \textit{race condition} il caso in cui diversi processi stanno cercando di fare qualcosa con una risorsa comune ed il risultato finale viene a dipendere dall'ordine di esecuzione dei medesimi. Ovviamente dato che -l'ordine di esecuzione di un processo, senza appositi meccanismi di -sincronizzazione, non è assolutamente prevedibile, queste situazioni sono -fonti di errori molto subdoli, che possono verificarsi solo in condizioni -particolari e quindi difficilmente riproducibili. +l'ordine di esecuzione di un processo rispetto agli altri, senza appositi +meccanismi di sincronizzazione, non è assolutamente prevedibile, queste +situazioni sono fonti di errori molto subdoli, che possono verificarsi solo in +condizioni particolari e quindi difficilmente riproducibili. + +Casi tipici di \textit{race condition} si hanno quando diversi processi +accedono allo stesso file, o nell'accesso a meccanismi di intercomunicazione +come la memoria condivisa. In questi casi, se non si dispone della possibilità +di eseguire atomicamente le operazioni necessarie, occorre che le risorse +condivise siano opportunamente protette da meccanismi di sincronizzazione +(torneremo su queste problematiche di questo tipo in \secref{sec:ipc_semaph}). + +Un caso particolare di \textit{race condition} sono poi i cosidetti +\textit{deadlock}; l'esempio tipico è quello di un flag di ``occupazione'' che +viene rilasciato da un evento asincrono fra il controllo (in cui viene trovato +occupato) e la successiva messa in attesa, attesa che a questo punto diventerà +perpetua (da cui il nome di \textit{deadlock}) in quanto l'evento di sblocco +di questa è stato perso. + +\subsection{Le funzioni rientranti} +\label{sec:proc_reentrant} -\subsection{I \textit{deadlock}} -\label{sec:proc_deadlock} +Si dice rientrante una funzione che può essere interrotta in qualunque momento +ed essere chiamata da capo (da questo il nome) da un altro filone di +esecuzione (thread e manipolatori di segnali sono i casi in cui occorre +prestare attenzione a questa problematica) senza che questo comporti nessun +problema. + +In genere una funzione non è rientrante se opera direttamente su memoria che +non è nello stack. Ad esempio una funzione non è rientrante se usa una +variabile globale o statica od un oggetto allocato dinamicamente che trova da +sola: due chiamate alla stessa funzione interferiranno. Una funzione può non +essere rientrante se usa e modifica un oggetto che le viene fornito dal +chiamante: due chiamate possono interferire se viene passato lo stesso +oggetto. + +Le glibc mettono a disposizione due macro di compilatore \macro{_REENTRANT} e +\macro{_THREAD_SAFE} per assicurare che siano usate delle versioni rientranti +delle funzioni di libreria.