X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=0c94547a6e9c0aa82692d910d16b1785aaccc17c;hp=c1fc583863e811e50ac271fe79d68ce145338e32;hb=a93a0bac951085a52c4b33c42a607fc81f6868d2;hpb=45c2192b1414b9dfc1cef8bb718761c495ca33f4;ds=sidebyside diff --git a/process.tex b/process.tex index c1fc583..0c94547 100644 --- a/process.tex +++ b/process.tex @@ -1,150 +1,163 @@ -\chapter{I processi} +\chapter{Il funzionamento di un processo unix} \label{cha:process} -Come accennato nell'introduzione in un sistema unix ogni attività del sistema -viene svolta tramite i processi. Questo significa che quando un programma -viene posto in esecuzione, viene fatto partire un processo che si incarica di -eseguirne il codice. In sostanza i processi costituiscono l'unità base per -l'allocazione e l'uso delle risorse del sistema. - -In questo capitolo affronteremo i dettagli della creazione e della distruzione -dei processi, della gestione dei loro attributi e privilegi, e di tutte le -funzioni a questo connesse. - - -\section{Una panoramica sui concetti base} -\label{sec:proc_gen} - -Una delle caratteristiche essenziali di unix (che esamineremo in dettaglio più -avanti) è che ogni processo può a sua volta generare altri processi figli -(\textit{child}): questo è ad esempio quello che fa la shell quando mette in -esecuzione il programma che gli indichiamo nella linea di comando. - -Una seconda caratteristica è che ogni processo viene sempre generato in tale -modo da un processo genitore (\textit{parent}) attraverso una apposita system -call. Questo vale per tutti i processi, tranne per un processo speciale, che -normalmente è \texttt{/sbin/init}, che invece viene lanciato dal kernel finita -la fase di avvio e che quindi non è figlio di nessuno. - -Tutto ciò significa che, come per i file su disco, i processi sono organizzati -gerarchicamente dalla relazione fra genitori e figli; alla base dell'albero in -questo caso c'è init che è progenitore di ogni altro processo. - - -\section{Gli identificatori dei processi} -\label{sec:proc_id} - -Ogni processo viene identificato dal sistema da un numero identificativo -unico, il \textit{process id} o \textit{pid}. Questo viene assegnato in forma -progressiva ogni volta che un nuovo processo viene creato, fino ad un limite -massimo (in genere essendo detto numero memorizzato in un intero a 16 bit si -arriva a 32767) oltre il quale si riparte dal numero più basso disponibile -(FIXME: verificare, non sono sicuro). Per questo motivo processo il processo -di avvio (init) ha sempre il pid uguale a uno. - -Ogni processo è identificato univocamente dal sistema per il suo -pid; quest'ultimo è un apposito tipo di dato, il \texttt{pid\_t} che in -genere è un intero con segno (nel caso di Linux e delle glibc il tipo usato è -\texttt{int}. - -Tutti i processi inoltre portano traccia del pid del genitore, chiamato in -genere \textit{ppid} (da \textit{Parente Process Id}). Questi identificativi -possono essere ottenuti da un programma usando le funzioni: -\begin{itemize} - \item \texttt{pid\_t getpid(void)} restituisce il pid del processo corrente. - - \item \texttt{pid\_t getppid(void)} restituisce il pid del padre del processo - corrente. +Prima di entrare nei dettagli di come un sistema unix gestisce la presenza di +molti processi concorrenti daremo una descrizione del funzionamento di un +singolo processo, come viene posto in esecuzione, come viene terminato, come +vede la memoria e la può gestire, come può ricevere e gestire i parametri. -\end{itemize} -(per l'accesso a queste funzioni e ai relativi tipi di dati occorre includere -gli header files \texttt{unistd.h} e \texttt{sys/types.h}). - - - -\section{Il controllo dei processi} -\label{sec:proc_control} - -Esamineremo in questa sezione le varie funzioni per il controllo dei processi: -la lore creazione, la terminazione, l'esecuzione di altri programmi. Prima di -trattare in dettaglio le singole funzioni, faremo un'introduzione generale ai -contetti che stanno alla base della gestione dei processi in unix. - -\subsection{Una panoramica} -\label{sec:proc_control_overview} - -I processi vengono creati dalla funzione \texttt{fork}; in genere questa è una -system call, ma linux però usa un'altra nomenclatura, e la funzione fork è -basata a sua volta sulla system call \texttt{clone}, che viene usata anche per -generare i \textit{thread}. Il processo figlio creato dalla \textit{fork} è -una copia identica del processo processo padre, solo che ha un suo pid -proprio. - -Se si vuole che il processo padre si fermi fino alla conclusione del processo -figlio questo deve essere specificato subito dopo la fork chiamando la -funzione \texttt{wait} o la funzione \texttt{waitpid}, che restituiscono anche -una informazione abbastanza limitata (il codice di uscita) sulle cause della -terminazione del processo. - -Quando un processo ha concluso il suo compito o ha incontrato un errore non -risolvibile esso può essere terminato con la funzione \texttt{exit} (la -questione è più complessa ma ci torneremo più avanti). La vita del processo -però termina solo quando viene chiamata la quando la sua conclusione viene -ricevuta dal processo padre, a quel punto tutte le risorse allocate nel -sistema ad esso associate vengono rilasciate. - -Avere due processi che eseguono esattamente lo stesso codice non è molto -utile, mormalmente si genera un secondo processo per affidagli l'esecuzione di -un compito specifico (ad esempio gestire una connessione dopo che questa è -stata stabilita), o fargli eseguire (come fa la shell) un altro programma. Per -questo si usa la seconda funzione fondamentale per programmazione coi processi -che è la \texttt{exec}. -Il programma che un processo sta eseguendo si chiama immagine del processo -(\textit{process image}), le funzioni della famiglia \textit{exec} permettono -di caricare un'altro programma da disco sostituendo quest'ultimo alla process -image corrente, questo fa si che la precedente immagine venga completamente -cancellata e quando il nuovo programma esce anche il processo termina, senza -ritornare alla precedente immagine. +\section{La funzione \texttt{main}} +\label{sec:proc_} -Per questo motivo la \texttt{fork} e la \texttt{exec} sono funzioni molto -particolari con caratteristiche uniche rispetto a tutte le altre, infatti la -prima ritorna due volte (nel processo padre e nel figlio) mentre la seconda -non ritorna mai (in quanto con essa viene eseguito un altro programma). -\subsection{La funzione \texttt{fork}} -\label{sec:proc_fork} - - -Dopo l'esecuzione di una fork sia il processo padre che il processo figlio -continuano ad essere eseguiti normalmente, ed il processo figlio esegue -esattamente lo stesso codice del padre. La sola differenza è che nel processo -padre il valore di ritorno della funzione fork è il pid del processo figlio, -mentre nel figlio è zero; in questo modo il programma può identificare se -viene eseguito dal padre o dal figlio. - +\subsection{} +\label{sec:proc_} \subsection{La funzione \texttt{exit}} \label{sec:proc_exit} -\subsection{Le funzioni \texttt{wait} e \texttt{waitpid}} -\label{sec:proc_wait} - -\subsection{Le funzioni \texttt{exec}} -\label{sec:proc_exec} - +\section{Gestione di parametri e opzioni} +\label{sec:parameter_options} + +Il passaggio dei parametri e delle variabili di ambiente dalla riga di comando +al singolo programma quando viene lanciato è effettuato attraverso le +variabili \texttt{argc}, \texttt{argv} che vengono passate al programma +come argomenti della funzione principale: + +\subsection{Il formato dei parametri} +\label{sec:proc_par_format} +In genere passaggio dei parametri al programma viene effettuato dalla shell, +che si incarica di leggere la linea di comando e di effettuarne la scansione +(il cosiddetto \textit{parsing}) per individuare le parole che la compongono, +ciascuna delle quali viene considerata un parametro; di default per +individuare le parole viene usato come separatore lo spazio (comportamento +modificabile attraverso il settaggio della variabile di ambiente IFS). + +Nella scansione viene costruito l'array di puntatori \texttt{argv} inserendo +in successione il puntatore alla stringa costituente l'$n$-simo parametro; la +variabile \texttt{argc} viene inizializzata al numero di parametri trovati, in +questo modo il primo parametro è sempre il nome del programma (vedi \nfig). + +\subsection{La gestione delle opzioni} +\label{sec:proc_opt_handling} + +In generale un programma unix riceve da linea di comando sia i parametri che +le opzioni, queste ultime sono standardizzate per essere riconosciute come +tali: un elemento di \texttt{argv} che inizia con \texttt{-} e che non sia un +singolo \texttt{-} o \texttt{--} viene considerato un'opzione. In in genere +le opzioni sono costituite da un lettera preceduta dal meno e possono avere o +no un parametro associato; un comando tipico può essere cioè qualcosa del +tipo: +\begin{verbatim} +touch -r riferimento.txt -m questofile.txt +\end{verbatim} +ed in questo caso le opzioni sono \texttt{m} ed \texttt{r}. + +Per gestire le opzioni all'interno dei parametri passati in \texttt{argv} le +librerie standard del C forniscono la funzione \texttt{getopt} (accessibile +includendo \texttt{unistd.h}), che ha il prototipo: +\begin{verbatim} +int getopt(int argc, char * const argv[], const char * optstring); +\end{verbatim} + +Questa funzione prende come argomenti le due variabili \texttt{argc} e +\texttt{argv} ed una stringa che indica quali sono le opzioni valide; la +funzione effettua la scansione della lista dei parametri ricercando ogni +stringa che comincia con \texttt{-} e ritorna ogni volta che trova una opzione +valida. + +La stringa \texttt{optstring} indica quali sono le opzioni riconosciute ed è +costituita da tutti i caratteri usati per identificare le singole opzioni, se +l'opzione ha un parametro al carattere deve essere fatto seguire un segno di +due punti \texttt{:} nel caso appena accennato ad esempio la stringa di +opzioni sarebbe \texttt{"r:m"}. + +La modalità di uso è pertanto quella di chiamare più volte la funzione +all'interno di un ciclo di while fintanto che essa non ritorna il valore +\texttt{-1} che indica che non ci sono più opzioni. Nel caso si incontri +un'opzione non dichiarata in \texttt{optstring} viene ritornato un \texttt{?} +mentre se l'opzione non è seguita da un parametro viene ritornato un +\texttt{:} infine se viene incontrato il valore \texttt{--} la scansione viene +considerata conclusa. + +Quando la funzione trova un'opzione essa ritorna il valore numerico del +carattere, in questo modo si possono prendere le azioni relative usando un +case; la funzione inizializza inoltre alcune varibili globali: +\begin{itemize} +\item \texttt{char * optarg} contiene il puntatore alla stringa argomento + dell'opzione. +\item \texttt{int optind} alla fine della scansione restituisce l'indice del + primo argomento che non è un'opzione. +\item \texttt{int opterr} previene, se posto a zero, la stampa di un messaggio + di errore in caso di riconoscimento di opzioni non definite. +\item \texttt{int optopt} contiene il carattere dell'opzione non riconosciuta. +\end{itemize} - - - -\section{Il controllo di accesso} -\label{sec:process_perms} - -Va messo qui tutta la storia su effective, real, saved uid, e pure le cose di -linux come il filesystem uid. +In \nfig è mostrato un programma di esempio, + + +\begin{figure}[htbp] + \footnotesize + \begin{lstlisting}{} + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "o:a:i:hve")) != -1) { + switch (i) { + case 'i': /* input file */ + in_file=open(optarg,O_RDONLY); + if (in_file<0) { + perror("Cannot open input file"); + exit(1); + } + break; + case 'o': /* output file (overwrite) */ + out_file=open(optarg,O_WRONLY|O_CREAT); + if (out_file<0) { + perror("Cannot open output file"); + exit(1); + } + break; + break; + case 'a': /* output file (append) */ + out_file=open(optarg,O_WRONLY|O_CREAT|O_APPEND); + break; + case 'h': /* print help usage */ + usage(); + break; + case 'v': /* set verbose mode */ + debug("Option -v active\n"); + verbose=1; + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + default: /* should not reached */ + debug("default option\n"); + usage(); + } + } + debug("Optind %d, argc %d\n",optind,argc); + \end{lstlisting} + \caption{Esempio di codice per la gestione delle opzioni.} + \label{fig:proc_options_code} +\end{figure} + +\subsection{Opzioni in formato esteso} +\label{sec:proc_opt_extended} + +Un'estensione di questo schema è costituito dalle cosiddette +\textit{long-options} espresse nella forma \texttt{--option=parameter}, anche +la gestione di queste ultime è stata standardizzata attraverso l'uso di una +versione estesa di \texttt{getopt}. + + +\subsection{Le variabili di ambiente} +\label{sec:proc_env_var} + +Questo va fatto.