[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]

24. 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 principale (root) che genera tutti gli altri, è quello dell'eseguibile init che a sua volta è attivato direttamente dal kernel.

In linea di principio, il programma avviato dal kernel come processo principale, può essere qualunque cosa, anche una shell (tenendo conto, comunque, che il kernel predilige l'eseguibile /sbin/init), ma in tal caso si tratta di applicazioni specifiche, e non di un sistema standard.

Qui si preferisce utilizzare il nome Init per identificare il processo principale, tenendo conto che questo si concretizza generalmente nell'eseguibile init.

24.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:

24.1.1 /proc/

Il kernel 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 in 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 funzione. Il listato seguente mostra il contenuto che potrebbe avere il file /proc/1/status.

Name:	init
State:	S (sleeping)
Pid:	1
PPid:	0
Uid:	0	0	0	0
Gid:	0	0	0	0
Groups:	
VmSize:	     764 kB
VmLck:	       0 kB
VmRSS:	      16 kB
VmData:	      64 kB
VmStk:	       4 kB
VmExe:	      24 kB
VmLib:	     628 kB
SigPnd:	0000000000000000
SigBlk:	0000000000000000
SigIgn:	0000000057f0d8fc
SigCgt:	00000000280b2603
CapInh:	00000000fffffeff
CapPrm:	00000000ffffffff
CapEff:	00000000fffffeff

24.2 Nascita e morte di un processo

Come è già stato accennato, 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, utilizzando la chiamata di sistema fork(). Per esempio, quando si avvia un programma attraverso il terminale, è l'interprete dei comandi (la shell) che genera il processo corrispondente.

Quando un processo termina, lo fa attraverso la chiamata di sistema exit(), e quindi 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. Questo segnale viene inviato proprio dalla funzione di sistema exit(), ma 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.

Il processo che termina potrebbe avere avviato a sua volta altri processi (figli). In tal caso, questi vengono affidati al processo numero 1, cioè Init.

24.2.1 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, contenente l'immagine del processo interrotto. Per tradizione, questo file è denominato core, in onore dei primo tipo di memoria centrale che sia stato utilizzato: la memoria a nuclei magnetici, ovvero core memory.

Questi file servono a documentare un incidente di funzionamento e a permetterne l'analisi attraverso strumenti diagnostici opportuni. 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 -print

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.

24.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 definito IPC, o Inter Process Communication, ma questa definizione viene confusa spesso con un tipo particolare di comunicazione definito IPC di System V.

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

24.3.1 Segnali

I segnali sono dei messaggi elementari che possono essere inviati a un processo, permettendo a questo di essere informato di una condizione particolare che si è manifestata 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 che sarebbero prevedibili per un determinato tipo di segnale. Segue un elenco dei segnali più importanti.

La tabella 24.1 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 sullo sfondo che richiede dell'input.
SIGTTOU D Processo sullo sfondo che deve emettere dell'output.

Tabella 24.1: Segnali gestiti da GNU/Linux secondo lo standard POSIX.1.

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

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

24.3.2 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 mio_file | 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 fare ricorso alle funzionalità della shell. Questi, sono dei file speciali definiti proprio «FIFO» e vengono creati attraverso il programma mkfifo. Nell'esempio seguente viene mostrata 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 mio_file >> fifo1 &

Invia mio_file 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 pipe con nome, e si contrappongono a quelle normali che a volte vengono dette pipe anonime.

Quando un processo viene interrotto all'interno di una pipeline di qualunque tipo, 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. Quando questa situazione viene segnalata, si potrebbe ottenere il messaggio broken pipe.

24.3.3 IPC di System V

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

24.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. *1*

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 capire 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.

24.5 Privilegi dei processi

Nei sistemi operativi Unix c'è la necessità di distinguere i privilegi concessi agli utenti, e questo lo si fa definendo un nominativo e un numero identificativo riferito all'utente e al gruppo (o ai gruppi) a cui appartiene. L'utente fisico è rappresentato virtualmente dai processi che lui stesso mette in esecuzione; pertanto, un'informazione essenziale riferita ai processi è quella che stabilisce l'appartenenza a un utente e a un gruppo. In altri termini, ogni processo porta con sé l'informazione del numero UID e del numero GID, in base ai quali ottiene i privilegi relativi e gli viene concesso o meno di compiere le operazioni per cui è stato avviato.

---------------------------

Appunti Linux 1999.09.21 --- Copyright © 1997-1999 Daniele Giacomini --  daniele @ pluto.linux.it


1.) 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.


[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]