\const{HZ},\footnote{fino al kernel 2.4 il valore usuale di questa costante
era 100, per tutte le architetture eccetto l'alpha, per la quale era 1000,
nel 2.6 è stato portato a 1000 su tutte le architetture; occorre fare
- attenzione a non confondere questo valore con quello dei clock tick (vedi
+ attenzione a non confondere questo valore con quello dei
+ \itindex{clock~tick} \textit{clock tick} (vedi
sez.~\ref{sec:sys_unix_time}).} definita in \file{asm/param.h}, ed il cui
valore è espresso in Hertz.\footnote{a partire dal kernel 2.6.21 è stato
introdotto (a cura di Ingo Molnar) un meccanismo completamente diverso,
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
non ritorna mai (in quanto con essa viene eseguito un altro programma).
-
\section{Le funzioni di base}% della gestione dei processi}
\label{sec:proc_handling}
al kernel 2.4.x, sono definiti dalla macro \const{PID\_MAX} in
\file{threads.h} e direttamente in \file{fork.c}, con il kernel 2.5.x e la
nuova interfaccia per i thread creata da Ingo Molnar anche il meccanismo di
- allocazione dei \acr{pid} è stato modificato.} che serve a riservare i
-\acr{pid} più bassi ai processi eseguiti direttamente dal kernel. Per questo
-motivo, come visto in sez.~\ref{sec:proc_hierarchy}, il processo di avvio
-(\cmd{init}) ha sempre il \acr{pid} uguale a uno.
+ allocazione dei \acr{pid} è stato modificato; il valore massimo è
+ impostabile attraverso il file \procfile{/proc/sys/kernel/pid\_max} e di
+ default vale 32768.} che serve a riservare i \acr{pid} più bassi ai processi
+eseguiti direttamente dal kernel. Per questo motivo, come visto in
+sez.~\ref{sec:proc_hierarchy}, il processo di avvio (\cmd{init}) ha sempre il
+\acr{pid} uguale a uno.
Tutti i processi inoltre memorizzano anche il \acr{pid} del genitore da cui
sono stati creati, questo viene chiamato in genere \acr{ppid} (da
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
\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
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 usando 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
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
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,
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
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
\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
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
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
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.}
(limitatamente a quelle che il processo
chiamante ha nel suo insieme di capacità
permesse) da qualunque processo.\\
+% TODO cambiata nel 2.4.24 rc1 ?
\const{CAP\_LINUX\_IMMUTABLE}& La capacità di impostare gli attributi
\textit{immutable} e \itindex{append~mode}
\textit{append only} per i file su un
% TODO documentare prctl ...
+% TODO: rivedere alla luce degli aggiornamenti del 2.6 (man sched_setscheduler)
+
\section{La gestione della priorità di esecuzione}
\label{sec:proc_priority}
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
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
\const{SCHED\_RR} & Scheduling real-time con politica \textit{Round
Robin}. \\
\const{SCHED\_OTHER}& Scheduling ordinario.\\
+ \const{SCHED\_BATCH}& Scheduling ordinario con l'assunzione ulteriore di
+ lavoro \textit{CPU intensive}.\footnotemark\\
\hline
\end{tabular}
\caption{Valori dell'argomento \param{policy} per la funzione
\label{tab:proc_sched_policy}
\end{table}
+\footnotetext{introdotto con il kernel 2.6.16.}
+
Il valore della priorità è passato attraverso la struttura
\struct{sched\_param} (riportata in fig.~\ref{fig:sig_sched_param}), il cui
solo campo attualmente definito è \var{sched\_priority}, che nel caso delle
questa funzione ritorna sempre un valore di 150 millisecondi, e non importa
specificare il PID di un processo reale.
-
Come accennato ogni processo che usa lo scheduling real-time può rilasciare
volontariamente la CPU; questo viene fatto attraverso la funzione
\funcd{sched\_yield}, il cui prototipo è:
in modalità \textit{fifo}, per permettere l'esecuzione degli altri processi
con pari priorità quando la sezione più urgente è finita.
+% TODO: con il 2.6.23 il comportamento è stato leggermente modificato ed è
+% stato introdotto /proc/sys/kernel/sched_compat_yield da mettere a 1 per aver
+% la compatibilità con il precedente.
-\subsection{Il controllo dello \textit{scheduler} per i sistemi multiprocessore}
+\subsection{Il controllo dello \textit{scheduler} per i sistemi
+ multiprocessore}
\label{sec:proc_sched_multiprocess}
Infine con il supporto dei sistemi multiprocessore sono state introdotte delle