Creazione di processi

La creazione di un processo richiede alcune operazioni da parte del Sistema Operativo:

  • Creazione nuovo ID (PID – Process Identifier),
  • Allocazione Memoria (Codice, Dati),
  • Allocazione altre risorse (stdin, stdout, dispositivi I/O in genere),
  • Gestione informazioni sul nuovo processo (es. priorità),
  • Creazione PCB (Process Control Block) contenente le informazioni precedenti.

Processi in Unix

Un processo è sempre creato da un altro processo tramite un’opportuna chiamata a sistema. Fa eccezione init (pid = 1) che viene creato al momento del boot.

Il processo creante è detto parent (genitore), mentre il processo creato child (figlio). Si genera una struttura di “parentela” ad albero.

Relazioni Dinamiche

Cosa accade dopo la creazione?

  • Il processo genitore attende il processo figlio.
    Esempio: Esecuzione di un programma da shell

    La shell è genitore del nuovo processo xcalc
    NOTA
    : il terminale è associato al nuovo programma: ctrl-c, ad esempio, termina la calcolatrice.
  • Il processo genitore continua.
    Esempio: Esecuzione in background di un programma da shell:

    In questo caso i due processi procedono in modo concorrente. Tramite ps possiamo osservare i processi in esecuzione.

    NOTA: ps di default mostra solo i processi associati al terminale da cui viene lanciato. In questo caso abbiamo la shell bash, il programma xcalc e lo stesso ps.

    Anche in esecuzione rimane un legame genitore-figlio. Ad esempio, se chiudiamo la calcolatrice la shell viene notificata:

    Può essere utile che un processo si dissoci dal processo genitore. Ad esempio i Daemon sono processi che restano attivi fino allo shutdown del sistema: devono, ad esempio, dissociarsi dalla shell per non essere terminati alla chiusura del terminale stesso. Quando si dissociano diventano figli del processo init (vedremo come questo sia possibile).

Per visualizzare il PID del genitore basta passare gli opportuni parametri al programma ps (PPID significa Parent process ID):

Si osservi che xcalc e ps sono entrambi figli di bash.

Relazioni di contenuto

Due possibilità:

  • Il figlio è un duplicato del genitore (ad esempio in UNIX)
  • Il figlio esegue un programma differente (ad esempio nei sistemi Windows)

Questo è il comportamento standard ma ovviamente è possibile anche l’altra modalità in entrambi i sistemi.

System Call “fork”

La chiamata a sistema fork permette di creare un processo duplicato del processo genitore.

NOTA fork, come la maggior parte delle chiamate a sistema che discuteremo, appartiene allo standard POSIX (Portable Operating System Interface) di IEEE (Institute of Electrical and Electronics Engineers). È utilizzabile in qualsiasi sistema che supporti POSIX.

La fork crea un nuovo processo che

  • condivide l’area codice del processo genitore
  • utilizza una copia dell’area dati del processo genitore

Come fanno i processi a differenziarsi? Si utilizza il valore di ritorno della fork:

  • =0 Processo figlio
  • >0 Processo genitore: il valore di ritorno è il PID del figlio.

Schema di base di utilizzo della fork:

NOTA: pid_t è un signed integer, cioè int in molti sistemi ma potrebbe essere di dimensioni differenti, es. long, ed è quindi bene usare sempre il tipo pid_t

Un esempio concreto:

che dà il seguente output:

Esempio Come possiamo visualizzare l’alternanza nell’esecuzione dei processi genitore e figlio? Un modo è mettere le printf dentro un while(1):

In output avremo un’alternanza (non stretta a causa dell’output bufferizzato!) degli output dei due processi. Il ciclo for “vuoto” prima della printf riduce il numero di stampe e permette di visualizzare meglio l’alternanza (ed evita di “saturare” il terminale di output). Variare, se necessario, il limite.

NOTA Su computer a più core i processi vengono eseguiti in parallelo su core differenti. In tal caso non è necessario rallentarli con il ciclo for per vedere l’alternanza nell’output. Per vedere il carico sui vari core si può lanciare ‘top’ da un altro terminale. Il Linux, premendo 1, viene visualizzato il carico delle differenti CPU. Provare per esercizio.

Fallimento della fork

Quando fallisce una fork? Quando non è possibile creare un processo e tipicamente questo accade quando non c’è memoria per il processo o per il kernel. Ecco un piccolo test.

NOTA BENE. Il test potrebbe bloccare tutto il sistema perché i processi generati vanno ad occupare tutte le risorse e il sistema non può più prendere il controllo. È necessario limitare in numero di processi utenti tramite il comando ulimit -u 600 (al massimo 600 processi sono ammessi per l’utente sul presente terminale). Attenzione che il limite è per terminale quindi il test va eseguito dalla stessa finestra su cui avete impostato il limite a 600.

Esercizio. Quanti processi eseguono la fork al loop i-esimo? (pensare a quanti processi ci sono alla prima fork, quanti alla seconda, e cosi’ via).

Processi orfani e processi zombie

Se metto una sleep subito prima della printf nel figlio lo rendo orfano perché termina il genitore prima di lui: viene adottato da init (genitore di tutti i processi di sistema) o da upstart nei moderni sistemi Linux:

ottengo:

dove si nota, appunto, l’adozione da parte di upstart.

NOTA Un processo orfano non viene più terminato da ctrl-c o dalla chiusura della shell (provare).

Gli zombie sono processi terminati ma in attesa che il genitore rilevi il loro stato di terminazione (vedremo come fare). Per osservare la generazione di un processo zombie ci basta porre la sleep prima della printf del processo genitore:

che dà il seguente output:

Leave a Reply

Your email address will not be published. Required fields are marked *