X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=3522bbb2a2ae78f252a3516711b28ff3d41885ff;hp=94d316eca7ec189ffb5bc8a5455a1fca12e9fd92;hb=47a00595786c34a03266f19dd5163a45da63e29f;hpb=4de1dd4a3e26e5ff1ccc64530927bae308b80688 diff --git a/prochand.tex b/prochand.tex index 94d316e..3522bbb 100644 --- a/prochand.tex +++ b/prochand.tex @@ -194,9 +194,9 @@ abbastanza limitata sulle cause della terminazione del processo figlio. Quando un processo ha concluso il suo compito o ha incontrato un errore non risolvibile esso può essere terminato con la funzione \func{exit} (si veda quanto discusso in sez.~\ref{sec:proc_conclusion}). La vita del processo però -termina solo quando la notifica della sua conclusione viene ricevuta dal -processo padre, a quel punto tutte le risorse allocate nel sistema ad esso -associate vengono rilasciate. +termina completamente solo quando la notifica della sua conclusione viene +ricevuta dal processo padre, a quel punto tutte le risorse allocate nel +sistema ad esso associate vengono rilasciate. Avere due processi che eseguono esattamente lo stesso codice non è molto utile, normalmente si genera un secondo processo per affidargli l'esecuzione @@ -431,7 +431,6 @@ Se eseguiamo il comando\footnote{che senza specificare attese (come si può notare in (\texttt{\small 17--19}) i valori predefiniti specificano di non attendere), otterremo come output sul terminale: - \footnotesize \begin{verbatim} [piccardi@selidor sources]$ export LD_LIBRARY_PATH=./; ./forktest 3 @@ -452,17 +451,14 @@ Go to next child \normalsize Esaminiamo questo risultato: una prima conclusione che si può trarre è che non -si può dire quale processo fra il padre ed il figlio venga eseguito per -primo\footnote{a partire dal kernel 2.5.2-pre10 è stato introdotto il nuovo - \itindex{scheduler} \textit{scheduler} di Ingo Molnar che esegue sempre per - primo il figlio; per mantenere la portabilità è opportuno non fare comunque - affidamento su questo comportamento.} dopo la chiamata a \func{fork}; -dall'esempio si può notare infatti come nei primi due cicli sia stato eseguito -per primo il padre (con la stampa del \acr{pid} del nuovo processo) per poi -passare all'esecuzione del figlio (completata con i due avvisi di esecuzione -ed uscita), e tornare all'esecuzione del padre (con la stampa del passaggio al -ciclo successivo), mentre la terza volta è stato prima eseguito il figlio -(fino alla conclusione) e poi il padre. +si può dire quale processo fra il padre ed il figlio venga eseguito per primo +dopo la chiamata a \func{fork}; dall'esempio si può notare infatti come nei +primi due cicli sia stato eseguito per primo il padre (con la stampa del +\acr{pid} del nuovo processo) per poi passare all'esecuzione del figlio +(completata con i due avvisi di esecuzione ed uscita), e tornare +all'esecuzione del padre (con la stampa del passaggio al ciclo successivo), +mentre la terza volta è stato prima eseguito il figlio (fino alla conclusione) +e poi il padre. In generale l'ordine di esecuzione dipenderà, oltre che dall'algoritmo di \itindex{scheduler} scheduling usato dal kernel, dalla particolare situazione @@ -479,6 +475,24 @@ occorrer rischio di incorrere nelle cosiddette \itindex{race~condition} \textit{race condition} (vedi sez.~\ref{sec:proc_race_cond}). +In realtà a partire dal kernel 2.5.2-pre10 il nuovo \itindex{scheduler} +\textit{scheduler} di Ingo Molnar esegue sempre per primo il +figlio;\footnote{i risultati precedenti sono stati ottenuti su un kernel della + serie 2.4.} questa è una ottimizzazione che serve a evitare che il padre, +effettuando per primo una operazione di scrittura in memoria, attivi il +meccanismo del \itindex{copy~on~write} \textit{copy on write}. Questa +operazione infatti potrebbe risultare del tutto inutile qualora il figlio +fosse stato creato solo per eseguire una \func{exec}, in tal caso infatti si +invocherebbe un'altro proramma scartando completamente lo spazio degli +indirizzi, rendendo superflua la copia della memoria modificata dal padre. + +Eseguendo sempre per primo il figlio la \func{exec} verrebbe effettuata subito +avendo così la certezza che il \itindex{copy~on~write} \textit{copy on write} +viene utilizzato solo quando necessario. Quanto detto in precedenza vale +allora soltanto per i kernel fino al 2.4, per mantenere la portabilità è però +opportuno non fare affidamento su questo comportamento, che non si riscontra +in altri Unix e nelle versioni del kernel precendenti a quella indicata. + Si noti inoltre che essendo i segmenti di memoria utilizzati dai singoli processi completamente separati, le modifiche delle variabili nei processi figli (come l'incremento di \var{i} in \texttt{\small 31}) sono visibili solo @@ -490,7 +504,6 @@ Un secondo aspetto molto importante nella creazione dei processi figli quello dell'interazione dei vari processi con i file; per illustrarlo meglio proviamo a redirigere su un file l'output del nostro programma di test, quello che otterremo è: - \footnotesize \begin{verbatim} [piccardi@selidor sources]$ ./forktest 3 > output @@ -538,7 +551,7 @@ ogni figlio riceve una copia della memoria del padre, esso ricever quanto c'è nel buffer delle funzioni di I/O, comprese le linee scritte dal padre fino allora. Così quando il buffer viene scritto su disco all'uscita del figlio, troveremo nel file anche tutto quello che il processo padre aveva -scritto prima della sua creazione. E alla fine del file (dato che in questo +scritto prima della sua creazione. E alla fine del file (dato che in questo caso il padre esce per ultimo) troveremo anche l'output completo del padre. L'esempio ci mostra un altro aspetto fondamentale dell'interazione con i file, @@ -727,6 +740,8 @@ che sia cos terminato (si potrebbe avere cioè quello che si chiama un processo \textsl{orfano}). +% TODO verificare il reparenting + Questa complicazione viene superata facendo in modo che il processo orfano venga \textsl{adottato} da \cmd{init}. Come già accennato quando un processo termina, il kernel controlla se è il padre di altri processi in esecuzione: in @@ -736,7 +751,6 @@ avr cui riportare il suo stato di terminazione. Come verifica di questo comportamento possiamo eseguire il nostro programma \cmd{forktest} imponendo a ciascun processo figlio due secondi di attesa prima di uscire, il risultato è: - \footnotesize \begin{verbatim} [piccardi@selidor sources]$ ./forktest -c2 3 @@ -1227,7 +1241,8 @@ ritorno di \func{waitid} verranno avvalorati i seguenti campi: \const{CLD\_STOPPED}, \const{CLD\_CONTINUED} (vedi tab.~\ref{xxx_si_code}). \end{basedescript} -%TODO mettere riferimento alla tabella giusta +%TODO mettere riferimento alla tabella giusta (vedere man credentials e man +% waitid) Infine Linux, seguendo un'estensione di BSD, supporta altre due funzioni per la lettura dello stato di terminazione di un processo, analoghe alle @@ -1256,7 +1271,7 @@ utilizzata anche dalla funzione \func{getrusage} (vedi sez.~\ref{sec:sys_resource_use}) per ottenere le risorse di sistema usate da un processo; la sua definizione è riportata in fig.~\ref{fig:sys_rusage_struct}. -\subsection{Le funzioni \func{exec}} +\subsection{La funzione \func{exec} e le funzioni di esecuzione dei programmi} \label{sec:proc_exec} Abbiamo già detto che una delle modalità principali con cui si utilizzano i @@ -1489,7 +1504,7 @@ chiamato come se si fosse eseguito il comando \cmd{interpreter [argomenti] lunga restituisce un errore di \const{ENAMETOOLONG}, una comparazione dei vari comportamenti si trova su \href{http://www.in-ulm.de/~mascheck/various/shebang/} - {\texttt{http://www.in-ulm.de/\tild mascheck/various/shebang/}}.} + {\textsf{http://www.in-ulm.de/\tild mascheck/various/shebang/}}.} Con la famiglia delle \func{exec} si chiude il novero delle funzioni su cui è basata la gestione dei processi in Unix: con \func{fork} si crea un nuovo @@ -2116,7 +2131,7 @@ implementata.\footnote{per attualmente si intende fino al kernel 2.6.23; benché l'infrastruttura per crearla sia presente (vedi anche sez.~\ref{sec:file_xattr}) finora non è disponibile nessuna realizzazione delle specifiche POSIX.1e, esistono però dei patch di sicurezza del kernel, - come LIDS (vedi \href{http://www.lids.org}{\texttt{http://www.lids.org/})} + come LIDS (vedi \href{http://www.lids.org}{\textsf{http://www.lids.org/})} che realizzano qualcosa di simile.} @@ -3097,7 +3112,7 @@ tocca al kernel decidere quale deve essere eseguito. Il meccanismo con cui vengono gestiti questi processi dipende dalla politica di scheduling che si è scelta; lo standard ne prevede due: \begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}} -\item[\textit{FIFO}] \textit{First In First Out}. Il processo viene eseguito +\item[\textsf{FIFO}] \textit{First In First Out}. Il processo viene eseguito fintanto che non cede volontariamente la CPU (con \func{sched\_yield}), si blocca, finisce o viene interrotto da un processo a priorità più alta. Se il processo viene interrotto da uno a priorità più alta esso resterà in cima @@ -3105,7 +3120,7 @@ scelta; lo standard ne prevede due: più alta diverranno inattivi. Se invece lo si blocca volontariamente sarà posto in coda alla lista (ed altri processi con la stessa priorità potranno essere eseguiti). -\item[\textit{RR}] \textit{Round Robin}. Il comportamento è del tutto analogo +\item[\textsf{RR}] \textit{Round Robin}. Il comportamento è del tutto analogo a quello precedente, con la sola differenza che ciascun processo viene eseguito al massimo per un certo periodo di tempo (la cosiddetta \textit{time slice}) dopo di che viene automaticamente posto in fondo alla