Sistemi operativi, A.A. 2011/2012 (M. Cesati)
Scaletta esercitazione #11, 24.05.2012

1 Mappatura in memoria dei file
  1.1 Concetti generali: area di memoria del processo che viene associata con
      il contenuto di un file su disco, sia in lettura che in scrittura
  1.2 La chiamata di sistema mmap()
      +------------------------------------------------------------+
      |void *mmap(void *addr, size_t length, int prot, int flags,  |
      |           int fd, off_t offset);                           |
      +------------------------------------------------------------+
      1.2.1 addr: tipicamente NULL, suggerimento sull'indirizzo iniziale
            dell'area di memoria
      1.2.2 length: lunghezza del memory mapping (e dell'area di 
            memoria) ATTENZIONE: non si puo' estendere un file tramite
            mmap!
      1.2.3 prot: OR logico di PROT_EXEC, PROT_READ e/o PROT_WRITE
      1.2.4 flags: in alternativa:
            1.2.4.1 MAP_SHARED: i cambiamenti sono visibili ad altri processi
                    che mappano il file e sono copiati nel file su disco
            1.2.4.2 MAP_PRIVATE: i cambiamenti non sono visibili e non 
                    copiati sul file su disco
            flags: in OR logico
            1.2.4.3 MAP_ANONYMOUS: mapping senza file associato
            1.2.4.4 MAP_FIXED: "addr" non e' un suggerimento, e' l'indirizzo
                    da utilizzare per l'area di memoria
      1.2.5 fd: il descrittore del file (deve essere aperto sia in lettura che
            scrittura (O_RDWR) in caso di MAP_SHARED e PROT_WRITE)
      1.2.6 offset: la posizione del file in cui il mapping inizia (multiplo
            di una pagina)
  1.3 Per la lunghezza di una pagina si puo' utilizza la funzione di libreria
      +-----------------------------------+
      |page_size = sysconf(_SC_PAGE_SIZE);|
      +-----------------------------------+
  1.4 Per allineare una quantita' x ad un multiplo di una lunghezza s:
      4.6.1 verso il basso: x & ~(s-1) equivalente a x & (-s)
      4.6.2 verso l'alto: (x+s-1) & (-s)
  1.5 La chiamata di sistema munmap()
      +---------------------------------------+
      |int munmap(void *addr, size_t length); |
      +---------------------------------------+
  1.6 I memory map sono ereditati dai figli dopo fork(). Un memory map 
      sopravvive anche dopo aver chiuso il file con close().

2 Esercizio: modificare il programma 'mandelbrot4' in modo che il file di
  output sia scritto in parallelo da tutti i core tramite memory mapping
  [mandelbrot5.c.]

3 Segnali (interruzioni software)
  3.1 I segnali "tradizionali"
      3.1.1 $ man 7 signal oppure   $ kill -l
            elenca i ~31 tipi di segnali "tradizionali"
      3.1.2 Un segnale e' caratterizzato da 
            3.1.2.1 Numero (macro): esempio #2 (SIGINT)
            3.1.2.2 Azione di default: terminazione, ignorato, 
                    terminazione con generazione di file "core", 
                    stop, continue
      3.1.3 Alcuni segnali tradizionali comunemente usati
            3.1.3.1  SIGABRT: generato da abort(), termina con core dump
            3.1.3.2  SIGALRM: un timer real-time e' scaduto
            3.1.3.3  SIGBUS: particolare tipo di errore d'accesso alla memoria
            3.1.3.4  SIGCONT: continua un processo stoppato
            3.1.3.5  SIGINT: interruzione da tastiera (Ctrl-C)
            3.1.3.6  SIGKILL: uccisione certa del processo (non puo' essere 
                              ignorato, gestito o bloccato)
            3.1.3.7  SIGQUIT: quit da tastiera, termina con core dump
            3.1.3.8  SIGSEGV: errore d'accesso alla memoria
            3.1.3.9  SIGSTOP: stop certo del processo (non puo' essere
                              ignorato, gestito o bloccato)
            3.1.3.10 SIGTERM: segnale standard per uccidere un processo
            3.1.3.11 SIGTSTP: stop da tastiera
            3.1.3.12 SIGUSR1: a disposizione dell'utente
            3.1.3.13 SIGUSR2: a disposizione dell'utente

4 La chiamata di sistema signal()
  +------------------------------------------------------+
  |typedef void (*sighandler_t)(int);                    |
  |sighandler_t signal(int signum, sighandler_t handler);|
  +------------------------------------------------------+
  4.1 signum: il numero del segnale
  4.2 handler: l'indirizzo del gestore del segnale ovvero
               SIG_IGN (ignora) o SIG_DFL (azione di default)
  4.3 valore restituito: l'indirizzo del precedente gestore del segnale,
                              o SIG_ERR in caso di errore

5 La chiamata di sistema kill()
  +-----------------------------+
  |int kill(pid_t pid, int sig);|
  +-----------------------------+
  5.1 pid: identificatore del processo destinatario, se > 0
             ogni processo del gruppo del processo invocante, se == 0
             ogni processo del sistema, se == -1
             tutti i processi del gruppo -pid, se < -1
  5.2 sig: il segnale da inviare; se == 0, nessun segnale viene inviato ma la
             chiamata di sistema verifica comunque la possibilita' di inviare
             un segnale al/ai processo/i pid
  5.3 valore restituito: 0 o -1 in caso di errore
  5.4 In generale e' possibile inviare segnali solo a processi lanciati
        dallo stesso utente del processo che esegue kill()

6 Il comando (interno od esterno) kill
  6.1 Invia un segnale ad un processo da linea comando

7 Gestione del "segmentation fault"
  +------------------------+
  |#include <stdio.h>      |
  |#include <stdlib.h>     |
  |int main(void)          |
  |{                       |
  |    char *p = NULL;     |
  |    *p = 1;             |
  |    return EXIT_SUCCESS;|
  |}                       |
  +------------------------+
  $ ./segfault
  Segmentation fault
  7.1 Variante per ignorare il segnale SIGSEGV
      +------------------------------------------------+
      |#include <stdio.h>                              |
      |#include <stdlib.h>                             |
      |#include <signal.h>                             |
      |int main(void)                                  |
      |{                                               |
      |    char *p = NULL;                             |
      |    if (signal(SIGSEGV, SIG_IGN) == SIG_ERR) {  |
      |        fprintf(stderr, "Error in signal()\n"); |
      |        return EXIT_FAILURE;                    |
      |    }                                           |
      |    *p = 1;                                     |
      |    return EXIT_SUCCESS;                        |
      |}                                               |
      +------------------------------------------------+
      $ ./segfault
      Segmentation fault
      7.1.1 Non funziona perche' non si puo' ignorare direttamente un
            segnale generato dall'hardware
  7.2 Variante per gestire il segnale SIGSEGV
      +-------------------------------------------------------------+
      |[...]                                                        |
      |void sighandler(int sig)                                     |
      |{                                                            |
      |    fprintf(stderr, "Got signal %d\n", sig); /* UNSAFE!! */  |
      |}                                                            |
      |int main(void)                                               |
      |{                                                            |
      |    char *p = NULL;                                          |
      |    if (signal(SIGSEGV, sighandler) == SIG_ERR) {            |
      |        fprintf(stderr, "Error in signal()\n");              |
      |        return EXIT_FAILURE;                                 |
      |    }                                                        |
      |    *p = 1;                                                  |
      |[...]                                                        |
      +-------------------------------------------------------------+
      $ ./segfault 
      Got signal 11
      Got signal 11
      ...           (si ripete finche' non si interrompe con Ctrl-C)
      7.2.1 La gestione del segnale non ha risolto il problema dell'accesso
            invalido alla memoria: poiche' il page fault e' una "trap", dopo
            aver gestito il segnale l'istruzione stessa viene ripetuta

==========