Capitolo successivo Capitolo precedente Indice

13. Introduzione ai processi di elaborazione

Un singolo programma, nel momento in cui viene eseguito, è un ``processo''. La nascita di un processo, cioè l'avvio di un programma, può avvenire solo tramite una richiesta da parte di un altro processo già esistente. Si forma quindi una sorta di gerarchia dei processi, organizzata ad albero. Il processo root, cioè quello che genera tutti gli altri è init che a sua volta, è attivato direttamente dal kernel.

13.1 Tabella dei processi

Il kernel gestisce una tabella dei processi che serve a tenere traccia del loro stato. In particolare sono registrati i valori seguenti:

/proc/

Linux, rende disponibile i dati della tabella dei processi attraverso un filesystem virtuale montato nella directory /proc/. Dalla presenza di questo filesystem virtuale dipendono la maggior parte dei programmi che si occupano di gestire i processi.

In particolare, a partire da questa directory se ne diramano altre, tante quanti sono i processi i esecuzione, ognuna identificata dal numero del processo stesso. Per esempio, /proc/1/ contiene una serie di file virtuali che rappresentano lo stato del processo numero 1, ovvero init che è sempre il primo a essere messo in esecuzione.

Il listato seguente mostra il contenuto del file /proc/1/status.

Name:   init
State:  S (sleeping)
Pid:    1
PPid:   0
Uid:    0       0       0       0
Gid:    0       0       0       0
VmSize:      880 kB
VmLck:         0 kB
VmRSS:       280 kB
VmData:      208 kB
VmStk:        12 kB
VmExe:        20 kB
VmLib:       588 kB
SigPnd: 00000000
SigBlk: 00000000
SigIgn: d7f0dafc
SigCgt: 280b2403

13.2 Nascita e morte di un processo

Come si era detto, la nascita di un processo, cioè l'avvio di un programma, può avvenire solo tramite una richiesta da parte di un altro processo già esistente. Per esempio, quando si avvia un programma attraverso il terminale, è l'interprete dei comandi (shell) che genera il processo corrispondente.

Quando un processo termina, si trasforma in un cosiddetto ``zombie''. È poi il processo che lo ha generato che si deve occupare di eliminarne le tracce.

Il processo genitore, per avviare l'eliminazione dei suoi processi zombie, deve essere avvisato che ne esiste la necessità attraverso un segnale SIGCHLD. Se il meccanismo non funziona come previsto, si può inviare manualmente un segnale SIGCHLD al processo genitore. In mancanza d'altro, si può far terminare l'esecuzione del processo genitore stesso.

Core dump

A volte, l'interruzione di un processo provoca il cosiddetto ``scarico della memoria'' o core dump. In pratica si ottiene un file, nella directory corrente, denominato core e contenente l'immagine del processo interrotto.

Questi file servono quindi solo a ``documentare'' un incidente di funzionamento e solitamente possono essere cancellati tranquillamente.

La proliferazione di questi file va tenuta sotto controllo: di solito non ci si rende conto se un processo interrotto ha generato o meno lo scarico della memoria. Ogni tanto vale la pena di fare una ricerca all'interno del filesystem per rintracciare questi file, come nell'esempio seguente.

# find / -name core -type f

Ciò che conta è di non confondere ``core'' con ``spazzatura'': ci possono essere dei file chiamati core per qualche motivo e che nulla hanno a che fare con lo scarico della memoria.

13.3 Comunicazione tra processi

Nel momento in cui l'attività di un processo dipende da quella di un altro ci deve essere una forma di comunicazione tra i due. Ciò viene normalmente definito IPC, o Inter Process Communication, ma questa definizione viene normalmente confusa con un tipo particolare di comunicazione definito IPC di System V.

I metodi normalmente utilizzati sono di tre tipi: invio di segnali, pipe e System V IPC.

Segnali

I segnali sono dei messaggi elementari che possono essere inviati a un processo, permettendo a questo di essere informato di una particolare condizione manifestatasi e di potersi uniformare. I programmi possono essere progettati in modo da intercettare questi segnali, allo scopo di compiere alcune operazioni prima di adeguarsi agli ``ordini'' ricevuti. Nello stesso modo, un programma potrebbe anche ignorare completamente un segnale, o compiere operazioni diverse da quelle normalmente prevedibili per un determinato tipo di segnale.

Segue un breve elenco dei segnali più importanti.

La tabella (seguente) elenca i segnali descritti dallo standard POSIX.1, mentre l'elenco completo può essere ottenuto consultando signal(7).


Segnale Azione Descrizione
SIGHUP A Il collegamento con il terminale è stato interrotto.
SIGINT A Interruzione attraverso un comando dalla tastiera.
SIGQUIT A Conclusione attraverso un comando dalla tastiera.
SIGILL A Istruzione non valida.
SIGABRT C Interruzioni di sistema.
SIGFPE C Eccezione in virgola mobile.
SIGKILL AEF Conclusione immediata.
SIGSEGV C Riferimento non valido a un segmento di memoria.
SIGPIPE A Pipe interrotta.
SIGALRM A Timer.
SIGTERM A Conclusione.
SIGUSR1 A Primo segnale definibile dall'utente.
SIGUSR2 A Secondo segnale definibile dall'utente.
SIGCHLD B Eliminazione di un processo figlio.
SIGCONT Riprende l'esecuzione se era stato fermato.
SIGTSTOP DEF Ferma immediatamente il processo.
SIGTSTP D Stop attraverso un comando della tastiera.
SIGTTIN D Processo in background che necessita dell'input.
SIGTTOU D Processo in background che necessita dell'output.
Segnali gestiti da Linux.

Le lettere contenute nella colonna ``Azione'' rappresentano il comportamento predefinito dei programmi che ricevono tale segnale:

L'utente ha a disposizione in particolare due mezzi per inviare segnali ai programmi:

Pipe

Attraverso la shell è possibile collegare più processi tra loro in una pipeline, come nell'esempio seguente, in modo che lo standard output di uno sia collegato direttamente con lo standard input del successivo.

$ cat miofile | sort | lpr

Ogni connessione tra un processo e il successivo, evidenziata dalla barra verticale (pipe), si comporta come un serbatoio provvisorio di dati ad accesso FIFO (First In First Out - il primo a entrare è il primo a uscire).

È possibile creare esplicitamente dei ``serbatoi FIFO'' di questo genere in modo da poterli gestire senza dover far ricorso alle funzionalità della shell. Questi, sono dei file speciali definiti proprio FIFO e vengono creati attraverso il programma mkfifo. Nell'esempio seguente vengono mostrati una sequenza di comandi con i quali, creando due file FIFO, si può eseguire la stessa operazione indicata nella pipeline vista poco sopra.

$ mkfifo fifo1 fifo2

Crea due file FIFO: fifo1 e fifo2.

$ cat miofile >> fifo1 &

Invia miofile a fifo1 senza attendere (&).

$ sort < fifo1 >> fifo2 &

Esegue il riordino di quanto ottenuto da fifo1 e invia il risultato a fifo2 senza attendere (&).

$ lpr < fifo2

Accoda la stampa di quanto ottenuto da fifo2.

<!>   I file FIFO, data la loro affinità di funzionamento con le pipeline gestite dalla shell, vengono anche chiamati named pipe, o pipe con nome, e si contrappongono a quelle normali che a volte vengono dette pipe anonime.

Broken pipe

Quando all'interno di una pipeline di qualunque tipo un processo viene interrotto, il processo che inviava dati a quest'ultimo riceve un segnale SIGPIPE e si interrompe a sua volta. Dall'altra parte, i processi che ricevevano dati da quello interrotto, vedono concludersi il flusso di questi dati e terminano la loro esecuzione in modo naturale.

System V IPC

Il System V IPC è un sistema di comunicazione tra processi sofisticato che permette di gestire code di messaggi, semafori e memoria condivisa.

13.4 Scheduling e priorità

La gestione simultanea dei processi è ottenuta normalmente attraverso la suddivisione del tempo di CPU, in maniera tale che a turno ogni processo abbia a disposizione un breve intervallo di tempo di elaborazione. Il modo con cui vengono regolati questi turni è lo scheduling, ovvero la pianificazione di questi processi.

La maggiore o minore percentuale di tempo di CPU che può avere un processo è regolata dalla priorità espressa da un numero. Il numero che rappresenta una priorità deve essere visto al contrario di come si è abituati di solito: un valore elevato rappresenta una bassa priorità, cioè meno tempo a disposizione, mentre un valore basso (o negativo) rappresenta una priorità elevata, cioè più tempo a disposizione.

Il concetto di priorità fa riferimento a una sequenza ordinata di elementi: il primo, cioè quello che ha precedenza sugli altri, è quello che ha il valore inferiore.

Sotto questo aspetto diventa difficile esprimersi in modo chiaro: una ``bassa priorità'' si riferisce al numero che ne esprime il valore o alle risorse disponibili? Si può solo fare attenzione al contesto per intendere bene il significato di ciò che si intende.

La priorità di esecuzione di un processo viene definita in modo autonomo da parte del sistema e può essere regolata da parte dell'utente sommandovi il cosiddetto valore nice. Di conseguenza, un valore nice positivo aumenta il valore della priorità, mentre un valore negativo lo diminuisce.

 

1997.10.26 - Scritto da Daniele Giacomini   daniele@calion.com   (vedi copyright: Appunti Linux).


Capitolo successivo Capitolo precedente Indice