X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=process.tex;h=2bde8679e5846caf82025b3b789e35b8e7a9a36a;hp=2f754b992438de42fdf9a6ad1eaabad7fa8105e2;hb=73d2d858e297b733923773d287b0079df449f044;hpb=f95b34c637c6915f0febb99d8c8c55f7f831049f diff --git a/process.tex b/process.tex index 2f754b9..2bde867 100644 --- a/process.tex +++ b/process.tex @@ -1,114 +1,220 @@ -\chapter{I processi} -\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. - -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{Una panoramica sui concetti base} -\label{sec:proc_gen} - -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. - -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. - -I processi vengono creati dalla funzione \texttt{fork}; in genere questa è una -system call, 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. - -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. - -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. - -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} permette 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. - -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 viene eseguito un altro programma). - - -\section{Identificazione} -\label{sec:proc_id} - -Come detto 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 getpid(void)} restituisce il pid del padre del processo - corrente. +\chapter{L'interfaccia base con i processi} +\label{cha:process_interface} -\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}. +Come accennato nell'introduzione il processo è l'unità di base con cui un +sistema unix alloca ed utilizza le risorse. Questo capitolo tratterà +l'interfaccia base fra il sistema e i processi, su come vengono passati i +parametri, come viene gestita e allocata la memoria, su come un processo può +richiedere servizi al sistema, su cosa deve fare quando ha finito la sua +esecuzione. + +In genere un programma viene eseguito quando un processo lo fa partire +eseguendo una funzione della famiglia \texttt{exec}; torneremo su questo e +sulla la creazione e gestione dei processi nel prossimo capitolo, in questo +affronteremo l'avvio e il funzionamento di un programma dal punto di vista del +programma posto in esecuzione. -\section{Provilegi e permessi} -\label{sec:process_perms} +\section{Esecuzione e conclusione di un programma} -Va messo qui tutta la storia su effective, real, saved uid, e pure le cose di -linux come il filesystem uid. +Una delle concetti base relativi ai processi è che un processo esegue sempre +uno ed un solo programma: si possono avere più processi che eseguono lo stesso +programma ma ciascun processo vedrà la sua copia del codice (in realtà il +kernel fa si che tutte le parti uguali siano condivise) avrà un suo spazio di +indirizzi, variabili proprie e sarà eseguito in maniera completamente +indipendente da tutti gli altri. +Anche quando all'interno di un programma possono essere presenti più +\textsl{filoni} di esecuzione (i cosiddetti \textit{thread}), o questo possa +essere composto da moduli multipli completamente separati, quando questo sarà +posto in esecuzione esso apparirà al sistema come un solo processo (il +discorso dei \textit{thread} comunque in linux necessita di una trattazione a +parte per la peculiarità dell'implementazione). -\section{Creazione dei processi} -\label{sec:proc_fork} +\section{La funzione \texttt{main}} +\label{sec:proc_main} + +Quando un programma viene lanciato dal kernel viene eseguito il +programma \texttt{ld-linux.so}, è questo programma che prima carica le +librerie condivise che servono al programma, effettua il link dinamico del +codice e poi alla fine lo esegue. La procedura è controllata da alcune +variabili di ambiente e dai settaggi di + +Il sistema fa partire qualunque programma chiamando la funzione \texttt{main}; +sta al programmatore chiamare così la funzione principale del programma, se +così non fosse lo stesso linker darebbe luogo ad errori. + +Lo stadard ISO C specifica che detta funzione può non avere argomenti o +prendere due argomenti che rappresentano gli argomenti passati da linea di +comando, in sostanza un prototipo che va sempre bene è il seguente: +\begin{verbatim} + int main (int argc, char *argv[]) +\end{verbatim} + + +In realtà nei sistemi unix esiste un'altro modo per definire la funzione +\texttt{main}, che prende un terzo parametro, \texttt{char *envp[]}, che +fornisce l'ambiente (vedi \secref{proc_environ}) del programma; questa forma +però non è prevista dallo standard POSIX.1 per cui se si vogliono scrivere +programmi portabili è meglio evitarla. + + + +\subsection{} +\label{sec:proc_} + + + +\subsection{La funzione \texttt{exit}} +\label{sec:proc_exit} + + +\section{La gestione della memoria} +\label{sec:proc_mem_manag} + + + +\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} + +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. + + + +\section{La gestione della memoria} +\label{sec:proc_memory_manag} \ No newline at end of file