Next Previous Contents

3. Rapido tour del Linux Kernel

3.1 Cos'e' il Kernel?

Il Kernel e' il cuore di ogni sistema operativo: e' quel software che permette agli utenti di condividere ed utilizzare al meglio le risorse del sistema di elaborazione.

Il Kernel puo' essere visto come il principale software del Sistema Operativo che potrebbe anche includere la gestione grafica: ad esempio sotto Linux (come nella maggiorparte dei sistemi Unix-like), l'ambiente XWindow non fa parte del Kernel di Linux perche' gestisce soltanto operazioni grafiche (grazie ad istruzioni I/O eseguite in User Mode e accesso diretto alla scheda video). Com'e' noto, invece, gli ambienti Windows (Win9x, WinME, WinNT, Win2K, WinXP, e cosi' via) sono un mix tra ambiente grafico e kernel.

3.2 Qual e' la differenza tra User Mode e Kernel Mode?

Introduzione

Molti anni fa, quando i computers erano grandi come stanze, gli utenti lanciavano le loro applicazioni con molta difficolta' e, a volte, il sistema di elaborazione andava in crash.

Modi operativi

Per evitare tale crash si e' pensato di introdurre 2 modi di funzionamento (ancora presenti nei nuovi microprocessori):

  1. Kernel Mode: in cui la macchina opera con risorse critiche, come l'hardware (IN/OUT o memory mapped), accesso diretto alla memoria, IRQ, DMA, e cosi' via.
  2. User Mode: in cui gli utenti possono far girare le loro applicazioni senza preoccuparsi di inchiodare il sistema.

                      
               |          Applicazioni           /|\
               |         ______________           |
               |         | User Mode  |           |  
               |         ______________           | 
               |               |                  |  
Dettaglio      |        _______ _______           |   Astrazione
Implementazione|        | Kernel Mode |           |
               |        _______________           |
               |               |                  |
               |               |                  | 
               |               |                  |
              \|/          Hardware               |

Kernel Mode impedisce alle applicazioni User Mode di danneggiare il sistema o le sue caratteristiche.

I moderni microprocessori implementano in hardware almeno 2 stati differenti. Ad esempio l'architettura Intel possiede 4 stati che determinano il PL (Privilege Level) permettendo quindi di avere gli stati 0,1,2,3 , con 0 usato in modo Kernel.

I sistemi Unix-like richiedono soltanto 2 livelli di privilegio e utilizzeremo questo come punto di riferimento.

3.3 Passaggio tra User Mode a Kernel Mode

Quando si salta?

Una volta capito che ci sono 2 modi operativi differenti, dobbiamo vedere quando avviene il ''salto'' tra uno e l'altro:

  1. Quando viene chiamata una System Call il task volontariamente inizia ad eseguire del codice nello stato Kernel.
  2. Quando arriva un IRQ (o eccezione) viene eseguito un gestore IRQ (o gestore eccezione), dopodiche' il controllo ritorna al task interrotto come se non fosse successo niente.

System Calls

Le System Calls sono come delle normali funzioni, soltanto che operano in Kernel Mode per eseguire operazioni sull'OS (in effetti le System Calls sono parte integrante dell'OS).

Una System Call puo' essere chiamata quando:

                                 |                |
                         ------->| System Call i  | (Accesso ai Devices)
|                |       |       |  [sys_read()]  |
| ...            |       |       |                |
| system_call(i) |--------       |                |
|   [read()]     |               |                |
| ...            |               |                |
| system_call(j) |--------       |                |  
|   [get_pid()]  |       |       |                |
| ...            |       ------->| System Call j  | (Accesso alle strutture dati del kernel)
|                |               |  [sys_getpid()]|
                                 |                | 
 
    USER MODE                        KERNEL MODE
 
  
                        Funzionamento System Calls Unix

Le System Calls teoricamente sono l'unica interfaccia disponibile in User Mode per accedere alle risorse hardware. In realta' esiste la SC ''ioperm'' che permette ad un Task di accedere direttamente ad un device (benche' senza IRQs).

NOTA: Non tutte le funzioni di libreria ''C'' sono delle system call, solo un piccolo sottoinsieme di esse.

Segue una lista delle System Calls presenti nel Linux Kernel 2.4.17, ricavabili dal file [ arch/i386/kernel/entry.S ]

        .long SYMBOL_NAME(sys_ni_syscall)       /* 0  -  old "setup()" system call*/
        .long SYMBOL_NAME(sys_exit)
        .long SYMBOL_NAME(sys_fork)
        .long SYMBOL_NAME(sys_read)
        .long SYMBOL_NAME(sys_write)
        .long SYMBOL_NAME(sys_open)             /* 5 */
        .long SYMBOL_NAME(sys_close)
        .long SYMBOL_NAME(sys_waitpid)
        .long SYMBOL_NAME(sys_creat)
        .long SYMBOL_NAME(sys_link)
        .long SYMBOL_NAME(sys_unlink)           /* 10 */
        .long SYMBOL_NAME(sys_execve)
        .long SYMBOL_NAME(sys_chdir)
        .long SYMBOL_NAME(sys_time)
        .long SYMBOL_NAME(sys_mknod)
        .long SYMBOL_NAME(sys_chmod)            /* 15 */
        .long SYMBOL_NAME(sys_lchown16)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old break syscall holder */
        .long SYMBOL_NAME(sys_stat)
        .long SYMBOL_NAME(sys_lseek)
        .long SYMBOL_NAME(sys_getpid)           /* 20 */
        .long SYMBOL_NAME(sys_mount)
        .long SYMBOL_NAME(sys_oldumount)
        .long SYMBOL_NAME(sys_setuid16)
        .long SYMBOL_NAME(sys_getuid16)
        .long SYMBOL_NAME(sys_stime)            /* 25 */
        .long SYMBOL_NAME(sys_ptrace)
        .long SYMBOL_NAME(sys_alarm)
        .long SYMBOL_NAME(sys_fstat)
        .long SYMBOL_NAME(sys_pause)
        .long SYMBOL_NAME(sys_utime)            /* 30 */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old stty syscall holder */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old gtty syscall holder */
        .long SYMBOL_NAME(sys_access)
        .long SYMBOL_NAME(sys_nice)
        .long SYMBOL_NAME(sys_ni_syscall)       /* 35 */                /* old ftime syscall holder */
        .long SYMBOL_NAME(sys_sync)
        .long SYMBOL_NAME(sys_kill)
        .long SYMBOL_NAME(sys_rename)
        .long SYMBOL_NAME(sys_mkdir)
        .long SYMBOL_NAME(sys_rmdir)            /* 40 */
        .long SYMBOL_NAME(sys_dup)
        .long SYMBOL_NAME(sys_pipe)
        .long SYMBOL_NAME(sys_times)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old prof syscall holder */
        .long SYMBOL_NAME(sys_brk)              /* 45 */
        .long SYMBOL_NAME(sys_setgid16)
        .long SYMBOL_NAME(sys_getgid16)
        .long SYMBOL_NAME(sys_signal)
        .long SYMBOL_NAME(sys_geteuid16)
        .long SYMBOL_NAME(sys_getegid16)        /* 50 */
        .long SYMBOL_NAME(sys_acct)
        .long SYMBOL_NAME(sys_umount)                                   /* recycled never used phys() */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old lock syscall holder */
        .long SYMBOL_NAME(sys_ioctl)
        .long SYMBOL_NAME(sys_fcntl)            /* 55 */
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old mpx syscall holder */
        .long SYMBOL_NAME(sys_setpgid)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old ulimit syscall holder */
        .long SYMBOL_NAME(sys_olduname)
        .long SYMBOL_NAME(sys_umask)            /* 60 */
        .long SYMBOL_NAME(sys_chroot)
        .long SYMBOL_NAME(sys_ustat)
        .long SYMBOL_NAME(sys_dup2)
        .long SYMBOL_NAME(sys_getppid)
        .long SYMBOL_NAME(sys_getpgrp)          /* 65 */
        .long SYMBOL_NAME(sys_setsid)
        .long SYMBOL_NAME(sys_sigaction)
        .long SYMBOL_NAME(sys_sgetmask)
        .long SYMBOL_NAME(sys_ssetmask)
        .long SYMBOL_NAME(sys_setreuid16)       /* 70 */
        .long SYMBOL_NAME(sys_setregid16)
        .long SYMBOL_NAME(sys_sigsuspend)
        .long SYMBOL_NAME(sys_sigpending)
        .long SYMBOL_NAME(sys_sethostname)
        .long SYMBOL_NAME(sys_setrlimit)        /* 75 */
        .long SYMBOL_NAME(sys_old_getrlimit)
        .long SYMBOL_NAME(sys_getrusage)
        .long SYMBOL_NAME(sys_gettimeofday)
        .long SYMBOL_NAME(sys_settimeofday)
        .long SYMBOL_NAME(sys_getgroups16)      /* 80 */
        .long SYMBOL_NAME(sys_setgroups16)
        .long SYMBOL_NAME(old_select)
        .long SYMBOL_NAME(sys_symlink)
        .long SYMBOL_NAME(sys_lstat)
        .long SYMBOL_NAME(sys_readlink)         /* 85 */
        .long SYMBOL_NAME(sys_uselib)
        .long SYMBOL_NAME(sys_swapon)
        .long SYMBOL_NAME(sys_reboot)
        .long SYMBOL_NAME(old_readdir)
        .long SYMBOL_NAME(old_mmap)             /* 90 */
        .long SYMBOL_NAME(sys_munmap)
        .long SYMBOL_NAME(sys_truncate)
        .long SYMBOL_NAME(sys_ftruncate)
        .long SYMBOL_NAME(sys_fchmod)
        .long SYMBOL_NAME(sys_fchown16)         /* 95 */
        .long SYMBOL_NAME(sys_getpriority)
        .long SYMBOL_NAME(sys_setpriority)
        .long SYMBOL_NAME(sys_ni_syscall)                               /* old profil syscall holder */
        .long SYMBOL_NAME(sys_statfs)
        .long SYMBOL_NAME(sys_fstatfs)          /* 100 */
        .long SYMBOL_NAME(sys_ioperm)
        .long SYMBOL_NAME(sys_socketcall)
        .long SYMBOL_NAME(sys_syslog)
        .long SYMBOL_NAME(sys_setitimer)
        .long SYMBOL_NAME(sys_getitimer)        /* 105 */
        .long SYMBOL_NAME(sys_newstat)
        .long SYMBOL_NAME(sys_newlstat)
        .long SYMBOL_NAME(sys_newfstat)
        .long SYMBOL_NAME(sys_uname)
        .long SYMBOL_NAME(sys_iopl)             /* 110 */
        .long SYMBOL_NAME(sys_vhangup)
        .long SYMBOL_NAME(sys_ni_syscall)       /* old "idle" system call */
        .long SYMBOL_NAME(sys_vm86old)
        .long SYMBOL_NAME(sys_wait4)
        .long SYMBOL_NAME(sys_swapoff)          /* 115 */
        .long SYMBOL_NAME(sys_sysinfo)
        .long SYMBOL_NAME(sys_ipc)
        .long SYMBOL_NAME(sys_fsync)
        .long SYMBOL_NAME(sys_sigreturn)
        .long SYMBOL_NAME(sys_clone)            /* 120 */
        .long SYMBOL_NAME(sys_setdomainname)
        .long SYMBOL_NAME(sys_newuname)
        .long SYMBOL_NAME(sys_modify_ldt)
        .long SYMBOL_NAME(sys_adjtimex)
        .long SYMBOL_NAME(sys_mprotect)         /* 125 */
        .long SYMBOL_NAME(sys_sigprocmask)
        .long SYMBOL_NAME(sys_create_module)
        .long SYMBOL_NAME(sys_init_module)
        .long SYMBOL_NAME(sys_delete_module)
        .long SYMBOL_NAME(sys_get_kernel_syms)  /* 130 */
        .long SYMBOL_NAME(sys_quotactl)
        .long SYMBOL_NAME(sys_getpgid)
        .long SYMBOL_NAME(sys_fchdir)
        .long SYMBOL_NAME(sys_bdflush)
        .long SYMBOL_NAME(sys_sysfs)            /* 135 */
        .long SYMBOL_NAME(sys_personality)
        .long SYMBOL_NAME(sys_ni_syscall)       /* for afs_syscall */
        .long SYMBOL_NAME(sys_setfsuid16)
        .long SYMBOL_NAME(sys_setfsgid16)
        .long SYMBOL_NAME(sys_llseek)           /* 140 */
        .long SYMBOL_NAME(sys_getdents)
        .long SYMBOL_NAME(sys_select)
        .long SYMBOL_NAME(sys_flock)
        .long SYMBOL_NAME(sys_msync)
        .long SYMBOL_NAME(sys_readv)            /* 145 */
        .long SYMBOL_NAME(sys_writev)
        .long SYMBOL_NAME(sys_getsid)
        .long SYMBOL_NAME(sys_fdatasync)
        .long SYMBOL_NAME(sys_sysctl)
        .long SYMBOL_NAME(sys_mlock)            /* 150 */
        .long SYMBOL_NAME(sys_munlock)
        .long SYMBOL_NAME(sys_mlockall)
        .long SYMBOL_NAME(sys_munlockall)
        .long SYMBOL_NAME(sys_sched_setparam)
        .long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
        .long SYMBOL_NAME(sys_sched_setscheduler)
        .long SYMBOL_NAME(sys_sched_getscheduler)
        .long SYMBOL_NAME(sys_sched_yield)
        .long SYMBOL_NAME(sys_sched_get_priority_max)
        .long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
        .long SYMBOL_NAME(sys_sched_rr_get_interval)
        .long SYMBOL_NAME(sys_nanosleep)
        .long SYMBOL_NAME(sys_mremap)
        .long SYMBOL_NAME(sys_setresuid16)
        .long SYMBOL_NAME(sys_getresuid16)      /* 165 */
        .long SYMBOL_NAME(sys_vm86)
        .long SYMBOL_NAME(sys_query_module)
        .long SYMBOL_NAME(sys_poll)
        .long SYMBOL_NAME(sys_nfsservctl)
        .long SYMBOL_NAME(sys_setresgid16)      /* 170 */
        .long SYMBOL_NAME(sys_getresgid16)
        .long SYMBOL_NAME(sys_prctl)
        .long SYMBOL_NAME(sys_rt_sigreturn)
        .long SYMBOL_NAME(sys_rt_sigaction)
        .long SYMBOL_NAME(sys_rt_sigprocmask)   /* 175 */
        .long SYMBOL_NAME(sys_rt_sigpending)
        .long SYMBOL_NAME(sys_rt_sigtimedwait)
        .long SYMBOL_NAME(sys_rt_sigqueueinfo)
        .long SYMBOL_NAME(sys_rt_sigsuspend)
        .long SYMBOL_NAME(sys_pread)            /* 180 */
        .long SYMBOL_NAME(sys_pwrite)
        .long SYMBOL_NAME(sys_chown16)
        .long SYMBOL_NAME(sys_getcwd)
        .long SYMBOL_NAME(sys_capget)
        .long SYMBOL_NAME(sys_capset)           /* 185 */
        .long SYMBOL_NAME(sys_sigaltstack)
        .long SYMBOL_NAME(sys_sendfile)
        .long SYMBOL_NAME(sys_ni_syscall)               /* streams1 */
        .long SYMBOL_NAME(sys_ni_syscall)               /* streams2 */
        .long SYMBOL_NAME(sys_vfork)            /* 190 */
        .long SYMBOL_NAME(sys_getrlimit)
        .long SYMBOL_NAME(sys_mmap2)
        .long SYMBOL_NAME(sys_truncate64)
        .long SYMBOL_NAME(sys_ftruncate64)
        .long SYMBOL_NAME(sys_stat64)           /* 195 */
        .long SYMBOL_NAME(sys_lstat64)
        .long SYMBOL_NAME(sys_fstat64)
        .long SYMBOL_NAME(sys_lchown)
        .long SYMBOL_NAME(sys_getuid)
        .long SYMBOL_NAME(sys_getgid)           /* 200 */
        .long SYMBOL_NAME(sys_geteuid)
        .long SYMBOL_NAME(sys_getegid)
        .long SYMBOL_NAME(sys_setreuid)
        .long SYMBOL_NAME(sys_setregid)
        .long SYMBOL_NAME(sys_getgroups)        /* 205 */
        .long SYMBOL_NAME(sys_setgroups)
        .long SYMBOL_NAME(sys_fchown)
        .long SYMBOL_NAME(sys_setresuid)
        .long SYMBOL_NAME(sys_getresuid)
        .long SYMBOL_NAME(sys_setresgid)        /* 210 */
        .long SYMBOL_NAME(sys_getresgid)
        .long SYMBOL_NAME(sys_chown)
        .long SYMBOL_NAME(sys_setuid)
        .long SYMBOL_NAME(sys_setgid)
        .long SYMBOL_NAME(sys_setfsuid)         /* 215 */
        .long SYMBOL_NAME(sys_setfsgid)
        .long SYMBOL_NAME(sys_pivot_root)
        .long SYMBOL_NAME(sys_mincore)
        .long SYMBOL_NAME(sys_madvise)
        .long SYMBOL_NAME(sys_getdents64)       /* 220 */
        .long SYMBOL_NAME(sys_fcntl64)
        .long SYMBOL_NAME(sys_ni_syscall)       /* reserved for TUX */
        .long SYMBOL_NAME(sys_ni_syscall)       /* Reserved for Security */
        .long SYMBOL_NAME(sys_gettid)
        .long SYMBOL_NAME(sys_readahead)        /* 225 */


Eventi IRQ

Quando arriva un IRQ, il task in esecuzione viene interrotto per far eseguire un gestore IRQ.

Dopo l'esecuzione di tale gestore il controllo ritorna tranquillamente al processo che non si accorge di nulla.


           
          Task in esecuzione
             |-----------|          (3)
ESECUZIONE   |   |       |  [stop esecuzione] IRQ Handler
NORMALE  (1) |   |       |     ------------->|---------| 
             |  \|/      |     |             |gestisce |         
 IRQ (2)---->| ..        |----->             | alcuni  |      
             |   |       |<-----             |  dati   |       
RITORNO  (6) |   |       |     |             |  ..(4). |
ALLA NORMALE |  \|/      |     <-------------|_________|
ESECUZIONE   |___________|  [ritorno al codice]
                                    (5)
               USER MODE                     KERNEL MODE

         Transizione User->Kernel Mode causata da evento IRQ
     

I seguenti passi si riferiscono al diagramma precedente:

  1. Processo in esecuzione
  2. Arriva un IRQ mentre il task sta' ''girando''
  3. Il Task viene interrotto per chiamare un gestore Interrupt.
  4. Il gestore Interrupt viene eseguito.
  5. Il controllo ritorna al Task interrotto
  6. Il Task torna ad essere eseguito dove era stato interrotto.

Un IRQ di particolare interesse e' quello relativo al Timer che arriva ogni tot millisecondi per gestire

  1. Allarmi
  2. Contatori di Task e di Sistema (usati dallo scheduler per decidere quando un processo deve venir interrotto o per l'accounting)
  3. Multitasking basato sul meccanismo di wake up dopo un periodo lungo TIMESLICE.

3.4 Multitasking

Funzionamento

La punto chiave di ogni moderno sistema operativo e' il ''Task''. Il Task e' un'applicazione che ''gira'' in memoria e codivide tutte le risorse del sistema (inclusi CPU e Memoria) con gli altri Tasks.

Questa ''condivisione di risorse'' viene gestita dal meccanismo di MultiTasking. Ogni tot (''timeslice'') millisecondi avviene il cambiamento di contesto (Task Switching'') grazie al quale gli utenti hanno l'illusione di possedere tutte le risorse; se consideriamo un solo utente si avra' invece l'illusione di poter eseguire piu' Tasks nello stesso istante.

Per implementare il Multitasking i Task utilizzano una variabile ''state'' che puo' assumere i valori:

  1. READY, pronto per l'esecuzione
  2. BLOCKED, in attesa di una risorsa

Lo stato del Task viene gestito dalla presenza o meno dello stesso nella lista relativa:

Cambiamento di contesto (Task Switching)

La commutazione da un Task ad un altro si chiama ''Task Switching''. Molti elaboratori hanno una istruzione hardware che esegue automaticamente questa operazione. Il Task Switching avviene nei seguenti casi:

  1. Dopo che il tempo ''Timeslice' si e' esaurito
  2. Quando un Task deve rimanere in attesa di un device *

In entrambi i casi abbiamo bisogno di schedulare un nuovo processo pronto per l'esecuzione (dalla ''Ready List'') ed eseguirlo.

* Questo serve ad evitare la ''Busy Form Waiting'', cioe' l'esecuzione inutile di un loop del processo in attesa della risorsa.

Il Task Switching viene gestito dall'entita' ''Schedule''.

 
Timer    |           |
 IRQ     |           |                            Schedule
  |      |           |                     ________________________
  |----->|   Task 1  |<------------------>|(1)Scegli un nuovo Task |
  |      |           |                    |(2)Task Switching       |
  |      |___________|                    |________________________|   
  |      |           |                               /|\
  |      |           |                                | 
  |      |           |                                |
  |      |           |                                |
  |      |           |                                |      
  |----->|   Task 2  |<-------------------------------|
  |      |           |                                |
  |      |___________|                                |
  .      .     .     .                                .
  .      .     .     .                                .
  .      .     .     .                                .
  |      |           |                                |
  |      |           |                                |
  ------>|   Task N  |<--------------------------------
         |           |
         |___________| 
    
            Task Switching basato sul TimeSlice
 

Un tipico Timeslice per Linux e' di circa 10 ms (in alcune architetture 1 ms).


 

 |           |            
 |           | Accesso     _____________________________
 |   Task 1  |----------->|(1) Accoda richiesta risorsa |
 |           | Risorsa    |(2) Marca Task come bloccato |
 |           |            |(3) Scegli un Task Pronto    |
 |___________|            |(4)    Task Switching        |
                          |_____________________________|
                                       |
                                       |
 |           |                         |
 |           |                         |
 |   Task 2  |<-------------------------
 |           |  
 |           |
 |___________|
 
     Task Switching basato sull'attesa di una risorsa
 

3.5 Microkernel vs Monolitico OS

Indtroduzione

Fino adesso abbiamo visto i cosiddetti OS Monolitici, ma esiste anche un'altra famiglia di OS, quella basata sui ''Microkernel''.

Un OS basato su Microkernel utilizza i Tasks, non solo per i processi in User Mode, ma anche come gestori del Kernel, come il Floppy-Task, l'HDD-Task, il Net-Task e cosi' via. Alcuni esempi sono Amoeba e Mach.

PRO e CONTRO degli OS basati su Microkernel

PRO:

CONTRO:

La mia opinione personale e' che gli OS Microkernel sono un buon esempio didattico (come Minix) ma non sono ''ottimali'' (cioe' partono gia' male come prestazioni) quindi non sono da considerarsi come buoni OS.

Linux utilizza alcuni Tasks, chiamati "Kernel Threads" per implementare un mini struttura microkernel, laddove e' evidente che le il tempo di accesso di un Task Switching sia notevolmente trascurabile (come ''kswapd'', che serve per recuperare pagine dalla memoria di massa).

3.6 Rete

Livelli ISO OSI

Lo Standard ISO-OSI descrive un'architettura di rete con i seguenti livelli:

  1. Livello Fisico (esempi: PPP ed Ethernet)
  2. Livello Data-link (esempi: PPP ed Ethernet)
  3. Livello di Rete (esempi: IP, e X.25)
  4. Livello di Transporto (esempi: TCP, UDP)
  5. Livello di Sessione (esempio: SSL)
  6. Livello di Presentazione (esempio: codifica binary-ascii sul protocollo FTP)
  7. Livello Applicazione (esempio: Netscape)

I primi 2 livelli sono di solito implementati in hardware mentre i livelli successivi in software (o in firmware per i routers).

Un OS e0 capace di gestire molti protocolli: uno di questi e' il TCP/IP (il piu' importante sui livelli 3-4).

Che cosa fa il kernel?

Il Kernel non conosce nulla dei primi 2 livelli

In RX l'OS:

  1. Gestisce il dialogo a basso livello con i devices (come schede ethernet o modem) ricevendo i pacchetti dall'hardware,
  2. Costruisce ''pacchetti'' TCP/IP partendo da ''frames" (come Ethernet o PPP),
  3. Converte i ''pacchetti in ''sockets'' passandoli al giusto applicativo (grazie al numero di porta) oppure
  4. Instrada i ''pacchetti'' nella giusta coda

frames         pacchetti            sockets
NIC ---------> Kernel ----------> Applicazione
                  |    pacchetti 
                  --------------> Instradamento
                        - RX - 

Nello stadio di TX l'OS:

  1. Converte i ''sockets'' oppure
  2. I dati accodati in ''pacchetti'' TCP/IP
  3. Espande i ''pacchetti" in ''frames'' (come Ethernet o PPP)
  4. Manda i ''frames'' utilizzando i devices Hardware

sockets       packets                     frames
Application ---------> Kernel ----------> NIC
              packets     /|\    
Forward  -------------------
                        - TX -  


3.7 Memoria Virtuale

Segmentazione

La Segmentazione e' il primo metodo per risolvere i problemi di allocazione di memoria: questa tecnica permette di compilare il codice sorgente senza preoccuparsi di dove i dati verranno eseguiti o memorizzati. In effetti questa feature e' di grande aiuto nello sviluppare di applicazioni in modo indipendente dall'OS e dall'Hardware.

     
            |       Stack          |
            |          |           |
            |         \|/          |
            |    Spazio Libero     | 
            |         /|\          |     Segmento <---> Processo
            |          |           |
            |        Heap          |
            |Dati non inizializzati|
            |  Dati inizializzati  |
            |       Codice         |
            |______________________|  
 
                   Segmento

Possiamo dire che un segmento e' la trasposizione logica di un'applicazione in memoria, o anche l'immagine dell'applicazione.

Quando si programmiamo non ci interessiamo di dove esattamente i dati vadano a finire in memoria, ci preoccupiamo soltanto dell'offset all'interno del segmento (quindi dell'applicazione).

Siamo soliti attribuire quindi, un Segment ad ogni Processo e viceversa. In Linux questo non e' vero fino in fondo, in quanto vengono usati soltanto 4 segmenti per il Kernel e tutti i Processi.

Problemi della Segmentazione

 
                                 ____________________
                          ----->|                    |----->
                          | IN  |     Segmento A     | OUT
 ____________________     |     |____________________|   
|                    |____|     |                    |   
|     Segmento B     |          |     Segmento B     |
|                    |____      |                    |   
|____________________|    |     |____________________|   
                          |     |     Segmento C     |   
                          |     |____________________|
                          ----->|     Segmento D     |-----> 
                            IN  |____________________| OUT 
 
                     Problema della Segmentazione


Nel diagramma vogliamo disallocare i processi A e D ad allocare il processo B.

Come si puo' vedere ci sarebbe abbastanza spazio per B, ma in pratica non possiamo splittarlo in 2 pezzi e quindi NON POSSIAMO caricarlo (manca memoria!).

La ragione di fondo di cio' e che i segmenti puri sono aree contigue proprio perche' sono aree logiche (di processo) e quindi non possono essere divise.

Paginazione

 
             ____________________
            |     Pagina 1       |
            |____________________|
            |     Pagina 2       |
            |____________________| 
            |      ..            |     Segmento <---> Processo    
            |____________________|
            |     Pagina n       |
            |____________________|
            |                    |
            |____________________|
            |                    |
            |____________________|  
 
                   Segmento  
 

La Paginazione divide la memoria in "N" pezzi, tutti a lunghezza fissa.

Un processo puo' essere caricato in una o piu' pagine. Quando un processo viene disallocato, tutte le sue pagine vengono liberate (vedi Problema della Segmentazione, prima).

La Paginazione viene sfruttata anche per un altro importante scopo: lo "Swapping": se una pagina infatti non e' presente in memoria fisica viene generata un'ECCEZIONE , che fara' cercare al Kernel una pagina in memoria di massa. Questo meccanismo permette all'OS di caricare piu' applicazioni rispetto a quelle realmente caricabili soltanto in memoria fisica.

Problema della Paginazione

             ____________________
   Pagina X |     Processo Y     |
            |____________________|
            |                    |
            |       SPAZIO       |
            |    INUTILIZZATO    |
            |____________________|  
   
          Problema della Paginazione
 

Nel diagramma possiamo notare qual e' il problema della Paginazione: quando un Processo Y viene caricato nella Pagina X, TUTTO la spazio di memoria viene allocato, cosicche' il rimanente spazio in fondo alla pagina rimanga inutilizzato.

Segmentazione Paginata

Come possiamo risolvere i problemi di segmentazione e paginazione? Utilizzando entrambe le politiche.

 
                                  |      ..            |
                                  |____________________|
                            ----->|      Pagina 1      |
                            |     |____________________|
                            |     |      ..            |
 ____________________       |     |____________________|
|                    |      |---->|      Pagina 2      |
|     Segmento X     |  ----|     |____________________|
|                    |      |     |       ..           |
|____________________|      |     |____________________|
                            |     |       ..           |
                            |     |____________________|
                            |---->|      Pagina 3      |
                                  |____________________|
                                  |       ..           |
 

Il Processo X, identificato dal Segmento X, viene diviso in 3 pezzi ognino poi caricato in una pagina.

Non abbiamo:

  1. Problemi di Segmentazione perche' allochiamo per Pagine, quindi liberiamo anche per Pagine e gestiamo lo spazio libero in maniera ottimale.
  2. Problemi di Paginazione: soltanto l'ultima pagina lascia dello spazio inutilizzato, ma possiamo decidere di utilizzare delle pagine molto piccole, ad esempio lunghe 4096 bytes (perdendo cosi' al massimo 4096*N_Tasks bytes) e attuando una allocazione di tipo gerarchico (con 2 o 3 levelli di paginazione)

 
 

                          |         |           |         |
                          |         |   Offset2 | Valore  |
                          |         |        /|\|         |
                  Offset1 |         |-----    | |         |
                      /|\ |         |    |    | |         |
                       |  |         |    |   \|/|         | 
                       |  |         |    ------>|         |
                      \|/ |         |           |         |
 Indirizzo Base --------->|         |           |         |
 Paginazione              | ....... |           | ....... |
                          |         |           |         |    
 
                      Paginazione Gerarchica

Next Previous Contents