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. Nella sezione finale acceneremo ad alcune problematiche generiche
+esecuzione. Nella sezione finale accenneremo ad alcune problematiche generiche
di programmazione.
In genere un programma viene eseguito quando un processo lo fa partire
\funcdecl{void free(void *ptr)}
Disalloca lo spazio di memoria puntato da \var{ptr}.
- La funzione non ritorna nulla.
+ La funzione non ritorna nulla e non riporta errori.
\end{functions}
Il puntatore che le funzioni di allocazione ritornano è garantito essere
sempre correttamente allineato per tutti i tipi di dati; ad esempio sulle
sotto Linux}), ad esempio quando si deve far crescere la dimensione di un
vettore; in questo caso se è disponibile dello spazio adiacente al precedente
la funzione lo utilizza, altrimenti rialloca altrove un blocco della dimensione
-voluta copiandoci automaticamente il contenuto, lo spazio in più non viene
+voluta copiandoci automaticamente il contenuto, lo spazio aggiunto non viene
inizializzato.
-Il fatto che il blocco di memoria restituito da \func{realloc} possa
-cambiare comporta che si deve sempre riassegnare al puntatore passato per il
-ridimensionamento il valore di ritorno della funzione, e che non ci devono
-essere altri puntatori che puntino all'interno di un'area che si vuole
-ridimensionare.
+Si deve sempre avere ben presente il fatto che il blocco di memoria restituito
+da \func{realloc} può non essere una estensione di quello che gli si è passato
+come parametro; pertanto esso deve essere trattato allo stesso modo di una
+nuova allocazione; in particolare si dovrà \emph{sempre} eseguire la
+riassegnazione di \var{ptr} al valore di ritorno della funzione, e
+reinizializzare (o provvedere ad un adeguato aggiornamento qualora ancora
+servano) tutti gli altri puntatori al blocco di dati ridimensionato.
Uno degli errori più comuni (specie se si ha a che fare con array di
puntatori) è infatti quello di chiamare \func{free} più di una volta sullo
può essere più o meno specializzata per il debugging).
-\subsection{La funzione \texttt{alloca}}
+\subsection{La funzione \func{alloca}}
\label{sec:proc_mem_alloca}
-Una alternativa possibile all'uso di \texttt{malloc}, che non soffre del tipo
+Una alternativa possibile all'uso di \func{malloc}, che non soffre del tipo
di problemi di memory leak descritti in precedenza è la funzione
-\texttt{alloca} che invece che allocare la memoria nello heap usa lo il
+\func{alloca} che invece che allocare la memoria nello heap usa lo il
segmento di stack della funzione corrente. La sintassi è identica:
\begin{prototype}{stdlib.h}{void *alloca(size\_t size)}
- Alloca \texttt{size} byte nel segmento di stack della funzione chiamante.
+ Alloca \var{size} byte nel segmento di stack della funzione chiamante.
La memoria non viene inizializzata.
La funzione restituisce il puntatore alla 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}.
+ di successo e \macro{NULL} in caso di fallimento, nel qual caso
+ \var{errno} viene settata a \macro{ENOMEM}.
\end{prototype}
ma in questo caso non è più necessario liberare la memoria in quanto questa
viene rilasciata automaticamente al ritorno della funzione.
Un altro vantaggio e che in Linux la funzione è molto veloce e non viene
sprecato spazio, infatti non è necessario gestire un pool di memoria da
-riservare e si evitano anche problemi di frammentazione.
+riservare e si evitano anche i problemi di frammentazione di quest'ultimo che
+comportano inefficienze sia nell'allocazione della memoria che nell'esecuzione
+della funzione.
-Gli svantaggi sono che la funzione non è disponibile su tutti gli unix, quando
-non è possibile aumentare le dimensioni dello stack una volta chiamata una
-funzione e quindi l'uso limita la portabilità dei programmi, inoltre se si
-cerca di allocare troppa memoria non si ottiene un messaggio di errore, ma un
-segnale di \textit{segment violation} analogo a quello che si avrebbe da una
-ricorsione infinita.
+Gli svantaggi sono che questa funzione non è disponibile su tutti gli unix,
+(quando non è possibile aumentare le dimensioni dello stack una volta chiamata
+una funzione) e quindi l'uso limita la portabilità dei programmi, inoltre se
+si cerca di allocare troppa memoria non si ottiene un messaggio di errore, ma
+un segnale di \textit{segment violation} analogo a quello che si avrebbe da
+una ricorsione infinita.
Inoltre non è chiaramente possibile usare questa funzione per allocare memoria
che deve poi essere usata anche al di fuori della funzione in cui questa viene
chiamata, in quanto all'uscita dalla funzione lo spazio allocato diventerebbe
libero, e potrebbe essere sovrascritto all'invocazione di nuove funzioni con
-conseguenze imprevedibili.
+conseguenze imprevedibili. Questo è lo stesso problema potenziale che si può
+avere con le variabili automatiche, su cui torneremo in
+\secref{sec:proc_auto_var}.
-Questo è lo stesso problema potenziale che si può avere con le variabili
-automatiche; un errore comune infatti è quello di restituire al chiamante un
-puntatore ad una di queste variabili, che sarà automaticamente distrutta
-all'uscita della funzione, con gli stessi problemi appena citati per
-\func{alloca}.
\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 \func{malloc}. Le funzione sono:
+una propria versione di \func{malloc}. Le funzioni sono:
\begin{prototype}{unistd.h}{int *brk(void end\_data\_segment)}
Sposta la fine del segmento dei dati all'indirizzo specificato da
\var{end\_data\_segment}.
memoria per metterle nello swap sulla base dell'utilizzo corrente da parte dei
vari processi.
-Nell'uso comune un processo non deve preoccuparsi di tutto ciò in quanto il
+Nell'uso comune un processo non deve preoccuparsi di tutto ciò, in quanto il
meccanismo della paginazione riporta in RAM, ed in maniera trasparente, tutte
le pagine che gli occorrono; esistono però esigenze particolari in cui non si
-vuole che il meccanismo dello \textit{swapping}, in generale i motivi per cui
-si possono avere queste necessità sono sostanzialmente due:
+vuole che si attivi il meccanismo dello \textit{swapping}, in generale i
+motivi per cui si possono avere queste necessità sono sostanzialmente due:
\begin{itemize}
\item La velocità. Il processo della paginazione è trasparente solo se il
programma in esecuzione se non è sensibile al tempo che occorre a riportare
\item La sicurezza. Se si tengono password o chiavi in memoria queste possono
essere portate su disco dal meccanismo della paginazione, questo rende più
- lungo il periodo di tempo in cui i segreti sono presenti in chiaro, e
- complessa la loro cancellazione (in genere è possibile cancellare della RAM
- ma altrettanto non vale per il disco su cui la pagina contenente i segreti
- può essere stata salvata). Per questo motivo programmi di crittografia
- richiedono il blocco di alcune pagine di memoria.
+ lungo il periodo di tempo in cui i segreti sono presenti in chiaro e più
+ complessa la loro cancellazione (ad un processo è possibile cancellare la
+ memoria su cui scrive le sue variabili, ma non può toccare lo spazio disco
+ su cui la pagina contenente i segreti può essere stata salvata). Per questo
+ motivo di solito i programmi di crittografia richiedono il blocco di alcune
+ pagine di memoria.
\end{itemize}
Il meccanismo che previene la paginazione di parte della memoria virtuale di
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 \textit{memory lock}.
+tutti i suoi \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.
+Siccome la presenza di un \textit{memory lock} riduce la memoria disponibile
+al sistema con un impatto su tutti gli 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, lo
+standard POSIX.1 richiede che sia definita in \file{unistd.h} la costante
+\macro{\_POSIX\_MEMLOCK\_RANGE} per indicare la capacità di eseguire il
+\textit{memory locking} e la costante \macro{PAGESIZE} in \file{limits.h} per
+indicare la dimensione di una pagina in byte.
+
Le funzioni per bloccare e sbloccare singole sezioni di memoria sono
\func{mlock} e \func{munlock}; i loro prototipi sono:
-
\begin{functions}
-\headdecl{stdlib.h}
-\funcdecl{void *calloc(size\_t size)}
- Alloca \var{size} byte nello heap. La memoria viene inizializzata a 0.
+ \headdecl{sys/mman.h}
+
+ \funcdecl{int mlock(const void *addr, size\_t len)}
+ Blocca la paginazione per l'intervallo di memoria da \var{addr} per
+ \var{len} byte. Tutte le pagine che contengono una parte dell'intervallo
+ sono mantenute in RAM per tutta la durata del blocco.
+
+ La funzione ritorna 0 in caso di successo e -1 in caso di errore, nel qual
+ caso \var{errno} è settata ad uno dei valori seguenti:
+ \begin{errlist}
+ \item \macro{ENOMEM} alcuni indirizzi dell'intervallo specificato non
+ corripondono allo spazio di indirizzi del processo o si è ecceduto il
+ numero massimo consentito di pagine bloccate.
+ \item \macro{EPERM} il processo non ha i privilegi richiesti per
+ l'operazione.
+ \item \macro{EINVAL} \var{len} non è un valore positivo.
+ \end{errlist}
- 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)}
+ \funcdecl{int munlock(const void *addr, size\_t len)}
Alloca \var{size} byte nello heap. La memoria non viene inizializzata.
- 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}.
+ Sblocca l'intervallo di memoria da \var{addr} per \var{len} byte. La
+ funzione ritorna 0 in caso di successo e -1 in caso di errore, nel qual caso
+ \var{errno} è settata ad uno dei valori seguenti:
+ \begin{errlist}
+ \item \macro{ENOMEM} alcuni indirizzi dell'intervallo specificato non
+ corripondono allo spazio di indirizzi del processo.
+ \item \macro{EINVAL} \var{len} non è un valore positivo.
+ \end{errlist}
+\end{functions}
- 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}.
+Altre due funzioni, \func{mlockall} e \func{munlockall}, consentono di
+bloccare genericamente lo spazio di indirizzi di un processo. I prototipi di
+queste funzioni sono:
- La funzione non ritorna nulla.
-\end{functions}
+\begin{functions}
+ \headdecl{sys/mman.h}
+ \funcdecl{int mlockall(int flags)}
+ Blocca la paginazione per lo spazio di indirizzi del processo corrente.
+
+ Codici di ritorno ed errori sono gli stessi di \func{mlock}.
+ \funcdecl{int munlockall(void)}
+ Sblocca la paginazione per lo spazio di indirizzi del processo corrente.
+
+ Codici di ritorno ed errori sono gli stessi di \func{munlock}.
+\end{functions}
+Il parametro \var{flags} di \func{mlockall} permette di controllarne il
+comportamento; esso può essere specificato come l'OR aritmetico delle due
+costanti:
+\begin{description*}
+\item[\macro{MCL\_CURRENT}] blocca tutte le pagine correntemente mappate nello
+ spazio di indirizzi del processo.
+\item[\macro{MCL\_FUTURE}] blocca tutte le pagine che saranno mappate nello
+ spazio di indirizzi del processo.
+\end{description*}
+
+Con \func{mlockall} si può bloccare tutte le pagine mappate nello spazio di
+indirizzi del processo, sia che comprendano il segmento di testi, di dati, lo
+stack e lo heap e pure le funzioni di libreria chiamate, i file mappati in
+memoria, i dati del kernel mappati in user space, la memoria condivisa. L'uso
+dei flag permette di selezionare con maggior finezza le pagine da bloccare, ad
+esempio limitandosi a tutte le pagine allocate a partire da un certo momento.
+
+In ogni caso un processo real-time che deve entrare in una sezione critica
+deve provvedere a riservare memoria sufficiente prima dell'ingresso, in genere
+questo si fa chiamando una funzione che ha allocato una quantità sufficiente
+ampia di variabili automatiche, in modo che esse vengano mappate in RAM dallo
+stack e poi ci scrive sopra, per scongiurare in partenza un eventuale page
+fault causato dal meccanismo di copy on write.
\section{La gestione di parametri e opzioni}
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
+opzioni 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
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{\&}).
+(diverso ad esempio da quanto avviene con il Fortran, dove le variabili 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
\label{sec:proc_gen}
Partiremo con una introduzione generale ai concetti che stanno alla base della
-gestione dei processi in un sitema unix-like. Introdurremo in questa sezione
+gestione dei processi in un sistema 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.
sessione, ad ogni processo sono associati altri identificatori, usati per il
controllo di accesso, che servono per determinare se il processo può o meno
eseguire le operazioni richieste, a seconda dei privilegi e dell'identità di
-chi lo ha posto in esecuzione; su questi torneremo in dettaglii più avanti in
+chi lo ha posto in esecuzione; su questi torneremo in dettagli più avanti in
\secref{sec:proc_perm}.
settati (il significato di questi bit è affrontato in dettaglio in
\secref{sec:file_suid_sgid}). In questo caso essi saranno settati all'utente e
al gruppo proprietari del file; questo consente, per programmi in cui ci sia
-necessità, di dare a qualunquee utente normale privilegi o permessi di
+necessità, di dare a qualunque utente normale privilegi o permessi di
un'altro (o dell'amministratore).
Come nel caso del \acr{pid} e del \acr{ppid} tutti questi identificatori
\subsection{Le funzioni \func{setuid} e \func{setgid}}
\label{sec:proc_setuid}
-Le due funzioni che venfono usate per cambiare identità (cioè utente e gruppo
+Le due funzioni che vengono usate per cambiare identità (cioè utente e gruppo
di appartenenza) ad un processo sono rispettivamente \func{setuid} e
\func{setgid}; come accennato in \secref{sec:proc_user_group} in Linux esse
-seguono la sematica POSIX che prevede l'esistenza di \textit{saved user id} e
+seguono la semantica POSIX che prevede l'esistenza di \textit{saved user id} e
\textit{saved group id}; i loro prototipi sono:
\begin{functions}
L'effetto della chiamata è diverso a seconda dei privilegi del processo; se
l'\textit{effective user id} è zero (cioè è quello dell'amministratore di
-sistema) allora tutti gli identificatatori (\textit{real}, \textit{effective}
+sistema) allora tutti gli identificatori (\textit{real}, \textit{effective}
e \textit{saved}) vengono settati al valore specificato da \var{uid},
altrimenti viene settato solo l'\textit{effective user id}, e soltanto se il
valore specificato corrisponde o al \textit{real user id} o al \textit{saved
Come esempio per chiarire dell'uso di queste funzioni prediamo quello con cui
viene gestito l'accesso al file \file{/var/log/utmp}. In questo file viene
registrato chi sta usando il sistema al momento corrente; chiaramente non può
-essere lasciato aperto in scrittura a qualunque utente, che protrebbe
+essere lasciato aperto in scrittura a qualunque utente, che potrebbe
falsificare la registrazione. Per questo motivo questo file (e l'analogo
\file{/var/log/wtmp} su cui vengono registrati login e logout) appartengono ad
un gruppo dedicato (\acr{utmp}) ed i programmi che devono accedervi (ad
l'unico errore possibile è \macro{EPERM}.
\end{functions}
-I processi non privileguiati possono settare i \textit{real id} soltanto ai
+I processi non privilegiati possono settare i \textit{real id} soltanto ai
valori dei loro \textit{effective id} o \textit{real id} e gli
\textit{effective id} ai valori dei loro \textit{real id}, \textit{effective
id} o \textit{saved id}; valori diversi comportano il fallimento della
I processi non privilegiati possono cambiare uno qualunque degli
identificatori usando uno qualunque dei valori correnti di \textit{real id},
-\textit{effective id} o \textit{saved id}, l'ammnistratore può specificare i
+\textit{effective id} o \textit{saved id}, l'amministratore può specificare i
valori che vuole; un valore di -1 per un qualunque parametro lascia inalterato
-l'dentificatore corrispondente.
+l'identificatore corrispondente.
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}, ovviamenete sono specifiche di Linux e non devono essere
+e \func{setfsgid}, ovviamente sono specifiche di Linux e non devono essere
usate se si intendono scrivere programmi portabili; i loro prototipi sono:
\begin{functions}
che devono essere compiuti per realizzarla verranno eseguiti senza possibilità
di interruzione in una fase intermedia.
-In un ambiente multitasking il concetto è esseziale, dato che un processo può
+In un ambiente multitasking il concetto è essenziale, dato che un processo può
essere interrotto in qualunque momento dal kernel che mette in esecuzione un
altro processo o dalla ricezione di un segnale; occorre pertanto essere
accorti nei confronti delle possibili \textit{race condition} (vedi
condivise siano opportunamente protette da meccanismi di sincronizzazione
(torneremo su queste problematiche di questo tipo in \secref{sec:ipc_semaph}).
-Un caso particolare di \textit{race condition} sono poi i cosidetti
+Un caso particolare di \textit{race condition} sono poi i cosiddetti
\textit{deadlock}; l'esempio tipico è quello di un flag di ``occupazione'' che
viene rilasciato da un evento asincrono fra il controllo (in cui viene trovato
occupato) e la successiva messa in attesa, attesa che a questo punto diventerà