X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=0bcc2a0f4bb8bef6501077196a581a1f87e60f2b;hp=ef75e52dd8c52b2235666884701029c885a738a7;hb=718a0a24b34dce09e40eafc33c02ae494d100181;hpb=d3cbe0a3984b7189d086ccb631d5b3b1955e223c diff --git a/prochand.tex b/prochand.tex index ef75e52..0bcc2a0 100644 --- a/prochand.tex +++ b/prochand.tex @@ -1,3 +1,13 @@ +%% prochand.tex +%% +%% Copyright (C) 2000-2002 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 "Prefazione", +%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the +%% license is included in the section entitled "GNU Free Documentation +%% License". +%% \chapter{La gestione dei processi} \label{cha:process_handling} @@ -32,8 +42,9 @@ generazione di nuovi processi caratteristiche di Unix (che esamineremo in dettaglio più avanti) è che qualunque processo può a sua volta generarne altri, detti processi figli (\textit{child process}). Ogni processo è identificato presso il sistema da un -numero unico, il cosiddetto \textit{process identifier} o, più brevemente, -\acr{pid}. +numero univoco, il cosiddetto \textit{process identifier} o, più brevemente, +\acr{pid}, assengnato in forma progressiva (vedi \secref{sec:proc_pid}) quando +il processo viene creato. Una seconda caratteristica di un sistema Unix è che la generazione di un processo è un'operazione separata rispetto al lancio di un programma. In @@ -109,9 +120,10 @@ Dato che tutti i processi attivi nel sistema sono comunque generati da possono classificare i processi con la relazione padre/figlio in un'organizzazione gerarchica ad albero, in maniera analoga a come i file sono organizzati in un albero di directory (si veda -\secref{sec:file_organization}); in \curfig\ si è mostrato il risultato del -comando \cmd{pstree} che permette di visualizzare questa struttura, alla cui -base c'è \cmd{init} che è progenitore di tutti gli altri processi. +\secref{sec:file_organization}); in \figref{fig:proc_tree} si è mostrato il +risultato del comando \cmd{pstree} che permette di visualizzare questa +struttura, alla cui base c'è \cmd{init} che è progenitore di tutti gli altri +processi. Il kernel mantiene una tabella dei processi attivi, la cosiddetta \textit{process table}; per ciascun processo viene mantenuta una voce nella @@ -120,7 +132,7 @@ contiene tutte le informazioni rilevanti per quel processo. Tutte le strutture usate a questo scopo sono dichiarate nell'header file \file{linux/sched.h}, ed uno schema semplificato, che riporta la struttura delle principali informazioni contenute nella \type{task\_struct} (che in seguito incontreremo a più -riprese), è mostrato in \nfig. +riprese), è mostrato in \figref{fig:proc_task_struct}. \begin{figure}[htb] \centering @@ -131,21 +143,22 @@ riprese), \end{figure} -Come accennato in \secref{sec:intro_unix_struct} è lo \textit{scheduler} che -decide quale processo mettere in esecuzione; esso viene eseguito ad ogni -system call ed ad ogni interrupt,\footnote{più in una serie di altre - occasioni. NDT completare questa parte.} (ma può essere anche attivato -esplicitamente). Il timer di sistema provvede comunque a che esso sia invocato -periodicamente, generando un interrupt periodico secondo la frequenza -specificata dalla costante \macro{HZ}, definita in \file{asm/param.h}, ed il -cui valore è espresso in Hertz.\footnote{Il valore usuale di questa costante è - 100, per tutte le architetture eccetto l'alpha, per la quale è 1000. Occorre - fare attenzione a non confondere questo valore con quello dei clock tick - (vedi \secref{sec:sys_unix_time}).} +Come accennato in \secref{sec:intro_unix_struct} è lo +\textit{scheduler}\index{scheduler} che decide quale processo mettere in +esecuzione; esso viene eseguito ad ogni system call ed ad ogni +interrupt,\footnote{più in una serie di altre occasioni. NDT completare questa + parte.} (ma può essere anche attivato esplicitamente). Il timer di sistema +provvede comunque a che esso sia invocato periodicamente, generando un +interrupt periodico secondo la frequenza specificata dalla costante +\const{HZ}, definita in \file{asm/param.h}, ed il cui valore è espresso in +Hertz.\footnote{Il valore usuale di questa costante è 100, per tutte le + architetture eccetto l'alpha, per la quale è 1000. Occorre fare attenzione a + non confondere questo valore con quello dei clock tick (vedi + \secref{sec:sys_unix_time}).} %Si ha cioè un interrupt dal timer ogni centesimo di secondo. -Ogni volta che viene eseguito, lo \textit{scheduler} effettua il calcolo delle -priorità dei vari processi attivi (torneremo su questo in +Ogni volta che viene eseguito, lo \textit{scheduler}\index{scheduler} effettua +il calcolo delle priorità dei vari processi attivi (torneremo su questo in \secref{sec:proc_priority}) e stabilisce quale di essi debba essere posto in esecuzione fino alla successiva invocazione. @@ -157,7 +170,7 @@ I processi vengono creati dalla funzione \func{fork}; in molti unix questa una system call, Linux però usa un'altra nomenclatura, e la funzione \func{fork} è basata a sua volta sulla system call \func{\_\_clone}, che viene usata anche per generare i \textit{thread}. Il processo figlio creato dalla -\func{fork} è una copia identica del processo processo padre, ma ha nuovo +\func{fork} è una copia identica del processo processo padre, ma ha un nuovo \acr{pid} e viene eseguito in maniera indipendente (le differenze fra padre e figlio sono affrontate in dettaglio in \secref{sec:proc_fork}). @@ -185,7 +198,7 @@ Il programma che un processo sta eseguendo si chiama immagine del processo (o \textit{process image}), le funzioni della famiglia \func{exec} permettono di caricare un'altro programma da disco sostituendo quest'ultimo all'immagine corrente; questo fa sì che l'immagine precedente venga completamente -cancellata. Questo significa che quando il nuovo programma esce, anche il +cancellata. Questo significa che quando il nuovo programma termina, anche il processo termina, e non si può tornare alla precedente immagine. Per questo motivo la \func{fork} e la \func{exec} sono funzioni molto @@ -210,21 +223,26 @@ programmi. \label{sec:proc_pid} Come accennato nell'introduzione, ogni processo viene identificato dal sistema -da un numero identificativo unico, il \textit{process id} o \acr{pid}; +da un numero identificativo univoco, il \textit{process id} o \acr{pid}; quest'ultimo è un tipo di dato standard, il \type{pid\_t} che in genere è un intero con segno (nel caso di Linux e delle \acr{glibc} il tipo usato è \ctyp{int}). -Il \acr{pid} viene assegnato in forma progressiva ogni volta che un nuovo -processo viene creato, fino ad un limite che, essendo il \acr{pid} un numero -positivo memorizzato in un intero a 16 bit, arriva ad un massimo di 32767. -Oltre questo valore l'assegnazione riparte dal numero più basso disponibile a -partire da un minimo di 300,\footnote{questi valori sono definiti dalla macro - \macro{PID\_MAX} in \file{threads.h} e direttamente in \file{fork.c} nei - sorgenti del kernel.} che serve a riservare i \acr{pid} più bassi ai processi -eseguiti dal direttamente dal kernel. Per questo motivo, come visto in -\secref{sec:proc_hierarchy}, il processo di avvio (\cmd{init}) ha sempre il -\acr{pid} uguale a uno. +Il \acr{pid} viene assegnato in forma progressiva\footnote{in genere viene + assegnato il numero successivo a quello usato per l'ultimo processo creato, + a meno che questo numero non sia già utilizzato per un altro \acr{pid}, + \acr{pgid} o \acr{sid} (vedi \secref{sec:sess_proc_group}).} ogni volta che +un nuovo processo viene creato, fino ad un limite che, essendo il \acr{pid} un +numero positivo memorizzato in un intero a 16 bit, arriva ad un 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.} che serve a riservare i \acr{pid} più bassi +ai processi eseguiti dal direttamente dal kernel. Per questo motivo, come +visto in \secref{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 @@ -282,32 +300,33 @@ prototipo della funzione zero al figlio; ritorna -1 al padre (senza creare il figlio) in caso di errore; \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{EAGAIN}] non ci sono risorse sufficienti per creare un'altro + \item[\errcode{EAGAIN}] non ci sono risorse sufficienti per creare un'altro processo (per allocare la tabella delle pagine e le strutture del task) o si è esaurito il numero di processi disponibili. - \item[\macro{ENOMEM}] non è stato possibile allocare la memoria per le + \item[\errcode{ENOMEM}] non è stato possibile allocare la memoria per le strutture necessarie al kernel per creare il nuovo processo. \end{errlist}} \end{functions} Dopo il successo dell'esecuzione di una \func{fork} sia il processo padre che -il processo figlio continuano ad essere eseguiti normalmente all'istruzione -seguente la \func{fork}; il processo figlio è però una copia del padre, e -riceve una copia dei segmenti di testo, stack e dati (vedi +il processo figlio continuano ad essere eseguiti normalmente a partire +dall'istruzione seccessiva alla \func{fork}; il processo figlio è però una +copia del padre, e riceve una copia dei segmenti di testo, stack e dati (vedi \secref{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. -Per quanto riguarda la gestione della memoria in generale il segmento di -testo, che è identico, è condiviso e tenuto in read-only per il padre e per i -figli. Per gli altri segmenti Linux utilizza la tecnica del \textit{copy on - write}\index{copy on write}; questa tecnica comporta che una pagina di -memoria viene effettivamente copiata per il nuovo processo solo quando ci -viene effettuata sopra una scrittura (e si ha quindi una reale differenza fra -padre e figlio). In questo modo si rende molto più efficiente il meccanismo -della creazione di un nuovo processo, non essendo più necessaria la copia di -tutto lo spazio degli indirizzi virtuali del padre, ma solo delle pagine di -memoria che sono state modificate, e solo al momento della modifica stessa. +Per quanto riguarda la gestione della memoria, in generale il segmento di +testo, che è identico per i due processi, è condiviso e tenuto in read-only +per il padre e per i figli. Per gli altri segmenti Linux utilizza la tecnica +del \textit{copy on write}\index{copy on write}; questa tecnica comporta che +una pagina di memoria viene effettivamente copiata per il nuovo processo solo +quando ci viene effettuata sopra una scrittura (e si ha quindi una reale +differenza fra padre e figlio). In questo modo si rende molto più efficiente +il meccanismo della creazione di un nuovo processo, non essendo più necessaria +la copia di tutto lo spazio degli indirizzi virtuali del padre, ma solo delle +pagine di memoria che sono state modificate, e solo al momento della modifica +stessa. La differenza che si ha nei due processi è che nel processo padre il valore di ritorno della funzione \func{fork} è il \acr{pid} del processo figlio, mentre @@ -384,16 +403,16 @@ sul numero totale di processi permessi all'utente (vedi L'uso di \func{fork} avviene secondo due modalità principali; la prima è quella in cui all'interno di un programma si creano processi figli cui viene affidata l'esecuzione di una certa sezione di codice, mentre il processo padre -ne esegue un'altra. È il caso tipico dei server (il modello -\textit{client-server} è illustrato in \secref{sec:net_cliserv}) di rete in -cui il padre riceve ed accetta le richieste da parte dei client, per ciascuna -delle quali pone in esecuzione un figlio che è incaricato di fornire il -servizio. +ne esegue un'altra. È il caso tipico dei programmi server (il modello +\textit{client-server} è illustrato in \secref{sec:net_cliserv}) in cui il +padre riceve ed accetta le richieste da parte dei programmi client, per +ciascuna delle quali pone in esecuzione un figlio che è incaricato di fornire +il servizio. La seconda modalità è quella in cui il processo vuole eseguire un altro programma; questo è ad esempio il caso della shell. In questo caso il processo -crea un figlio la cui unica operazione è quella fare una \func{exec} (di cui -parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}. +crea un figlio la cui unica operazione è quella di fare una \func{exec} (di +cui parleremo in \secref{sec:proc_exec}) subito dopo la \func{fork}. Alcuni sistemi operativi (il VMS ad esempio) combinano le operazioni di questa seconda modalità (una \func{fork} seguita da una \func{exec}) in un'unica @@ -407,16 +426,16 @@ dell'output, identificatori) prima della \func{exec}, rendendo cos relativamente facile intervenire sulle le modalità di esecuzione del nuovo programma. -In \figref{fig:proc_fork_code} si è riportato il corpo del codice del -programma di esempio \cmd{forktest}, che ci permette di illustrare molte -caratteristiche dell'uso della funzione \func{fork}. Il programma permette di -creare un numero di figli specificato da linea di comando, e prende anche -alcune opzioni per indicare degli eventuali tempi di attesa in secondi -(eseguiti tramite la funzione \func{sleep}) per il padre ed il figlio (con -\cmd{forktest -h} si ottiene la 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://gapil.firenze.linux.it/gapil_source.tgz} +In \figref{fig:proc_fork_code} è riportato il corpo del codice del programma +di esempio \cmd{forktest}, che permette di illustrare molte caratteristiche +dell'uso della funzione \func{fork}. Il programma crea un numero di figli +specificato da linea di comando, e prende anche alcune opzioni per indicare +degli eventuali tempi di attesa in secondi (eseguiti tramite la funzione +\func{sleep}) per il padre ed il figlio (con \cmd{forktest -h} si ottiene la +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://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 @@ -456,15 +475,15 @@ Go to next child 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 - 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. + scheduler\index{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. In generale l'ordine di esecuzione dipenderà, oltre che dall'algoritmo di scheduling usato dal kernel, dalla particolare situazione in si trova la @@ -599,8 +618,9 @@ 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 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} impostati - (vedi \secref{sec:proc_exec} e \secref{sec:file_fcntl}). +\item i file aperti e gli eventuali flag di + \textit{close-on-exec}\index{close-on-exec} impostati (vedi + \secref{sec:proc_exec} e \secref{sec:file_fcntl}). \item gli identificatori per il controllo di accesso: l'\textsl{userid reale}, il \textsl{groupid reale}, l'\textsl{userid effettivo}, il \textsl{groupid effettivo} ed i \textit{groupid supplementari} (vedi @@ -676,7 +696,7 @@ modalit chiamare la funzione \func{abort} per invocare una chiusura anomala, o essere terminato da un segnale. In realtà anche la prima modalità si riconduce alla seconda, dato che \func{abort} si limita a generare il segnale -\macro{SIGABRT}. +\const{SIGABRT}. Qualunque sia la modalità di conclusione di un processo, il kernel esegue comunque una serie di operazioni: chiude tutti i file aperti, rilascia la @@ -687,15 +707,16 @@ eseguite alla chiusura di un processo \item viene memorizzato lo stato di terminazione del processo. \item ad ogni processo figlio viene assegnato un nuovo padre (in genere \cmd{init}). -\item viene inviato il segnale \macro{SIGCHLD} al processo padre (vedi +\item viene inviato il segnale \const{SIGCHLD} al processo padre (vedi \secref{sec:sig_sigchld}). -\item se il processo è un leader di sessione viene mandato un segnale di - \macro{SIGHUP} a tutti i processi in background e il terminale di - controllo viene disconnesso (vedi \secref{sec:sess_xxx}). +\item se il processo è un leader di sessione ed il suo terminale di controllo + è quello della sessione viene mandato un segnale di \const{SIGHUP} a tutti i + processi del gruppo di foreground e il terminale di controllo viene + disconnesso (vedi \secref{sec:sess_ctrl_term}). \item se la conclusione di un processo rende orfano un \textit{process group} ciascun membro del gruppo viene bloccato, e poi gli vengono - inviati in successione i segnali \macro{SIGHUP} e \macro{SIGCONT} - (vedi \secref{sec:sess_ctrl_term}). + inviati in successione i segnali \const{SIGHUP} e \const{SIGCONT} + (vedi ancora \secref{sec:sess_ctrl_term}). \end{itemize*} Oltre queste operazioni è però necessario poter disporre di un meccanismo @@ -770,12 +791,12 @@ memorizzando alcuni dati essenziali, come il \acr{pid}, i tempi di CPU usati dal processo (vedi \secref{sec:sys_unix_time}) e lo stato di terminazione, mentre la memoria in uso ed i file aperti vengono rilasciati immediatamente. I processi che sono terminati, ma il cui stato di terminazione non è stato -ancora ricevuto dal padre sono chiamati \textit{zombie}, essi restano presenti -nella tabella dei processi ed in genere possono essere identificati -dall'output di \cmd{ps} per la presenza di una \texttt{Z} nella colonna che ne -indica lo stato (vedi \tabref{tab:proc_proc_states}). Quando il padre -effettuerà la lettura dello stato di uscita anche questa informazione, non più -necessaria, verrà scartata e la terminazione potrà dirsi completamente +ancora ricevuto dal padre sono chiamati \textit{zombie}\index{zombie}, essi +restano presenti nella tabella dei processi ed in genere possono essere +identificati dall'output di \cmd{ps} per la presenza di una \texttt{Z} nella +colonna che ne indica lo stato (vedi \tabref{tab:proc_proc_states}). Quando il +padre effettuerà la lettura dello stato di uscita anche questa informazione, +non più necessaria, verrà scartata e la terminazione potrà dirsi completamente conclusa. Possiamo utilizzare il nostro programma di prova per analizzare anche questa @@ -848,31 +869,32 @@ segnale termina il processo o chiama una funzione di gestione. \bodydesc{La funzione restituisce il \acr{pid} del figlio in caso di successo e -1 in caso di errore; \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{EINTR}] la funzione è stata interrotta da un segnale. + \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. \end{errlist}} \end{functions} \noindent è presente fin dalle prime versioni di Unix; la funzione ritorna non appena un processo figlio termina. Se un figlio è già terminato la funzione ritorna -immediatamente. - -Al ritorno lo stato di terminazione del processo viene salvato nella -variabile puntata da \var{status} e tutte le informazioni relative al -processo (vedi \secref{sec:proc_termination}) vengono rilasciate. Nel -caso un processo abbia più figli il valore di ritorno permette di -identificare qual'è quello che è uscito. - -Questa funzione ha il difetto di essere poco flessibile, in quanto -ritorna all'uscita di un figlio qualunque. Nelle occasioni in cui è -necessario attendere la conclusione di un processo specifico occorre +immediatamente, se più di un figlio è terminato occorre chiamare la funzione +più volte se si vuole recuperare lo stato di terminazione di tutti quanti. + +Al ritorno della funzione lo stato di terminazione del figlio viene salvato +nella variabile puntata da \var{status} e tutte le risorse del kernel relative +al processo (vedi \secref{sec:proc_termination}) vengono rilasciate. Nel caso +un processo abbia più figli il valore di ritorno (il \acr{pid} del figlio) +permette di identificare qual'è quello che è uscito. + +Questa funzione ha il difetto di essere poco flessibile, in quanto ritorna +all'uscita di un qualunque processo figlio. Nelle occasioni in cui è +necessario attendere la conclusione di un processo specifico occorrerebbe predisporre un meccanismo che tenga conto dei processi già terminati, e -provveda a ripetere la chiamata alla funzione nel caso il processo -cercato sia ancora attivo. +provvedere a ripetere la chiamata alla funzione nel caso il processo cercato +sia ancora attivo. Per questo motivo lo standard POSIX.1 ha introdotto la funzione \func{waitpid} che effettua lo stesso servizio, ma dispone di una serie di funzionalità più ampie, legate anche al controllo di sessione (si veda -\ref{sec:sess_job_control}). Dato che è possibile ottenere lo stesso +\secref{sec:sess_job_control}). Dato che è possibile ottenere lo stesso comportamento di \func{wait} si consiglia di utilizzare sempre questa funzione, il cui prototipo è: \begin{functions} @@ -882,36 +904,37 @@ funzione, il cui prototipo Attende la conclusione di un processo figlio. \bodydesc{La funzione restituisce il \acr{pid} del processo che è uscito, 0 se - è stata specificata l'opzione \macro{WNOHANG} e il processo non è uscito e + è stata specificata l'opzione \const{WNOHANG} e il processo non è uscito e -1 per un errore, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\macro{EINTR}] se non è stata specificata l'opzione \macro{WNOHANG} e + \item[\errcode{EINTR}] se non è stata specificata l'opzione \const{WNOHANG} e la funzione è stata interrotta da un segnale. - \item[\macro{ECHILD}] il processo specificato da \param{pid} non esiste o + \item[\errcode{ECHILD}] il processo specificato da \param{pid} non esiste o non è figlio del processo chiamante. \end{errlist}} \end{functions} Le differenze principali fra le due funzioni sono che \func{wait} si blocca sempre fino a che un processo figlio non termina, mentre \func{waitpid} ha la -possibilità si specificare un'opzione \macro{WNOHANG} che ne previene il -blocco; inoltre \func{waitpid} può specificare quale processo attendere sulla -base del valore fornito dall'argomento \param{pid}, secondo lo -specchietto riportato in \tabref{tab:proc_waidpid_pid}: +possibilità si specificare un'opzione \const{WNOHANG} che ne previene il +blocco; inoltre \func{waitpid} può specificare in maniera flessibile quale +processo attendere, sulla base del valore fornito dall'argomento \param{pid}, +secondo lo specchietto riportato in \tabref{tab:proc_waidpid_pid}. + \begin{table}[!htb] \centering \footnotesize \begin{tabular}[c]{|c|c|p{8cm}|} \hline - \textbf{Valore} & \textbf{Macro} &\textbf{Significato}\\ + \textbf{Valore} & \textbf{Opzione} &\textbf{Significato}\\ \hline \hline $<-1$& -- & attende per un figlio il cui \textit{process group} (vedi \secref{sec:sess_proc_group}) è uguale al valore assoluto di \var{pid}. \\ - $-1$ & \macro{WAIT\_ANY} & attende per un figlio qualsiasi, usata in + $-1$ & \const{WAIT\_ANY} & attende per un figlio qualsiasi, usata in questa maniera è equivalente a \func{wait}.\\ - $0$ & \macro{WAIT\_MYPGRP} & attende per un figlio il cui \textit{process + $0$ & \const{WAIT\_MYPGRP} & attende per un figlio il cui \textit{process group} è uguale a quello del processo chiamante. \\ $>0$ & -- &attende per un figlio il cui \acr{pid} è uguale al valore di \var{pid}.\\ @@ -924,23 +947,24 @@ specchietto riportato in \tabref{tab:proc_waidpid_pid}: Il comportamento di \func{waitpid} può inoltre essere modificato passando delle opportune opzioni tramite l'argomento \param{option}. I valori possibili -sono il già citato \macro{WNOHANG}, che previene il blocco della funzione -quando il processo figlio non è terminato, e \macro{WUNTRACED}. Quest'ultimo -viene generalmente usato per il controllo di sessione, (trattato in -\secref{sec:sess_job_control}) in quanto permette di identificare i processi -bloccati. La funzione infatti in tal caso ritorna, restituendone il \acr{pid}, -se c'è un processo figlio che è entrato in stato di sleep (vedi -\tabref{tab:proc_proc_states}) di cui non si è ancora letto lo stato (con -questa stessa opzione). Il valore dell'opzione deve essere specificato come -maschera binaria ottenuta con l'OR delle suddette costanti con zero. In Linux +sono il già citato \const{WNOHANG}, che previene il blocco della funzione +quando il processo figlio non è terminato, e \const{WUNTRACED} che permette di +tracciare i processi bloccati. Il valore dell'opzione deve essere specificato +come maschera binaria ottenuta con l'OR delle suddette costanti con zero. + +In genere si utilizza \const{WUNTRACED} all'interno del controllo di sessione, +(l'argomento è trattato in \secref{sec:sess_job_control}). In tal caso infatti +la funzione ritorna, restituendone il \acr{pid}, quando c'è un processo figlio +che è entrato in stato di sleep (vedi \tabref{tab:proc_proc_states}) e del +quale non si è ancora letto lo stato (con questa stessa opzione). In Linux sono previste altre opzioni non standard relative al comportamento con i -thread, che saranno trattate in \secref{sec:thread_xxx}. +thread, che riprenderemo in \secref{sec:thread_xxx}. La terminazione di un processo figlio è chiaramente un evento asincrono rispetto all'esecuzione di un programma e può avvenire in un qualunque momento. Per questo motivo, come accennato nella sezione precedente, una delle azioni prese dal kernel alla conclusione di un processo è quella di mandare un -segnale di \macro{SIGCHLD} al padre. L'azione predefinita (si veda +segnale di \const{SIGCHLD} al padre. L'azione predefinita (si veda \secref{sec:sig_base}) per questo segnale è di essere ignorato, ma la sua generazione costituisce il meccanismo di comunicazione asincrona con cui il kernel avverte il processo padre che uno dei suoi figli è terminato. @@ -950,7 +974,7 @@ conclusione di un processo per proseguire, specie se tutto questo serve solo per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}), per questo la modalità più usata per chiamare queste funzioni è quella di utilizzarle all'interno di un \textit{signal handler} (vedremo un esempio di -come gestire \macro{SIGCHLD} con i segnali in \secref{sec:sig_example}). In +come gestire \const{SIGCHLD} con i segnali in \secref{sec:sig_example}). In questo caso infatti, dato che il segnale è generato dalla terminazione di un figlio, avremo la certezza che la chiamata a \func{wait} non si bloccherà. @@ -967,23 +991,23 @@ figlio, avremo la certezza che la chiamata a \func{wait} non si bloccher \macro{WEXITSTATUS(s)} & Restituisce gli otto bit meno significativi dello stato di uscita del processo (passato attraverso \func{\_exit}, \func{exit} o come valore di ritorno di \func{main}). Può essere valutata solo se - \macro{WIFEXITED} ha restituito un valore non nullo.\\ + \val{WIFEXITED} ha restituito un valore non nullo.\\ \macro{WIFSIGNALED(s)} & Vera se il processo figlio è terminato in maniera anomala a causa di un segnale che non è stato catturato (vedi \secref{sec:sig_notification}).\\ \macro{WTERMSIG(s)} & restituisce il numero del segnale che ha causato la terminazione anomala del processo. Può essere valutata solo se - \macro{WIFSIGNALED} ha restituito un valore non nullo.\\ + \val{WIFSIGNALED} ha restituito un valore non nullo.\\ \macro{WCOREDUMP(s)} & Vera se il processo terminato ha generato un file si \textit{core dump}. Può essere valutata solo se - \macro{WIFSIGNALED} ha restituito un valore non nullo.\footnote{questa + \val{WIFSIGNALED} ha restituito un valore non nullo.\footnote{questa macro non è definita dallo standard POSIX.1, ma è presente come estensione sia in Linux che in altri Unix.}\\ \macro{WIFSTOPPED(s)} & Vera se il processo che ha causato il ritorno di \func{waitpid} è bloccato. L'uso è possibile solo avendo specificato - l'opzione \macro{WUNTRACED}. \\ + l'opzione \const{WUNTRACED}. \\ \macro{WSTOPSIG(s)} & restituisce il numero del segnale che ha bloccato - il processo, Può essere valutata solo se \macro{WIFSTOPPED} ha + il processo, Può essere valutata solo se \val{WIFSTOPPED} ha restituito un valore non nullo. \\ \hline \end{tabular} @@ -1010,7 +1034,7 @@ presente che queste macro prendono come parametro la variabile di tipo \ctyp{int} puntata da \var{status}). Si tenga conto che nel caso di conclusione anomala il valore restituito da -\macro{WTERMSIG} può essere confrontato con le costanti definite in +\val{WTERMSIG} può essere confrontato con le costanti definite in \file{signal.h} ed elencate in \tabref{tab:sig_signal_list}, e stampato usando le apposite funzioni trattate in \secref{sec:sig_strsignal}. @@ -1069,26 +1093,26 @@ famiglia di funzioni) che possono essere usate per questo compito, in realt \bodydesc{La funzione ritorna solo in caso di errore, restituendo -1; nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{EACCES}] il file non è eseguibile, oppure il filesystem è + \item[\errcode{EACCES}] il file non è eseguibile, oppure il filesystem è montato in \cmd{noexec}, oppure non è un file regolare o un interprete. - \item[\macro{EPERM}] il file ha i bit \acr{suid} o \acr{sgid}, l'utente non + \item[\errcode{EPERM}] il file ha i bit \acr{suid} o \acr{sgid}, l'utente non è root, e o il processo viene tracciato, o il filesystem è montato con l'opzione \cmd{nosuid}. - \item[\macro{ENOEXEC}] il file è in un formato non eseguibile o non + \item[\errcode{ENOEXEC}] il file è in un formato non eseguibile o non riconosciuto come tale, o compilato per un'altra architettura. - \item[\macro{ENOENT}] il file o una delle librerie dinamiche o l'interprete + \item[\errcode{ENOENT}] il file o una delle librerie dinamiche o l'interprete necessari per eseguirlo non esistono. - \item[\macro{ETXTBSY}] L'eseguibile è aperto in scrittura da uno o più + \item[\errcode{ETXTBSY}] L'eseguibile è aperto in scrittura da uno o più processi. - \item[\macro{EINVAL}] L'eseguibile ELF ha più di un segmento - \macro{PF\_INTERP}, cioè chiede di essere eseguito da più di un + \item[\errcode{EINVAL}] L'eseguibile ELF ha più di un segmento + \const{PF\_INTERP}, cioè chiede di essere eseguito da più di un interprete. - \item[\macro{ELIBBAD}] Un interprete ELF non è in un formato + \item[\errcode{ELIBBAD}] Un interprete ELF non è in un formato riconoscibile. \end{errlist} - ed inoltre anche \macro{EFAULT}, \macro{ENOMEM}, \macro{EIO}, - \macro{ENAMETOOLONG}, \macro{E2BIG}, \macro{ELOOP}, \macro{ENOTDIR}, - \macro{ENFILE}, \macro{EMFILE}.} + ed inoltre anche \errval{EFAULT}, \errval{ENOMEM}, \errval{EIO}, + \errval{ENAMETOOLONG}, \errval{E2BIG}, \errval{ELOOP}, \errval{ENOTDIR}, + \errval{ENFILE}, \errval{EMFILE}.} \end{prototype} La funzione \func{exec} esegue il file o lo script indicato da @@ -1121,10 +1145,11 @@ linea di comando e l'ambiente ricevuti dal nuovo processo. \end{functions} Per capire meglio le differenze fra le funzioni della famiglia si può fare -riferimento allo specchietto riportato in \ntab. La prima differenza riguarda -le modalità di passaggio dei parametri che poi andranno a costituire gli -argomenti a linea di comando (cioè i valori di \var{argv} e \var{argc} visti -dalla funzione \func{main} del programma chiamato). +riferimento allo specchietto riportato in \tabref{tab:proc_exec_scheme}. La +prima differenza riguarda le modalità di passaggio dei parametri che poi +andranno a costituire gli argomenti a linea di comando (cioè i valori di +\var{argv} e \var{argc} visti dalla funzione \func{main} del programma +chiamato). Queste modalità sono due e sono riassunte dagli mnemonici \code{v} e \code{l} che stanno rispettivamente per \textit{vector} e \textit{list}. Nel primo caso @@ -1149,8 +1174,8 @@ per indicare il nome del file che contiene il programma che verr \multicolumn{1}{|c|}{\textbf{Caratteristiche}} & \multicolumn{6}{|c|}{\textbf{Funzioni}} \\ \hline - &\func{execl\ }&\func{execlp}&\func{execle} - &\func{execv\ }& \func{execvp}& \func{execve} \\ + &\func{execl}\texttt{ }&\func{execlp}&\func{execle} + &\func{execv}\texttt{ }& \func{execvp}& \func{execve} \\ \hline \hline argomenti a lista &$\bullet$&$\bullet$&$\bullet$&&& \\ @@ -1177,10 +1202,10 @@ viene eseguita automaticamente una ricerca fra i file presenti nella lista di directory specificate dalla variabile di ambiente \var{PATH}. Il file che viene posto in esecuzione è il primo che viene trovato. Se si ha un errore relativo a permessi di accesso insufficienti (cioè l'esecuzione della -sottostante \func{execve} ritorna un \macro{EACCESS}), la ricerca viene +sottostante \func{execve} ritorna un \errcode{EACCES}), la ricerca viene proseguita nelle eventuali ulteriori directory indicate in \var{PATH}; solo se non viene trovato nessun altro file viene finalmente restituito -\macro{EACCESS}. +\errcode{EACCES}. Le altre quattro funzioni si limitano invece a cercare di eseguire il file indicato dal parametro \var{path}, che viene interpretato come il @@ -1196,7 +1221,7 @@ indicato dal parametro \var{path}, che viene interpretato come il La terza differenza è come viene passata la lista delle variabili di ambiente. Con lo mnemonico \code{e} vengono indicate quelle funzioni che necessitano di un vettore di parametri \var{envp[]} analogo a quello usato per gli argomenti -a riga di comando (terminato quindi da un \macro{NULL}), le altre usano il +a riga di comando (terminato quindi da un \val{NULL}), le altre usano il valore della variabile \var{environ} (vedi \secref{sec:proc_environ}) del processo di partenza per costruire l'ambiente. @@ -1227,22 +1252,23 @@ la lista completa Inoltre i segnali che sono stati impostati per essere ignorati nel processo chiamante mantengono la stessa impostazione pure nel nuovo programma, tutti gli altri segnali vengono impostati alla loro azione predefinita. Un caso -speciale è il segnale \macro{SIGCHLD} che, quando impostato a -\macro{SIG\_IGN}, può anche non essere reimpostato a \macro{SIG\_DFL} (si veda +speciale è il segnale \const{SIGCHLD} che, quando impostato a +\const{SIG\_IGN}, può anche non essere reimpostato a \const{SIG\_DFL} (si veda \secref{sec:sig_gen_beha}). La gestione dei file aperti dipende dal valore che ha il flag di -\textit{close-on-exec} (trattato in \secref{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. +\textit{close-on-exec}\index{close-on-exec} (vedi anche +\secref{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 \secref{sec:file_dir_read}) che effettua da sola -l'impostazione del flag di \textit{close-on-exec} sulle directory che apre, in -maniera trasparente all'utente. +l'impostazione del flag di \textit{close-on-exec}\index{close-on-exec} sulle +directory che apre, in maniera trasparente all'utente. Abbiamo detto che l'\textsl{userid reale} ed il \textsl{groupid reale} restano gli stessi all'esecuzione di \func{exec}; lo stesso vale per l'\textsl{userid @@ -1257,7 +1283,7 @@ Se il file da eseguire condivise, viene lanciato il \textit{linker} dinamico \cmd{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 \macro{PT\_INTERP}, +dinamiche viene usato l'interprete indicato nel segmento \const{PT\_INTERP}, in genere questo è \file{/lib/ld-linux.so.1} per programmi linkati con le \acr{libc5}, e \file{/lib/ld-linux.so.2} per programmi linkati con le \acr{glibc}. Infine nel caso il file sia uno script esso deve iniziare con @@ -1268,10 +1294,10 @@ chiamato come se si fosse eseguito il comando \cmd{interpreter [arg] 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 -processo, con \func{exec} si avvia un nuovo programma, con \func{exit} e -\func{wait} si effettua e verifica la conclusione dei programmi. Tutte le -altre funzioni sono ausiliarie e servono la lettura e l'impostazione dei vari -parametri connessi ai processi. +processo, con \func{exec} si lancia un nuovo programma, con \func{exit} e +\func{wait} si effettua e verifica la conclusione dei processi. Tutte le +altre funzioni sono ausiliarie e servono per la lettura e l'impostazione dei +vari parametri connessi ai processi. @@ -1378,10 +1404,10 @@ completata la procedura di autenticazione, lancia una shell per la quale imposta questi identificatori ai valori corrispondenti all'utente che entra nel sistema. -Al secondo gruppo appartengono l'\textsl{userid effettivo} e l'\textsl{groupid - effettivo} (a cui si aggiungono gli eventuali \textsl{groupid supplementari} -dei gruppi dei quali l'utente fa parte). Questi sono invece gli -identificatori usati nella verifiche dei permessi del processo e per il +Al secondo gruppo appartengono lo \textsl{userid effettivo} ed il +\textsl{groupid effettivo} (a cui si aggiungono gli eventuali \textsl{groupid + supplementari} dei gruppi dei quali l'utente fa parte). Questi sono invece +gli identificatori usati nella verifiche dei permessi del processo e per il controllo di accesso ai file (argomento affrontato in dettaglio in \secref{sec:file_perm_overview}). @@ -1469,7 +1495,7 @@ corrente. corrente. \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso - di fallimento: l'unico errore possibile è \macro{EPERM}.} + di fallimento: l'unico errore possibile è \errval{EPERM}.} \end{functions} Il funzionamento di queste due funzioni è analogo, per cui considereremo solo @@ -1484,13 +1510,13 @@ sistema) allora tutti gli identificatori (\textit{real}, \textit{effective} e altrimenti viene impostato solo l'\textsl{userid effettivo}, e soltanto se il valore specificato corrisponde o all'\textsl{userid reale} o all'\textsl{userid salvato}. Negli altri casi viene segnalato un errore (con -\macro{EPERM}). +\errcode{EPERM}). Come accennato l'uso principale di queste funzioni è quello di poter -consentire ad un programma con i bit \acr{suid} o \acr{sgid} impostati di -riportare l'\textsl{userid effettivo} a quello dell'utente che ha lanciato il -programma, effettuare il lavoro che non necessita di privilegi aggiuntivi, ed -eventualmente tornare indietro. +consentire ad un programma con i bit \acr{suid} o \acr{sgid} impostati (vedi +\secref{sec:file_suid_sgid}) di riportare l'\textsl{userid effettivo} a quello +dell'utente che ha lanciato il programma, effettuare il lavoro che non +necessita di privilegi aggiuntivi, ed eventualmente tornare indietro. Come esempio per chiarire l'uso di queste funzioni prendiamo quello con cui viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene @@ -1569,7 +1595,7 @@ specificati da \var{ruid} e \var{euid}. specificati da \var{rgid} e \var{egid}. \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso - di fallimento: l'unico errore possibile è \macro{EPERM}.} + di fallimento: l'unico errore possibile è \errval{EPERM}.} \end{functions} La due funzioni sono analoghe ed il loro comportamento è identico; quanto @@ -1580,7 +1606,7 @@ fallimento della chiamata; l'amministratore invece pu qualunque. Specificando un argomento di valore -1 l'identificatore corrispondente verrà lasciato inalterato. -Con queste funzione si possono scambiare fra loro gli userid reale e +Con queste funzioni si possono scambiare fra loro gli userid reale e effettivo, e pertanto è possibile implementare un comportamento simile a quello visto in precedenza per \func{setgid}, cedendo i privilegi con un primo scambio, e recuperandoli, eseguito il lavoro non privilegiato, con un secondo @@ -1621,7 +1647,7 @@ corrente a \var{uid}. corrente a \var{gid}. \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso - di fallimento: l'unico errore possibile è \macro{EPERM}.} + di fallimento: l'unico errore è \errval{EPERM}.} \end{functions} Come per le precedenti le due funzioni sono identiche, per cui tratteremo solo @@ -1652,7 +1678,7 @@ corrente ai valori specificati rispettivamente da \var{rgid}, \var{egid} e \var{sgid}. \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso - di fallimento: l'unico errore possibile è \macro{EPERM}.} + di fallimento: l'unico errore è \errval{EPERM}.} \end{functions} Le due funzioni sono identiche, quanto detto per la prima riguardo gli userid @@ -1677,7 +1703,7 @@ groupid reale, il groupid effettivo e il groupid salvato del processo corrente. \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso di - fallimento: l'unico errore possibile è \macro{EFAULT} se gli indirizzi delle + fallimento: l'unico errore possibile è \errval{EFAULT} se gli indirizzi delle variabili di ritorno non sono validi.} \end{functions} @@ -1724,7 +1750,7 @@ processo corrente a \var{fsuid}. processo corrente a \var{fsgid}. \bodydesc{Le funzioni restituiscono 0 in caso di successo e -1 in caso - di fallimento: l'unico errore possibile è \macro{EPERM}.} + di fallimento: l'unico errore possibile è \errval{EPERM}.} \end{functions} \noindent queste funzioni hanno successo solo se il processo chiamante ha i privilegi di amministratore o, per gli altri utenti, se il valore specificato @@ -1736,7 +1762,7 @@ coincide con uno dei di quelli del gruppo \textit{real}, \textit{effective} o \label{sec:proc_setgroups} Le ultime funzioni che esamineremo sono quelle che permettono di operare sui -gruppi supplementari. Ogni processo può avere fino a \macro{NGROUPS\_MAX} +gruppi supplementari. Ogni processo può avere fino a \const{NGROUPS\_MAX} gruppi supplementari in aggiunta al gruppo primario, questi vengono ereditati dal processo padre e possono essere cambiati con queste funzioni. @@ -1754,8 +1780,8 @@ questa funzione successo e -1 in caso di fallimento, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\macro{EFAULT}] \param{list} non ha un indirizzo valido. - \item[\macro{EINVAL}] il valore di \param{size} è diverso da zero ma + \item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido. + \item[\errcode{EINVAL}] il valore di \param{size} è diverso da zero ma minore del numero di gruppi supplementari del processo. \end{errlist}} \end{functions} @@ -1795,10 +1821,10 @@ delle due \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di fallimento, nel qual caso \var{errno} assumerà i valori: \begin{errlist} - \item[\macro{EFAULT}] \param{list} non ha un indirizzo valido. - \item[\macro{EPERM}] il processo non ha i privilegi di amministratore. - \item[\macro{EINVAL}] il valore di \param{size} è maggiore del valore - massimo (\macro{NGROUPS}, che per Linux è 32). + \item[\errcode{EFAULT}] \param{list} non ha un indirizzo valido. + \item[\errcode{EPERM}] il processo non ha i privilegi di amministratore. + \item[\errcode{EINVAL}] il valore di \param{size} è maggiore del valore + massimo (\const{NGROUPS}, che per Linux è 32). \end{errlist}} \end{functions} @@ -1814,8 +1840,8 @@ un utente specifico, si pu \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di fallimento, nel qual caso \var{errno} assumerà gli stessi valori di - \func{setgroups} più \macro{ENOMEM} quando non c'è memoria sufficiente per - allocare lo spazio per informazioni dei gruppi.} + \func{setgroups} più \errval{ENOMEM} quando non c'è memoria sufficiente + per allocare lo spazio per informazioni dei gruppi.} \end{functions} La funzione esegue la scansione del database dei gruppi (usualmente @@ -1832,9 +1858,10 @@ quando si definisce \macro{\_POSIX\_SOURCE} o si compila con il flag \label{sec:proc_priority} In questa sezione tratteremo più approfonditamente i meccanismi con il quale -lo \textit{scheduler} assegna la CPU ai vari processi attivi. In particolare -prenderemo in esame i vari meccanismi con cui viene gestita l'assegnazione del -tempo di CPU, ed illustreremo le varie funzioni di gestione. +lo \textit{scheduler}\index{scheduler} assegna la CPU ai vari processi attivi. +In particolare prenderemo in esame i vari meccanismi con cui viene gestita +l'assegnazione del tempo di CPU, ed illustreremo le varie funzioni di +gestione. \subsection{I meccanismi di \textit{scheduling}} @@ -1852,8 +1879,8 @@ contrario di altri sistemi (che usano invece il cosiddetto \textit{cooperative multitasking}) non sono i singoli processi, ma il kernel stesso a decidere quando la CPU deve essere passata ad un altro processo. Come accennato in \secref{sec:proc_hierarchy} questa scelta viene eseguita da una sezione -apposita del kernel, lo \textit{scheduler}, il cui scopo è quello di -distribuire al meglio il tempo di CPU fra i vari processi. +apposita del kernel, lo \textit{scheduler}\index{scheduler}, il cui scopo è +quello di distribuire al meglio il tempo di CPU fra i vari processi. La cosa è resa ancora più complicata dal fatto che con le architetture multi-processore si deve anche scegliere quale sia la CPU più opportuna da @@ -1882,6 +1909,7 @@ sia la sua priorit fintanto che esso si trova in uno qualunque degli altri stati. \begin{table}[htb] + \footnotesize \centering \begin{tabular}[c]{|p{2.8cm}|c|p{10cm}|} \hline @@ -1896,7 +1924,7 @@ fintanto che esso si trova in uno qualunque degli altri stati. attesa di un risposta dal sistema (in genere per I/O), e non può essere interrotto in nessuna circostanza. \\ \textbf{Stopped} & \texttt{T} & Il processo è stato fermato con un - \macro{SIGSTOP}, o è tracciato.\\ + \const{SIGSTOP}, o è tracciato.\\ \textbf{Zombie} & \texttt{Z} & Il processo è terminato ma il suo stato di terminazione non è ancora stato letto dal padre. \\ \hline @@ -1936,8 +1964,7 @@ processi che devono essere eseguiti in un determinato momento non debbano aspettare la conclusione di altri che non hanno questa necessità. Il concetto di priorità assoluta dice che quando due processi si contendono -l'esecuzione, vince sempre quello con la priorità assoluta più alta, anche -quando l'altro è in esecuzione (grazie al \textit{prehemptive scheduling}). +l'esecuzione, vince sempre quello con la priorità assoluta più alta. Ovviamente questo avviene solo per i processi che sono pronti per essere eseguiti (cioè nello stato \textit{runnable}). La priorità assoluta viene in genere indicata con un numero intero, ed un valore più alto comporta una @@ -1980,26 +2007,27 @@ viene assegnato ad un altro campo della struttura (\var{counter}) quando il processo viene eseguito per la prima volta e diminuito progressivamente ad ogni interruzione del timer. -Quando lo scheduler viene eseguito scandisce la coda dei processi in stato -\textit{runnable} associando, sulla base del valore di \var{counter}, un peso -a ciascun processo in attesa di esecuzione,\footnote{il calcolo del peso in - realtà è un po' più complicato, ad esempio nei sistemi multiprocessore viene - favorito un processo che è eseguito sulla stessa CPU, e a parità del valore - di \var{counter} viene favorito chi ha una priorità più elevata.} chi ha il -peso più alto verrà posto in esecuzione, ed il precedente processo sarà -spostato in fondo alla coda. Dato che ad ogni interruzione del timer il -valore di \var{counter} del processo corrente viene diminuito, questo assicura -che anche i processi con priorità più bassa verranno messi in esecuzione. +Quando lo scheduler\index{scheduler} viene eseguito scandisce la coda dei +processi in stato \textit{runnable} associando, sulla base del valore di +\var{counter}, un peso a ciascun processo in attesa di esecuzione,\footnote{il + calcolo del peso in realtà è un po' più complicato, ad esempio nei sistemi + multiprocessore viene favorito un processo che è eseguito sulla stessa CPU, + e a parità del valore di \var{counter} viene favorito chi ha una priorità + più elevata.} chi ha il peso più alto verrà posto in esecuzione, ed il +precedente processo sarà spostato in fondo alla coda. Dato che ad ogni +interruzione del timer il valore di \var{counter} del processo corrente viene +diminuito, questo assicura che anche i processi con priorità più bassa +verranno messi in esecuzione. La priorità di un processo è così controllata attraverso il valore di \var{nice}, che stabilisce la durata della \textit{time-slice}; per il meccanismo appena descritto infatti un valore più lungo infatti assicura una maggiore attribuzione di CPU. L'origine del nome di questo parametro sta nel -fatto che in genere esso viene generalmente usato per diminuire la priorità di -un processo, come misura di cortesia nei confronti degli altri. -I processi infatti vengono creati dal sistema con lo stesso valore di -\var{nice} (nullo) e nessuno è privilegiato rispetto agli altri; il valore può -essere modificato solo attraverso la funzione \func{nice}, il cui prototipo è: +fatto che generalmente questo viene usato per diminuire la priorità di un +processo, come misura di cortesia nei confronti degli altri. I processi +infatti vengono creati dal sistema con lo stesso valore di \var{nice} (nullo) +e nessuno è privilegiato rispetto agli altri; il valore può essere modificato +solo attraverso la funzione \func{nice}, il cui prototipo è: \begin{prototype}{unistd.h} {int nice(int inc)} Aumenta il valore di \var{nice} per il processo corrente. @@ -2007,14 +2035,14 @@ essere modificato solo attraverso la funzione \func{nice}, il cui prototipo \bodydesc{La funzione ritorna zero in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{EPERM}] un processo senza i privilegi di amministratore ha + \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha specificato un valore di \param{inc} negativo. \end{errlist}} \end{prototype} L'argomento \param{inc} indica l'incremento del valore di \var{nice}: -quest'ultimo può assumere valori compresi fra \macro{PRIO\_MIN} e -\macro{PRIO\_MAX} (che nel caso di Linux sono $-19$ e $20$), ma per +quest'ultimo può assumere valori compresi fra \const{PRIO\_MIN} e +\const{PRIO\_MAX} (che nel caso di Linux sono $-19$ e $20$), ma per \param{inc} si può specificare un valore qualunque, positivo o negativo, ed il sistema provvederà a troncare il risultato nell'intervallo consentito. Valori positivi comportano maggiore \textit{cortesia} e cioè una diminuzione della @@ -2033,9 +2061,9 @@ Restituisce il valore di \var{nice} per l'insieme dei processi specificati. \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{ESRCH}] non c'è nessun processo che corrisponda ai valori di + \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di \param{which} e \param{who}. - \item[\macro{EINVAL}] il valore di \param{which} non è valido. + \item[\errcode{EINVAL}] il valore di \param{which} non è valido. \end{errlist}} \end{prototype} \noindent (in vecchie versioni può essere necessario includere anche @@ -2056,9 +2084,9 @@ quest'ultimo indica il processo, il gruppo di processi o l'utente correnti. \param{which} & \param{who} & \textbf{Significato} \\ \hline \hline - \macro{PRIO\_PROCESS} & \type{pid\_t} & processo \\ - \macro{PRIO\_PRGR} & \type{pid\_t} & process group \\ - \macro{PRIO\_USER} & \type{uid\_t} & utente \\ + \const{PRIO\_PROCESS} & \type{pid\_t} & processo \\ + \const{PRIO\_PRGR} & \type{pid\_t} & process group \\ + \const{PRIO\_USER} & \type{uid\_t} & utente \\ \hline \end{tabular} \caption{Legenda del valore dell'argomento \param{which} e del tipo @@ -2082,12 +2110,12 @@ impostare la priorit \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{ESRCH}] non c'è nessun processo che corrisponda ai valori di + \item[\errcode{ESRCH}] non c'è nessun processo che corrisponda ai valori di \param{which} e \param{who}. - \item[\macro{EINVAL}] il valore di \param{which} non è valido. - \item[\macro{EPERM}] un processo senza i privilegi di amministratore ha + \item[\errcode{EINVAL}] il valore di \param{which} non è valido. + \item[\errcode{EPERM}] un processo senza i privilegi di amministratore ha specificato un valore di \param{inc} negativo. - \item[\macro{EACCESS}] un processo senza i privilegi di amministratore ha + \item[\errcode{EACCES}] un processo senza i privilegi di amministratore ha cercato di modificare la priorità di un processo di un altro utente. \end{errlist}} \end{prototype} @@ -2111,9 +2139,10 @@ priorit nel caso di Linux non si tratta di un vero hard real-time, in quanto in presenza di eventuali interrupt il kernel interrompe l'esecuzione di un processo qualsiasi sia la sua priorità,\footnote{questo a meno che non si - siano installate le patch di RTLinux o RTAI, con i quali è possibile + siano installate le patch di RTLinux, RTAI o Adeos, con i quali è possibile ottenere un sistema effettivamente hard real-time. In tal caso infatti gli - interrupt vengono intercettati dall'interfaccia real-time, e gestiti + interrupt vengono intercettati dall'interfaccia real-time (o nel caso di + Adeos gestiti dalle code del nano-kernel), in modo da poterlo controllare 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\index{page fault} si possono avere ritardi non previsti. Se @@ -2131,23 +2160,22 @@ quando si lavora con processi che usano priorit shell cui si sia assegnata la massima priorità assoluta, in modo da poter essere comunque in grado di rientrare nel sistema. -Quando c'è un processo con priorità assoluta lo scheduler lo metterà in -esecuzione prima di ogni processo normale. In caso di più processi sarà -eseguito per primo quello con priorità assoluta più alta. Quando ci sono più -processi con la stessa priorità assoluta questi vengono tenuti in una coda -tocca al kernel decidere quale deve essere eseguito. - - +Quando c'è un processo con priorità assoluta lo scheduler\index{scheduler} lo +metterà in esecuzione prima di ogni processo normale. In caso di più processi +sarà eseguito per primo quello con priorità assoluta più alta. Quando ci sono +più processi con la stessa priorità assoluta questi vengono tenuti in una coda +tocca al kernel decidere quale deve essere eseguito. Il meccanismo con cui vengono gestiti questi processi dipende dalla politica di scheduling che si è scelto; lo standard ne prevede due: -\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}} -\item[\textit{FIFO}] il processo viene eseguito fintanto che non cede - volontariamente la CPU, si blocca, finisce o viene interrotto da un processo - a priorità più alta. -\item[\textit{Round Robin}] ciascun processo viene eseguito a turno per un - certo periodo di tempo (una \textit{time slice}). Solo i processi con la - stessa priorità ed in stato \textit{runnable} entrano nel circolo. +\begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}} +\item[\textit{FIFO}] \textit{First In First Out}. Il processo viene eseguito + fintanto che non cede volontariamente la CPU, si blocca, finisce o viene + interrotto da un processo a priorità più alta. +\item[\textit{RR}] \textit{Round Robin}. Ciascun processo viene eseguito a + turno per un certo periodo di tempo (una \textit{time slice}). Solo i + processi con la stessa priorità ed in stato \textit{runnable} entrano nel + circolo. \end{basedescript} La funzione per impostare le politiche di scheduling (sia real-time che @@ -2160,12 +2188,12 @@ prototipo \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{ESRCH}] il processo \param{pid} non esiste. - \item[\macro{EINVAL}] il valore di \param{policy} non esiste o il relativo + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il relativo valore di \param{p} non è valido. - \item[\macro{EPERM}] il processo non ha i privilegi per attivare la - politica richiesta (vale solo per \macro{SCHED\_FIFO} e - \macro{SCHED\_RR}). + \item[\errcode{EPERM}] il processo non ha i privilegi per attivare la + politica richiesta (vale solo per \const{SCHED\_FIFO} e + \const{SCHED\_RR}). \end{errlist}} \end{prototype} @@ -2185,10 +2213,10 @@ la politica di scheduling corrente. \textbf{Policy} & \textbf{Significato} \\ \hline \hline - \macro{SCHED\_FIFO} & Scheduling real-time con politica \textit{FIFO} \\ - \macro{SCHED\_RR} & Scheduling real-time con politica \textit{Round + \const{SCHED\_FIFO} & Scheduling real-time con politica \textit{FIFO} \\ + \const{SCHED\_RR} & Scheduling real-time con politica \textit{Round Robin} \\ - \macro{SCHED\_OTHER}& Scheduling ordinario\\ + \const{SCHED\_OTHER}& Scheduling ordinario\\ \hline \end{tabular} \caption{Valori dell'argomento \param{policy} per la funzione @@ -2236,13 +2264,13 @@ e \func{sched\_get\_priority\_min}, i cui prototipi sono: \bodydesc{La funzioni ritornano il valore della priorità in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{EINVAL}] il valore di \param{policy} è invalido. + \item[\errcode{EINVAL}] il valore di \param{policy} è invalido. \end{errlist}} \end{functions} -I processi con politica di scheduling \macro{SCHED\_OTHER} devono specificare -un valore nullo (altrimenti si avrà un errore \macro{EINVAL}), questo valore +I processi con politica di scheduling \const{SCHED\_OTHER} devono specificare +un valore nullo (altrimenti si avrà un errore \errcode{EINVAL}), questo valore infatti non ha niente a che vedere con la priorità dinamica determinata dal valore di \var{nice}, che deve essere impostato con le funzioni viste in precedenza. @@ -2250,7 +2278,7 @@ precedenza. Il kernel mantiene i processi con la stessa priorità assoluta in una lista, ed esegue sempre il primo della lista, mentre un nuovo processo che torna in stato \textit{runnable} viene sempre inserito in coda alla lista. Se la -politica scelta è \macro{SCHED\_FIFO} quando il processo viene eseguito viene +politica scelta è \const{SCHED\_FIFO} quando il processo viene eseguito viene automaticamente rimesso in coda alla lista, e la sua esecuzione continua fintanto che non viene bloccato da una richiesta di I/O, o non rilascia volontariamente la CPU (in tal caso, tornando nello stato \textit{runnable} @@ -2266,8 +2294,8 @@ La priorit \bodydesc{La funzione ritorna la politica di scheduling in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{ESRCH}] il processo \param{pid} non esiste. - \item[\macro{EINVAL}] il valore di \param{pid} è negativo. + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EINVAL}] il valore di \param{pid} è negativo. \end{errlist}} \end{prototype} @@ -2293,8 +2321,8 @@ prototipi sono: \bodydesc{La funzione ritorna la priorità in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{ESRCH}] il processo \param{pid} non esiste. - \item[\macro{EINVAL}] il valore di \param{pid} è negativo. + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{EINVAL}] il valore di \param{pid} è negativo. \end{errlist}} \end{functions} @@ -2303,7 +2331,7 @@ L'uso di \func{sched\_setparam} che \func{sched\_setscheduler} specificando 0 come valore di \param{pid} si opera sul processo corrente. La disponibilità di entrambe le funzioni può essere verificata controllando la macro \macro{\_POSIX\_PRIORITY\_SCHEDULING} che è -definita nell'header \macro{sched.h}. +definita nell'header \file{sched.h}. L'ultima funzione che permette di leggere le informazioni relative ai processi real-time è \func{sched\_rr\_get\_interval}, che permette di ottenere la @@ -2316,8 +2344,8 @@ il suo prototipo \bodydesc{La funzione ritorna 0in caso di successo e -1 in caso di errore, nel qual caso \var{errno} può assumere i valori: \begin{errlist} - \item[\macro{ESRCH}] il processo \param{pid} non esiste. - \item[\macro{ENOSYS}] la system call non è stata implementata. + \item[\errcode{ESRCH}] il processo \param{pid} non esiste. + \item[\errcode{ENOSYS}] la system call non è stata implementata. \end{errlist}} \end{prototype} @@ -2338,7 +2366,7 @@ volontariamente la CPU; questo viene fatto attraverso la funzione nel qual caso \var{errno} viene impostata opportunamente.} \end{prototype} -La funzione fa si che il processo rilasci la CPU, in modo da essere rimesso in +La funzione fa sì che il processo rilasci la CPU, in modo da essere rimesso in coda alla lista dei processi da eseguire, e permettere l'esecuzione di un altro processo; se però il processo è l'unico ad essere presente sulla coda l'esecuzione non sarà interrotta. In genere usano questa funzione i processi @@ -2442,7 +2470,12 @@ problematiche di questo tipo in \capref{cha:IPC}). Un caso particolare di \textit{race condition} sono poi i cosiddetti \textit{deadlock}, particolarmente gravi in quanto comportano spesso il blocco -completo di un servizio, e non il fallimento di una singola operazione. +completo di un servizio, e non il fallimento di una singola operazione. Per +definizione un \textit{deadlock} è una situazione in cui due o più processi +non sono più in grado di proseguire perché ciascuno aspetta il risultato di +una operazione che dovrebbe essere eseguita dall'altro. + + L'esempio tipico di una situazione che può condurre ad un \textit{deadlock} è quello in cui un flag di ``occupazione'' viene rilasciato da un evento asincrono (come un segnale o un altro processo) fra il momento in cui lo si è