X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=prochand.tex;h=5d62dabc2d41e60b81908522bec154b82d2897e1;hp=93212327f92db341aa265bbc8164802ecd006a5c;hb=d25090faca15102552d77c38161a8a34b0bac41e;hpb=da0899b61653d07d75c8df134906261f1afd2485 diff --git a/prochand.tex b/prochand.tex index 9321232..5d62dab 100644 --- a/prochand.tex +++ b/prochand.tex @@ -1,6 +1,6 @@ %% prochand.tex %% -%% Copyright (C) 2000-2002 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2003 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", @@ -43,7 +43,7 @@ caratteristiche di Unix (che esamineremo in dettaglio pi qualunque processo può a sua volta generarne altri, detti processi figli (\textit{child process}). Ogni processo è identificato presso il sistema da un numero univoco, il cosiddetto \textit{process identifier} o, più brevemente, -\acr{pid}, assengnato in forma progressiva (vedi \secref{sec:proc_pid}) quando +\acr{pid}, assegnato 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 @@ -126,13 +126,14 @@ struttura, alla cui base c' processi. Il kernel mantiene una tabella dei processi attivi, la cosiddetta -\textit{process table}; per ciascun processo viene mantenuta una voce nella -tabella dei processi costituita da una struttura \struct{task\_struct}, che -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 \struct{task\_struct} (che in seguito incontreremo a più -riprese), è mostrato in \figref{fig:proc_task_struct}. +\textit{process table}; per ciascun processo viene mantenuta una voce, +costituita da una struttura \struct{task\_struct}, nella tabella dei processi +che 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 \struct{task\_struct} (che in +seguito incontreremo a più riprese), è mostrato in +\figref{fig:proc_task_struct}. \begin{figure}[htb] \centering @@ -196,7 +197,7 @@ coi processi che 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 +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 termina, anche il processo termina, e non si può tornare alla precedente immagine. @@ -223,7 +224,7 @@ programmi. \label{sec:proc_pid} Come accennato nell'introduzione, ogni processo viene identificato dal sistema -da un numero identificativo univoco, 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}). @@ -240,13 +241,13 @@ disponibile a partire da un minimo di 300,\footnote{questi valori, fino al 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. +ai processi eseguiti 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 -\textit{parent process id}). Questi due identificativi possono essere +\textit{parent process ID}). Questi due identificativi possono essere ottenuti usando le due funzioni \funcd{getpid} e \funcd{getppid}, i cui prototipi sono: \begin{functions} @@ -263,13 +264,13 @@ prototipi sono: \bodydesc{Entrambe le funzioni non riportano condizioni di errore.} \end{functions} \noindent esempi dell'uso di queste funzioni sono riportati in -\figref{fig:proc_fork_code}, nel programma di esempio \file{ForkTest.c}. +\figref{fig:proc_fork_code}, nel programma \file{ForkTest.c}. Il fatto che il \acr{pid} sia un numero univoco per il sistema lo rende un candidato per generare ulteriori indicatori associati al processo di cui diventa possibile garantire l'unicità: ad esempio in alcune implementazioni la funzione \func{tmpname} (si veda \secref{sec:file_temp_file}) usa il \acr{pid} -per generare un pathname univoco, che non potrà essere replicato da un'altro +per generare un pathname univoco, che non potrà essere replicato da un altro processo che usi la stessa funzione. Tutti i processi figli dello stesso processo padre sono detti @@ -307,7 +308,7 @@ 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[\errcode{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[\errcode{ENOMEM}] non è stato possibile allocare la memoria per le @@ -317,7 +318,7 @@ prototipo della funzione 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 seccessiva alla \func{fork}; il processo figlio è però una +dall'istruzione successiva 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, @@ -349,53 +350,11 @@ sempre un solo padre (il cui \acr{pid} pu che non è il \acr{pid} di nessun processo. \begin{figure}[!htb] - \footnotesize - \begin{lstlisting}{} -#include /* error definitions and routines */ -#include /* C standard library */ -#include /* unix standard library */ -#include /* standard I/O library */ -#include /* string functions */ - -/* Help printing routine */ -void usage(void); - -int main(int argc, char *argv[]) -{ -/* - * Variables definition - */ - int nchild, i; - pid_t pid; - int wait_child = 0; - int wait_parent = 0; - int wait_end = 0; - ... /* handling options */ - nchild = atoi(argv[optind]); - printf("Test for forking %d child\n", nchild); - /* loop to fork children */ - for (i=0; i}, questo non è più necessario con versioni recenti delle -librerie, ma è comunque utile per portabilità). +librerie, ma è comunque utile per portabilità. -La funzione permette di leggere la priorità di un processo, di un gruppo di -processi (vedi \secref{sec:sess_proc_group}) o di un utente, a seconda del -valore di \param{which}, secondo la legenda di \tabref{tab:proc_getpriority}, -specificando un corrispondente valore per \param{who}; un valore nullo di -quest'ultimo indica il processo, il gruppo di processi o l'utente correnti. +La funzione permette, a seconda del valore di \param{which}, di leggere la +priorità di un processo, di un gruppo di processi (vedi +\secref{sec:sess_proc_group}) o di un utente, specificando un corrispondente +valore per \param{who} secondo la legenda di \tabref{tab:proc_getpriority}; un +valore nullo di quest'ultimo indica il processo, il gruppo di processi o +l'utente correnti. \begin{table}[htb] \centering @@ -2151,10 +2120,10 @@ La funzione imposta la priorit tutti i processi indicati dagli argomenti \param{which} e \param{who}. La gestione dei permessi dipende dalle varie implementazioni; in Linux, secondo le specifiche dello standard SUSv3, e come avviene per tutti i sistemi che -derivano da SYSV, è richiesto che l'userid reale o effettivo del processo -chiamante corrispondano al real user id (e solo quello) del processo di cui si +derivano da SysV, è richiesto che l'user-ID reale o effettivo del processo +chiamante corrispondano al real user-ID (e solo quello) del processo di cui si vuole cambiare la priorità; per i sistemi derivati da BSD invece (SunOS, -Ultrix, *BSD) la corrispondenza può essere anche con l'userid effettivo. +Ultrix, *BSD) la corrispondenza può essere anche con l'user-ID effettivo. @@ -2169,7 +2138,7 @@ processo qualsiasi sia la sua priorit 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 (o nel caso di - Adeos gestiti dalle code del nano-kernel), in modo da poterlo controllare + Adeos gestiti dalle code del nano-kernel), in modo da poterli 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 @@ -2178,24 +2147,23 @@ controllo della memoria virtuale (vedi \secref{sec:proc_mem_lock}), il primo non è superabile e può comportare ritardi non prevedibili riguardo ai tempi di esecuzione di qualunque processo. -In ogni caso occorre usare le priorità assolute con molta attenzione: se si dà -ad un processo una priorità assoluta e questo finisce in un loop infinito, -nessun altro processo potrà essere eseguito, ed esso sarà mantenuto in -esecuzione permanentemente assorbendo tutta la CPU e senza nessuna possibilità -di riottenere l'accesso al sistema. Per questo motivo è sempre opportuno, -quando si lavora con processi che usano priorità assolute, tenere attiva una -shell cui si sia assegnata la massima priorità assoluta, in modo da poter -essere comunque in grado di rientrare nel sistema. +Occorre usare le priorità assolute con molta attenzione: se si dà ad un +processo una priorità assoluta e questo finisce in un loop infinito, nessun +altro processo potrà essere eseguito, ed esso sarà mantenuto in esecuzione +permanentemente assorbendo tutta la CPU e senza nessuna possibilità di +riottenere l'accesso al sistema. Per questo motivo è sempre opportuno, quando +si lavora con processi che usano priorità assolute, tenere attiva una 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\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. - +e 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{2cm}\desclabelstyle{\nextlinelabel}} +\begin{basedescript}{\desclabelwidth{1.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. @@ -2219,18 +2187,17 @@ prototipo \item[\errcode{EINVAL}] il valore di \param{policy} non esiste o il relativo valore di \param{p} non è valido. \item[\errcode{EPERM}] il processo non ha i privilegi per attivare la - politica richiesta (vale solo per \const{SCHED\_FIFO} e - \const{SCHED\_RR}). + politica richiesta. \end{errlist}} \end{prototype} La funzione esegue l'impostazione per il processo specificato dall'argomento \param{pid}; un valore nullo esegue l'impostazione per il processo corrente. -Solo un processo con i privilegi di amministratore può impostare delle -priorità assolute diverse da zero. La politica di scheduling è specificata -dall'argomento \param{policy} i cui possibili valori sono riportati in -\tabref{tab:proc_sched_policy}; un valore negativo per \param{policy} mantiene -la politica di scheduling corrente. +La politica di scheduling è specificata dall'argomento \param{policy} i cui +possibili valori sono riportati in \tabref{tab:proc_sched_policy}; un valore +negativo per \param{policy} mantiene la politica di scheduling corrente. +Solo un processo con i privilegi di amministratore può impostare priorità +assolute diverse da zero o politiche \const{SCHED\_FIFO} e \const{SCHED\_RR}. \begin{table}[htb] \centering @@ -2258,14 +2225,10 @@ priorit massimo ed uno minimo, che nel caso sono rispettivamente 1 e 99 (il valore zero è legale, ma indica i processi normali). -\begin{figure}[!htb] +\begin{figure}[!bht] \footnotesize \centering \begin{minipage}[c]{15cm} - \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{} -struct sched_param { - int sched_priority; -}; - \end{lstlisting} + \includestruct{listati/sched_param.c} \end{minipage} \normalsize \caption{La struttura \structd{sched\_param}.} @@ -2289,7 +2252,7 @@ e \funcd{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[\errcode{EINVAL}] il valore di \param{policy} è invalido. + \item[\errcode{EINVAL}] il valore di \param{policy} non è valido. \end{errlist}} \end{functions} @@ -2324,7 +2287,7 @@ La priorit \end{errlist}} \end{prototype} -La funzione restituisce il valore (secondo la quanto elencato in +La funzione restituisce il valore (secondo quanto elencato in \tabref{tab:proc_sched_policy}) della politica di scheduling per il processo specificato; se \param{pid} è nullo viene restituito quello del processo chiamante. @@ -2338,7 +2301,6 @@ prototipi sono: \funcdecl{int sched\_setparam(pid\_t pid, const struct sched\_param *p)} Imposta la priorità assoluta del processo \param{pid}. - \funcdecl{int sched\_getparam(pid\_t pid, struct sched\_param *p)} Legge la priorità assoluta del processo \param{pid}. @@ -2464,16 +2426,16 @@ condiviso, onde evitare problemi con le ottimizzazioni del codice. \textit{deadlock}\index{deadlock}} \label{sec:proc_race_cond} -Si definiscono \textit{race condition} tutte quelle situazioni in cui processi -diversi operano su una risorsa comune, ed in cui il risultato viene a -dipendere dall'ordine in cui essi effettuano le loro operazioni. Il caso -tipico è quello di un'operazione che viene eseguita da un processo in più -passi, e può essere compromessa dall'intervento di un altro processo che -accede alla stessa risorsa quando ancora non tutti i passi sono stati -completati. +Si definiscono \textit{race condition}\index{race condition} tutte quelle +situazioni in cui processi diversi operano su una risorsa comune, ed in cui il +risultato viene a dipendere dall'ordine in cui essi effettuano le loro +operazioni. Il caso tipico è quello di un'operazione che viene eseguita da un +processo in più passi, e può essere compromessa dall'intervento di un altro +processo che accede alla stessa risorsa quando ancora non tutti i passi sono +stati completati. Dato che in un sistema multitasking ogni processo può essere interrotto in -qualunque momento per farne subentrare un'altro in esecuzione, niente può +qualunque momento per farne subentrare un altro in esecuzione, niente può assicurare un preciso ordine di esecuzione fra processi diversi o che una sezione di un programma possa essere eseguita senza interruzioni da parte di altri. Queste situazioni comportano pertanto errori estremamente subdoli e @@ -2483,32 +2445,32 @@ funzioner Per questo occorre essere ben consapevoli di queste problematiche, e del fatto che l'unico modo per evitarle è quello di riconoscerle come tali e prendere gli adeguati provvedimenti per far sì che non si verifichino. Casi tipici di -\textit{race condition} si hanno quando diversi processi accedono allo stesso -file, o nell'accesso a meccanismi di intercomunicazione come la memoria -condivisa. In questi casi, se non si dispone della possibilità di eseguire -atomicamente le operazioni necessarie, occorre che quelle parti di codice in -cui si compiono le operazioni sulle risorse condivise (le cosiddette +\textit{race condition}\index{race condition} si hanno quando diversi processi +accedono allo stesso file, o nell'accesso a meccanismi di intercomunicazione +come la memoria condivisa. In questi casi, se non si dispone della possibilità +di eseguire atomicamente le operazioni necessarie, occorre che quelle parti di +codice in cui si compiono le operazioni sulle risorse condivise (le cosiddette \textsl{sezioni critiche}\index{sezioni critiche}) del programma, siano opportunamente protette da meccanismi di sincronizzazione (torneremo su queste problematiche di questo tipo in \capref{cha:IPC}). -Un caso particolare di \textit{race condition} sono poi i cosiddetti -\textit{deadlock}\index{deadlock}, particolarmente gravi in quanto comportano -spesso il blocco completo di un servizio, e non il fallimento di una singola -operazione. Per definizione un \textit{deadlock}\index{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. +Un caso particolare di \textit{race condition}\index{race condition} sono poi +i cosiddetti \textit{deadlock}\index{deadlock}, particolarmente gravi in +quanto comportano spesso il blocco completo di un servizio, e non il +fallimento di una singola operazione. Per definizione un +\textit{deadlock}\index{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}\index{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 è controllato (trovandolo occupato) e la -successiva operazione di attesa per lo sblocco. In questo caso, dato che -l'evento di sblocco del flag è avvenuto senza che ce ne accorgessimo proprio -fra il controllo e la messa in attesa, quest'ultima diventerà perpetua (da cui -il nome di \textit{deadlock}\index{deadlock}). +\textit{deadlock}\index{deadlock} è quello in cui un flag di +``\textsl{occupazione}'' viene rilasciato da un evento asincrono (come un +segnale o un altro processo) fra il momento in cui lo si è controllato +(trovandolo occupato) e la successiva operazione di attesa per lo sblocco. In +questo caso, dato che l'evento di sblocco del flag è avvenuto senza che ce ne +accorgessimo proprio fra il controllo e la messa in attesa, quest'ultima +diventerà perpetua (da cui il nome di \textit{deadlock}\index{deadlock}). In tutti questi casi è di fondamentale importanza il concetto di atomicità visto in \secref{sec:proc_atom_oper}; questi problemi infatti possono essere