 \chapter{Sessioni di lavoro e terminali}
\label{cha:session}

Esamineremo in questo capitolo i concetti base del sistema delle sessioni di
lavoro, vale a dire il metodo con cui il kernel gestisce l'accesso concorrente
al sistema da parte di più utenti, permettendo loro di eseguire più programmi
in contemporanea.  Nella seconda parte del capitolo tratteremo poi il
funzionamento dell'I/O su terminale, e delle varie peculiarità che esso viene
ad assumere a causa del suo stretto legame con le modalità di accesso al
sistema da parte degli utenti.


\section{Il \textit{job control}}
\label{sec:sess_job_control}

Viene comunemente chiamato \textit{job control} quell'insieme di funzionalità
il cui scopo è quello di permettere ad un utente di poter sfruttare le
capacità multitasking di un sistema Unix per eseguire in contemporanea più
processi, pur potendo accedere, di solito, ad un solo terminale,\footnote{con
  X e con i terminali virtuali tutto questo non è più vero, dato che si può
  accedere a molti terminali in contemporanea, ma il sistema è nato prima
  dell'esistenza di tutto ciò.} avendo cioè un solo punto in cui si può avere
accesso all'input ed all'output degli stessi. 


\subsection{Una panoramica introduttiva}
\label{sec:sess_job_control_overview}

Il \textit{job control} è una caratteristica opzionale, introdotta in BSD
negli anni '80, e successivamente standardizzata da POSIX.1; la sua
disponibilità nel sistema è verificabile attraverso il controllo della macro
\macro{\_POSIX\_JOB\_CONTROL}. In generale il \textit{job control} richiede il
supporto sia da parte della shell (quasi tutte ormai lo fanno), che da parte
del kernel; in particolare il kernel deve assicurare sia la presenza di un
driver per i terminali abilitato al \textit{job control} che quella dei
relativi segnali illustrati in \secref{sec:sig_job_control}. 

In un sistema che supporta il \textit{job control} una volta completato il
login (che esamineremo in dettaglio in \secref{sec:sess_login}), l'utente avrà
a disposizione una shell dalla quale eseguire i comandi e potrà iniziare
quella che viene chiamata una \textsl{sessione}, che riunisce (vedi
\secref{sec:sess_proc_group}) tutti i processi eseguiti all'interno dello
stesso login.

Siccome la shell è collegata ad un solo terminale (il \textsl{terminale di
  controllo}, vedi \secref{sec:sess_control_term}) solo un comando alla volta,
quello che viene detto in \textit{foreground}, potrà scrivere e leggere dal
terminale. La shell però può eseguire anche più comandi in contemporanea,
mandandoli in \textit{background} (si fa aggiungendo una \cmd{\&} alla fine
del comando), nel qual caso essi saranno eseguiti senza essere collegati al
terminale.

Si noti come si sia parlato di comandi e non di programmi o processi; fra le
funzionalità della shell infatti c'è anche quella di consentire di concatenare
più programmi in una sola riga di comando con le pipe, ed in tal caso verranno
eseguiti più programmi, inoltre, anche quando si invoca un singolo programma,
questo potrà sempre lanciare altri processi per eseguire dei compiti
specifici.

Per questo l'esecuzione di un comando può originare più di un processo; quindi
nella gestione del job control non si può far riferimento ai singoli processi,
per questo il kernel prevede la possibilità di raggruppare più processi in un
\textit{process group} (detto anche \textsl{raggruppamento}, vedi
\secref{sec:sess_proc_group}) e la shell farà sì che tutti i processi che
originano da una riga di comando appartengano allo stesso \textit{process
  group}, in modo che le varie funzioni di controllo, ed i segnali inviati dal
terminale, possano fare riferimento ad esso.

In generale allora all'interno di una sessione avremo un eventuale (possono
non esserci comandi in \textit{foreground}) \textit{process group} in
\textit{foreground}, che riunisce i processi che possono accedere al
terminale, e più \textit{process group} in \textit{background}, che non
possono accedervi. Il job control prevede che quando un processo appartenente
ad un raggruppamento in \textit{background} cerca di accedere al terminale
questo invii a tutti i processi del raggruppamento un segnale di
\macro{SIGTTIN} o di \macro{SIGTTOU}, a seconda che l'accesso sia
rispettivamente in lettura o scrittura, bloccando (secondo il comportamento
di default esposto in \secref{sec:sig_job_control}) i processi.

Un comportamento analogo sia ha anche per i segnali generati dai comandi di
tastiera inviati dal terminale con \cmd{C-z}, \cmd{C-c}, \cmd{C-y} e
\verb|C-\|; questi generano rispettivamente i segnali \macro{SIGTSTP},
\macro{SIGINT}, \macro{SIGQUIT} e \macro{SIGTERM}, che vengono inviati a tutti
i processi del raggruppamento in \textit{foreground}. In particolare il primo
di essi, \macro{SIGTSTP}, interrompe l'esecuzione del comando, che può poi
essere mandato in \textit{background} con il comando \cmd{bg}. Il comando
\cmd{fg} consente invece di mettere in \textit{foreground} un comando
precedentemente lanciato in \textit{background}.

Di norma la shell si cura anche di notificare all'utente (di solito prima
della stampa a video del prompt) lo stato dei vari processi, essa infatti usa
le caratteristiche della funzione \func{waitpid} (si riveda quanto detto in
\secref{sec:proc_wait}) per verificare quali gruppi di processi sono bloccati
e quali sono terminati. 


\subsection{I \textit{process group} e le \textsl{sessioni}}
\label{sec:sess_proc_group}

Come accennato in \secref{sec:sess_job_control_overview} nel job control i
processi vengono raggruppati in \textit{process group} e \textit{sessioni};
per far questo vengono utilizzati due ulteriori identificatori (oltre quelli
visti in \secref{sec:proc_pid}) che il kernel associa a ciascun processo:
l'identificatore del \textit{process group} e l'identificatore della
\textsl{sessione}, che vengono indicati rispettivamente con le sigle
\acr{pgid} e \acr{sid}, e sono mantenuti in variabili di tipo \type{pid\_t}. I
valori di questi identificatori possono essere visualizzati dal comando
\cmd{ps} usando l'opzione \cmd{-j}.

Un \textit{process group} è pertanto definito da tutti i processi che hanno lo
stesso \acr{pgid}; è possibile leggere il valore di questo identificatore con
le funzioni \func{getpgid} e \func{getpgrp},\footnote{\func{getpgrp} è
  definita nello standard POSIX.1, mentre \func{getpgid} è richiesta da SVr4.}
i cui prototipi sono:
\begin{functions}
  \headdecl{unistd.h}

  \funcdecl{pid\_t getpgid(pid\_t pid)} 
  Legge il \acr{pgid} del processo \param{pid}.

  \funcdecl{pid\_t getpgrp(void)}
  Legge il \acr{pgid} del processo corrente.
  
  \bodydesc{Le funzioni restituiscono il \acr{pgid} del processo,
    \func{getpgrp} ha sempre successo, mentre \func{getpgid} restituisce -1
    ponendo \var{errno} a \macro{ESRCH} se il processo selezionato non esiste.}
\end{functions}

La funzione \func{getpgid} permette di specificare il \acr{pid} del processo
di cui si vuole sapere il \acr{pgid}; un valore nullo per \param{pid}
restituisce il \acr{pgid} del processo corrente; \func{getpgrp} è di norma
equivalente a \code{getpgid(0)}.

In maniera analoga l'identificatore della sessione può essere letto dalla
funzione \func{getsid}, che però nelle \acr{glibc}\footnote{la system call è
  stata introdotta in Linux a partire dalla versione 1.3.44, il supporto nelle
  librerie del C è iniziato dalla versione 5.2.19. La funzione non è prevista
  da POSIX.1, che parla solo di processi leader di sessione, e non di
  \textit{session id}.} è accessibile solo definendo \macro{\_XOPEN\_SOURCE} e
\macro{\_XOPEN\_SOURCE\_EXTENDED}; il suo prototipo è:
\begin{prototype}{unistd.h}{pid\_t getsid(pid\_t pid)}
  Legge l'identificatore di sessione del processo \param{pid}.
  
  \bodydesc{La funzione restituisce l'identificatore (un numero positivo) in
  caso di successo, e -1 in caso di errore, nel qual caso \var{errno} assumerà
  i valori:
    \begin{errlist}
    \item[\macro{ESRCH}] Il processo selezionato non esiste.
    \item[\macro{EPERM}] In alcune implementazioni viene restituito quando il
      processo selezionato non fa parte della stessa sessione del processo
      corrente.
    \end{errlist}
  }
\end{prototype}

Entrambi gli identificatori vengono inizializzati alla creazione di ciascun
processo con lo stesso valore che hanno nel processo padre, per cui un
processo appena creato appartiene sempre allo stesso raggruppamento e alla
stessa sessione del padre. Vedremo poi come sia possibile creare più
\textit{process group} all'interno della stessa sessione, e spostare i
processi dall'uno all'altro, ma sempre all'interno di una stessa sessione.

Ciascun gruppo di processi ha sempre un processo principale, il cosiddetto
\textit{process group leader}, che è identificato dall'avere un \acr{pgid}
uguale al suo \acr{pid}, in genere questo è il primo processo del gruppo, che
si incarica di lanciare tutti gli altri. Un nuovo gruppo si crea con la
funzione \func{setpgrp},\footnote{questa è la definizione di POSIX.1, BSD
  definisce una funzione con lo stesso nome, che però è identica a
  \func{setpgid}; nelle \acr{glibc} viene sempre usata sempre questa
  definizione, a meno di non richiedere esplicitamente la compatibilità
  all'indietro con BSD, definendo la macro \macro{\_BSD\_SOURCE}.} il cui
prototipo è:
\begin{prototype}{unistd.h}{int setpgrp(void)}
  Modifica il \acr{pgid} al valore del \acr{pid} del processo corrente.
  
  \bodydesc{La funzione restituisce il valore del nuovo \textit{process
      group}.}
\end{prototype}

La funzione, assegnando al \acr{pgid} il valore del \acr{pid} processo
corrente, rende questo \textit{process leader} di un nuovo gruppo, tutti i
successivi processi da esso creati apparterranno (a meno di non cambiare di
nuovo il \acr{pgid}) al nuovo gruppo. È possibile invece spostare un processo
da un gruppo ad un altro con la funzione \func{setpgid}, il cui prototipo è:
\begin{prototype}{unistd.h}{int setpgid(pid\_t pid, pid\_t pgid)}
  Assegna al \acr{pgid} del processo \param{pid} il valore \param{pgid}.
  
  \bodydesc{La funzione ritorna il valore del nuovo \textit{process group}, e
  -1 in caso di errore, nel qual caso \var{errno} assumerà i valori:
    \begin{errlist}
    \item[\macro{ESRCH}] Il processo selezionato non esiste.
    \item[\macro{EPERM}] Il cambiamento non è consentito.
    \item[\macro{EINVAL}] Il valore di \param{pgid} è negativo.
    \end{errlist}
 }
\end{prototype}

La funzione permette di cambiare il \acr{pgid} del processo \param{pid}, ma il
cambiamento può essere effettuato solo se \param{pgid} indica un
\textit{process group} che è nella stessa sessione del processo chiamante.
Inoltre la funzione può essere usata soltanto sul processo corrente o su uno
dei suoi figli, ed in quest'ultimo caso ha successo soltanto se questo non ha
ancora eseguito una \func{exec}. Specificando un valore nullo per \param{pid}
si indica il processo corrente, mentre specificando un valore nullo per
\param{pgid} si imposta il \textit{process group} al valore del \acr{pid} del
processo selezionato; pertanto \func{setpgrp} è equivalente a \code{setpgid(0,
  0)}.

Di norma questa funzione viene usata dalla shell quando si usano delle
pipeline, per mettere nello stesso process group tutti i programmi lanciati su
ogni linea di comando; essa viene chiamata dopo una \func{fork} sia dal
processo padre, per impostare il valore nel figlio, che da quest'ultimo, per
sé stesso, in modo che il cambiamento di \textit{process group} sia immediato
per entrambi; una delle due chiamate sarà ridondante, ma non potendo
determinare quale dei due processi viene eseguito per primo, occorre eseguirle
comunque entrambe per evitare di esporsi ad una race condition. 

Si noti come nessuna delle funzioni esaminate finora permetta di spostare un
processo da una sessione ad un altra; infatti l'unico modo di far cambiare
sessione ad un processo è quello di crearne una nuova con l'uso di
\func{setsid}; il suo prototipo è:
\begin{prototype}{unistd.h}{pid\_t setsid(void)}
  Crea una nuova sessione sul processo corrente settandone \acr{sid} e
  \acr{pgid}.
  
  \bodydesc{La funzione ritorna il valore del nuovo \acr{sid}, e -1 in caso di
    errore, il solo errore possibile è \macro{EPERM}, che si ha quando il
    \acr{pgid} e \acr{pid} del processo concidono.}
\end{prototype}

La funzione imposta il \acr{pgid} ed il \acr{sid} del processo corrente al
valore del suo \acr{pid}, creando così una nuova sessione ed un nuovo
\textit{process group} di cui esso diventa leader (come per i \textit{process
  group} un processo si dice leader di sessione se il suo \acr{sid} è uguale
al suo \acr{pid}).  Infine il processo viene distaccato dal terminale di
controllo.

La funzione ha successo soltanto se il processo non è già leader per un
\textit{process group}, per cui di norma si esegue una \func{fork} e si esce,
per poi chiamare \func{setsid} nel processo figlio, in modo che, avendo questo
lo stesso \acr{pgid} del padre ma un \acr{pid} diverso, non ci siano
possibilità di errore.  Questa funzione viene usata di solito dal processo di
login quando si lancia una nuova shell per un utente.




\subsection{Il terminale di controllo}
\label{sec:sess_ctrl_term}

Come accennato in \secref{sec:sess_job_control_overview} ad ogni sessione di
lavoro è associato un terminale di controllo.  



\subsection{Dal login alla shell}
\label{sec:sess_login}

L'organizzazione del sistema del job control è strettamente connessa alle
modalità con cui un utente accede al sistema per dare comandi, collegandosi ad
esso con un terminale, che sia questo realmente tale, come un VT100 collegato
ad una seriale o virtuale, come quelli associati a schermo e tastiera o ad una
connessione di rete. Dato che i concetti base sono gli stessi, e dato che alla
fine le differenze sono\footnote{in generale nel caso di login via rete o di
  terminali lanciati dall'interfaccia grafica cambia anche il processo da cui
  ha origine l'esecuzione della shell.} nel device cui il kernel associa i
file standard (vedi \secref{sec:file_std_descr}) per l'I/O, tratteremo solo il
caso classico del terminale.

Abbiamo già brevemente illustrato in \secref{sec:intro_kern_and_sys} le
modalità con cui il sistema si avvia, e di come, a partire da \cmd{init},
vengano lanciati tutti gli altri processi. Adesso vedremo in maniera più
dettagliata le modalità con cui il sistema arriva a fornire ad un utente la
shell che gli permette di lanciare i suoi comandi su un terminale.

Nella maggior parte delle distribuzioni di GNU/Linux\footnote{fa eccezione la
  distribuzione \textit{Slackware}, come alcune distribuzioni su dischetto, ed
  altre distribuzioni dedicate a compiti limitati e specifici.}  viene usata
la procedura di avvio di System V; questa prevede che \cmd{init} legga dal
file di configurazione \file{/etc/inittab} quali programmi devono essere
lanciati, ed in quali modalità, a seconda del cosiddetto \textit{run level},
anch'esso definito nello stesso file.

Tralasciando la descrizione del sistema dei run level, (per il quale si
rimanda alla lettura delle pagine di manuale di \cmd{init} e di
\file{inittab}) quello che comunque viene sempre fatto è di eseguire almeno
una istanza di un programma che permetta l'accesso ad un terminale. Uno schema
di massima della procedura è riportato in \secref{fig:sess_term_login}.

\begin{figure}[htb]
  \centering
  \includegraphics[width=15cm]{img/tty_login}
  \caption{Schema della procedura di login su un terminale.}
  \label{fig:sess_term_login}
\end{figure}

Un terminale, che esso sia un terminale effettivo, attaccato ad una seriale o
ad un altro tipo di porta di comunicazione, o una delle console virtuali
associate allo schermo,  viene sempre visto attraverso attraverso un device
driver che ne presenta un'interfaccia comune su un apposito file di
dispositivo. Storicamente i primi terminali erano appunto terminali di
telescriventi (\textit{teletype}), da cui deriva sia il nome dell'interfaccia,
\textit{tty}, che quello dei relativi file di dispositivo, che sono sempre
della forma \texttt{/dev/tty*}.\footnote{questo vale anche per i terminali
  associati alle connessioni di rete con \cmd{telnet} o \cmd{ssh}.}

Per controllare i terminali si usa di solito il programma \cmd{getty} (od una
delle sue varianti), che permette di mettersi in ascolto sugli stessi. Alla
radice della catena che porta ad una shell per i comandi perciò c'è sempre
\cmd{init} che esegue prima una \func{fork} e poi una \func{exec} per lanciare
una istanza di questo programma su un terminale, il tutto ripetuto per
ciascuno dei terminali che si hanno a disposizione (o per un certo numero di
essi, nel caso delle console virtuali), secondo quanto indicato
dall'amministratore nel file di configurazione del programma,
\file{/etc/inittab}.

Quando viene lanciato da \cmd{init} il programma parte con i privilegi di
amministratore e con un ambiente vuoto; \cmd{getty} si cura di chiamare
\func{setsid} per creare una nuova sessione ed un nuovo process group, e di
aprire il terminale in lettura sullo standard input ed in scrittura sullo
standard output e sullo standard error, e di effettuare, qualora servano,
ulteriori settaggi,\footnote{ad esempio, come qualcuno si sarà accorto
  scrivendo un nome di login in maiuscolo, può effettuare la conversione
  automatica dell'input in minuscolo, ponendosi in una modalità speciale che
  non distingue fra i due tipi di caratteri (a beneficio di alcuni vecchi
  terminali che non supportavano le minuscole).} ed infine il programma
stamperà un messaggio di benvenuto per poi porsi in attesa dell'immissione del
nome di un utente.

Una volta che si sia immesso il nome di login \cmd{getty} esegue direttamente
il programma \cmd{login} con una \func{exevle}, passando come argomento la
suddetta stringa ed un ambiente opportunamente costruito che contenga quanto
necessario (ad esempio di solito viene opportunamente inizializzata la
variabile di ambiente \texttt{TERM}) ad identificare il terminale su cui si
sta operando, a beneficio dei programmi che verranno lanciati in seguito.

A sua volta \cmd{login}, che mantiene i privilegi di amministratore, usa il
nome dell'utente per effettuare una ricerca nel database degli
utenti,\footnote{in genere viene chiamata \func{getpwnam}, che abbiamo visto
  in \secref{sec:sys_user_group}, per leggere la password e gli altri dati dal
  database degli utenti.} e richiede una password. Se l'utente non esiste o se
la password non corrisponde\footnote{il confronto non viene effettuato con un
  valore in chiaro; quanto immesso da terminale viene invece a sua volta
  criptato, ed è il risultato che viene confrontato con il valore che viene
  mantenuto nel database degli utenti.} la richiesta viene ripetuta un certo
numero di volte dopo di che \cmd{login} esce ed \cmd{init} provvede a
rilanciare un'altra istanza di \func{getty}.

Se invece la password corrisponde a questo punto \cmd{login} esegue
\func{chdir} per settare la \textit{home directory} dell'utente, cambia i
diritti di accesso al terminale (con \func{chown} e \func{chmod}) per
assegnarne la titolarità all'utente ed al suo gruppo principale, assegnandogli
al contempo i diritti di lettura e scrittura. Inoltre il programma provvede
a costruire gli opportuni valori per le variabili di ambiente, come
\texttt{HOME}, \texttt{SHELL}, ecc. Infine attraverso l'uso di \func{setuid},
\func{setpid} e \func{initgroups} verrà cambiata l'identità del proprietario
del processo, infatti, come spiegato in \secref{sec:proc_setuid}, avendo
invocato tali funzioni con i privilegi di amministratore, tutti gli userid ed
i groupid (reali, effettivi e salvati) saranno settati a quelli dell'utente.

A questo punto \cmd{login} provvederà (fatte salve eventuali altre azioni
iniziali, come la stampa di messaggi di benvenuto o il controllo della posta)
ad eseguire con un'altra \func{exec} la shell di login, che si troverà con un
ambiente già pronto e con file standard di \secref{sec:file_std_descr}
impostati sul terminale, pronta ad eseguire i comandi fino all'uscita. Dato
che il processo genitore resta sempre \cmd{init} quest'ultimo provvederà,
ricevendo un \macro{SIGCHLD} all'uscita della shell, a rilanciare \cmd{getty}
per ripetere da capo tutto il procedimento.

Una volta arrivati alla 




\section{L'I/O su terminale}
\label{sec:sess_terminal_io}

Esamineremo in questa sezione le peculiarità dell'I/O su terminale, tenendo
conto delle 


%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "gapil"
%%% End: 
