X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=9f7b0d937a132cad4f443deb521e463a2e2e00d3;hp=3810e41304717abde3a19c34b0484a334ad06ae7;hb=0196c376e39fc18f8cd5e7fef47b61264f943faf;hpb=06a2f2c7718cebc7cc4ccb894999028a62b6256d diff --git a/prochand.tex b/prochand.tex index 3810e41..9f7b0d9 100644 --- a/prochand.tex +++ b/prochand.tex @@ -1,6 +1,6 @@ %% prochand.tex %% -%% Copyright (C) 2000-2007 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2008 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the %% Free Software Foundation; with the Invariant Sections being "Un preambolo", @@ -138,7 +138,7 @@ fig.~\ref{fig:proc_task_struct}. \begin{figure}[htb] \centering - \includegraphics[width=11cm]{img/task_struct} + \includegraphics[width=12cm]{img/task_struct} \caption{Schema semplificato dell'architettura delle strutture usate dal kernel nella gestione dei processi.} \label{fig:proc_task_struct} @@ -248,13 +248,13 @@ massimo di 32768. Oltre questo valore l'assegnazione riparte dal numero pi basso disponibile a partire da un minimo di 300,\footnote{questi valori, fino 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; 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. + nuova interfaccia per i \itindex{thread} \textit{thread} creata da Ingo + Molnar anche il meccanismo di 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 @@ -331,7 +331,7 @@ Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che il processo figlio continuano ad essere eseguiti normalmente a partire dall'istruzione successiva alla \func{fork}; il processo figlio è però una copia del padre, e riceve una copia dei \index{segmento!testo} segmenti di -testo, \itindex{stack} stack e \index{segmento!dati} dati (vedi +testo, \itindex{stack} \textit{stack} e \index{segmento!dati} dati (vedi sez.~\ref{sec:proc_mem_layout}), ed esegue esattamente lo stesso codice del padre. Si tenga presente però che la memoria è copiata, non condivisa, pertanto padre e figlio vedono variabili diverse. @@ -432,8 +432,7 @@ 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} +\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm] [piccardi@selidor sources]$ export LD_LIBRARY_PATH=./; ./forktest 3 Process 1963: forking 3 child Spawned 1 child, pid 1964 @@ -448,8 +447,8 @@ Child 3 successfully executing Child 3, parent 1963, exiting Spawned 3 child, pid 1966 Go to next child -\end{verbatim} %$ -\normalsize +\end{Verbatim} +%$ 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 @@ -484,7 +483,7 @@ 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 +invocherebbe un altro programma 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 @@ -505,8 +504,7 @@ 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} +\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm] [piccardi@selidor sources]$ ./forktest 3 > output [piccardi@selidor sources]$ cat output Process 1967: forking 3 child @@ -531,8 +529,7 @@ Spawned 2 child, pid 1969 Go to next child Spawned 3 child, pid 1970 Go to next child -\end{verbatim} -\normalsize +\end{Verbatim} che come si vede è completamente diverso da quanto ottenevamo sul terminale. Il comportamento delle varie funzioni di interfaccia con i file è analizzato @@ -563,14 +560,15 @@ sez.~\ref{sec:file_sharing}), ma anche che, a differenza di quanto avviene per le variabili, la posizione corrente sul file è condivisa fra il padre e tutti i processi figli. -Quello che succede è che quando lo standard output del padre viene rediretto, -lo stesso avviene anche per tutti i figli; la funzione \func{fork} infatti ha -la caratteristica di duplicare nei figli tutti i file descriptor aperti nel -padre (allo stesso modo in cui lo fa la funzione \func{dup}, trattata in -sez.~\ref{sec:file_dup}), il che comporta che padre e figli condividono le -stesse voci della \itindex{file~table} \textit{file table} (per la spiegazione -di questi termini si veda sez.~\ref{sec:file_sharing}) fra cui c'è anche la -posizione corrente nel file. +Quello che succede è che quando lo standard output del padre viene rediretto +come si è fatto nell'esempio, lo stesso avviene anche per tutti i figli; la +funzione \func{fork} infatti ha la caratteristica di duplicare nei processi +figli tutti i file descriptor aperti nel processo padre (allo stesso modo in +cui lo fa la funzione \func{dup}, trattata in sez.~\ref{sec:file_dup}), il che +comporta che padre e figli condividono le stesse voci della +\itindex{file~table} \textit{file table} (per la spiegazione di questi termini +si veda sez.~\ref{sec:file_sharing}) fra cui c'è anche la posizione corrente +nel file. In questo modo se un processo scrive sul file aggiornerà la posizione corrente sulla \itindex{file~table} \textit{file table}, e tutti gli altri processi, @@ -582,21 +580,20 @@ mescolato, ma non ci saranno parti perdute per via di una sovrascrittura. Questo tipo di comportamento è essenziale in tutti quei casi in cui il padre crea un figlio e attende la sua conclusione per proseguire, ed entrambi -scrivono sullo stesso file (un caso tipico è la shell quando lancia un -programma, il cui output va sullo standard output). - -In questo modo, anche se l'output viene rediretto, il padre potrà sempre -continuare a scrivere in coda a quanto scritto dal figlio in maniera -automatica; se così non fosse ottenere questo comportamento sarebbe -estremamente complesso necessitando di una qualche forma di comunicazione fra -i due processi per far riprendere al padre la scrittura al punto giusto. +scrivono sullo stesso file; un caso tipico è la shell quando lancia un +programma, il cui output va sullo standard output. In questo modo, anche se +l'output viene rediretto, il padre potrà sempre continuare a scrivere in coda +a quanto scritto dal figlio in maniera automatica; se così non fosse ottenere +questo comportamento sarebbe estremamente complesso necessitando di una +qualche forma di comunicazione fra i due processi per far riprendere al padre +la scrittura al punto giusto. In generale comunque non è buona norma far scrivere più processi sullo stesso file senza una qualche forma di sincronizzazione in quanto, come visto anche con il nostro esempio, le varie scritture risulteranno mescolate fra loro in una sequenza impredicibile. Per questo le modalità con cui in genere si usano i file dopo una \func{fork} sono sostanzialmente due: -\begin{enumerate*} +\begin{enumerate} \item Il processo padre aspetta la conclusione del figlio. In questo caso non è necessaria nessuna azione riguardo ai file, in quanto la sincronizzazione della posizione corrente dopo eventuali operazioni di lettura e scrittura @@ -604,7 +601,7 @@ i file dopo una \func{fork} sono sostanzialmente due: \item L'esecuzione di padre e figlio procede indipendentemente. In questo caso ciascuno dei due processi deve chiudere i file che non gli servono una volta che la \func{fork} è stata eseguita, per evitare ogni forma di interferenza. -\end{enumerate*} +\end{enumerate} Oltre ai file aperti i processi figli ereditano dal padre una serie di altre proprietà; la lista dettagliata delle proprietà che padre e figlio hanno in @@ -695,7 +692,7 @@ Qualunque sia la modalit comunque una serie di operazioni: chiude tutti i file aperti, rilascia la memoria che stava usando, e così via; l'elenco completo delle operazioni eseguite alla chiusura di un processo è il seguente: -\begin{itemize*} +\begin{itemize} \item tutti i file descriptor sono chiusi; \item viene memorizzato lo stato di terminazione del processo; \item ad ogni processo figlio viene assegnato un nuovo padre (in genere @@ -710,7 +707,7 @@ eseguite alla chiusura di un processo group} ciascun membro del gruppo viene bloccato, e poi gli vengono inviati in successione i segnali \const{SIGHUP} e \const{SIGCONT} (vedi ancora sez.~\ref{sec:sess_ctrl_term}). -\end{itemize*} +\end{itemize} Oltre queste operazioni è però necessario poter disporre di un meccanismo ulteriore che consenta di sapere come la terminazione è avvenuta: dato che in @@ -752,8 +749,7 @@ 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} +\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm] [piccardi@selidor sources]$ ./forktest -c2 3 Process 1972: forking 3 child Spawned 1 child, pid 1973 @@ -768,8 +764,7 @@ Go to next child [piccardi@selidor sources]$ Child 3, parent 1, exiting Child 2, parent 1, exiting Child 1, parent 1, exiting -\end{verbatim} -\normalsize +\end{Verbatim} come si può notare in questo caso il processo padre si conclude prima dei figli, tornando alla shell, che stampa il prompt sul terminale: circa due secondi dopo viene stampato a video anche l'output dei tre figli che @@ -799,9 +794,7 @@ condizione: lanciamo il comando \cmd{forktest} in \textit{background} (vedi sez.~\ref{sec:sess_job_control}), indicando al processo padre di aspettare 10 secondi prima di uscire; in questo caso, usando \cmd{ps} sullo stesso terminale (prima dello scadere dei 10 secondi) otterremo: - -\footnotesize -\begin{verbatim} +\begin{Verbatim}[fontsize=\footnotesize,xleftmargin=1cm,xrightmargin=1.5cm] [piccardi@selidor sources]$ ps T PID TTY STAT TIME COMMAND 419 pts/0 S 0:00 bash @@ -810,8 +803,9 @@ terminale (prima dello scadere dei 10 secondi) otterremo: 570 pts/0 Z 0:00 [forktest ] 571 pts/0 Z 0:00 [forktest ] 572 pts/0 R 0:00 ps T -\end{verbatim} %$ -\normalsize e come si vede, dato che non si è fatto nulla per riceverne lo +\end{Verbatim} +%$ +e come si vede, dato che non si è fatto nulla per riceverne lo stato di terminazione, i tre processi figli sono ancora presenti pur essendosi conclusi, con lo stato di \index{zombie} \textit{zombie} e l'indicazione che sono stati terminati. @@ -955,8 +949,9 @@ funzione delle opportune opzioni tramite l'argomento \param{options}; questo deve essere specificato come maschera binaria dei flag riportati in tab.~\ref{tab:proc_waitpid_options},\footnote{oltre a queste in Linux sono previste del altre opzioni non standard, relative al comportamento con i - thread, che riprenderemo in sez.~\ref{sec:thread_xxx}.} che possono essere -combinati fra loro con un OR aritmetico. + \itindex{thread} \textit{thread}, che riprenderemo in + sez.~\ref{sec:thread_xxx}.} che possono essere combinati fra loro con un OR +aritmetico. L'uso dell'opzione \const{WNOHANG} consente di prevenire il blocco della funzione qualora nessun figlio sia uscito (o non si siano verificate le altre @@ -1224,13 +1219,14 @@ specificata l'opzione \const{WNOHANG} e la funzione senza che nessun figlio sia terminato. Pertanto per verificare il motivo del ritorno della funzione occorre analizzare le informazioni che essa restituisce; queste, al contrario delle precedenti \func{wait} e -\func{waitpid}, sono ritornate nella struttura di tipo \struct{siginfo\_t} -(vedi fig.~\ref{fig:sig_siginfo_t}) all'indirizzo puntato dall'argomento -\param{infop}. - -Tratteremo nei dettagli questa struttura ed il significato dei suoi vari campi -in sez.~\ref{sec:sig_sigaction}, per quanto ci interessa qui basta dire che al -ritorno di \func{waitid} verranno avvalorati i seguenti campi: +\func{waitpid} che usavano un semplice valore numerico, sono ritornate in una +struttura di tipo \struct{siginfo\_t} (vedi fig.~\ref{fig:sig_siginfo_t}) +all'indirizzo puntato dall'argomento \param{infop}. + +Tratteremo nei dettagli la struttura \struct{siginfo\_t} ed il significato dei +suoi vari campi in sez.~\ref{sec:sig_sigaction}, per quanto ci interessa qui +basta dire che al ritorno di \func{waitid} verranno avvalorati i seguenti +campi: \begin{basedescript}{\desclabelwidth{2.0cm}} \item[\var{si\_pid}] con il \acr{pid} del figlio. \item[\var{si\_uid}] con l'user-ID reale (vedi sez.~\ref{sec:proc_perms}) del @@ -1248,10 +1244,10 @@ ritorno di \func{waitid} verranno avvalorati i seguenti campi: Infine Linux, seguendo un'estensione di BSD, supporta altre due funzioni per la lettura dello stato di terminazione di un processo, analoghe alle precedenti ma che prevedono un ulteriore argomento attraverso il quale il -kernel può restituire al padre informazioni sulle risorse usate dal processo -terminato e dai vari figli. Le due funzioni sono \funcd{wait3} e -\funcd{wait4}, che diventano accessibili definendo la macro -\macro{\_USE\_BSD}; i loro prototipi sono: +kernel può restituire al padre informazioni sulle risorse (vedi +sez.~\ref{sec:sys_res_limits}) usate dal processo terminato e dai vari figli. +Le due funzioni sono \funcd{wait3} e \funcd{wait4}, che diventano accessibili +definendo la macro \macro{\_USE\_BSD}; i loro prototipi sono: \begin{functions} \headdecl{sys/times.h} \headdecl{sys/types.h} \headdecl{sys/wait.h} \headdecl{sys/resource.h} @@ -1431,7 +1427,7 @@ l'ambiente. Oltre a mantenere lo stesso \acr{pid}, il nuovo programma fatto partire da \func{exec} assume anche una serie di altre proprietà del processo chiamante; la lista completa è la seguente: -\begin{itemize*} +\begin{itemize} \item il \textit{process id} (\acr{pid}) ed il \textit{parent process id} (\acr{ppid}); \item l'\textsl{user-ID reale}, il \textit{group-ID reale} ed i @@ -1442,7 +1438,7 @@ la lista completa \item il tempo restante ad un allarme (vedi sez.~\ref{sec:sig_alarm_abort}); \item la directory radice e la directory di lavoro corrente (vedi sez.~\ref{sec:file_work_dir}); -\item la maschera di creazione dei file (\var{umask}, vedi +\item la maschera di creazione dei file \itindex{umask} (\textit{umask}, vedi sez.~\ref{sec:file_perm_management}) ed i \textit{lock} sui file (vedi sez.~\ref{sec:file_locking}); \item i segnali sospesi (\textit{pending}) e la maschera dei segnali (si veda @@ -1450,7 +1446,7 @@ la lista completa \item i limiti sulle risorse (vedi sez.~\ref{sec:sys_resource_limit}); \item i valori delle variabili \var{tms\_utime}, \var{tms\_stime}, \var{tms\_cutime}, \var{tms\_ustime} (vedi sez.~\ref{sec:sys_cpu_times}). -\end{itemize*} +\end{itemize} Inoltre i segnali che sono stati impostati per essere ignorati nel processo chiamante mantengono la stessa impostazione pure nel nuovo programma, tutti @@ -1465,32 +1461,34 @@ sez.~\ref{sec:file_fcntl}) per ciascun file descriptor. I file per cui impostato vengono chiusi, tutti gli altri file restano aperti. Questo significa che il comportamento predefinito è che i file restano aperti attraverso una \func{exec}, a meno di una chiamata esplicita a \func{fcntl} -che imposti il suddetto flag. - -Per le directory, lo standard POSIX.1 richiede che esse vengano chiuse -attraverso una \func{exec}, in genere questo è fatto dalla funzione -\func{opendir} (vedi sez.~\ref{sec:file_dir_read}) che effettua da sola -l'impostazione del flag di \itindex{close-on-exec} \textit{close-on-exec} -sulle directory che apre, in maniera trasparente all'utente. +che imposti il suddetto flag. Per le directory, lo standard POSIX.1 richiede +che esse vengano chiuse attraverso una \func{exec}, in genere questo è fatto +dalla funzione \func{opendir} (vedi sez.~\ref{sec:file_dir_read}) che effettua +da sola l'impostazione del flag di \itindex{close-on-exec} +\textit{close-on-exec} sulle directory che apre, in maniera trasparente +all'utente. Abbiamo detto che l'\textsl{user-ID reale} ed il \textsl{group-ID reale} -restano gli stessi all'esecuzione di \func{exec}; lo stesso vale per -l'\textsl{user-ID effettivo} ed il \textsl{group-ID effettivo} (il significato -di questi identificatori è trattato in sez.~\ref{sec:proc_access_id}), tranne -quando il file che si va ad eseguire abbia o il \itindex{suid~bit} \acr{suid} -bit o lo \itindex{sgid~bit} \acr{sgid} bit impostato, in questo caso -l'\textsl{user-ID effettivo} ed il \textsl{group-ID effettivo} vengono -impostati rispettivamente all'utente o al gruppo cui il file appartiene (per i -dettagli vedi sez.~\ref{sec:proc_perms}). +restano gli stessi all'esecuzione di \func{exec}; normalmente vale lo stesso +anche per l'\textsl{user-ID effettivo} ed il \textsl{group-ID effettivo} (il +significato di questi identificatori è trattato in +sez.~\ref{sec:proc_access_id}), tranne quando il file di cui viene chiesta +l'esecuzione ha o il \itindex{suid~bit} \acr{suid} bit o lo \itindex{sgid~bit} +\acr{sgid} bit impostato, in questo caso l'\textsl{user-ID effettivo} ed il +\textsl{group-ID effettivo} vengono impostati rispettivamente all'utente o al +gruppo cui il file appartiene (per i dettagli di questo comportamento si veda +sez.~\ref{sec:proc_perms}). Se il file da eseguire è in formato \emph{a.out} e necessita di librerie condivise, viene lanciato il \textit{linker} dinamico \cmd{/lib/ld.so} prima del programma per caricare le librerie necessarie ed effettuare il link -dell'eseguibile. Se il programma è in formato ELF per caricare le librerie -dinamiche viene usato l'interprete indicato nel segmento \const{PT\_INTERP}, -in genere questo è \sysfile{/lib/ld-linux.so.1} per programmi collegati con le -\acr{libc5}, e \sysfile{/lib/ld-linux.so.2} per programmi collegati con le -\acr{glibc}. +dell'eseguibile.\footnote{il formato è ormai in completo disuso, per cui è + molto probabile che non il relativo supporto non sia disponibile.} Se il +programma è in formato ELF per caricare le librerie dinamiche viene usato +l'interprete indicato nel segmento \const{PT\_INTERP} previsto dal formato +stesso, in genere questo è \sysfile{/lib/ld-linux.so.1} per programmi +collegati con le \acr{libc5}, e \sysfile{/lib/ld-linux.so.2} per programmi +collegati con le \acr{glibc}. Infine nel caso il file sia uno script esso deve iniziare con una linea nella forma \cmd{\#!/path/to/interpreter [argomenti]} dove l'interprete indicato @@ -2138,6 +2136,8 @@ implementata.\footnote{per attualmente si intende fino al kernel 2.6.23; % TODO verificare per process capability bounding set, vedi: % http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=3b7391de67da515c91f48aa371de77cb6cc5c07e +% TODO capire cosa cambia con i patch del 2.6.26, vedi +% http://lwn.net/Articles/280279/ \begin{table}[!h!bt] \centering @@ -2421,9 +2421,12 @@ fig.~\ref{fig:cap_kernel_struct}, come i prototipi delle due funzioni \func{capget} e \func{capset}, sono soggette ad essere modificate con il cambiamento del kernel (in particolare i tipi di dati delle strutture) ed anche se finora l'interfaccia è risultata stabile, non c'è nessuna -assicurazione che questa venga mantenuta. Pertanto se si vogliono scrivere -programmi portabili che possano essere eseguiti su qualunque versione del -kernel è opportuno utilizzare le interfacce di alto livello. +assicurazione che questa venga mantenuta.\footnote{anzi, visto lo scarso + utilizzo di questa funzionalità ci sono state varie discussioni fra gli + sviluppatori del kernel relative all'eliminarla o al modificarla + radicalmente.} Pertanto se si vogliono scrivere programmi portabili che +possano essere eseguiti su qualunque versione del kernel è opportuno +utilizzare le interfacce di alto livello. \begin{figure}[!htb] \footnotesize @@ -2491,8 +2494,9 @@ con tutte le \textit{capabilities} azzerate. In caso di errore (cio non c'è memoria sufficiente ad allocare i dati) viene restituito \macro{NULL} ed \var{errno} viene impostata a \errval{ENOMEM}. La memoria necessaria a mantenere i dati viene automaticamente allocata da \func{cap\_init}, ma dovrà -essere disallocata esplicitamente quando non più necessaria utilizzando la -funzione \funcd{cap\_free}, il cui prototipo è: +essere disallocata esplicitamente quando non è più necessaria utilizzando, per +questo l'interfaccia fornisce una apposita funzione, \funcd{cap\_free}, il cui +prototipo è: \begin{functions} \headdecl{sys/capability.h} @@ -2508,10 +2512,11 @@ La funzione permette di liberare la memoria allocata dalle altre funzioni della libreria sia per un \textit{capability state}, nel qual caso l'argomento dovrà essere un dato di tipo \type{cap\_t}, che per una descrizione testuale dello stesso,\footnote{cioè quanto ottenuto tramite la funzione - \func{cap\_to\_text}.} nel qual caso l'argomento dovrà essere di tipo -\texttt{char *}. L'argomento \param{obj\_d} deve corrispondere ad un oggetto -ottenuto tramite altre funzioni della libreria, altrimenti la funzione fallirà -con un errore di \errval{EINVAL}. + \func{cap\_to\_text}.} nel qual caso l'argomento dovrà essere un dato di +tipo \texttt{char *}. Per questo l'argomento \param{obj\_d} è dichiarato come +\texttt{void *} e deve sempre corrispondere ad un puntatore ottenuto tramite +le altre funzioni della libreria, altrimenti la funzione fallirà con un errore +di \errval{EINVAL}. Infine si può creare una copia di un \textit{capability state} ottenuto in precedenza tramite la funzione \funcd{cap\_dup}, il cui prototipo è: @@ -2532,11 +2537,13 @@ La funzione crea una copia del \textit{capability state} posto all'indirizzo copia, che conterrà gli stessi valori delle \textit{capabilities} presenti nell'originale. La memoria necessaria viene allocata automaticamente dalla funzione. Una volta effettuata la copia i due \textit{capability state} -potranno essere modificati in maniera completamente indipendente. +potranno essere modificati in maniera completamente +indipendente.\footnote{alla fine delle operazioni si ricordi però di + disallocare anche la copia, oltre all'originale. } -Una seconda classe di funzioni di servizio sono quelle per la gestione dei -dati contenuti all'interno di un \textit{capability state}; la prima di esse è -\funcd{cap\_clear}, il cui prototipo è: +Una seconda classe di funzioni di servizio previste dall'interfaccia sono +quelle per la gestione dei dati contenuti all'interno di un \textit{capability + state}; la prima di queste è \funcd{cap\_clear}, il cui prototipo è: \begin{functions} \headdecl{sys/capability.h} @@ -2611,10 +2618,12 @@ combinare diversi valori in una maschera binaria, una variabile di tipo \type{cap\_value\_t} deve indicare una sola capacità.\footnote{nel file di header citato nella nota precedente il tipo \type{cap\_value\_t} è definito come \ctyp{int}, ma i valori validi sono soltanto quelli di - tab.~\ref{tab:proc_capabilities}.} Infine lo stato di una capacità è -descritto ad una variabile di tipo \type{cap\_flag\_value\_t}, che a sua volta -può assumere soltanto uno\footnote{anche questo è un tipo enumerato.} dei -valori di tab.~\ref{tab:cap_value_type}. + tab.~\ref{tab:proc_capabilities}.} + +Infine lo stato di una capacità è descritto ad una variabile di tipo +\type{cap\_flag\_value\_t}, che a sua volta può assumere soltanto +uno\footnote{anche questo è un tipo enumerato.} dei valori di +tab.~\ref{tab:cap_value_type}. \begin{table}[htb] \centering @@ -2640,11 +2649,11 @@ puntato dall'argomento \param{value\_p}; stato di una capacità alla volta. La funzione \func{cap\_set\_flag} può invece impostare in una sola chiamata -più capacità, anche se solo all'interno dello stesso insieme; per questo essa -prende un vettore di valori di tipo \type{cap\_value\_t} nell'argomento -\param{caps}, la cui dimensione è specificata dall'argomento \param{ncap}. Il -tipo di impostazione da eseguire (cancellazione o impostazione) viene indicato -dall'argomento \param{value}. +più \textit{capabilities}, anche se solo all'interno dello stesso insieme. Per +questo motivo essa prende un vettore di valori di tipo \type{cap\_value\_t} +nell'argomento \param{caps}, la cui dimensione viene specificata dall'argomento +\param{ncap}. Il tipo di impostazione da eseguire (cancellazione o +impostazione) viene indicato dall'argomento \param{value}. Per la visualizzazione dello stato delle \textit{capabilities} l'interfaccia prevede una funzione apposita, \funcd{cap\_to\_text}, il cui prototipo è: @@ -2667,12 +2676,13 @@ testuale del contenuto del \textit{capabilities state} \param{caps} passato come argomento, e, qualora l'argomento \param{length\_p} sia diverso da \val{NULL}, restituisce nella variabile intera da questo puntata la lunghezza della stringa. La stringa restituita viene allocata automaticamente dalla -funzione e deve essere liberata con \func{cap\_free}. +funzione e pertanto dovrà essere liberata con \func{cap\_free}. -Fin quei abbiamo trattato delle funzioni di manipolazione dei -\textit{capabilities state}; quando si vuole eseguire la lettura delle -\textit{capabilities} del processo corrente si deve usare la funzione -\funcd{cap\_get\_proc}, il cui prototipo è: +Fin quei abbiamo trattato solo le funzioni di servizio relative alla +manipolazione dei \textit{capabilities state}; l'interfaccia di gestione +prevede però anche le funzioni per la gestione delle \textit{capabilities} +stesse. La prima di queste è \funcd{cap\_get\_proc} che consente la lettura +delle \textit{capabilities} del processo corrente, il suo prototipo è: \begin{functions} \headdecl{sys/capability.h} @@ -2684,10 +2694,11 @@ Fin quei abbiamo trattato delle funzioni di manipolazione dei assumere i valori \errval{EINVAL}, \errval{EPERM} o \errval{ENOMEM}. } \end{functions} -La funzione legge il valore delle \textit{capabilities} del processo corrente -e restituisce il puntatore ad un \textit{capabilities state} contenente il -risultato, che provvede ad allocare autonomamente, e che occorrerà liberare -con \func{cap\_free} quando non sarà più utilizzato. +La funzione legge il valore delle \textit{capabilities} associate al processo +da cui viene invocata, restituendo il risultato tramite il puntatore ad un +\textit{capabilities state} contenente tutti i dati che provvede ad allocare +autonomamente e che di nuovo occorrerà liberare con \func{cap\_free} quando +non sarà più utilizzato. Se invece si vogliono leggere le \textit{capabilities} di un processo specifico occorre usare la funzione \funcd{capgetp}, il cui @@ -2706,19 +2717,23 @@ prototipo\footnote{su alcune pagine di manuale la funzione \errval{EPERM} o \errval{ENOMEM}. } \end{functions} +%TODO controllare e correggere i codici di errore!!! La funzione legge il valore delle \textit{capabilities} del processo indicato -con l'argomento \param{pid}, salvando il risultato nel \textit{capabilities - state} all'indirizzo \param{cap\_d} che deve essere stato creato in -precedenza. Qualora il processo non esista si avrà un errore di -\errval{ESRCH}. Gli stessi valori possono essere letti direttamente nel -filesystem \textit{proc}, nei file \texttt{/proc//status}; ad esempio per -\texttt{init} si otterrà qualcosa del tipo: +con l'argomento \param{pid}, e restituisce il risultato nel +\textit{capabilities state} posto all'indirizzo indicato con l'argomento +\param{cap\_d}; a differenza della precedente in questo caso il +\textit{capability state} deve essere stato creato in precedenza. Qualora il +processo indicato non esista si avrà un errore di \errval{ESRCH}. Gli stessi +valori possono essere letti direttamente nel filesystem \textit{proc}, nei +file \texttt{/proc//status}; ad esempio per \texttt{init} si otterrà +qualcosa del tipo: \begin{Verbatim} ... CapInh: 0000000000000000 CapPrm: 00000000fffffeff CapEff: 00000000fffffeff +... \end{Verbatim} Infine per impostare le \textit{capabilities} del processo corrente (non @@ -2814,7 +2829,7 @@ essenziale anche dal tipo di utilizzo che deve essere fatto del sistema, per cui non esiste un meccanismo che sia valido per tutti gli usi. La caratteristica specifica di un sistema multitasking come Linux è quella del -cosiddetto \itindex{prehemptive~multitasking} \textit{prehemptive +cosiddetto \itindex{preemptive~multitasking} \textit{preemptive multitasking}: questo significa che al contrario di altri sistemi (che usano invece il cosiddetto \itindex{cooperative~multitasking} \textit{cooperative multitasking}) non sono i singoli processi, ma il kernel stesso a decidere @@ -2872,6 +2887,11 @@ fintanto che esso si trova in uno qualunque degli altri stati. \textbf{Zombie}\index{zombie} & \texttt{Z} & Il processo è terminato ma il suo stato di terminazione non è ancora stato letto dal padre. \\ + \textbf{Killable}& \texttt{D} & Un nuovo stato introdotto con il kernel + 2.6.25, sostanzialmente identico + all'\textbf{Uninterrutible Sleep} con la + sola differenza che il processo può + terminato (con \const{SIGKILL}). \\ \hline \end{tabular} \caption{Elenco dei possibili stati di un processo in Linux, nella colonna @@ -2982,8 +3002,9 @@ attraverso la funzione \funcd{nice}, il cui prototipo {int nice(int inc)} Aumenta il valore di \var{nice} per il processo corrente. - \bodydesc{La funzione ritorna zero in caso di successo e -1 in caso di - errore, nel qual caso \var{errno} può assumere i valori: + \bodydesc{La funzione ritorna zero o il nuovo valore di \var{nice} in caso + di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere + i valori: \begin{errlist} \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha specificato un valore di \param{inc} negativo. @@ -3000,9 +3021,27 @@ priorit l'amministratore può specificare valori negativi che permettono di aumentare la priorità di un processo. -In SUSv2 la funzione ritorna il nuovo valore di \var{nice}; Linux non segue -questa convenzione, e per leggere il nuovo valore occorre invece usare la -funzione \funcd{getpriority}, derivata da BSD, il cui prototipo è: +Gli standard SUSv2 e POSIX.1 prevedono che la funzione ritorni il nuovo valore +di \var{nice} del processo; tuttavia la system call di Linux non segue questa +convenzione e restituisce sempre 0 in caso di successo, questo perché $-1$ è +un valore di \var{nice} legittimo e questo comporta una confusione con una +eventuale condizione di errore. + +Fino alle \acr{glibc} 2.2.4 la funzione di libreria riportava direttamente il +valore ottenuto dalla system call, violando lo standard, per cui per ottenere +il nuovo valore occorreva una successiva chiamata alla funzione +\func{getpriority}. A partire dalla \acr{glibc} 2.2.4 \func{nice} è stata +reimplementata come funzione di libreria, e restituisce il valore di +\var{nice} come richiesto dallo standard.\footnote{questo viene fatto + chiamando al suo interno \func{getpriority}, ed è questo il motivo delle due + possibilità per i valori di ritorno citati nella descrizione del prototipo.} +In questo caso l'unico modo per rilevare in maniera affidabile una condizione +di errore è quello di azzerare \var{errno} prima della chiamata della funzione +e verificarne il valore quando \func{nice} restituisce $-1$. + + +Per leggere il valore di nice di un processo occorre usare la funzione +\funcd{getpriority}, derivata da BSD; il suo prototipo è: \begin{prototype}{sys/resource.h} {int getpriority(int which, int who)} @@ -3048,12 +3087,12 @@ l'utente correnti. \end{table} La funzione restituisce la priorità più alta (cioè il valore più basso) fra -quelle dei processi specificati; dato che -1 è un valore possibile, per poter -rilevare una condizione di errore è necessario cancellare sempre \var{errno} -prima della chiamata alla funzione, per verificare che essa resti uguale a -zero. +quelle dei processi specificati; di nuovo, dato che $-1$ è un valore +possibile, per poter rilevare una condizione di errore è necessario cancellare +sempre \var{errno} prima della chiamata alla funzione per verificare che essa +resti uguale a zero. -Analoga a \func{getpriority} la funzione \funcd{setpriority} permette di +Analoga a \func{getpriority} è la funzione \funcd{setpriority} che permette di impostare la priorità di uno o più processi; il suo prototipo è: \begin{prototype}{sys/resource.h} {int setpriority(int which, int who, int prio)} @@ -3187,6 +3226,8 @@ assolute diverse da zero o politiche \const{SCHED\_FIFO} e \const{SCHED\_RR}. \footnotetext{introdotto con il kernel 2.6.16.} +% TODO manca SCHED_IDLE + 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 @@ -3405,18 +3446,10 @@ problematiche nei nuovi kernel\footnote{le due system call per la gestione l'opportuna infrastruttura ed una nuova system call che permette di impostare su quali processori far eseguire un determinato processo attraverso una \textsl{maschera di affinità}. La corrispondente funzione di libreria è -\funcd{sched\_setaffinity} ed il suo prototipo\footnote{di questa funzione (e - della corrispondente \func{sched\_setaffinity}) esistono versioni diverse - per gli argomenti successivi a \param{pid}: la prima (quella riportata nella - pagina di manuale) prevedeva due ulteriori argomenti di tipo - \texttt{unsigned int len} e \texttt{unsigned long *mask}, poi l'argomento - \texttt{len} è stato eliminato, successivamente si è introdotta la versione - riportata con però un secondo argomento di tipo \texttt{size\_t cpusetsize} - (anche questa citata nella pagina di manuale); la versione citata è quella - riportata nel manuale delle \textsl{glibc} e corrispondente alla definizione - presente in \file{sched.h}.} è: +\funcd{sched\_setaffinity} ed il suo prototipo è: \begin{prototype}{sched.h} - {int sched\_setaffinity (pid\_t pid, const cpu\_set\_t *cpuset)} + {int sched\_setaffinity (pid\_t pid, unsigned int cpusetsize, const + cpu\_set\_t *cpuset)} Imposta la maschera di affinità del processo \param{pid}. \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, @@ -3431,6 +3464,21 @@ su quali processori far eseguire un determinato processo attraverso una ed inoltre anche \errval{EFAULT}.} \end{prototype} + +Questa funzione e la corrispondente \func{sched\_setaffinity} hanno una storia +abbastanza complessa, la system call prevede l'uso di due ulteriori argomenti +di tipo \texttt{unsigned int len} e \texttt{unsigned long *mask}, che +corrispondono al fatto che la implementazione effettiva usa una semplice +maschera binaria. Quando le funzioni vennero incluse nelle \acr{glibc} +assunsero invece il prototipo appena mostrato. A complicare la cosa si +aggiunge il fatto che nella versione 2.3.3 delle \acr{glibc} l'argomento +\param{cpusetsize} è stato eliminato, per poi essere ripristinato nella +versione 2.3.4.\footnote{pertanto se la vostra pagina di manuale non è + aggiornata, o usate quella particolare versione delle \acr{glibc}, potrete + trovare indicazioni diverse, il prototipo illustrato è quello riportato + nella versione corrente (maggio 2008) delle pagine di manuale e + corrispondente alla definizione presente in \file{sched.h}.} + La funzione imposta, con l'uso del valore contenuto all'indirizzo \param{cpuset}, l'insieme dei processori sui quali deve essere eseguito il processo identificato tramite il valore passato in \param{pid}. Come in @@ -3454,10 +3502,11 @@ o periferiche) pu avviene nelle architetture NUMA). Infine se un gruppo di processi accede alle stesse risorse condivise (ad -esempio una applicazione con più thread) può avere senso usare lo stesso -processore in modo da sfruttare meglio l'uso della sua cache; questo -ovviamente riduce i benefici di un sistema multiprocessore nell'esecuzione -contemporanea dei thread, ma in certi casi (quando i thread sono inerentemente +esempio una applicazione con più \itindex{thread} \textit{thread}) può avere +senso usare lo stesso processore in modo da sfruttare meglio l'uso della sua +cache; questo ovviamente riduce i benefici di un sistema multiprocessore +nell'esecuzione contemporanea dei \itindex{thread} \textit{thread}, ma in +certi casi (quando i \itindex{thread} \textit{thread} sono inerentemente serializzati nell'accesso ad una risorsa) possono esserci sufficienti vantaggi nell'evitare la perdita della cache da rendere conveniente l'uso dell'affinità di processore. @@ -3505,7 +3554,8 @@ possa essere eseguito su qualunque processore, se pu valore per un processo specifico usando la funzione \funcd{sched\_getaffinity}, il suo prototipo è: \begin{prototype}{sched.h} - {int sched\_getaffinity (pid\_t pid, const cpu\_set\_t *cpuset)} + {int sched\_getaffinity (pid\_t pid, unsigned int cpusetsize, + const cpu\_set\_t *cpuset)} Legge la maschera di affinità del processo \param{pid}. \bodydesc{La funzione ritorna 0 in caso di successo e -1 in caso di errore, @@ -3655,19 +3705,22 @@ eseguire in maniera atomica le operazioni necessarie. \subsection{Le funzioni rientranti} \label{sec:proc_reentrant} +\index{funzioni!rientranti|(} + Si dice \textsl{rientrante} una funzione che può essere interrotta in qualunque punto della sua esecuzione ed essere chiamata una seconda volta da -un altro thread di esecuzione senza che questo comporti nessun problema -nell'esecuzione della stessa. La problematica è comune nella programmazione -multi-thread, ma si hanno gli stessi problemi quando si vogliono chiamare -delle funzioni all'interno dei gestori dei segnali. +un altro \itindex{thread} \textit{thread} di esecuzione senza che questo +comporti nessun problema nell'esecuzione della stessa. La problematica è +comune nella programmazione \itindex{thread} \textit{multi-thread}, ma si +hanno gli stessi problemi quando si vogliono chiamare delle funzioni +all'interno dei gestori dei segnali. Fintanto che una funzione opera soltanto con le variabili locali è rientrante; -queste infatti vengono allocate nello \itindex{stack} stack, ed un'altra -invocazione non fa altro che allocarne un'altra copia. Una funzione può non -essere rientrante quando opera su memoria che non è nello \itindex{stack} -stack. Ad esempio una funzione non è mai rientrante se usa una variabile -globale o statica. +queste infatti vengono allocate nello \itindex{stack} \textit{stack}, ed +un'altra invocazione non fa altro che allocarne un'altra copia. Una funzione +può non essere rientrante quando opera su memoria che non è nello +\itindex{stack} \textit{stack}. Ad esempio una funzione non è mai rientrante +se usa una variabile globale o statica. Nel caso invece la funzione operi su un oggetto allocato dinamicamente, la cosa viene a dipendere da come avvengono le operazioni: se l'oggetto è creato @@ -3681,16 +3734,20 @@ parte del programmatore. In genere le funzioni di libreria non sono rientranti, molte di esse ad esempio utilizzano variabili statiche, le \acr{glibc} però mettono a -disposizione due macro di compilatore, \macro{\_REENTRANT} e +disposizione due macro di compilatore,\footnote{si ricordi quanto illustrato + in sez.~\ref{sec:intro_gcc_glibc_std}.} \macro{\_REENTRANT} e \macro{\_THREAD\_SAFE}, la cui definizione attiva le versioni rientranti di varie funzioni di libreria, che sono identificate aggiungendo il suffisso \code{\_r} al nome della versione normale. +\index{funzioni!rientranti|)} + + % LocalWords: multitasking like VMS child process identifier pid sez shell fig % LocalWords: parent kernel init pstree keventd kswapd table struct linux call % LocalWords: nell'header scheduler system interrupt timer HZ asm Hertz clock % LocalWords: l'alpha tick fork wait waitpid exit exec image glibc int pgid ps -% LocalWords: sid threads thread Ingo Molnar ppid getpid getppid sys unistd LD +% LocalWords: sid thread Ingo Molnar ppid getpid getppid sys unistd LD % LocalWords: void ForkTest tempnam pathname sibling cap errno EAGAIN ENOMEM % LocalWords: stack read only copy write tab client spawn forktest sleep PATH % LocalWords: source LIBRARY scheduling race condition printf descriptor dup @@ -3715,7 +3772,7 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: shmctl ioperm iopl chroot ptrace accounting swap reboot hangup % LocalWords: vhangup mknod lease permitted inherited inheritable bounding AND % LocalWords: capability capget capset header ESRCH undef version obj clear PT -% LocalWords: pag ssize length proc capgetp prehemptive cache runnable Stopped +% LocalWords: pag ssize length proc capgetp preemptive cache runnable Stopped % LocalWords: Uninterrutible SIGSTOP soft slice nice niceness counter which SC % LocalWords: getpriority who setpriority RTLinux RTAI Adeos fault FIFO First % LocalWords: yield Robin setscheduler policy param OTHER priority setparam to @@ -3731,9 +3788,10 @@ varie funzioni di libreria, che sono identificate aggiungendo il suffisso % LocalWords: cpusetsize cpuset atomic tickless redirezione WCONTINUED stopped % LocalWords: waitid NOCLDSTOP ENOCHLD WIFCONTINUED ifdef endif idtype siginfo % LocalWords: infop ALL WEXITED WSTOPPED WNOWAIT signo CLD EXITED KILLED page -% LocalWords: CONTINUED +% LocalWords: CONTINUED sources forking Spawned successfully executing exiting %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil" %%% End: +% LocalWords: next cat for COMMAND pts bash defunct