Completato il capitolo sui socket elementari, con tanto di spiegazioni delle
[gapil.git] / process.tex
index c1fc583863e811e50ac271fe79d68ce145338e32..0c94547a6e9c0aa82692d910d16b1785aaccc17c 100644 (file)
-\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.