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.
+esecuzione. Nella sezione finale acceneremo ad alcune problematiche generiche
+di programmazione.
In genere un programma viene eseguito quando un processo lo fa partire
eseguendo una funzione della famiglia \func{exec}; torneremo su questo e
all'uscita della funzione, con gli stessi problemi appena citati per
\func{alloca}.
-\subsection{Le funzioni \texttt{brk} e \texttt{sbrk}}
+\subsection{Le funzioni \func{brk} e \func{sbrk}}
\label{sec:proc_mem_sbrk}
L'uso di queste funzioni è necessario solo quando si voglia accedere alle
analoghe system call a cui fanno da interfaccia (ad esempio per implementare
-una propria versione di \texttt{malloc}. Le funzione sono:
+una propria versione di \func{malloc}. Le funzione sono:
\begin{prototype}{unistd.h}{int *brk(void end\_data\_segment)}
Sposta la fine del segmento dei dati all'indirizzo specificato da
- \texttt{end\_data\_segment}.
+ \var{end\_data\_segment}.
La funzione restituisce 0 in caso di successo e -1 in caso di fallimento,
- nel qual caso \texttt{errno} viene settata a \texttt{ENOMEM}.
+ nel qual caso \var{errno} viene settata a \macro{ENOMEM}.
\end{prototype}
\begin{prototype}{unistd.h}{int *sbrk(ptrdiff\_t increment)}
- Incrementa lo spazio dati di un programma di \texttt{increment}. Un valore
+ Incrementa lo spazio dati di un programma di \var{increment}. Un valore
zero restituisce l'attuale posizione della fine del segmento dati.
La funzione restituisce il puntatore all'inizio della nuova zona di memoria
- allocata in caso di successo e \texttt{NULL} in caso di fallimento, nel qual
- caso \texttt{errno} viene settata a \texttt{ENOMEM}.
+ allocata in caso di successo e \macro{NULL} in caso di fallimento, nel qual
+ caso \macro{errno} viene settata a \macro{ENOMEM}.
\end{prototype}
Queste funzioni sono state deliberatamente escluse dallo standard POSIX.1 e
per i programmi normali è opportuno usare le funzioni di allocazione standard
descritte in precedenza, che sono costruite su di esse. In genere si usa
-\texttt{sbrk} con un valore zero per ottenere l'attuale posizione della fine
+\func{sbrk} con un valore zero per ottenere l'attuale posizione della fine
del segmento dati.
non si accumulano, se si blocca due volte la stessa pagina non è necessario
sbloccarla due volte, una pagina o è bloccata o no.
-Il blocco di memoria persiste fintanto che il processo che lo detiene la
+Il \textit{memory lock} persiste fintanto che il processo che detiene la
memoria bloccata non la sblocca. Chiaramente la terminazione del processo
comporta anche la fine dell'uso della sua memoria virtuale, e quindi anche di
-tutti i blocchi di memoria.
-
-I memory lock non sono ereditati dai processi figli\footnote{ma siccome Linux
- usa il copy on write gli indirizzi virtuali del figlio sono mantenuti sullo
- stesso segmento di RAM del padre, quindi usufruiscono dei memory lock di
- questo}. Siccome la presenza di memory lock ha un impatto sugli altri
-processi solo root ha la capacità di bloccare una pagina, ogni processo può
-però sbloccare le sue pagine. Il sistema pone dei limiti all'ammontare di
+tutti i \textit{memory lock}.
+
+I \textit{memory lock} non sono ereditati dai processi figli\footnote{ma
+ siccome Linux usa il copy on write gli indirizzi virtuali del figlio sono
+ mantenuti sullo stesso segmento di RAM del padre, quindi fintanto che un
+ figlio non scrive su un segmento, può usufruire dei memory lock del padre}.
+Siccome la presenza di \textit{memory lock} ha un impatto sugli altri processi
+solo l'amministratore ha la capacità di bloccare una pagina; ogni processo
+però può sbloccare le sue pagine. Il sistema pone dei limiti all'ammontare di
memoria di un processo che può essere bloccata e al totale di memoria fisica
che può dedicare a questo.
+Le funzioni per bloccare e sbloccare singole sezioni di memoria sono
+\func{mlock} e \func{munlock}; i loro prototipi sono:
-\section{Il controllo di flusso non locale}
-\label{sec:proc_longjmp}
+\begin{functions}
+\headdecl{stdlib.h}
+\funcdecl{void *calloc(size\_t size)}
+ Alloca \var{size} byte nello heap. La memoria viene inizializzata a 0.
+
+ La funzione restituisce il puntatore alla zona di memoria allocata in caso
+ di successo e \macro{NULL} in caso di fallimento, nel qual caso
+ \var{errno} viene settata a \macro{ENOMEM}.
+\funcdecl{void *malloc(size\_t size)}
+ Alloca \var{size} byte nello heap. La memoria non viene inizializzata.
-Il controllo del flusso di un programma in genere viene effettuato con le
-varie istruzioni del linguaggio C, la più bistrattata delle quali è il
-\func{goto}, ampiamente deprecato in favore di costrutti più puliti; esiste
-però un caso in l'uso di questa istruzione porta all'implementazione più
-efficiente, quello dell'uscita in caso di errore.
+ La funzione restituisce il puntatore alla zona di memoria allocata in caso
+ di successo e \macro{NULL} in caso di fallimento, nel qual caso
+ \var{errno} viene settata a \macro{ENOMEM}.
+\funcdecl{void *realloc(void *ptr, size\_t size)}
+ Cambia la dimensione del blocco allocato all'indirizzo \var{ptr}
+ portandola a \var{size}.
-Il C però non consente di effettuare un salto ad una label definita in
-un'altra funzione, per cui se l'errore avviene in funzioni profondamente
-annidate occorre usare la funzione \func{longjump}.
+ La funzione restituisce il puntatore alla zona di memoria allocata in caso
+ di successo e \macro{NULL} in caso di fallimento, nel qual caso
+ \var{errno} viene settata a \macro{ENOMEM}.
+\funcdecl{void free(void *ptr)}
+ Disalloca lo spazio di memoria puntato da \var{ptr}.
+
+ La funzione non ritorna nulla.
+\end{functions}
\subsection{La gestione delle opzioni}
\label{sec:proc_opt_handling}
-In generale un programma unix riceve da linea di comando sia i parametri che
+In generale un programma unix riceve da linea di comando sia gli argomenti che
le opzioni, queste ultime sono standardizzate per essere riconosciute come
tali: un elemento di \var{argv} che inizia con \texttt{-} e che non sia un
singolo \texttt{-} o \texttt{--} viene considerato un'opzione. In in genere
\end{verbatim}
ed in questo caso le opzioni sono \texttt{m} ed \texttt{r}.
-Per gestire le opzioni all'interno dei parametri passati in \func{argv} le
-librerie standard del C forniscono la funzione \func{getopt} che ha il
-prototipo:
+Per gestire le opzioni all'interno dei argomenti a linea di comando passati in
+\func{argv} le librerie standard del C forniscono la funzione \func{getopt}
+che ha il seguente prototipo:
\begin{prototype}{unistd.h}
{int getopt(int argc, char * const argv[], const char * optstring)}
La funzione esegue il parsing degli argomenti passati da linea di comando
riconoscendo le possibili opzioni segnalate con \var{optstring}.
-Ritorna il carattere che segue l'opzione, \cmd{:} se manca un parametro
-all'opzione, \cmd{?} se l'opzione è sconosciuta, e -1 se non esistono altre
+Ritorna il carattere che segue l'opzione, \cmd{':'} se manca un parametro
+all'opzione, \cmd{'?'} se l'opzione è sconosciuta, e -1 se non esistono altre
opzioni.
\end{prototype}
-Questa funzione prende come argomenti le due variabili \var{argc} e
-\var{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 \cmd{-} e ritorna ogni volta che trova una opzione
-valida.
+Questa funzione prende come argomenti le due variabili \var{argc} e \var{argv}
+passate a \func{main} (vedi \secref{sec:proc_main}) ed una stringa che indica
+quali sono le opzioni valide; la funzione effettua la scansione della lista
+degli argomenti ricercando ogni stringa che comincia con \cmd{-} e ritorna ogni
+volta che trova una opzione valida.
La stringa \var{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, anche se vi sono altri parametri che cominciano con
-\texttt{-}.
+due punti \var{':'}, nel caso appena accennato ad esempio la stringa di
+opzioni sarebbe \var{"r:m"}.
+
+La modalità di uso di \func{getopt} è pertanto quella di chiamare più volte la
+funzione all'interno di un ciclo fintanto che essa non ritorna il valore -1
+che indica che non ci sono più opzioni. Nel caso si incontri un'opzione non
+dichiarata in \var{optstring} viene ritornato il carattere \texttt{'?'}
+mentre se un opzione che lo richiede non è seguita da un parametro viene
+ritornato il carattere \texttt{':'}, infine se viene incontrato il valore
+\cmd{--} la scansione viene considerata conclusa, anche se vi sono altri
+elementi di \var{argv} che cominciano con il carattere \texttt{'-'}.
-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 variabili globali:
-\begin{itemize*}
-\item \var{char * optarg} contiene il puntatore alla stringa argomento
- dell'opzione.
-\item \var{int optind} alla fine della scansione restituisce l'indice del
- primo argomento che non è un'opzione.
-\item \var{int opterr} previene, se posto a zero, la stampa di un messaggio
- di errore in caso di riconoscimento di opzioni non definite.
-\item \var{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) {
+ while ( (i = getopt(argc, argv, "hp:c:e:")) != -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);
- }
+ /*
+ * Handling options
+ */
+ case 'h': /* help option */
+ printf("Wrong -h option use\n");
+ usage();
+ return -1;
break;
- case 'a': /* output file (append) */
- out_file=open(optarg,O_WRONLY|O_CREAT|O_APPEND);
+ case 'c': /* take wait time for childen */
+ wait_child = strtol(optarg, NULL, 10); /* convert input */
break;
- case 'h': /* print help usage */
- usage();
+ case 'p': /* take wait time for childen */
+ wait_parent = strtol(optarg, NULL, 10); /* convert input */
break;
- case 'v': /* set verbose mode */
- debug("Option -v active\n");
- verbose=1;
+ case 'e': /* take wait before parent exit */
+ wait_end = strtol(optarg, NULL, 10); /* convert input */
break;
case '?': /* unrecognized options */
printf("Unrecognized options -%c\n",optopt);
usage();
default: /* should not reached */
- debug("default option\n");
usage();
}
}
\label{fig:proc_options_code}
\end{figure}
+Quando la funzione trova un'opzione essa ritorna il valore numerico del
+carattere, in questo modo si possono prendere le azioni relative usando uno
+\func{switch}; la funzione inizializza inoltre alcune variabili globali:
+\begin{itemize*}
+\item \var{char * optarg} contiene il puntatore alla stringa parametro
+ dell'opzione.
+\item \var{int optind} alla fine della scansione restituisce l'indice del
+ primo elemento di \var{argv} che non è un'opzione.
+\item \var{int opterr} previene, se posto a zero, la stampa di un messaggio
+ di errore in caso di riconoscimento di opzioni non definite.
+\item \var{int optopt} contiene il carattere dell'opzione non riconosciuta.
+\end{itemize*}
+
+In \figref{fig:proc_options_code} è mostrata la sezione del programma
+\file{ForkTest.c} (che useremo nel prossimo capitolo per effettuare dei test
+sulla creazione dei processi) deputata alla decodifica delle opzioni a riga di
+comando.
+
+Anzitutto si può notare che si è anzitutto (\texttt{\small 1}) disabilitata la
+stampa di messaggi di errore per opzioni non riconosciute, per poi passare al
+ciclo per la verifica delle opzioni (\texttt{\small 2-27}); per ciascuna delle
+opioni possibili si è poi provveduto ad una opportuna azione, ad esempio per
+le tre opzioni che prevedono un parametro si è effettuata la decodifica del
+medesimo, il cui indirizzo è contenuto nella variabile \var{optarg},
+avvalorando la relativa variabile (\texttt{\small 12-14}, \texttt{\small
+ 15-17} e \texttt{\small 18-20}). Completato il ciclo troveremo in
+\var{optind} l'indice in \var{argv[]} del primo degli argomenti a linea di
+comando restanti.
+
+Normalmente \func{getopt} compie una permutazione degli elementi di \var{argv}
+così che alla fine della scansione gli elementi che non sono opzioni sono
+spostati in coda al vettore. Oltre a questa esistono altre due modalità di
+gestire gli elementi di \var{argv}; se \var{optstring} inizia con il carattere
+\texttt{'+'} (o è settata la variabile di ambiente \macro{POSIXLY\_CORRECT})
+la scansione viene fermata non appena si incontra un elemento che non è
+un'opzione. L'ultima modalità, usata quando un programma può gestire la
+mescolanza fra opzioni e argomenti, ma se li aspetta in un ordine definito, si
+attiva quando \var{optstring} inizia con il carattere \texttt{'-'}. In questo
+caso ogni elemento che non è un'opzione viene considerato comunque un'opzione
+e associato ad un valore di ritorno pari ad 1, questo permette di identificare
+gli elementi che non sono opzioni, ma non effettua il riordinamento del
+vettore \var{argv}.
+
\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
+\textit{long-options} espresse nella forma \cmd{--option=parameter}, anche la
+gestione di queste ultime è stata standardizzata attraverso l'uso di una
versione estesa di \func{getopt}.
(NdA: da finire).
\subsection{Le variabili di ambiente}
\label{sec:proc_environ}
-Oltre ai parametri passati da linea di comando ogni processo riceve dal
+Oltre agli argomenti passati a linea di comando ogni processo riceve dal
sistema un \textsl{ambiente}, nella forma di una lista di variabili
-(\textit{environment list}) messa a disposizione dal processo costruita nella
-chiamata ad \func{exec} che lo ha lanciato.
+(\textit{environment list}) messa a disposizione dal processo, e costruita
+nella chiamata alla funzione \func{exec} quando questo viene lanciato.
Come per la lista dei parametri anche questa lista è un array di puntatori a
-caratteri, ciascuno dei quali punta ad una stringa (terminata da un NULL). A
-differenza di \var{argv[]} però in questo caso non si ha la lunghezza
-dell'array dato da un equivalente di \var{argc}, ma la lista è terminata da un
-puntatore nullo.
+caratteri, ciascuno dei quali punta ad una stringa (terminata da un
+\macro{NULL}). A differenza di \var{argv[]} però in questo caso non si ha una
+lunghezza dell'array data da un equivalente di \var{argc}, ma la lista è
+terminata da un puntatore nullo.
L'indirizzo della lista delle variabili di ambiente è passato attraverso la
variabile globale \var{environ}, a cui si può accedere attraverso una semplice
Per convenzione le stringhe che definiscono l'ambiente sono tutte del tipo
\textsl{\texttt{nome=valore}}. Inoltre alcune variabili, come quelle elencate
-in \curfig, sono definite dal sistema per queste c'è la convezione di usare
-nomi espressi in caratteri maiuscoli.
+in \curfig, sono definite dal sistema per essere usate da diversi programmi e
+funzioni: per queste c'è l'ulteriore convezione di usare nomi espressi in
+caratteri maiuscoli.
Il kernel non usa mai queste variabili, il loro uso e la loro interpretazione è
riservata alle applicazioni e ad alcune funzioni di libreria; in genere esse
Gli standard POSIX e XPG3 definiscono alcune di queste variabili (le più
comuni), come riportato in \ntab. GNU/Linux le supporta tutte e ne definisce
-anche altre per una lista parziale si può controllare \cmd{man environ}.
+anche altre: per una lista parziale si può controllare \cmd{man environ}.
+
+
+
+\section{Problematiche di programmazione generica}
+\label{sec:proc_gen_prog}
+Benché questo non sia un libro di C, è opportuno affrontare alcune delle
+problematiche generali che possono emergere nella programmazione e di quali
+precauzioni o accorgimenti occorre prendere per risolverle. Queste
+problematiche non sono specifiche di sistemi unix-like o multitasking, ma
+avendo trattato in questo capitolo il comportamento dei processi visti come
+entità a se stanti, le riportiamo qui.
+\subsection{Il passaggio delle variabili e dei valori di ritorno}
+\label{sec:proc_var_passing}
+Una delle caratteristiche standard del C è che le variabili vengono passate
+alle subroutine attraverso un meccanismo che viene chiamato \textit{by value}
+(diverso ad esempio da quanto avviene con il Fortran, dove le variabli sono
+passate, come suol dirsi, \textit{by reference}, o dal C++ dove la modalità del
+passaggio può essere controllata con l'operatore \cmd{\&}).
+
+Il passaggio di una variabile \textit{by value} significa che in realtà quello
+che viene passato alla subroutine è una copia del valore attuale di quella
+variabile, copia che la subroutine potrà modificare a piacere, senza che il
+valore originale nella routine chiamante venga toccato. In questo modo non
+occorre preoccuparsi di eventuali effetti delle operazioni della subroutine.
+
+La maggior parte delle funzioni di libreria e delle system call funziona
+esattamente in questo modo restituendo eventuali risultati alla routine
+chiamante attraverso il valore di ritorno. Talvolta però è necessario che la
+funzione possa restituire indietro alla funzione chiamate un valore relativo
+ad uno dei sui parametri.
+
+
+
+
+\subsection{Potenziali problemi con le variabili automatiche}
+\label{sec:proc_auto_var}
+
+
+\subsection{Il controllo di flusso non locale}
+\label{sec:proc_longjmp}
+
+Il controllo del flusso di un programma in genere viene effettuato con le
+varie istruzioni del linguaggio C, la più bistrattata delle quali è il
+\func{goto}, ampiamente deprecato in favore di costrutti più puliti; esiste
+però un caso in l'uso di questa istruzione porta all'implementazione più
+efficiente, quello dell'uscita in caso di errore.
+
+Il C però non consente di effettuare un salto ad una label definita in
+un'altra funzione, per cui se l'errore avviene in funzioni profondamente
+annidate occorre usare la funzione \func{longjump}.
Nel precedente capitolo abbiamo visto come funziona un singolo processo, 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.
+funzioni a questo connesse. Infine nella sezione finale affronteremo alcune
+problematiche generiche della programmazione in ambiente multitasking.
+
\section{Introduzione}
\label{sec:proc_gen}
Partiremo con una introduzione generale ai concetti che stanno alla base della
-gestione dei processi in unix. Introdurremo in questa sezione l'architettura
-della gestione dei processi e le sue principali caratteristiche, e daremo una
-panoramica sull'uso delle principali funzioni per la gestione dei processi.
+gestione dei processi in un sitema unix-like. Introdurremo in questa sezione
+l'architettura della gestione dei processi e le sue principali
+caratteristiche, e daremo una panoramica sull'uso delle principali funzioni
+per la gestione dei processi.
+
\subsection{La gerarchia dei processi}
\label{sec:proc_hierarchy}
valori che vuole; un valore di -1 per un qualunque parametro lascia inalterato
l'dentificatore corrispondente.
+
+
\subsection{Le funzioni \func{seteuid} e \func{setegid}}
\label{sec:proc_seteuid}
\headdecl{sys/types.h}
\funcdecl{int seteuid(uid\_t uid)} setta l'\textit{effective user ID} del
-processo corrente.
+processo corrente a \var{uid}.
\funcdecl{int setegid(gid\_t gid)} setta l'\textit{effective group ID} del
-processo corrente.
+processo corrente a \var{gid}.
Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento:
l'unico errore possibile è \macro{EPERM}.
Gli utenti normali possono settare l'\textit{effective id} solo al valore del
\textit{real id} o del \textit{saved id}, l'amministratore può specificare
-qualunque valore.
-
+qualunque valore. Queste funzioni sono usate per permettere a root di settare
+solo l'\textit{effective id}, dato che l'uso normale di \func{setuid} comporta
+il settagio di tutti gli identificatori.
+
\subsection{Le funzioni \func{setfsuid} e \func{setfsgid}}
\label{sec:proc_setfsuid}
+Queste funzioni sono usate per settare gli identificatori usati da Linux per
+il controllo dell'accesso ai file. Come già accennato in
+\secref{sec:proc_user_group} Linux definisce questo ulteriore gruppo di
+identificatori che di norma sono assolutamente equivalenti agli
+\textit{effective id} (ed in seguito faremo sempre riferimento a questi
+ultimi), dato che ogni cambiamento di questi ultimi viene immediatamente
+riportato sui \textit{filesystem id}.
+
+C'è un solo caso in cui si ha necessità di introdurre una differenza fra
+\textit{effective id} e i \textit{filesystem id}, che è per ovviare ad un
+problema di sicurezza che si presneta quando si deve implementare un server
+NFS. Il server NFS infatti deve poter cambiare l'identificatore con cui accede
+ai file per assumere l'identità del singolo utente remoto, ma se questo viene
+fatto cambiando l'\textit{effective id} o il \textit{real id} il server si
+espone alla ricezione di eventuali segnali ostili da parte dell'utente di cui
+ha temporaneamente assunto l'identità. Cambiando solo il \textit{filesystem
+ id} si ottengono i privilegi necessari per accedere ai file, mantenendo
+quelli originari per quanto riguarda tutti gli altri controlli di accesso.
+
+Le due funzioni usate per cambiare questi identificatori sono \func{setfsuid}
+e \func{setfsgid}, sono specifiche di Linux e non devono essere usate se si
+intendono scrivere programmi portabili; i loro prototipi sono:
+
+\begin{functions}
+\headdecl{sys/fsuid.h}
+
+\funcdecl{int setfsuid(uid\_t fsuid)} setta il \textit{filesystem user ID} del
+processo corrente a \var{fsuid}.
+
+\funcdecl{int setfsgid(gid\_t fsgid)} setta l'\textit{filesystem group ID} del
+processo corrente a \var{fsgid}.
+
+Le funzioni restituiscono 0 in caso di successo e -1 in caso di fallimento:
+l'unico errore possibile è \macro{EPERM}.
+\end{functions}
+
+Queste funzioni hanno successo solo se il processo chiamante ha i privilegi di
+amministratore o se il valore specificato coincide con uno dei
+\textit{real}, \textit{effective} o \textit{saved id}.
+
+
+
+
+\section{Problematiche di programmazione multitasking}
+\label{sec:proc_multi_prog}
+
+Benché i processi siano strutturati in modo da apparire il più possibile come
+independenti l'uno dall'altro, nella programmazione in un sistema multiutente
+occorre tenere conto di tutta una serie di problematiche che normalmente non
+esistono quando si ha a che fare con un sistema in cui viene eseguito un solo
+programma alla volta.
+
+
+Pur non essendo tutto questo direttamente legato alla modalità specifica in
+cui il multitasking è implementato in un sistema unix-like, avendo affrontato
+la gestione dei processi in questo capitolo, tratteremo queste problematiche,
+e relative precauzioni ed accorgimenti, in questa sezione conclusiva.
+
+
+\subsection{Le funzioni rientranti}
+\label{sec:proc_reentrant}
+
+
+\subsection{I \textit{deadlock}}
+\label{sec:proc_deadlock}
+
+
+\subsection{Le operazioni atomiche}
+\label{sec:proc_atom_oper}
\subsection{Le \textit{race condition}}
sincronizzazione, non è assolutamente prevedibile, queste situazioni sono
fonti di errori molto subdoli, che possono verificarsi solo in condizioni
particolari e quindi difficilmente riproducibili.
+
+