Sophie

Sophie

distrib > Mandriva > 2010.0 > i586 > media > contrib-release > by-pkgid > 6f31691e252df0379b2dc094814df461 > files > 84

howto-sgml-it-2006-6mdv2010.0.noarch.rpm

<!doctype linuxdoc system>

<article><!-- LyX 1.2 created this file. For more info see http://www.lyx.org/ -->
<title>KernelAnalysis-HOWTO
</title><author>Roberto Arcomano 
</author><date>v0.65, 23 Agosto 2002
</date><abstract>Questo documento cerca di illustrare alcune caratteristiche del
 Kernel di Linux, i componenti principali, come lavorano e cosi' via.
 Questo HOWTO dovrebbe permettere al lettore di trovare subito la
 funzione del kernel che si vuole trovare senza dover conoscere a
 priori tutta la struttura dei sorgenti. Puoi trovare l'ultima versione
 di questo document su <url url="http://www.bertolinux.com" name="http://www.bertolinux.com"> Se hai suggerimenti per questo documento,
 manda un'email all'indirizzo <url url="mailto:berto@bertolinux.com" name="berto@bertolinux.com">
</abstract><sect>Introduzione
<sect1>Introduzione
<p>Questo HOWTO cerca di descrivere come le componenti del Kernel
 di Linux funzionano, quali sono le funzioni principali e le strutture
 dati utilizzate e come ''gira il tutto''. Puoi trovare l'ultima versione
 di questo documento su <url url="http://www.bertolinux.com" name="http://www.bertolinux.com"> Se hai dei suggerimento per migliorare questo
 documento, manda un'email all'indirizzo: <url url="mailto:berto@bertolinux.com" name="berto@bertolinux.com">. Il codice descritto nel
 documento si riferisce al Kernel Linux versione 2.4.x, l'ultima versione
 stabile al momento della compilazione dell'HOWTO.
</p><sect1>Copyright
<p>Copyright (C) 2000,2001,2002 Roberto Arcomano. This document
 is free; you can redistribute it and/or modify it under the terms
 of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option)
 any later version. This document is distributed in the hope that
 it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU General Public License for more details. You can get
 a copy of the GNU GPL <url url="http://www.gnu.org/copyleft/gpl.html" name="here">
</p><sect1>Traduzioni
<p>Sei libero di tradurre questo documento, devi soltanto rispettare
 2 regole: 
</p><p><enum><item>Controllare che non esista gia' un'altra versione nel tuo LDP
 locale 
<item>Mantenere tutta la sezione 'Introduzione' (inclusi 'Introduzione',
 'Copyright', 'Traduzioni', 'Ringraziamenti').
</enum></p><p>Attenzione! Non bisogna tradurre il file TXT o HTML, bensi' il
 file LYX, cosicche' sia possibile convertirlo negli altri formati
 (TXT, HTML, RIFF, ecc.): puoi utilizzare l'applicativo LyX scaricabile
 da <url url="http://www.lyx.org" name="http://www.lyx.org">.
</p><p>Non c'e' bisogno di chiedermi l'autorizzazione! E' sufficiente
 comunicarmelo.
</p><p>Grazie per la tua traduzione!
</p><sect1>Ringraziamenti
<p>Ringraziamenti a <url url="http://www.fatamorgana.com" name="Fatamorgana Computers"> per i mezzi hardware offerti e la possibilita'
 di sperimentazione.
</p><sect>Sintassi utilizzata
<sect1>Sintassi funzioni
<p>Quando descriviamo una funzione, scriviamo: 
</p><p><verb>&quot;nome_funzione  &lsqb; percorso . estensione &rsqb;&quot;
</verb></p><p>Ad esempio:
</p><p><verb>&quot;schedule &lsqb;kernel/sched.c&rsqb;&quot; 
</verb></p><p>ci dice che stiamo parlando della funzione
</p><p>&quot;schedule&quot; definita nel file
</p><p>&lsqb; kernel/sched.c &rsqb;
</p><p>Nota: Il percorso di riferimento e' la directory dei sorgenti
 del Kernel ''/usr/src/linux''.
</p><sect1>Indentazione
<p>L'indendazione nel codice sorgente del documento e' di 3 caratteri.
</p><sect1>InterCallings Analysis
<sect2>Introduzione
<p>Nel documento verranno utilizzate le "InterCallings Analysis "(ICA)
 (analisi delle reciproche chiamate) per vedere (utilizzando anche
 l'indentazione) come le funzioni del Kernel si chiamano l'un l'altra.
</p><p>Ad esempio, la funzione ''sleep_on'' e' descritto di seguito:
</p><p><verb>|sleep_on
|init_waitqueue_entry      --
|__add_wait_queue            |   Accodamento della richiesta
   |list_add                 |
      |__list_add          -- 
   |schedule              ---     In attesa di esecuzione
      |__remove_wait_queue --   
      |list_del              |   Disaccodamento richiesta
         |__list_del       -- 
 
                          sleep_on ICA

</verb></p><p>L'ICA indentata e' seguita dal percorso delle funzioni descritte:
</p><p><itemize><item>sleep_on &lsqb;kernel/sched.c&rsqb;
<item>init_waitqueue_entry &lsqb;include/linux/wait.h&rsqb;
<item>__add_wait_queue
<item>list_add &lsqb;include/linux/list.h&rsqb;
<item>__list_add
<item>schedule &lsqb;kernel/sched.c&rsqb;
<item>__remove_wait_queue &lsqb;include/linux/wait.h&rsqb;
<item>list_del &lsqb;include/linux/list.h&rsqb;
<item>__list_del
</itemize></p><p>Nota: Se il percorso non cambia non verra' piu' specificato nelle
 righe seguenti.
</p><sect2>Dettagli
<p>In una ICA una linea come la seguente
</p><p><verb> funzione1 -&gt; funzione2
</verb></p><p>significa che la &lt; funzione1 &gt; e' un generico puntatore
 a funzione che, in questo caso, punta a &lt; function2 &gt;.
</p><p>Quando scriviamo:
</p><p><verb>  funzione:
</verb></p><p>significa che &lt; funzione &gt; non e' una vera funzione, bensi'
 un'etichetta assembler.
</p><p>A volte verra' riportato del codice ''C'' o pseudo codice al
 posto di linee ''assembler'' o di codice '' non strutturato'' per
 poter chiarire l'esposizione.
</p><sect2>PRO delle ICA
<p>I vantaggi nell'utilizzo delle ICA (InterCallings Analysis) sono
 molti:
</p><p><itemize><item>Si ottiene una visione d'insieme del funzionamento di una chiamata
 e dell'avvicendarsi delle routine del Kernel.
<item>Il percorso delle funzioni viene riportato di seguito cosicche'
 le ICA possano rappresentare una sorta di ''riferimento'' per scovare
 subito le funzioni cercate.
<item>Possono essere molto utili in meccanismi di sleep/awake, dove
 si descrive quello che viene fatto prima della sleep, durante la
 stessa e dopo il risveglio (dopo la schedulazione).
</itemize></p><sect2>CONTRO delle ICA
<p><itemize><item>Alcuni svantaggi nell'utilizzare le ICA sono riportati di seguito:
</itemize></p><p>Come tutti i modelli teorici, viene semplificata la realta' sorvolando
 alcuni dettagli, come il vero codice sorgente e i casi eccezionali.
</p><p><itemize><item>Diagrammi addizionali sarebbero importanti per rappresentare
 le condizioni dello stack, i dati, etc.
</itemize></p><sect>Rapido tour del Linux Kernel
<sect1>Cos'e' il Kernel?
<p>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.
</p><p>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.
</p><sect1>Qual e' la differenza tra User Mode e Kernel Mode?
<sect2>Introduzione
<p>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.
</p><sect2>Modi operativi
<p>Per evitare tale crash si e' pensato di introdurre 2 modi di
 funzionamento (ancora presenti nei nuovi microprocessori):
</p><p><enum><item>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.
<item>User Mode: in cui gli utenti possono far girare le loro applicazioni
 senza preoccuparsi di inchiodare il sistema.
</enum><p><verb>                      
               |          Applicazioni           /|&bsol;
               |         ______________           |
               |         | User Mode  |           |  
               |         ______________           | 
               |               |                  |  
Dettaglio      |        _______ _______           |   Astrazione
Implementazione|        | Kernel Mode |           |
               |        _______________           |
               |               |                  |
               |               |                  | 
               |               |                  |
              &bsol;|/          Hardware               |
</verb></p><p>Kernel Mode impedisce alle applicazioni User Mode di danneggiare
 il sistema o le sue caratteristiche.
</p><p>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.
</p><p>I sistemi Unix-like richiedono soltanto 2 livelli di privilegio
 e utilizzeremo questo come punto di riferimento.
</p><sect1>Passaggio tra User Mode a Kernel Mode
<sect2>Quando si salta?
<p>Una volta capito che ci sono 2 modi operativi differenti, dobbiamo
 vedere quando avviene il ''salto'' tra uno e l'altro:
</p><p><enum><item>Quando viene chiamata una System Call il task volontariamente
 inizia ad eseguire del codice nello stato Kernel.
<item>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.
</enum></p><sect2>System Calls
<p>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).
</p><p>Una System Call puo' essere chiamata quando:
</p><p><itemize><item>si deve accedere ad un device di I/O device o ad un file (come
 le SC read e write )
<item>e' richiesto un elevato livello di privilegio per accedere ad
 alcune informazioni riservate (come il pid, o per cambiare la politica
 di scheduling e cosi' via)
<item>e' richiesto un cambiamento di contesto esecutivo (come eseguire
 la ''fork'' o eseguire un'altra applicazione con la SC ''exec'').
<item>si deve eseguire un particolare tipo di comando (come ''chdir'',
 ''kill", ''brk'', o ''signal'')
</itemize><p><verb>                                 |                |
                         -------&gt;| System Call i  | (Accesso ai Devices)
|                |       |       |  &lsqb;sys_read()&rsqb;  |
| ...            |       |       |                |
| system_call(i) |--------       |                |
|   &lsqb;read()&rsqb;     |               |                |
| ...            |               |                |
| system_call(j) |--------       |                |  
|   &lsqb;get_pid()&rsqb;  |       |       |                |
| ...            |       -------&gt;| System Call j  | (Accesso alle strutture dati del kernel)
|                |               |  &lsqb;sys_getpid()&rsqb;|
                                 |                | 
 
    USER MODE                        KERNEL MODE
 
  
                        Funzionamento System Calls Unix
</verb></p><p>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).
</p><p>NOTA: Non tutte le funzioni di libreria ''C'' sono delle system
 call, solo un piccolo sottoinsieme di esse.
</p><p>Segue una lista delle System Calls presenti nel Linux Kernel
 2.4.17, ricavabili dal file &lsqb; arch/i386/kernel/entry.S &rsqb;
</p><p><verb>        .long SYMBOL_NAME(sys_ni_syscall)       /* 0  -  old &quot;setup()&quot; 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 &quot;idle&quot; 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 */


</verb></p><sect2>Eventi IRQ
<p>Quando arriva un IRQ, il task in esecuzione viene interrotto
 per far eseguire un gestore IRQ.
</p><p>Dopo l'esecuzione di tale gestore il controllo ritorna tranquillamente
 al processo che non si accorge di nulla.
</p><p><verb>
           
          Task in esecuzione
             |-----------|          (3)
ESECUZIONE   |   |       |  &lsqb;stop esecuzione&rsqb; IRQ Handler
NORMALE  (1) |   |       |     -------------&gt;|---------| 
             |  &bsol;|/      |     |             |gestisce |         
 IRQ (2)----&gt;| ..        |-----&gt;             | alcuni  |      
             |   |       |&lt;-----             |  dati   |       
RITORNO  (6) |   |       |     |             |  ..(4). |
ALLA NORMALE |  &bsol;|/      |     &lt;-------------|_________|
ESECUZIONE   |___________|  &lsqb;ritorno al codice&rsqb;
                                    (5)
               USER MODE                     KERNEL MODE

         Transizione User-&gt;Kernel Mode causata da evento IRQ
     
</verb></p><p>I seguenti passi si riferiscono al diagramma precedente:
</p><p><enum><item>Processo in esecuzione
<item>Arriva un IRQ mentre il task sta' ''girando''
<item>Il Task viene interrotto per chiamare un gestore Interrupt.
<item>Il gestore Interrupt viene eseguito.
<item>Il controllo ritorna al Task interrotto
<item>Il Task torna ad essere eseguito dove era stato interrotto.
</enum></p><p>Un IRQ di particolare interesse e' quello relativo al Timer che
 arriva ogni tot millisecondi per gestire
</p><p><enum><item>Allarmi
<item>Contatori di Task e di Sistema (usati dallo scheduler per decidere
 quando un processo deve venir interrotto o per l'accounting)
<item>Multitasking basato sul meccanismo di wake up dopo un periodo
 lungo TIMESLICE.
</enum></p><sect1>Multitasking
<sect2>Funzionamento
<p>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.
</p><p>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.
</p><p>Per implementare il Multitasking i Task utilizzano una variabile
 ''state'' che puo' assumere i valori:
</p><p><enum><item>READY, pronto per l'esecuzione
<item>BLOCKED, in attesa di una risorsa
</enum></p><p>Lo stato del Task viene gestito dalla presenza o meno dello stesso
 nella lista relativa: 
</p><p><itemize><item>lista READY
<item>lista BLOCKED
</itemize></p><sect2>Cambiamento di contesto (Task Switching)
<p>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:
</p><p><enum><item>Dopo che il tempo ''Timeslice' si e' esaurito
<item>Quando un Task deve rimanere in attesa di un device *
</enum></p><p>In entrambi i casi abbiamo bisogno di schedulare un nuovo processo
 pronto per l'esecuzione (dalla ''Ready List'') ed eseguirlo.
</p><p>* Questo serve ad evitare la ''Busy Form Waiting'', cioe' l'esecuzione
 inutile di un loop del processo in attesa della risorsa.
</p><p>Il Task Switching viene gestito dall'entita' ''Schedule''.
</p><p><verb> 
Timer    |           |
 IRQ     |           |                            Schedule
  |      |           |                     ________________________
  |-----&gt;|   Task 1  |&lt;------------------&gt;|(1)Scegli un nuovo Task |
  |      |           |                    |(2)Task Switching       |
  |      |___________|                    |________________________|   
  |      |           |                               /|&bsol;
  |      |           |                                | 
  |      |           |                                |
  |      |           |                                |
  |      |           |                                |      
  |-----&gt;|   Task 2  |&lt;-------------------------------|
  |      |           |                                |
  |      |___________|                                |
  .      .     .     .                                .
  .      .     .     .                                .
  .      .     .     .                                .
  |      |           |                                |
  |      |           |                                |
  ------&gt;|   Task N  |&lt;--------------------------------
         |           |
         |___________| 
    
            Task Switching basato sul TimeSlice
 
</verb></p><p>Un tipico Timeslice per Linux e' di circa 10 ms (in alcune architetture
 1 ms).
</p><p><verb>
 

 |           |            
 |           | Accesso     _____________________________
 |   Task 1  |-----------&gt;|(1) Accoda richiesta risorsa |
 |           | Risorsa    |(2) Marca Task come bloccato |
 |           |            |(3) Scegli un Task Pronto    |
 |___________|            |(4)    Task Switching        |
                          |_____________________________|
                                       |
                                       |
 |           |                         |
 |           |                         |
 |   Task 2  |&lt;-------------------------
 |           |  
 |           |
 |___________|
 
     Task Switching basato sull'attesa di una risorsa
 
</verb></p><sect1>Microkernel vs Monolitico OS
<sect2>Indtroduzione
<p>Fino adesso abbiamo visto i cosiddetti OS Monolitici, ma esiste
 anche un'altra famiglia di OS, quella basata sui ''Microkernel''.
</p><p>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. 
</p><sect2>PRO e CONTRO degli OS basati su Microkernel
<p>PRO:
</p><p><itemize><item>L'OS e' piu' semplice da gestire perche' il Task e' l'unita'
 base di tutti i devices: quindi per gestisce ad esempio la rete basta
 modificare il Net-Task (in teoria e se non e' necessaria una modifica
 strutturale).
</itemize></p><p>CONTRO:
</p><p><itemize><item>Le prestazioni sono peggiori rispetto agli OS Monolitici in quanto
 e' necessario un periodo di 2*TASK_SWITCHING in piu' per gestire
 i devices (il primo per entrare nel Task mentre il secondo per ritornare
 al processo interrotto): in effetti questo tempo e' assolutamente
 necessario nel caso di sistema Monolotico.
</itemize></p><p>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. 
</p><p>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).
</p><sect1>Rete
<sect2>Livelli ISO OSI
<p>Lo Standard ISO-OSI descrive un'architettura di rete con i seguenti
 livelli:
</p><p><enum><item>Livello Fisico (esempi: PPP ed Ethernet)
<item>Livello Data-link (esempi: PPP ed Ethernet)
<item>Livello di Rete (esempi: IP, e X.25)
<item>Livello di Transporto (esempi: TCP, UDP)
<item>Livello di Sessione (esempio: SSL)
<item>Livello di Presentazione (esempio: codifica binary-ascii sul
 protocollo FTP)
<item>Livello Applicazione (esempio: Netscape)
</enum></p><p>I primi 2 livelli sono di solito implementati in hardware mentre
 i livelli successivi in software (o in firmware per i routers).
</p><p>Un OS e0 capace di gestire molti protocolli: uno di questi e'
 il TCP/IP (il piu' importante sui livelli 3-4).
</p><sect2>Che cosa fa il kernel?
<p>Il Kernel non conosce nulla dei primi 2 livelli
</p><p>In RX l'OS:
</p><p><enum><item>Gestisce il dialogo a basso livello con i devices (come schede
 ethernet o modem) ricevendo i pacchetti dall'hardware,
<item>Costruisce ''pacchetti'' TCP/IP partendo da ''frames" (come Ethernet
 o PPP), 
<item>Converte i ''pacchetti in ''sockets'' passandoli al giusto applicativo
 (grazie al numero di porta) oppure
<item>Instrada i ''pacchetti'' nella giusta coda
</enum><p><verb>frames         pacchetti            sockets
NIC ---------&gt; Kernel ----------&gt; Applicazione
                  |    pacchetti 
                  --------------&gt; Instradamento
                        - RX - 
</verb></p><p>Nello stadio di TX l'OS:
</p><p><enum><item>Converte i ''sockets'' oppure 
<item>I dati accodati in ''pacchetti'' TCP/IP
<item>Espande i ''pacchetti" in ''frames'' (come Ethernet o PPP)
<item>Manda i ''frames'' utilizzando i devices Hardware
</enum><p><verb>sockets       packets                     frames
Application ---------&gt; Kernel ----------&gt; NIC
              packets     /|&bsol;    
Forward  -------------------
                        - TX -  


</verb></p><sect1>Memoria Virtuale
<sect2>Segmentazione
<p>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.
</p><p><verb>     
            |       Stack          |
            |          |           |
            |         &bsol;|/          |
            |    Spazio Libero     | 
            |         /|&bsol;          |     Segmento &lt;---&gt; Processo
            |          |           |
            |        Heap          |
            |Dati non inizializzati|
            |  Dati inizializzati  |
            |       Codice         |
            |______________________|  
 
                   Segmento

</verb></p><p>Possiamo dire che un segmento e' la trasposizione logica di un'applicazione
 in memoria, o anche l'immagine dell'applicazione.
</p><p>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).
</p><p>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.
</p><sect3>Problemi della Segmentazione
<p><verb> 
                                 ____________________
                          -----&gt;|                    |-----&gt;
                          | IN  |     Segmento A     | OUT
 ____________________     |     |____________________|   
|                    |____|     |                    |   
|     Segmento B     |          |     Segmento B     |
|                    |____      |                    |   
|____________________|    |     |____________________|   
                          |     |     Segmento C     |   
                          |     |____________________|
                          -----&gt;|     Segmento D     |-----&gt; 
                            IN  |____________________| OUT 
 
                     Problema della Segmentazione


</verb></p><p>Nel diagramma vogliamo disallocare i processi A e D ad allocare
 il processo B. 
</p><p>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!).
</p><p>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.
</p><sect2>Paginazione
<p><verb> 
             ____________________
            |     Pagina 1       |
            |____________________|
            |     Pagina 2       |
            |____________________| 
            |      ..            |     Segmento &lt;---&gt; Processo    
            |____________________|
            |     Pagina n       |
            |____________________|
            |                    |
            |____________________|
            |                    |
            |____________________|  
 
                   Segmento  
 
</verb></p><p>La Paginazione divide la memoria in &quot;N&quot; pezzi, tutti
 a lunghezza fissa.
</p><p>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).
</p><p>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.
</p><sect3>Problema della Paginazione
<p><verb>             ____________________
   Pagina X |     Processo Y     |
            |____________________|
            |                    |
            |       SPAZIO       |
            |    INUTILIZZATO    |
            |____________________|  
   
          Problema della Paginazione
 
</verb></p><p>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.
</p><sect2>Segmentazione Paginata
<p>Come possiamo risolvere i problemi di segmentazione e paginazione?
 Utilizzando entrambe le politiche.
</p><p><verb> 
                                  |      ..            |
                                  |____________________|
                            -----&gt;|      Pagina 1      |
                            |     |____________________|
                            |     |      ..            |
 ____________________       |     |____________________|
|                    |      |----&gt;|      Pagina 2      |
|     Segmento X     |  ----|     |____________________|
|                    |      |     |       ..           |
|____________________|      |     |____________________|
                            |     |       ..           |
                            |     |____________________|
                            |----&gt;|      Pagina 3      |
                                  |____________________|
                                  |       ..           |
 
</verb></p><p>Il Processo X, identificato dal Segmento X, viene diviso in 3
 pezzi ognino poi caricato in una pagina.
</p><p>Non abbiamo:
</p><p><enum><item>Problemi di Segmentazione perche' allochiamo per Pagine, quindi
 liberiamo anche per Pagine e gestiamo lo spazio libero in maniera
 ottimale.
<item>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)
</enum><p><verb> 
 

                          |         |           |         |
                          |         |   Offset2 | Valore  |
                          |         |        /|&bsol;|         |
                  Offset1 |         |-----    | |         |
                      /|&bsol; |         |    |    | |         |
                       |  |         |    |   &bsol;|/|         | 
                       |  |         |    ------&gt;|         |
                      &bsol;|/ |         |           |         |
 Indirizzo Base ---------&gt;|         |           |         |
 Paginazione              | ....... |           | ....... |
                          |         |           |         |    
 
                      Paginazione Gerarchica
</verb></p><sect>Linux Startup
<p>Il Kernel di Linux inizia dal codice C eseguito a partire dall'etichetta
 assember ''startup_32:'':
</p><p><verb>|startup_32:
   |start_kernel
      |lock_kernel
      |trap_init
      |init_IRQ
      |sched_init
      |softirq_init
      |time_init
      |console_init 
      |&num;ifdef CONFIG_MODULES 
         |init_modules 
      |&num;endif 
      |kmem_cache_init 
      |sti 
      |calibrate_delay 
      |mem_init
      |kmem_cache_sizes_init
      |pgtable_cache_init
      |fork_init
      |proc_caches_init 
      |vfs_caches_init
      |buffer_init
      |page_cache_init
      |signals_init 
      |&num;ifdef CONFIG_PROC_FS 
        |proc_root_init 
      |&num;endif 
      |&num;if defined(CONFIG_SYSVIPC) 
         |ipc_init
      |&num;endif 
      |check_bugs      
      |smp_init
      |rest_init
         |kernel_thread
         |unlock_kernel
         |cpu_idle
</verb><p><itemize><item>startup_32 &lsqb;arch/i386/kernel/head.S&rsqb;
<item>start_kernel &lsqb;init/main.c&rsqb;
<item>lock_kernel &lsqb;include/asm/smplock.h&rsqb;
<item>trap_init &lsqb;arch/i386/kernel/traps.c&rsqb;
<item>init_IRQ &lsqb;arch/i386/kernel/i8259.c&rsqb;
<item>sched_init &lsqb;kernel/sched.c&rsqb;
<item>softirq_init &lsqb;kernel/softirq.c&rsqb;
<item>time_init &lsqb;arch/i386/kernel/time.c&rsqb;
<item>console_init &lsqb;drivers/char/tty_io.c&rsqb;
<item>init_modules &lsqb;kernel/module.c&rsqb;
<item>kmem_cache_init &lsqb;mm/slab.c&rsqb;
<item>sti &lsqb;include/asm/system.h&rsqb;
<item>calibrate_delay &lsqb;init/main.c&rsqb;
<item>mem_init &lsqb;arch/i386/mm/init.c&rsqb;
<item>kmem_cache_sizes_init &lsqb;mm/slab.c&rsqb;
<item>pgtable_cache_init &lsqb;arch/i386/mm/init.c&rsqb;
<item>fork_init &lsqb;kernel/fork.c&rsqb;
<item>proc_caches_init 
<item>vfs_caches_init &lsqb;fs/dcache.c&rsqb;
<item>buffer_init &lsqb;fs/buffer.c&rsqb;
<item>page_cache_init &lsqb;mm/filemap.c&rsqb;
<item>signals_init &lsqb;kernel/signal.c&rsqb;
<item>proc_root_init &lsqb;fs/proc/root.c&rsqb;
<item>ipc_init &lsqb;ipc/util.c&rsqb;
<item>check_bugs &lsqb;include/asm/bugs.h&rsqb;
<item>smp_init &lsqb;init/main.c&rsqb;
<item>rest_init
<item>kernel_thread &lsqb;arch/i386/kernel/process.c&rsqb;
<item>unlock_kernel &lsqb;include/asm/smplock.h&rsqb;
<item>cpu_idle &lsqb;arch/i386/kernel/process.c&rsqb;
</itemize></p><p>L'ultima funzione ''rest_init'':
</p><p><enum><item>lancia il Kernel Thread ''init''
<item>chiama ''unlock_kernel''
<item>Esegue il loop infinito ''cpu_idle'', in attesa che altri processi
 con maggiore priorita' vengano schedulati.
</enum></p><p>In effetti la funzione ''start_kernel'' non finisce mai.
</p><p>Segue la descrizione di init che e' il primo kernel_thread in
 esecuzione (che lanciera' poi tutti gli altri)
</p><p><verb>|init
   |lock_kernel
   |do_basic_setup
      |mtrr_init
      |sysctl_init
      |pci_init
      |sock_init
      |start_context_thread
      |do_init_calls
         |(*call())-&gt; kswapd_init
   |prepare_namespace
   |free_initmem
   |unlock_kernel
   |execve
</verb></p><sect>Peculiarita' di Linux
<sect1>Introduzione
<p>Linux possiede alcune caratteristiche pressoche' uniche rispetto
 agli altri OSs. These peculiarities include:
</p><p><enum><item>Utilizzo della sola Paginazione
<item>Softirq
<item>Kernel Threads
<item>Kernel Modules
<item>Directory ''Proc'' 
</enum></p><sect2>Elementi di flessibilita'
<p>I punti 4 e 5 danno agli ammistratori un'enorme flessibilita'
 sulla configurazione del sistema in User Mode permettendo loro di
 risolvere anche problemi critici (come kernel bugs) senza dover riavviare
 la macchina
</p><p>Ad esempio se si vuol cambiare qualcosa nel Kernel di un grosso
 server non e' necessario riavviarlo, bastera' preparare la prima
 volta il kernel ad accogliere un modulo e, in seguito, creare il
 modulo per effettuare le modifiche volute.
</p><sect1>Solo Paginazione
<p>Linux non utilizza la Segmentazione per distinguere i Tasks l'uno
 dall'altro, usa soltanto la Paginazione (solo 2 segmenti vengono
 utilizzati in tutti i Tasks, CODE e DATA/STACK).
</p><p>Possiamo anche dire che una ''Page Fault'' tra Task non puo'
 mai avvenire (cioe' quell'evento scatenato quando un Task vuole scrivere
 sull'area di memoria di un altro Task), poiche' ogni Task utilizza
 un set di Page Tables (Tabelle di Paginazione) differenti per ogni
 Processo: queste tabelle non possono mai puntare allo stesso indirizzo
 fisico (per costruzione).
</p><sect2>Segmenti Linux
<p>Sotto Linux vengono utilizzati soltanto 4 segmenti: 
</p><p><enum><item>Kernel Code &lsqb;0x10&rsqb;
<item>Kernel Data / Stack &lsqb;0x18&rsqb;
<item>User Code &lsqb;0x23&rsqb;
<item>User Data / Stack &lsqb;0x2b&rsqb;
</enum></p><p>&lsqb;La sintassi e' ''Utilizzo &lsqb;Segmento&rsqb;''&rsqb;
</p><p>Nell'architettura Intel, i registri di segmenti usati sono:
</p><p><itemize><item>CS per il Code Segment
<item>DS per il Data Segment
<item>SS per lo Stack Segment
<item>ES per l'Alternative Segment (usato ad esempio quando si vuole
 effettuare una copia tra 2 segmenti differenti)
</itemize></p><p>Quindi ogni Task utilizza il segmenti 0x23 per il codice e il
 segmenti 0x2b per data/stack.
</p><sect2>Paginazione di Linux
<p>Linux utilizza uno schema con 3 levelli di paginazione, a seconda
 dell'architecture. 
</p><p>Intel permette di sfruttare solo 2 levelli. Per ottimizzare l'utilizzo
 della memoria viene anche utilizzato il meccanismo della ''Copy on
 Write'' (si veda il Cap.10 per ulteriori informazioni).
</p><sect2>Perche' esistono ''conflitti'' tra gli indirizzi relativi a Tasks
 diversi?
<p>La risposta e' molto molto semplice: i conflitti tra indirizzi
 sono impossibili.
</p><p>La mappatura tra indirizzi Lineari-&gt; Fisici viene gestita
 dalla Paginazione, quindi e' sufficiente assegnare le pagine fisiche
 in modo univoco.
</p><sect2>E' necessario deframmentare la memoria?
<p>No. L'assegnazione delle Pagine e' un processo dinamico: abbiamo
 bisogno di allocare una pagina soltanto quando un Task lo richiede
 e quindi possiamo sceglierlo tra le pagine di memoria libere in modo
 ordinato. Quando vogliamo rilasciare una pagina dobbiamo soltanto
 aggiungerla nella ''free page list''.
</p><sect2>E le pagine del Kernel?
<p>Lo spazio del Kernel ha una caratteristica: lo spazio Lineare
 coincide con quello Fisico (questo per semplificare la vita!). Questa
 peculiarita' comporta che sia necessario allocare una volta per tutte
 un blocco di memoria senza poi possibilita' alcuna di aggiungere
 un altro blocco in modo CONTIGUO (non lo si puo' garantire perche'
 vi possono essere altre pagine in User Mode in mezzo!).
</p><p>Tutto cio' non porta a grossi problemi per quanto riguarda il
 CODICE in quanto esso non modifica a Run-Time la propria dimensione
 (si intende la dimensione in maniera CONTIGUA), bastera' quindi allocare
 la prima volta uno spazio di memoria sufficiente e non si avranno
 problemi.
</p><p>Il problema vero si ha nel caso dello STACK, in quanto ogni processo
 (in Kernel Mode) utilizza 1 pagina di Kernel Stack: com'e' noto lo
 Stack e' una struttura dati che richiede di essere CONTIGUA, quindi,
 una volta stabilito il limite massimo per lo stack (come detto 1
 pagina) non lo si potra' piu' aumentare.
</p><p>Se si dovesse utilizzare lo Stack oltre lo spazio consentito
 vi sarebbe una scrittura sulle strutture dati relative al processo
 in questione (in Kernel Mode).
</p><p>Fortunatamente la struttura del Kernel ci aiuta, in quanto le
 funzioni del sistema:
</p><p><itemize><item>Non sono mai Ricorsive
<item>Non si chiamano mai piu' di N volte l'una con l'altra
</itemize></p><p>Una volta noto N, e una volta noto il massimo utilizzo di stack
 da parte delle funzioni del Kernel chiamate si potra' avere la giusta
 stima per lo Stack.
</p><p>Per verificare il problema e' sufficiente creare un modulo che
 abbiam una funzione ricorsiva che si richiami un certo numero di
 volte. Dopo un certo numero di volte il Kernel si blocchera' generando
 una eccezione di page fault.
</p><sect1>SoftIrq
<p>Quando arriva un IRQ, il ''Task Switching'' viene posticipato
 per non intaccare le prestazioni del sistema. 
</p><p>Alcuni lavori (che possono essere eseguiti tranquillamente anche
 dopo l'IRQ e che consumano molta CPU, come la costruzione di un pacchetto
 TCP/IP) vengono accodati per essere poi eseguiti durante lo scheduling
 (una volta cioe' che il TimeSlice dell'attuale processo e' terminato).
</p><p>Nei Kernels recenti (2.4.x) il meccanismo "SoftIrq'' viene gestito
 da un Kernel Thread: ''ksoftirqd_CPUn'', dove n sta' per il numero
 di CPU che sta eseguendo il tale Thread (in un sistema monoprocessore
 il nome e' ''ksoftirqd_CPU0'' avente PID 3).
</p><sect2>Preparazione dei SoftIrq
<sect2>Abilitazione dei SoftIrq
<p>''cpu_raise_softirq'' e' la routine che sveglia il Kernel Thread
 ''ksoftirqd_CPU0'' che gestira' poi il lavoro accodato:
</p><p><verb>|cpu_raise_softirq
   |__cpu_raise_softirq
   |wakeup_softirqd
      |wake_up_process
</verb><p><itemize><item>cpu_raise_softirq &lsqb;kernel/softirq.c&rsqb;
<item>__cpu_raise_softirq &lsqb;include/linux/interrupt.h&rsqb;
<item>wakeup_softirq &lsqb;kernel/softirq.c&rsqb;
<item>wake_up_process &lsqb;kernel/sched.c&rsqb;
</itemize></p><p>La routine ''__cpu_raise_softirq'' attiva il bit giusto relativo
 a lavoro in questione sul vettore della coda SoftIrq.
</p><p>''wakeup_softirq'' usa ''wakeup_process'' per svegliare il Kernel
 Thread ''ksoftirqd_CPU0''.
</p><sect2>Esecuzione dei Softirq
<p>DAFARE: descrivere la struttura dati del meccanismo SoftIrq.
</p><p>Quando ''ksoftirqd_CPU0'' si sveglia, andra' ad eseguire i lavori
 accodati.
</p><p>Il codice di ''ksoftirqd_CPU0'' (ciclo principale) e':
</p><p><verb>for (;;) &lcub;
   if (!softirq_pending(cpu)) 
      schedule();
      __set_current_state(TASK_RUNNING);
   while (softirq_pending(cpu)) &lcub; 
      do_softirq(); 
      if (current-&gt;need_resched) 
         schedule 
   &rcub;
   __set_current_state(TASK_INTERRUPTIBLE)
&rcub;
</verb><p><itemize><item>ksoftirqd &lsqb;kernel/softirq.c&rsqb;
</itemize></p><sect1>Kernel Threads
<p>Sebbene Linux sia un OS Monolitico, esistono alcuni ''Kernel
 Threads'' per eseguire del lavoro di manutenzione del sistema.
</p><p>Questi Tasks non utilizzano memoria UTENTE, ma condividono quella
 del KERNEL; operano al piu' alto privilegio (RING 0 su architettura
 386+) come ogni altro codice del Kernel.
</p><p>I Kernel Threads vengono creati dalla funzione ''kernel_thread
 &lsqb;arch/i386/kernel/process&rsqb;'', che va poi a chiamare la
 System Call ''clone'' &lsqb;arch/i386/kernel/process.c&rsqb; in assembler
 (che praticamente e' come la ''fork''):
</p><p><verb>int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
&lcub;
        long retval, d0;
 
        __asm__ __volatile__(
                &quot;movl &percnt;&percnt;esp,&percnt;&percnt;esi&bsol;n&bsol;t&quot;
                &quot;int &dollar;0x80&bsol;n&bsol;t&quot;         /* Linux/i386 system call */
                &quot;cmpl &percnt;&percnt;esp,&percnt;&percnt;esi&bsol;n&bsol;t&quot;  /* child or parent? */
                &quot;je 1f&bsol;n&bsol;t&quot;             /* parent - jump */
                /* Load the argument into eax, and push it.  That way, it does
                 * not matter whether the called function is compiled with
                 * -mregparm or not.  */
                &quot;movl &percnt;4,&percnt;&percnt;eax&bsol;n&bsol;t&quot;
                &quot;pushl &percnt;&percnt;eax&bsol;n&bsol;t&quot;               
                &quot;call *&percnt;5&bsol;n&bsol;t&quot;          /* call fn */
                &quot;movl &percnt;3,&percnt;0&bsol;n&bsol;t&quot;        /* exit */
                &quot;int &dollar;0x80&bsol;n&quot;
                &quot;1:&bsol;t&quot;
                :&quot;=&amp;a&quot; (retval), &quot;=&amp;S&quot; (d0)
                :&quot;0&quot; (__NR_clone), &quot;i&quot; (__NR_exit),
                 &quot;r&quot; (arg), &quot;r&quot; (fn),
                 &quot;b&quot; (flags | CLONE_VM)
                : &quot;memory&quot;);
        return retval;
&rcub;
</verb></p><p>Una volta chiamata, avremo un nuovo Task (di solito con PID molto
 basso, come 2,3, ecc.) all'interno di un ciclo infinito in attesa
 di una risorsa molto lenta, come lo swap o un evento usb (soltanto
 risorse lente altrimenti il tempo di Task Switching potrebbe influenzare
 negativamente le prestazioni del sistema)
</p><p>Segue una lista dei piu' comuni Kernel Threads (dal comando ''ps
 x''):
</p><p><verb>PID      COMMAND
 1        init
 2        keventd
 3        kswapd
 4        kreclaimd
 5        bdflush
 6        kupdated
 7        kacpid
67        khubd

</verb></p><p>'init' e' il primo processo creato, che chiamera' tutti gli altri
 Tasks in User Modes (dal file /etc/inittab) come i demoni di console,
 di tty, di rete e cosi' via (''rc'' scripts).
</p><sect2>Esempio di Kernel Threads: kswapd &lsqb;mm/vmscan.c&rsqb;.
<p>''kswapd'' viene creato dalla ''clone() &lsqb;arch/i386/kernel/process.c&rsqb;''
</p><p>Routines di inzializzazione:
</p><p><verb>|do_initcalls
   |kswapd_init
      |kernel_thread
         |syscall fork (in assembler)
</verb></p><p>do_initcalls &lsqb;init/main.c&rsqb;
</p><p>kswapd_init &lsqb;mm/vmscan.c&rsqb;
</p><p>kernel_thread &lsqb;arch/i386/kernel/process.c&rsqb;
</p><sect1>Moduli del Kernel
<sect2>Introduzione
<p>I Moduli del Kernel sono pezzi di codice (che gestiscono ad esempio
 fs, net, e hw driver) che girano in Modo Kernel e che possono essere
 caricati in qualunque momento.
</p><p>Quasi tutto il codice puo' essere modularizzato, tranne il core
 piu' interno del Kernel, quello che gestisce scheduling, interrupt,
 core della rete, e cosi' via.
</p><p>Nella directory "/lib/modules/KERNEL_VERSION/" si trovano tutti
 i moduli installati nel sistema.
</p><sect2>Inserimento e rimozione dei Moduli
<p>Per caricare un module, basta digitare:
</p><p><verb>insmod NOME_MODULO parametri

esempio: insmod ne io=0x300 irq=9
</verb></p><p>NOTA: Si puo' utilizzare ''modprobe'' al posto di ''insmod''
 per far caricare alcuni parametri in automatico (come per le periferiche
 PCI, oppure quando si specificano i parametri nel file /etc/conf.modules
 o nel nuovo /etc/modules.conf).
</p><p>Per rimuovere un modulo, digitare:
</p><p><verb> rmmod NOME_MODULO
</verb></p><sect2>Definizione di un Modulo
<p>Un Module contiene sempre le funzioni
</p><p><enum><item>''init_module'', eseguita dal comando ''insmod'' (o ''modprobe'')
<item>''cleanup_module'' function, executed dal comando ''rmmod''
</enum></p><p>In alternativa si possono specificare altre funzioni di inizializzazione
 e chiusura tramite le macro:
</p><p><enum><item>module_init(FUNCTION_NAME)
<item>module_exit(FUNCTION_NAME)
</enum></p><p>NOTA: un modulo puo' ''vedere'' le variabili del Kernel solo
 se queste sono state precedentemente esportate (nella loro dichiarazione
 (con la macro EXPORT_SYMBOL).
</p><sect2>Un trucco utile per rendere piu' flessibile il Kernel
<p><verb>// Parte kernel
void (*foo_function_pointer)(void *);
 
if (foo_function_pointer)
  (foo_function_pointer)(parameter);
  
 


// Parte modulo
extern void (*foo_function_pointer)(void *);

void my_function(void *parameter) &lcub;
  //My code
&rcub;
 
int init_module() &lcub;
  foo_function_pointer = &amp;my_function;
&rcub;

int cleanup_module() &lcub;
  foo_function_pointer = NULL;
&rcub;
</verb></p><p>Questo artifizio permette di rendere il Kernel piu' flessibile,
 perche' soltanto quando si carica il modulo, la funzione ''my_function''
 verra' effettivamente eseguita: questa routine potra' fare qualunque
 cosa vogliamo, ad esempio il modulo ''rshaper'' (per limitare il
 traffico in entrata dalla rete) funziona in questo modo.
</p><p>Si noti come l'intero meccanismo dei moduli sia possibile grazie
 ad alcune variabili globali nel kernel che vengono esportate ai moduli,
 come puntatori a liste (permettendo di estendere tali liste quanto
 si vuole): esempi tipici sono i fs e i devices (char, block, net,
 telephony). 
</p><p>In definitiva, per caricare un modulo, bisogna in qualche modo
 ''preparare il kernel'' perche' lo accetti (se si vuole fargli fare
 qualcosa di interessante): in alcuni casi e' necessario creare una
 nuova infrastruttura che sia poi il piu' standard possibile (come
 quella ''telephony'' creata di recente).
</p><sect1>Directory Proc
<p>Nella directory /proc e' presente il FS proc, che e' un sistema
 speciale per consentire il dialogo diretto con il Kernel in user
 mode.
</p><p>Linux utilizza tale directory, ad esempio per dialogare con le
 strutture dati dei processi, per gestire le opzioni di rete delle
 interfacce come il ''proxy-arp'', il massimo numero di Threads, o
 per controllare lo state dei bus ISA o PCI, per sapere quali schede
 sono installate nel sistema e con quali indirizzi di I/O e IRQs
</p><p>Segue l'elenco delle sottodirectory piu' importanti della directory
 proc:
</p><p><verb>|-- bus
|   |-- pci
|   |   |-- 00
|   |   |   |-- 00.0
|   |   |   |-- 01.0
|   |   |   |-- 07.0
|   |   |   |-- 07.1
|   |   |   |-- 07.2
|   |   |   |-- 07.3
|   |   |   |-- 07.4
|   |   |   |-- 07.5
|   |   |   |-- 09.0
|   |   |   |-- 0a.0
|   |   |   `-- 0f.0
|   |   |-- 01
|   |   |   `-- 00.0
|   |   `-- devices
|   `-- usb
|-- cmdline
|-- cpuinfo
|-- devices
|-- dma
|-- dri
|   `-- 0
|       |-- bufs
|       |-- clients
|       |-- mem
|       |-- name
|       |-- queues
|       |-- vm
|       `-- vma
|-- driver
|-- execdomains
|-- filesystems
|-- fs
|-- ide
|   |-- drivers
|   |-- hda -&gt; ide0/hda
|   |-- hdc -&gt; ide1/hdc
|   |-- ide0
|   |   |-- channel
|   |   |-- config
|   |   |-- hda
|   |   |   |-- cache
|   |   |   |-- capacity
|   |   |   |-- driver
|   |   |   |-- geometry
|   |   |   |-- identify
|   |   |   |-- media
|   |   |   |-- model
|   |   |   |-- settings
|   |   |   |-- smart_thresholds
|   |   |   `-- smart_values
|   |   |-- mate
|   |   `-- model
|   |-- ide1
|   |   |-- channel
|   |   |-- config
|   |   |-- hdc
|   |   |   |-- capacity
|   |   |   |-- driver
|   |   |   |-- identify
|   |   |   |-- media
|   |   |   |-- model
|   |   |   `-- settings
|   |   |-- mate
|   |   `-- model
|   `-- via
|-- interrupts
|-- iomem
|-- ioports
|-- irq
|   |-- 0
|   |-- 1
|   |-- 10
|   |-- 11
|   |-- 12
|   |-- 13
|   |-- 14
|   |-- 15
|   |-- 2
|   |-- 3
|   |-- 4
|   |-- 5
|   |-- 6
|   |-- 7
|   |-- 8
|   |-- 9
|   `-- prof_cpu_mask
|-- kcore
|-- kmsg
|-- ksyms
|-- loadavg
|-- locks
|-- meminfo
|-- misc
|-- modules
|-- mounts
|-- mtrr
|-- net
|   |-- arp
|   |-- dev
|   |-- dev_mcast
|   |-- ip_fwchains
|   |-- ip_fwnames
|   |-- ip_masquerade
|   |-- netlink
|   |-- netstat
|   |-- packet
|   |-- psched
|   |-- raw
|   |-- route
|   |-- rt_acct
|   |-- rt_cache
|   |-- rt_cache_stat
|   |-- snmp
|   |-- sockstat
|   |-- softnet_stat
|   |-- tcp
|   |-- udp
|   |-- unix
|   `-- wireless
|-- partitions
|-- pci
|-- scsi
|   |-- ide-scsi
|   |   `-- 0
|   `-- scsi
|-- self -&gt; 2069
|-- slabinfo
|-- stat
|-- swaps
|-- sys
|   |-- abi
|   |   |-- defhandler_coff
|   |   |-- defhandler_elf
|   |   |-- defhandler_lcall7
|   |   |-- defhandler_libcso
|   |   |-- fake_utsname
|   |   `-- trace
|   |-- debug
|   |-- dev
|   |   |-- cdrom
|   |   |   |-- autoclose
|   |   |   |-- autoeject
|   |   |   |-- check_media
|   |   |   |-- debug
|   |   |   |-- info
|   |   |   `-- lock
|   |   `-- parport
|   |       |-- default
|   |       |   |-- spintime
|   |       |   `-- timeslice
|   |       `-- parport0
|   |           |-- autoprobe
|   |           |-- autoprobe0
|   |           |-- autoprobe1
|   |           |-- autoprobe2
|   |           |-- autoprobe3
|   |           |-- base-addr
|   |           |-- devices
|   |           |   |-- active
|   |           |   `-- lp
|   |           |       `-- timeslice
|   |           |-- dma
|   |           |-- irq
|   |           |-- modes
|   |           `-- spintime
|   |-- fs
|   |   |-- binfmt_misc
|   |   |-- dentry-state
|   |   |-- dir-notify-enable
|   |   |-- dquot-nr
|   |   |-- file-max
|   |   |-- file-nr
|   |   |-- inode-nr
|   |   |-- inode-state
|   |   |-- jbd-debug
|   |   |-- lease-break-time
|   |   |-- leases-enable
|   |   |-- overflowgid
|   |   `-- overflowuid
|   |-- kernel
|   |   |-- acct
|   |   |-- cad_pid
|   |   |-- cap-bound
|   |   |-- core_uses_pid
|   |   |-- ctrl-alt-del
|   |   |-- domainname
|   |   |-- hostname
|   |   |-- modprobe
|   |   |-- msgmax
|   |   |-- msgmnb
|   |   |-- msgmni
|   |   |-- osrelease
|   |   |-- ostype
|   |   |-- overflowgid
|   |   |-- overflowuid
|   |   |-- panic
|   |   |-- printk
|   |   |-- random
|   |   |   |-- boot_id
|   |   |   |-- entropy_avail
|   |   |   |-- poolsize
|   |   |   |-- read_wakeup_threshold
|   |   |   |-- uuid
|   |   |   `-- write_wakeup_threshold
|   |   |-- rtsig-max
|   |   |-- rtsig-nr
|   |   |-- sem
|   |   |-- shmall
|   |   |-- shmmax
|   |   |-- shmmni
|   |   |-- sysrq
|   |   |-- tainted
|   |   |-- threads-max
|   |   `-- version
|   |-- net
|   |   |-- 802
|   |   |-- core
|   |   |   |-- hot_list_length
|   |   |   |-- lo_cong
|   |   |   |-- message_burst
|   |   |   |-- message_cost
|   |   |   |-- mod_cong
|   |   |   |-- netdev_max_backlog
|   |   |   |-- no_cong
|   |   |   |-- no_cong_thresh
|   |   |   |-- optmem_max
|   |   |   |-- rmem_default
|   |   |   |-- rmem_max
|   |   |   |-- wmem_default
|   |   |   `-- wmem_max
|   |   |-- ethernet
|   |   |-- ipv4
|   |   |   |-- conf
|   |   |   |   |-- all
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   |-- default
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   |-- eth0
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   |-- eth1
|   |   |   |   |   |-- accept_redirects
|   |   |   |   |   |-- accept_source_route
|   |   |   |   |   |-- arp_filter
|   |   |   |   |   |-- bootp_relay
|   |   |   |   |   |-- forwarding
|   |   |   |   |   |-- log_martians
|   |   |   |   |   |-- mc_forwarding
|   |   |   |   |   |-- proxy_arp
|   |   |   |   |   |-- rp_filter
|   |   |   |   |   |-- secure_redirects
|   |   |   |   |   |-- send_redirects
|   |   |   |   |   |-- shared_media
|   |   |   |   |   `-- tag
|   |   |   |   `-- lo
|   |   |   |       |-- accept_redirects
|   |   |   |       |-- accept_source_route
|   |   |   |       |-- arp_filter
|   |   |   |       |-- bootp_relay
|   |   |   |       |-- forwarding
|   |   |   |       |-- log_martians
|   |   |   |       |-- mc_forwarding
|   |   |   |       |-- proxy_arp
|   |   |   |       |-- rp_filter
|   |   |   |       |-- secure_redirects
|   |   |   |       |-- send_redirects
|   |   |   |       |-- shared_media
|   |   |   |       `-- tag
|   |   |   |-- icmp_echo_ignore_all
|   |   |   |-- icmp_echo_ignore_broadcasts
|   |   |   |-- icmp_ignore_bogus_error_responses
|   |   |   |-- icmp_ratelimit
|   |   |   |-- icmp_ratemask
|   |   |   |-- inet_peer_gc_maxtime
|   |   |   |-- inet_peer_gc_mintime
|   |   |   |-- inet_peer_maxttl
|   |   |   |-- inet_peer_minttl
|   |   |   |-- inet_peer_threshold
|   |   |   |-- ip_autoconfig
|   |   |   |-- ip_conntrack_max
|   |   |   |-- ip_default_ttl
|   |   |   |-- ip_dynaddr
|   |   |   |-- ip_forward
|   |   |   |-- ip_local_port_range
|   |   |   |-- ip_no_pmtu_disc
|   |   |   |-- ip_nonlocal_bind
|   |   |   |-- ipfrag_high_thresh
|   |   |   |-- ipfrag_low_thresh
|   |   |   |-- ipfrag_time
|   |   |   |-- neigh
|   |   |   |   |-- default
|   |   |   |   |   |-- anycast_delay
|   |   |   |   |   |-- app_solicit
|   |   |   |   |   |-- base_reachable_time
|   |   |   |   |   |-- delay_first_probe_time
|   |   |   |   |   |-- gc_interval
|   |   |   |   |   |-- gc_stale_time
|   |   |   |   |   |-- gc_thresh1
|   |   |   |   |   |-- gc_thresh2
|   |   |   |   |   |-- gc_thresh3
|   |   |   |   |   |-- locktime
|   |   |   |   |   |-- mcast_solicit
|   |   |   |   |   |-- proxy_delay
|   |   |   |   |   |-- proxy_qlen
|   |   |   |   |   |-- retrans_time
|   |   |   |   |   |-- ucast_solicit
|   |   |   |   |   `-- unres_qlen
|   |   |   |   |-- eth0
|   |   |   |   |   |-- anycast_delay
|   |   |   |   |   |-- app_solicit
|   |   |   |   |   |-- base_reachable_time
|   |   |   |   |   |-- delay_first_probe_time
|   |   |   |   |   |-- gc_stale_time
|   |   |   |   |   |-- locktime
|   |   |   |   |   |-- mcast_solicit
|   |   |   |   |   |-- proxy_delay
|   |   |   |   |   |-- proxy_qlen
|   |   |   |   |   |-- retrans_time
|   |   |   |   |   |-- ucast_solicit
|   |   |   |   |   `-- unres_qlen
|   |   |   |   |-- eth1
|   |   |   |   |   |-- anycast_delay
|   |   |   |   |   |-- app_solicit
|   |   |   |   |   |-- base_reachable_time
|   |   |   |   |   |-- delay_first_probe_time
|   |   |   |   |   |-- gc_stale_time
|   |   |   |   |   |-- locktime
|   |   |   |   |   |-- mcast_solicit
|   |   |   |   |   |-- proxy_delay
|   |   |   |   |   |-- proxy_qlen
|   |   |   |   |   |-- retrans_time
|   |   |   |   |   |-- ucast_solicit
|   |   |   |   |   `-- unres_qlen
|   |   |   |   `-- lo
|   |   |   |       |-- anycast_delay
|   |   |   |       |-- app_solicit
|   |   |   |       |-- base_reachable_time
|   |   |   |       |-- delay_first_probe_time
|   |   |   |       |-- gc_stale_time
|   |   |   |       |-- locktime
|   |   |   |       |-- mcast_solicit
|   |   |   |       |-- proxy_delay
|   |   |   |       |-- proxy_qlen
|   |   |   |       |-- retrans_time
|   |   |   |       |-- ucast_solicit
|   |   |   |       `-- unres_qlen
|   |   |   |-- route
|   |   |   |   |-- error_burst
|   |   |   |   |-- error_cost
|   |   |   |   |-- flush
|   |   |   |   |-- gc_elasticity
|   |   |   |   |-- gc_interval
|   |   |   |   |-- gc_min_interval
|   |   |   |   |-- gc_thresh
|   |   |   |   |-- gc_timeout
|   |   |   |   |-- max_delay
|   |   |   |   |-- max_size
|   |   |   |   |-- min_adv_mss
|   |   |   |   |-- min_delay
|   |   |   |   |-- min_pmtu
|   |   |   |   |-- mtu_expires
|   |   |   |   |-- redirect_load
|   |   |   |   |-- redirect_number
|   |   |   |   `-- redirect_silence
|   |   |   |-- tcp_abort_on_overflow
|   |   |   |-- tcp_adv_win_scale
|   |   |   |-- tcp_app_win
|   |   |   |-- tcp_dsack
|   |   |   |-- tcp_ecn
|   |   |   |-- tcp_fack
|   |   |   |-- tcp_fin_timeout
|   |   |   |-- tcp_keepalive_intvl
|   |   |   |-- tcp_keepalive_probes
|   |   |   |-- tcp_keepalive_time
|   |   |   |-- tcp_max_orphans
|   |   |   |-- tcp_max_syn_backlog
|   |   |   |-- tcp_max_tw_buckets
|   |   |   |-- tcp_mem
|   |   |   |-- tcp_orphan_retries
|   |   |   |-- tcp_reordering
|   |   |   |-- tcp_retrans_collapse
|   |   |   |-- tcp_retries1
|   |   |   |-- tcp_retries2
|   |   |   |-- tcp_rfc1337
|   |   |   |-- tcp_rmem
|   |   |   |-- tcp_sack
|   |   |   |-- tcp_stdurg
|   |   |   |-- tcp_syn_retries
|   |   |   |-- tcp_synack_retries
|   |   |   |-- tcp_syncookies
|   |   |   |-- tcp_timestamps
|   |   |   |-- tcp_tw_recycle
|   |   |   |-- tcp_window_scaling
|   |   |   `-- tcp_wmem
|   |   `-- unix
|   |       `-- max_dgram_qlen
|   |-- proc
|   `-- vm
|       |-- bdflush
|       |-- kswapd
|       |-- max-readahead
|       |-- min-readahead
|       |-- overcommit_memory
|       |-- page-cluster
|       `-- pagetable_cache
|-- sysvipc
|   |-- msg
|   |-- sem
|   `-- shm
|-- tty
|   |-- driver
|   |   `-- serial
|   |-- drivers
|   |-- ldisc
|   `-- ldiscs
|-- uptime
`-- version

</verb></p><p>Nella directory sono presenti anche tutti i Tasks attivi tramite
 il PID (che funge da nome della directory) tramite cui si puo' avere
 accesso alle informazioni relative ai Tasks, come percorso del file
 binario, memoria usata e cosi' via).
</p><p>Il punto piu' interessante e' che non e' soltanto possibile ''vedere''
 alcuni valori, ma anche modificarli, come quelli nella sottodirectory
 /proc/sys:
</p><p><verb>/proc/sys/ 
          acpi
          dev
          debug
          fs
          proc
          net
          vm
          kernel
</verb></p><sect2>/proc/sys/kernel
<p>Sotto seguono alcuni file utili per modificare dei settaggi del
 kernel:
</p><p><verb>overflowgid
overflowuid
random
threads-max // Massimo numero dei Threads, tipicamente 16384
sysrq // kernel hack: per consultare istantaneamente i valori dei registri del processore
sem
msgmnb
msgmni
msgmax
shmmni
shmall
shmmax
rtsig-max
rtsig-nr
modprobe // percorso di modprobe
printk
ctrl-alt-del
cap-bound
panic
domainname // domain name del Linux box
hostname // host name del Linux box
version // data del Kernel
osrelease // versione del kernel (i.e. 2.4.5)
ostype // Linux!
</verb></p><sect2>/proc/sys/net
<p>Questa puo' essere cosiderate la piu' utile sottodirectory del
 kernel: permette di cambiare alcuni importanti settaggi della rete
 del Kernel:
</p><p><verb>core
ipv4
ipv6
unix
ethernet
802
</verb></p><sect3>/proc/sys/net/core
<p>Sotto sono elencati alcuni settaggi di rete generali come "netdev_max_backlog"
 (tipicamente) che indica la lunghezza massima della coda di tutti
 i pacchetti di rete. Questo valore puo' limitare la banda della rete
 quando si ricevono i pacchetti, perche' Linux deve attendere fino
 al successivo tempo di schedulazione (1000/HZ ms) per poter svuotare
 tale buffer di pacchetti (per il meccanismo del bottom half).
</p><p><verb>  300    *        100             =     30 000
pacchetti    HZ(freq Timeslice)         pacchetti/s
 
30 000   *       1000             =      30 M
pacchetti     Media(Bytes/packet)   throughput Bytes/s
</verb></p><p>Per aumentare le prestazioni bisogna aumentare necessariamente
 il valore di netdev_max_backlog, digitando:
</p><p><verb>echo 4000 &gt; /proc/sys/net/core/netdev_max_backlog
</verb></p><p>Nota: Attenzione ai valori di HZ: alcune architetture (come alpha
 o arm-tbox) utilizzano 1000, che permettono quindi di arrivare a
 300 MBytes/s di banda media.
</p><sect3>/proc/sys/net/ipv4
<p>"ip_forward", abilita o disabilita l'ip forwarding nel Linux box:
 questo e' un settaggio generale, ma e' possibile specificare per
 ogni interfaccia un valore diverso.
</p><sect4>/proc/sys/net/ipv4/conf/interface
<p>Ritengo questo sia la directory /proc piu' utile, in quanto permette
 di cambiare molti settaggi di rete utili soprattutto in caso di reti
 wireless (si veda il <url url="http://www.bertolinux.com" name="Wireless-HOWTO"> per maggiori informazioni).
</p><p>Ecco alcuni esempi di utilizzo:
</p><p><itemize><item>"forwarding", per abilitare l'ip forwarding per una singola interfaccia
<item>"proxy_arp", to abilitare o disabilitare il proxy arp. Per ulteriori
 informazioni sul Proxy arp si cerchi il Proxyarp-HOWTO su <url url="http://www.tldp.org" name="Linux Documentation Project"> e <url url="http://www.bertolinux.com" name="Wireless-HOWTO"> per
 l'utilizzo del proxy arp nelli reti Wireless.
<item>"send_redirects" per evitare che le interfacce mandino i pacchetti
 di tipo ICMP_REDIRECT (come prima si veda <url url="http://www.bertolinux.com" name="Wireless-HOWTO"> per maggiori informazioni).
</itemize></p><sect>Multitasking di Linux
<sect1>Introduzione
<p>Questa capitolo analizza le strutture dati e funzionamento del
 Multitasking di Linux
</p><sect2>Stati dei Tasks
<p>Un Task Linux puo' esssere in uno dei seguenti stati (come da
 file &lsqb;include/linux.h&rsqb;):
</p><p><enum><item>TASK_RUNNING, significa che il Task e' nella "Ready List" (quindi
 pronto per l'esecuzione)
<item>TASK_INTERRUPTIBLE, Task in attesa di un segnale o di una risorsa
 (sta' dormendo)
<item>TASK_UNINTERRUPTIBLE, Task in attesa di una risorsa, presente
 nella ''Wait Queue" relativa
<item>TASK_ZOMBIE, Task senza padre (poi adottato da init)
<item>TASK_STOPPED, Task in modo debugging
</enum></p><sect2>Interazione grafica
<p><verb>       ______________     CPU Disponibile   _______________
      |              |  ----------------&gt;  |               |
      | TASK_RUNNING |                     |Vera esecuzione|  
      |______________|  &lt;----------------  |_______________|
                           CPU Occupata
            |   /|&bsol;       
In attesa di|    | Risorsa  
una Risorsa |    | Disponibile             
           &bsol;|/   |      
    ______________________                     
   |                      |
   | TASK_INTERRUPTIBLE / |
   | TASK-UNINTERRUPTIBLE |
   |______________________|
 
                     Flusso Principale Multitasking
</verb></p><sect1>TimeSlice
<sect2>Programmazione del PIT 8253
<p>Ogni 10 ms (a seconda del valore di HZ) arriva un IRQ0, che permmette
 di gestire il multitasking: questo segnale arriva dal PIC 8259 (nell'architettura
 386+) connesso a sua volta con il PIT 8253 avente clock di 1.19318
 MHz.
</p><p><verb>    _____         ______        ______        
   | CPU |&lt;------| 8259 |------| 8253 |
   |_____| IRQ0  |______|      |___/|&bsol;|
                                    |_____ CLK 1.193.180 MHz
          
// From include/asm/param.h
&num;ifndef HZ 
&num;define HZ 100 
&num;endif
 
// From include/asm/timex.h
&num;define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
 
// From include/linux/timex.h
&num;define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
 
// From arch/i386/kernel/i8259.c
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ 
outb_p(LATCH &amp; 0xff , 0x40); /* LSB */
outb(LATCH &gt;&gt; 8 , 0x40); /* MSB */
 
</verb></p><p>Quindi quello che si fa e' programmare l'8253 (PIT, Programmable
 Interval Timer) con LATCH = (1193180/HZ) = 11931.8, dove HZ=100 (default).
 LATCH indica il fattore di divisione frequenza per il clock.
</p><p>LATCH = 11931.8 fornisce all'8253 (in output) una frequenza di
 1193180 / 11931.8 = 100 Hz, quindi il periodo tra 2 IRQ0 e' di 10ms
</p><p>Quindi il Timeslice si misura come 1/HZ.
</p><p>Ogni TimeSlice viene sospeso il processo attualmente di esecuzione
 (senza Task Switching), e viene fatto del lavoro di ''manutenzione'',
 dopo di che il controllo ritorna al processo precedentemente interrotto.
</p><sect2>Linux Timer IRQ ICA
<p><verb>Linux Timer IRQ
IRQ 0 &lsqb;Timer&rsqb;
 |  
&bsol;|/
|IRQ0x00_interrupt        //   wrapper IRQ handler
   |SAVE_ALL              ---   
      |do_IRQ                |   wrapper routines
         |handle_IRQ_event  ---
            |handler() -&gt; timer_interrupt  // registered IRQ 0 handler
               |do_timer_interrupt
                  |do_timer  
                     |jiffies++;
                     |update_process_times  
                     |if (--counter &lt;= 0) &lcub; // if time slice ended then
                        |counter = 0;        //   reset counter           
                        |need_resched = 1;   //   prepare to reschedule
                     |&rcub;
         |do_softirq
         |while (need_resched) &lcub; // if necessary
            |schedule             //   reschedule
            |handle_softirq
         |&rcub;
   |RESTORE_ALL
 
</verb></p><p>Le Funzioni si trovano:
</p><p><itemize><item>IRQ0x00_interrupt, SAVE_ALL &lsqb;include/asm/hw_irq.h&rsqb;
<item>do_IRQ, handle_IRQ_event &lsqb;arch/i386/kernel/irq.c&rsqb;
<item>timer_interrupt, do_timer_interrupt &lsqb;arch/i386/kernel/time.c&rsqb;
<item>do_timer, update_process_times &lsqb;kernel/timer.c&rsqb;
<item>do_softirq &lsqb;kernel/soft_irq.c&rsqb;
<item>RESTORE_ALL, while loop &lsqb;arch/i386/kernel/entry.S&rsqb;
</itemize></p><p>Note:
</p><p><enum><item>La Funzione "IRQ0x00_interrupt" (come le altre IRQ0xXY_interrupt)
 e' direttamente puntata dalla IDT (Interrupt Descriptor Table, simile
 all'Interrupt Vector Table del modo reale, si veda Cap 11 per informazioni),
 cosicche' OGNI interrupt in arrivo al processore venga gestito dalla
 routine "IRQ0x&num;NR_interrupt" routine, dove &num;NR e' il numero
 dell'interrupt. Possiamo definire queste macro come "wrapper irq handler".
<item>Le wrapper routines vengono eseguite, come anche le "do_IRQ" e
 ''handle_IRQ_event" &lsqb;arch/i386/kernel/irq.c&rsqb;.
<item>Dopo di questo, il controllo passa alla routing IRQ ''ufficiale''
 (puntata da "handler()"), precedentemente registrata con ''request_irq"
 &lsqb;arch/i386/kernel/irq.c&rsqb;: nel caso IRQ0 avremo "timer_interrupt"
 &lsqb;arch/i386/kernel/time.c&rsqb;.
<item>Viene eseguita la "timer_interrupt" &lsqb;arch/i386/kernel/time.c&rsqb;
 e, quando termina,
<item>il controllo torna ad alcune routines assembler &lsqb;arch/i386/kernel/entry.S&rsqb;.
</enum></p><p>Descrizione: 
</p><p>Per gestire il Multitasking quindi, Linux (come ogni sistema
 Unix-like) utilizza un ''contatore'' per tenere traccia di quanto
 e' stata utilizzata la CPU dal Task. 
</p><p>Quindi, ad ogni IRQ 0, il contatore viene decrementato (punto
 4) e, quando raggiunge 0, siamo dobbiamo effettuare un Task Switching
 (punto 4, la variabile "need_resched" viene settata ad 1, cosicche'
 nel punto 5 tale valore porta a chiamare la "schedule" &lsqb;kernel/sched.c&rsqb;).
</p><sect1>Scheduler
<p>Lo scheduler e' quella parte di codice che sceglie QUALE Task
 deve venir eseguito di volta in volta.
</p><p>Ogni volta che si deve cambiare Task viene scelto un candidato.
 
</p><p>Segue la funzione ''schedule &lsqb;kernel/sched.c&rsqb;''.
</p><p><verb>|schedule
   |do_softirq // manages post-IRQ work
   |for each task
      |calculate counter
   |prepare_to__switch // does anything
   |switch_mm // change Memory context (change CR3 value)
   |switch_to (assembler)
      |SAVE ESP
      |RESTORE future_ESP
      |SAVE EIP
      |push future_EIP *** push parametro come se facessimo una call 
         |jmp __switch_to (funzione per gestire alcuni registri) 
         |__switch_to()   (si veda dopo per la spiegazione del funzionamento del Task Switching
          ..
         |ret *** ret dalla call usando il nuovo EIP
      new_task

</verb></p><sect1>Bottom Half, Task Queues e Tasklets
<sect2>Introduzione
<p>Nei classici Unix, quando arriva un IRQ (da un device), il sistema
 effettua il Task Switching per interrogare il Task che ha fatto accesso
 al Device.
</p><p>Per migliorare le performance, Linux posticipa il lavoro non
 urgente. 
</p><p>Questa funzionalita' e' stata gestita fin dalle prime versioni
 (kernel 1.x in poi) dai cosiddetti "bottom halves" (BH). In sostanza
 l'IRQ handler ''marca'' un bottom half (flag), per essere eseguito
 piu' tardi, e durante la schedulazione vengono poi eseguiti tutti
 i BH attivi.
</p><p>Negli ultimi Kernels compare il meccanismo del "Task Queue" piu'
 dinamico del BH e nascono anche i "Tasklet" per gestire i sistemi multiprocessore.
</p><p>Lo schema e':
</p><p><enum><item>Dichiarazione
<item>Marcatura
<item>Esecuzione
</enum></p><sect2>Dichiarazione
<p><verb>&num;define DECLARE_TASK_QUEUE(q) LIST_HEAD(q)
&num;define LIST_HEAD(name) &bsol;
   struct list_head name = LIST_HEAD_INIT(name) 
struct list_head &lcub; 
   struct list_head *next, *prev; 
&rcub;;
&num;define LIST_HEAD_INIT(name) &lcub; &amp;(name), &amp;(name) &rcub; 
 
      ''DECLARE_TASK_QUEUE'' &lsqb;include/linux/tqueue.h, include/linux/list.h&rsqb; 
</verb></p><p>La macro "DECLARE_TASK_QUEUE(q)" viene usata per dichiarare una
 struttura chiamata "q" per gestire i Task Queue.
</p><sect2>Marcatura
<p>Segue lo schema ICA per la "mark_bh" &lsqb;include/linux/interrupt.h&rsqb;:
</p><p><verb>|mark_bh(NUMBER)
   |tasklet_hi_schedule(bh_task_vec + NUMBER)
      |insert into tasklet_hi_vec
         |__cpu_raise_softirq(HI_SOFTIRQ) 
            |soft_active |= (1 &lt;&lt; HI_SOFTIRQ)
 
                   ''mark_bh''&lsqb;include/linux/interrupt.h&rsqb;
</verb></p><p>Quindi, ad esempio, quando un IRQ handler vuole posticipare del
 lavoro, basta che esegua una marcatura con la mark_bh(NUMBER)", dove
 NUMBER e' un BH precedentemente dichiarato (si veda sezione precedente).
</p><sect2>Esecuzione
<p>Vediamo l'esecuzione a partire dalla funzione "do_IRQ" &lsqb;arch/i386/kernel/irq.c&rsqb;:
</p><p><verb>if (softirq_pending(cpu)) 
  do_softirq();
</verb></p><p>quindi la ''do_softirq.c" &lsqb;kernel/softirq.c&rsqb;:
</p><p><verb>asmlinkage void do_softirq() &lcub; 
  int cpu = smp_processor_id(); 
  __u32 pending; 
  long flags; 
  __u32 mask;
  debug_function(DO_SOFTIRQ,NULL);
  if (in_interrupt()) 
    return;
  local_irq_save(flags);
  pending = softirq_pending(cpu);
  if (pending) &lcub; 
    struct softirq_action *h;
    mask = &tilde;pending; 
    local_bh_disable(); 
    restart: 
        /* Reset the pending bitmask before enabling irqs */ 
    softirq_pending(cpu) = 0;
    local_irq_enable();
    h = softirq_vec;
    do &lcub; 
      if (pending &amp; 1) 
        h-&gt;action(h); 
      h++; 
      pending &gt;&gt;= 1; 
    &rcub; while (pending);
    local_irq_disable();
    pending = softirq_pending(cpu); 
    if (pending &amp; mask) &lcub; 
      mask &amp;= &tilde;pending; 
      goto restart; 
    &rcub; 
    __local_bh_enable();
    if (pending) 
      wakeup_softirqd(cpu); 
  &rcub;
  local_irq_restore(flags); 
&rcub;

</verb></p><p>"h-&gt;action(h);" rappresenta la funzione precedentemente accodata.
</p><sect1>Routines a bassissimo livello
<p>set_intr_gate
</p><p>set_trap_gate
</p><p>set_task_gate (non used).
</p><p>(*interrupt)&lsqb;NR_IRQS&rsqb;(void) = &lcub; IRQ0x00_interrupt,
 IRQ0x01_interrupt, ..&rcub;
</p><p>NR_IRQS = 224 &lsqb;kernel 2.4.2&rsqb;
</p><p>DAFARE: Descrizione
</p><sect1>Task Switching
<sect2>Quando avviene?
<p>Il Task Switching e' necessario in molti casi:
</p><p><itemize><item>quando termina il TimeSlice, dobbiamo scegliere un nuovo Task
 da eseguire
<item>quando un Task decide di accedere ad una risorsa, si addormenta
 e rilascia la CPU
<item>quando un Task aspetta per una pipe, dobbiamo dare accesso ad
 un altro Task (che magari sara' quello che scrivera' nella pipe e
 fara' poi risvegliare il processo).
</itemize></p><sect2>Task Switching
<p><verb>                           TRUCCO DEL TASK SWITCHING

 &num;define switch_to(prev,next,last) do &lcub;                                 &bsol;
        asm volatile(&quot;pushl &percnt;&percnt;esi&bsol;n&bsol;t&quot;                                  &bsol;
                     &quot;pushl &percnt;&percnt;edi&bsol;n&bsol;t&quot;                                  &bsol;
                     &quot;pushl &percnt;&percnt;ebp&bsol;n&bsol;t&quot;                                  &bsol;
                     &quot;movl &percnt;&percnt;esp,&percnt;0&bsol;n&bsol;t&quot;        /* save ESP */          &bsol;
                     &quot;movl &percnt;3,&percnt;&percnt;esp&bsol;n&bsol;t&quot;        /* restore ESP */       &bsol;
                     &quot;movl &dollar;1f,&percnt;1&bsol;n&bsol;t&quot;          /* save EIP */          &bsol;
                     &quot;pushl &percnt;4&bsol;n&bsol;t&quot;             /* restore EIP */       &bsol;
                     &quot;jmp __switch_to&bsol;n&quot;                                &bsol;
                     &quot;1:&bsol;t&quot;                                             &bsol;
                     &quot;popl &percnt;&percnt;ebp&bsol;n&bsol;t&quot;                                   &bsol;
                     &quot;popl &percnt;&percnt;edi&bsol;n&bsol;t&quot;                                   &bsol;
                     &quot;popl &percnt;&percnt;esi&bsol;n&bsol;t&quot;                                   &bsol;
                     :&quot;=m&quot; (prev-&gt;thread.esp),&quot;=m&quot; (prev-&gt;thread.eip),  &bsol;
                      &quot;=b&quot; (last)                                       &bsol;
                     :&quot;m&quot; (next-&gt;thread.esp),&quot;m&quot; (next-&gt;thread.eip),    &bsol;
                      &quot;a&quot; (prev), &quot;d&quot; (next),                           &bsol;
                      &quot;b&quot; (prev));                                      &bsol;
&rcub; while (0)

</verb></p><p>Come si puo' notare il trucco sta' nel
</p><p>Il trucco sta' qui: 
</p><p><enum><item>''pushl &percnt;4'' che inserisce nello stack il nuovo EIP (del
 futuro Task)
<item>''jmp __switch_to'' che esegue la ''__switch_to'', ma che al
 contrario di una ''call'', ci fa ritornare al valore messo nello
 stack al punto 1 (quindi al nuovo Task!)
</enum><p><verb>  

     U S E R   M O D E                 K E R N E L     M O D E

 |          |     |          |       |          |     |          |
 |          |     |          | Timer |          |     |          |
 |          |     |  Normal  |  IRQ  |          |     |          |
 |          |     |   Exec   |------&gt;|Timer_Int.|     |          |
 |          |     |     |    |       | ..       |     |          |
 |          |     |    &bsol;|/   |       |schedule()|     | Task1 Ret|
 |          |     |          |       |_switch_to|&lt;--  |  Address |
 |__________|     |__________|       |          |  |  |          |
                                     |          |  |S |          | 
Task1 Data/Stack   Task1 Code        |          |  |w |          |
                                     |          | T|i |          |
                                     |          | a|t |          |
 |          |     |          |       |          | s|c |          |
 |          |     |          | Timer |          | k|h |          |
 |          |     |  Normal  |  IRQ  |          |  |i |          | 
 |          |     |   Exec   |------&gt;|Timer_Int.|  |n |          |
 |          |     |     |    |       | ..       |  |g |          |
 |          |     |    &bsol;|/   |       |schedule()|  |  | Task2 Ret|
 |          |     |          |       |_switch_to|&lt;--  |  Address |
 |__________|     |__________|       |__________|     |__________|
 
Task2 Data/Stack   Task2 Code        Kernel Code  Kernel Data/Stack
</verb></p><sect1>Fork
<sect2>Introduzione
<p>La Fork e' usata per creare un nuovo Task.
</p><p>Si parte dal Task padre, e si copiano le strutture dati al Task
 figlio.
</p><p><verb> 
                               |         |
                               | ..      |
         Task Parent           |         |
         |         |           |         |
         |  fork   |----------&gt;|  CREATE |   
         |         |          /|   NEW   |
         |_________|         / |   TASK  |
                            /  |         |
             ---           /   |         |
             ---          /    | ..      |
                         /     |         |
         Task Child     / 
         |         |   /
         |  fork   |&lt;-/
         |         |
         |_________|
              
                       Fork SysCall
</verb></p><sect2>Cosa non viene copiato
<p>Il Task appena creato (''Task figlio'') e' quasi identico al
 padre (''Task padre''), as eccezione di:
</p><p><enum><item>PID, ovviamente!
<item>La ''fork()'' del figlio ritorna con 0, mentre quella del padre
 con il Task del figlio (per distinguerli in User Mode)
<item>Tutte le pagine del figlio sono marcate ''READ + EXECUTE'', senza
 il diritto "WRITE'' (mentre il padre continua ad avere i diritti come
 prima) cosicche', quando viene fatta una richiesta di scrittura,
 viene scatenata una eccezione di ''Page Fault'' che creera' a questo
 punto un pagina fisicamente indipendente: questo meccanismo viene
 chiamata ''Copy on Write'' (si veda il Cap.10 per ulteriori informazioni).
</enum></p><sect2>Fork ICA
<p><verb>|sys_fork 
   |do_fork
      |alloc_task_struct 
         |__get_free_pages
       |p-&gt;state = TASK_UNINTERRUPTIBLE
       |copy_flags
       |p-&gt;pid = get_pid    
       |copy_files
       |copy_fs
       |copy_sighand
       |copy_mm // gestisce la CopyOnWrite (I parte)
          |allocate_mm
          |mm_init
             |pgd_alloc -&gt; get_pgd_fast
                |get_pgd_slow
          |dup_mmap
             |copy_page_range
                |ptep_set_wrprotect
                   |clear_bit // marca la pagina read-only              
          |copy_segments // per LDT
       |copy_thread
          |childregs-&gt;eax = 0  
          |p-&gt;thread.esp = childregs // figlio ritorna 0
          |p-&gt;thread.eip = ret_from_fork // figlio ricomincia fall'uscita della fork
       |retval = p-&gt;pid // la fork del padre ritorna il pid del figlio
       |SET_LINKS // Il Task viene inserito nella lista dei processi
       |nr_threads++ // variabile globale
       |wake_up_process(p) // Adesso possiamo svegliare il Task figlio
       |return retval
              
                      fork ICA
 
</verb><p><itemize><item>sys_fork &lsqb;arch/i386/kernel/process.c&rsqb;
<item>do_fork &lsqb;kernel/fork.c&rsqb;
<item>alloc_task_struct &lsqb;include/asm/processor.c&rsqb;
<item>__get_free_pages &lsqb;mm/page_alloc.c&rsqb;
<item>get_pid &lsqb;kernel/fork.c&rsqb;
<item>copy_files 
<item>copy_fs
<item>copy_sighand
<item>copy_mm
<item>allocate_mm
<item>mm_init
<item>pgd_alloc -&gt; get_pgd_fast &lsqb;include/asm/pgalloc.h&rsqb;
<item>get_pgd_slow
<item>dup_mmap &lsqb;kernel/fork.c&rsqb;
<item>copy_page_range &lsqb;mm/memory.c&rsqb;
<item>ptep_set_wrprotect &lsqb;include/asm/pgtable.h&rsqb;
<item>clear_bit &lsqb;include/asm/bitops.h&rsqb;
<item>copy_segments &lsqb;arch/i386/kernel/process.c&rsqb;
<item>copy_thread
<item>SET_LINKS &lsqb;include/linux/sched.h&rsqb;
<item>wake_up_process &lsqb;kernel/sched.c&rsqb;
</itemize></p><sect2>Copy on Write
<p>Per implementare la Copy on Write Linux:
</p><p><enum><item>Marca tutte le pagine copiate come READ-ONLY, facendo poi scaturire
 un Page Fault al primo tentativo di scrittura della pagina.
<item>Il gestore di Page Fault crea una nuova ed indipendente copia
 della pagina
</enum><p><verb> 
 | Page 
 | Fault 
 | Exception
 |
 |
 -----------&gt; |do_page_fault
                 |handle_mm_fault
                    |handle_pte_fault 
                       |do_wp_page        
                          |alloc_page      // Allocata una nuova pagina
                          |break_cow
                             |copy_cow_page // Copia la vecchia pagina su quella nuova
                             |establish_pte // riconfigura i puntatori della Page Table
                                |set_pte
                            
                    Page Fault ICA
 
</verb><p><itemize><item>do_page_fault &lsqb;arch/i386/mm/fault.c&rsqb; 
<item>handle_mm_fault &lsqb;mm/memory.c&rsqb;
<item>handle_pte_fault 
<item>do_wp_page
<item>alloc_page &lsqb;include/linux/mm.h&rsqb;
<item>break_cow &lsqb;mm/memory.c&rsqb;
<item>copy_cow_page
<item>establish_pte
<item>set_pte &lsqb;include/asm/pgtable-3level.h&rsqb;
</itemize></p><sect>Gestione della Memoria su Linux
<sect1>Introduzione
<p>Linux usa la segmentazione paginata che semplifica molto la notazione.
</p><sect2>Segmenti
<p>Vengono utilizzati soltanto 4 segmenti:
</p><p><itemize><item>2 segmenti (codice e dati/stack) per il KERNEL MODE da &lsqb;0xC000
 0000&rsqb; (3 GB) a &lsqb;0xFFFF FFFF&rsqb; (4 GB)
<item>2 segmenti (codice e dati/stack) per lo USER MODE da &lsqb;0&rsqb;
 (0 GB) a &lsqb;0xBFFF FFFF&rsqb; (3 GB)
</itemize><p><verb>                               __
   4 GB---&gt;|                |    |
           |     Kernel     |    |  Kernel Mode (Codice + Dati/Stack)
           |                |  __|
   3 GB---&gt;|----------------|  __
           |                |    |
           |                |    |
   2 GB---&gt;|                |    |
           |     Tasks      |    |  User Mode (Codice + Dati/Stack)
           |                |    |
   1 GB---&gt;|                |    |
           |                |    |
           |________________|  __| 
 0x00000000
          Indirizzi Lineari Kernel/User
 
</verb></p><sect1>Implementazione 386+
<p>Linux implementa la Paginazione usando 3 Levelli di Pagine, ma
 su architettura 386+ solo 2 sono effettivamente utilizzati:
</p><p><verb> 
   -----------------------------------------------------------------
   I   N   D   I   R   I   Z   Z   I       L   I   N   E   A   R   I
   -----------------------------------------------------------------
        &bsol;___/                 &bsol;___/                     &bsol;_____/ 
 
     PD offset              PF offset                 Frame offset 
     &lsqb;10 bits&rsqb;              &lsqb;10 bits&rsqb;                 &lsqb;12 bits&rsqb;       
          |                     |                          |
          |                     |     -----------          |        
          |                     |     | Valore  |----------|---------
          |     |         |     |     |---------|   /|&bsol;    |        |
          |     |         |     |     |         |    |     |        |
          |     |         |     |     |         |    | Frame offset |
          |     |         |     |     |         |   &bsol;|/             |
          |     |         |     |     |---------|&lt;------            |
          |     |         |     |     |         |      |            |
          |     |         |     |     |         |      | x 4096     |
          |     |         |  PF offset|_________|-------            |
          |     |         |       /|&bsol; |         |                   |
      PD offset |_________|-----   |  |         |          _________|
            /|&bsol; |         |    |   |  |         |          | 
             |  |         |    |  &bsol;|/ |         |         &bsol;|/
 _____       |  |         |    ------&gt;|_________|   INDIRIZZO FISICO 
|     |     &bsol;|/ |         |    x 4096 |         |
| CR3 |--------&gt;|         |           |         |
|_____|         | ....... |           | ....... |
                |         |           |         |    
 
               Page Directory          Page File

                       Paginazione su 386+
 


</verb></p><sect1>Mappatura Memory
<p>Linux gestisce il Controllo di Accesso soltanto sulla Paginazione,
 quindi Tasks differenti possono condividere lo stesso indirizzo di
 Segmento ma, avendo differenti valori nel registro CR3 (usato per
 puntare alla Page Directory), puntano effettivamente a differenti
 Pagine.
</p><p>In User mode un Task non puo' superare il limite di 3 GB (0 x
 C0 00 00 00), quindi soltanto le prime 768 page directories hanno
 senso (768*4MB = 3GB, perche' ogni page directory e' grande 4MB).
</p><p>Quando un Task entra in Kernel Mode (tramite System call o IRQ)
 le altre 256 pages directories sono effettivamente utilizzate e sono
 in comune rispetto a tutti gli altri Tasks (quindi sempre le stesse
 aree di memoria).
</p><p>Si noti che lo Spazio Lineare del Kernel (e soltanto del Kernel)
 e' uguale allo Spazio Fisico, cosicche':
</p><p><verb> 
            ________________ _____                    
           |Altri Dati Kernel|___  |  |                   |
           |-----------------|   | |__|                   |
           |     Kernel      |&bsol;  |____|   Spazio Reale    |
  3 GB ---&gt;|-----------------| &bsol;      | Altri Dati Kernel |
           |                 |&bsol; &bsol;     |                   |
           |              ___|_&bsol;_&bsol;____|__   Spazio        |
           |      Tasks      |  &bsol; &bsol;   |     Reale         |
           |              ___|___&bsol;_&bsol;__|__   Tasks         |
           |                 |    &bsol; &bsol; |                   |
           |                 |     &bsol; &bsol;|-------------------|
           |                 |      &bsol; |Spazio Reale Kernel|
           |_________________|       &bsol;|___________________|
      
           Indirizzi Logici          Indirizzi Fisici
 
</verb></p><p>Lo Spazio Lineare del Kernel corrisponde allo Spazio Fisico traslato
 di 3 GB in basso (infatti le page tables sono del tipo &lcub; "00000000",
 "00000001" &rcub;, di modo che possano lavorare senza virtualizzazione,
 riportando semplicemente il valore lineare su quello fisico).
</p><p>Si noti come non esista neanche conflitto tra gli indirizzi fisici
 Kernel e quelli User perche' il Kernel ha un'allocazione di tipo
 statica (quindi conosciamo subito dove piazzare i dati): per quanto
 riguarda i moduli o i dati aggiuntivi, basta scegliere i valori in
 modo tale che non vadano in conflitto con le Page Tables gia' presenti
 dello User Mode.
</p><sect1>Allocazione della memoria a basso livello
<sect2>Inizializzazione di Avvio
<p>Si parte dalla funzione ''kmem_cache_init'' (lanciata da start_kernel
 &lsqb;init/main.c&rsqb; all'avvio):
</p><p><verb>|kmem_cache_init
   |kmem_cache_estimate

</verb><p><itemize><item>kmem_cache_init &lsqb;mm/slab.c&rsqb;
<item>kmem_cache_estimate
</itemize></p><p>Continuiamo con ''mem_init'' (anch'essa lanciata da start_kernel&lsqb;init/main.c&rsqb;)
</p><p><verb>|mem_init
   |free_all_bootmem
      |free_all_bootmem_core
</verb><p><itemize><item>mem_init &lsqb;arch/i386/mm/init.c&rsqb;
<item>free_all_bootmem &lsqb;mm/bootmem.c&rsqb;
<item>free_all_bootmem_core
</itemize></p><sect2>Allocazione Run-time
<p>Su Linux, quando vogliamo allocate memoria, ad esempio durante
 la "copy_on_write" (si veda il Cap.10), chiamiamo:
</p><p><verb>|copy_mm 
   |allocate_mm = kmem_cache_alloc
      |__kmem_cache_alloc
         |kmem_cache_alloc_one
            |alloc_new_slab
               |kmem_cache_grow
                  |kmem_getpages
                     |__get_free_pages
                        |alloc_pages
                           |alloc_pages_pgdat
                              |__alloc_pages
                                 |rmqueue   
                                 |reclaim_pages

</verb><p><itemize><item>copy_mm &lsqb;kernel/fork.c&rsqb;
<item>allocate_mm &lsqb;kernel/fork.c&rsqb;
<item>kmem_cache_alloc &lsqb;mm/slab.c&rsqb;
<item>__kmem_cache_alloc 
<item>kmem_cache_alloc_one
<item>alloc_new_slab
<item>kmem_cache_grow
<item>kmem_getpages
<item>__get_free_pages &lsqb;mm/page_alloc.c&rsqb;
<item>alloc_pages &lsqb;mm/numa.c&rsqb;
<item>alloc_pages_pgdat
<item>__alloc_pages &lsqb;mm/page_alloc.c&rsqb;
<item>rm_queue
<item>reclaim_pages &lsqb;mm/vmscan.c&rsqb;
</itemize></p><p>DAFARE: Capire le Zone
</p><sect1>Swapping
<sect2>Introduzione
<p>Lo Swapping viene gestito dal demone ''kswapd'' (Kernel Thread).
</p><sect2>kswapd
<p>Come tutti i Kernel Threads, ''kswapd'' ha un ciclo principale
 in attesa di essere svegliato.
</p><p><verb>|kswapd
   |// routines di inizializzazione
   |for (;;) &lcub; // Ciclo principale
      |do_try_to_free_pages
      |recalculate_vm_stats
      |refill_inactive_scan
      |run_task_queue
      |interruptible_sleep_on_timeout // In attesa di essere svegliati
   |&rcub;
</verb><p><itemize><item>kswapd &lsqb;mm/vmscan.c&rsqb;
<item>do_try_to_free_pages
<item>recalculate_vm_stats &lsqb;mm/swap.c&rsqb;
<item>refill_inactive_scan &lsqb;mm/vmswap.c&rsqb;
<item>run_task_queue &lsqb;kernel/softirq.c&rsqb;
<item>interruptible_sleep_on_timeout &lsqb;kernel/sched.c&rsqb;
</itemize></p><sect2>Quando abbiamo bisogno dello swapping?
<p>Lo Swapping e' necessario quando dobbiamo accedere ad una pagina
 che non e' presente in memoria fisica: il tutto viene scatenato da
 un'eccezione (generata dall'accesso non autorizzato):
</p><p><verb> | Page Fault Exception
 | cause by all these conditions: 
 |   a-) User page 
 |   b-) Read or write access 
 |   c-) Page not present
 |
 |
 -----------&gt; |do_page_fault
                 |handle_mm_fault
                    |pte_alloc 
                       |pte_alloc_one
                          |__get_free_page = __get_free_pages
                             |alloc_pages
                                |alloc_pages_pgdat
                                   |__alloc_pages
                                      |wakeup_kswapd // Svegliamo kswapd
   
                   Page Fault ICA
 
</verb><p><itemize><item>do_page_fault &lsqb;arch/i386/mm/fault.c&rsqb; 
<item>handle_mm_fault &lsqb;mm/memory.c&rsqb;
<item>pte_alloc
<item>pte_alloc_one &lsqb;include/asm/pgalloc.h&rsqb;
<item>__get_free_page &lsqb;include/linux/mm.h&rsqb;
<item>__get_free_pages &lsqb;mm/page_alloc.c&rsqb;
<item>alloc_pages &lsqb;mm/numa.c&rsqb;
<item>alloc_pages_pgdat
<item>__alloc_pages
<item>wakeup_kswapd &lsqb;mm/vmscan.c&rsqb;
</itemize></p><sect>La Rete su Linux
<sect1>Come viene gestita la Rete su Linux?
<p>Per ogni tipo di NIC vi e' un device driver che lo gestisce (ad
 esempio per la il device NE2000 compatibile oppure per la periferica
 3COM 3C59X, ecc.).
</p><p>Dopo il device a basso livello, Linux chiama SEMPRE la routine
 di routing ad alto livello "netif_rx &lsqb;net/core/dev.c&rsqb;", che
 controlla:
</p><p><itemize><item>A quale protocollo di 3 livello appartiene il pacchetto in questione
<item>Quale chiamata (tramite puntatori virtuali) eseguire per gestirlo
</itemize></p><sect1>Esempio pratico: TCP
<p>Vedremo un esempio di quello che accade quando mandamo dobbiamo
 ricevere un pacchetto TCP, partendo dalla ''netif_rx &lsqb;net/core/dev.c&rsqb;''
 (in sostanza analizziamo ''a grandi linee'' lo stack TCP/IP di Linux).
</p><sect2>Gestione Interrupt: "netif_rx"
<p><verb>|netif_rx
   |__skb_queue_tail
      |qlen++
      |* Inserimento tramite puntatori nella coda pacchetti*    
   |cpu_raise_softirq
      |softirq_active(cpu) |= (1 &lt;&lt; NET_RX_SOFTIRQ) // settiamo il  bit NET_RX_SOFTIRQ nel vettore BH
 
</verb><p><itemize><item>__skb_queue_tail &lsqb;include/linux/skbuff.h&rsqb;
<item>cpu_raise_softirq &lsqb;kernel/softirq.c&rsqb;
</itemize></p><sect2>Gestione Post Interrupt: "net_rx_action"
<p>Una volta che l'interazione IRQ e' terminata, seguiamo cosa accade
 in fase di scheduling quando si esegue il BH relativo alla rete che
 abbiamo attivato tramite NET_RX_SOFTIRQ: in pratica andiamo a chiamare
 la ''net_rx_action &lsqb;net/core/dev.c&rsqb;'' come specificato
 da "net_dev_init &lsqb;net/core/dev.c&rsqb;".
</p><p><verb>|net_rx_action
   |skb = __skb_dequeue (operazione inversa della __skb_queue_tail)
   |for (ptype = first_protocol; ptype &lt; max_protocol; ptype++) // Determiniamo 
      |if (skb-&gt;protocol == ptype)                               // qual'e' il protocollo di rete
         |ptype-&gt;func -&gt; ip_rcv // come specificato sulla ''struct ip_packet_type &lsqb;net/ipv4/ip_output.c&rsqb;''
 
    **** ADESSO SAPPIAMO CHE IL PACCHETTO E' DI TIPO IP ****
         |ip_rcv
            |NF_HOOK (ip_rcv_finish)
               |ip_route_input // accediamo alla tabella di routing per capire qual e' la funzione da chiamare (qual e' cioe' l'interfaccia)
                  |skb-&gt;dst-&gt;input -&gt; ip_local_deliver // come da controllo della tabella di routing la destinazione e' la macchina locale
                     |ip_defrag // riassembliamo i frammenti IP
                        |NF_HOOK (ip_local_deliver_finish)
                           |ipprot-&gt;handler -&gt; tcp_v4_rcv // come da ''tcp_protocol &lsqb;include/net/protocol.c&rsqb;''
 
     **** ADESSO SAPPIAMO CHE IL PACCHETTO E' TCP ****
                           |tcp_v4_rcv   
                              |sk = __tcp_v4_lookup 
                              |tcp_v4_do_rcv
                                 |switch(sk-&gt;state) 

     *** Il pacchetto puo' essere mandato al Task tramite il socket aperto ***
                                 |case TCP_ESTABLISHED:
                                    |tcp_rcv_established
                                       |__skb_queue_tail // accoda il pacchetto sul socket
                                       |sk-&gt;data_ready -&gt; sock_def_readable 
                                          |wake_up_interruptible
                                

     *** Dobbiamo gestire il 3-way TCP handshake ***
                                 |case TCP_LISTEN:
                                    |tcp_v4_hnd_req
                                       |tcp_v4_search_req
                                       |tcp_check_req
                                          |syn_recv_sock -&gt; tcp_v4_syn_recv_sock
                                       |__tcp_v4_lookup_established
                                 |tcp_rcv_state_process

                    *** 3-Way TCP Handshake ***
                                    |switch(sk-&gt;state)
                                    |case TCP_LISTEN: // Riceviamo il SYN
                                       |conn_request -&gt; tcp_v4_conn_request
                                          |tcp_v4_send_synack // Mandiamo SYN + ACK
                                             |tcp_v4_synq_add // settiamo lo stato SYN
                                    |case TCP_SYN_SENT: // riceviamo SYN + ACK
                                       |tcp_rcv_synsent_state_process
                                          tcp_set_state(TCP_ESTABLISHED)
                                             |tcp_send_ack
                                                |tcp_transmit_skb
                                                   |queue_xmit -&gt; ip_queue_xmit
                                                      |ip_queue_xmit2
                                                         |skb-&gt;dst-&gt;output
                                    |case TCP_SYN_RECV: // Riceviamo ACK
                                       |if (ACK)
                                          |tcp_set_state(TCP_ESTABLISHED)
                              
</verb><p><itemize><item>net_rx_action &lsqb;net/core/dev.c&rsqb;
<item>__skb_dequeue &lsqb;include/linux/skbuff.h&rsqb;
<item>ip_rcv &lsqb;net/ipv4/ip_input.c&rsqb;
<item>NF_HOOK -&gt; nf_hook_slow &lsqb;net/core/netfilter.c&rsqb;
<item>ip_rcv_finish &lsqb;net/ipv4/ip_input.c&rsqb;
<item>ip_route_input &lsqb;net/ipv4/route.c&rsqb;
<item>ip_local_deliver &lsqb;net/ipv4/ip_input.c&rsqb;
<item>ip_defrag &lsqb;net/ipv4/ip_fragment.c&rsqb;
<item>ip_local_deliver_finish &lsqb;net/ipv4/ip_input.c&rsqb;
<item>tcp_v4_rcv &lsqb;net/ipv4/tcp_ipv4.c&rsqb;
<item>__tcp_v4_lookup
<item>tcp_v4_do_rcv
<item>tcp_rcv_established &lsqb;net/ipv4/tcp_input.c&rsqb;
<item>__skb_queue_tail &lsqb;include/linux/skbuff.h&rsqb;
<item>sock_def_readable &lsqb;net/core/sock.c&rsqb;
<item>wake_up_interruptible &lsqb;include/linux/sched.h&rsqb;
<item>tcp_v4_hnd_req &lsqb;net/ipv4/tcp_ipv4.c&rsqb;
<item>tcp_v4_search_req
<item>tcp_check_req
<item>tcp_v4_syn_recv_sock
<item>__tcp_v4_lookup_established
<item>tcp_rcv_state_process &lsqb;net/ipv4/tcp_input.c&rsqb;
<item>tcp_v4_conn_request &lsqb;net/ipv4/tcp_ipv4.c&rsqb;
<item>tcp_v4_send_synack
<item>tcp_v4_synq_add
<item>tcp_rcv_synsent_state_process &lsqb;net/ipv4/tcp_input.c&rsqb;
<item>tcp_set_state &lsqb;include/net/tcp.h&rsqb;
<item>tcp_send_ack &lsqb;net/ipv4/tcp_output.c&rsqb;
</itemize></p><p>Descrizione:
</p><p><itemize><item>Prima determiniamo il tipo di protocollo (IP, poi TCP)
<item>NF_HOOK (funzione) e' una routine di incapsulazione che prima
 gestisce il filtro di rete (firewall), eppoi chiama la ''funzione''.
<item>Dopo gestiamo il 3-way TCP Handshake:
</itemize><p><verb>SERVER (LISTENING)                       CLIENT (CONNECTING)
                           SYN 
                   &lt;-------------------
 
 
                        SYN + ACK
                   -------------------&gt;

 
                           ACK 
                   &lt;-------------------

                    3-Way TCP handshake

</verb><p><itemize><item>Alla fine dobbiamo solo piu' lanciare "tcp_rcv_established &lsqb;net/ipv4/tcp_input.c&rsqb;"
 che manda il pacchetto al socket e sveglia il processo in attesa
 dello stesso
</itemize></p><sect>Linux File System
<p>DAFARE
</p><sect>Utili Note
<sect1>Stack e Heap
<sect2>Introduzione
<p>Qui vediamo come vengono allocati in memoria "stack" ed "heap".
</p><sect2>Allocazione di memoria
<p><verb>
FF..        |                 | &lt;-- inizio dello stack
       /|&bsol;  |                 |   | 
 valori |   |                 |   |   stack
piu'alti|   |                 |  &bsol;|/  crescente
di memoria  |                 |
XX..        |                 | &lt;-- attuale puntatore stack 
            |                 |
            |                 |
            |                 |
00..        |_________________| &lt;-- fine dello stack &lsqb;Stack Segment&rsqb;
                 
                   Stack

</verb></p><p>Gli indirizzi di memoria partono da 00.. (che e' il dove il Segmento
 dello Stack comincia) e aumentano verso il valore FF.., mentre lo
 stack cresce all'opposto, cioe' dal valore FF.. al valoce 00.. verso
 valori bassi di memoria
</p><p>XX.. e' il valore attuale dello Stack Pointer.
</p><p>Lo Stack viene solitamente usato per:
</p><p><enum><item>variabili globali
<item>variabili locali
<item>indirizzo di ritorno
</enum></p><p>Ad esempio una classica funzione
</p><p><verb>
 |int funzione (parametro_1, parametro_2, ..., parametro_N) &lcub;
    |dichiarazione variabile_1;
    |dichiarazione variabile_2;
      ..
    |dichiarazione variabile_N;
   
    |// Corpo della funzione
    |dichiarazione variabile_dinamica_1;
    |dichiarazione variabile_dinamica_2;
     ..
    |dichiarazione variabile_dinamica_N;

    |// Il Codice e' all'interno del Segmento di Codice, non nel Segmento Dati/Stack! 
    
    |return (ret-type) valore; // Spesso finisce in qualche registro, per 386+: registro ''eax''
 |&rcub;
</verb><p><quote>utilizza uno stack del tipo
</quote><p><verb>
          |                       |
          | 1. parametro_1 pushato| &bsol;
    S     | 2. parameter_2 pushato|  | Prima della
    T     | ...................   |  | chiamata
    A     | N. parameter_N pushato| /
    C     | *Indirizzo di Ritorno*| -- Chiamata
    K     | 1. variabile_1 locale | &bsol; 
          | 2. variabile_2 locale |  | Dopo la
          | .................     |  | chiamata
          | N. variabile_N locale | /
          |                       | 
         ...                     ...   Stack
         ...                     ...   Libero
          |                       |
    H     | N.variabile_N dinamica| &bsol;
    E     | ...................   |  | Allocato dalle
    A     | 2.variabile_2 dinamica|  | malloc &amp; kmalloc
    P     | 1.variabile_1 dinamica| /
          |_______________________|
        
            Utilizzo tipico dello Stack
 
</verb></p><p>Nota: L'ordine delle variabile puo' essere differente a seconda
 dell'architettura hardware (come sempre qui si ipotizza l'utilizzo
 del 386+).
</p><p><verb>
</verb></p><sect1>Applicazione vs Task
<sect2>Definizioni di Base
<p>Distinguiamo 2 2 concetti:
</p><p><itemize><item>Applicazione: che e' il codice che vogliamo eseguire
<item>Processo: che rappresenta l'Immagine in memoria dell'applicazione
 (dipende dalla strategia di memoria utilizzata, Segmentazione e/o
 Paginazione).
</itemize></p><p>Spesso i Processi vengono chiamati Task o Thread.
</p><sect1>Locks
<sect2>Introduzione
<p>2 tipi di locks:
</p><p><enum><item>intraCPU
<item>interCPU
</enum></p><sect1>Copy_on_write
<p>Il meccanismo della Copy_on_write consente di ridurre l'utilizzo
 di memoria, postponendo l'allocazione per un nuovo Thread fino a
 quando non e' strettamente necessario.
</p><p>Ad esempio, quando un Task esegue la "fork()" per creare un nuovo
 Processo, le pagine del vecchio processo non vengono copiate, ma
 vengono soltanto "referenziate" in modalita' READ ONLY (sia per il
 padre che per il figlio) in modo che, quando qualcuno andra' a scrivervi
 sopra, l'eccezione generata si occupera' di creare una nuova copia
 della pagina marcandola, questa volta READ + WRITE.
</p><p>Ecco uno schema riassuntivo: 
</p><p><verb>
1-) La Pagina X e' condivisa tra Il Task Padre e quello figlio
 Task Padre
 |         | Accesso RO ________
 |         |----------&gt;|Pagina X|    
 |_________|           |________|
                          /|&bsol;
                           |
 Task Figlio               | 
 |         | Accesso RO    |  
 |         |----------------                
 |_________| 
 
 
2-) Richiesta di Scrittura
 Task Figlio
 |         | Accesso RO ________
 |         |----------&gt;|Pagina X| (tentativo di scrittura)
 |_________|           |________|           |
                          /|&bsol;               |
                           |               &bsol;|/
 Task Figlio               |            ECCEZIONE
 |         | Accesso RO    |  
 |         |----------------                
 |_________|  
 
 
3-) Configurazione Finale: il Task Padre e quello Figlio hanno ognuno una copia indipendente della Pagina, X e Y
 Task Padre
 |         | Accesso RW ________
 |         |----------&gt;|Pagina X|    
 |_________|           |________|
              
              
 Task Figlio
 |         | Accesso RW ________
 |         |----------&gt;|Pagina Y|    
 |_________|           |________|
</verb></p><sect>Dettagli specifici su 386+
<sect1>Avvio
<p><verb>bbootsect.s &lsqb;arch/i386/boot&rsqb;
setup.S (+video.S) 
head.S (+misc.c) &lsqb;arch/i386/boot/compressed&rsqb;
start_kernel &lsqb;init/main.c&rsqb;
</verb></p><sect1>Descrittori 386+
<sect2>Introduzione
<p>I Descrittori sono delle strutture dati usate nell'architettura
 i386+ per utilizzare la memoria virtuale.
</p><sect2>Tipi di descrittori
<p><itemize><item>GDT (Global Descriptor Table)
<item>LDT (Local Descriptor Table)
<item>IDT (Interrupt Descriptor Table)
</itemize></p><sect>IRQ 
<sect1>Introduzione
<p>L'IRQ e' un segnale asincrono mandato al microprocessore per
 avvertirlo che un lavoro e' stato completato o che si e' verificato
 un errore.
</p><sect1>Schema di Interazione
<p><verb>                                 |&lt;--&gt;  IRQ(0) &lsqb;Timer&rsqb;
                                 |&lt;--&gt;  IRQ(1) &lsqb;Device 1&rsqb;
                                 | ..
                                 |&lt;--&gt;  IRQ(n) &lsqb;Device n&rsqb;
    _____________________________| 
     /|&bsol;      /|&bsol;          /|&bsol;
      |        |            |
     &bsol;|/      &bsol;|/          &bsol;|/
 
    Task(1)  Task(2) ..   Task(N)
              
             
             Schema di interazione Tasks-IRQ 
  

</verb></p><sect2>Cosa accade?
<p>Un O.S. tipico utilizza molti segnali IRQ per interrompere la
 normale esecuzione di un processo e gestire del lavoro relativo ad
 un device. Lo schema e' il seguente:
</p><p><enum><item>Arriva un IRQ (i) e il Task(j) viene interrotto
<item>Viene eseguito il relativo IRQ(i)_handler
<item>Il controllo torna al Task(j) precedentemente interrotto
</enum></p><p>In particolare Linux, quando arriva un IRQ esegue prima di tutto
 la funzione di incapsulazione IRQ (chiamata "interrupt0x??"), e soltanto
 dopo l'IRQ(i)_handler ufficile. Questo permette di eseguire alcune
 operazioni comuni a tutti gli IRQ come la gestione del TimeSlice.
</p><sect>Funzioni di comune utilizzo
<sect1>list_entry &lsqb;include/linux/list.h&rsqb;
<p>Definizione
</p><p><verb>&num;define list_entry(ptr, type, member) &bsol;
((type *)((char *)(ptr)-(unsigned long)(&amp;((type *)0)-&gt;member)))
</verb></p><p>Significato:
</p><p>La macro "list_entry" viene usata per ricavare il puntatore ad
 una struttura utilizzando soltanto un elemento interno alla struttura.
</p><p>Esempio
</p><p><verb>struct __wait_queue &lcub;
   unsigned int flags; 
   struct task_struct * task; 
   struct list_head task_list;
&rcub;;
struct list_head &lcub; 
   struct list_head *next, *prev; 
&rcub;;

// e con la definizione del tipo:
typedef struct __wait_queue wait_queue_t;

// avremo:
wait_queue_t *out list_entry(tmp, wait_queue_t, task_list);

// dove tmp punta a list_head
</verb></p><p>Quindi, in questo caso, usando il puntatore *tmp &lsqb;list_head&rsqb;
 troviamo il puntatore *out &lsqb;wait_queue_t&rsqb;.
</p><p><verb>
 ____________ &lt;---- *out &lsqb;abbiamo calcolato questo&rsqb;
|flags       |             /|&bsol;
|task *--&gt;   |              |
|task_list   |&lt;----    list_entry
|  prev * --&gt;|    |         |
|  next * --&gt;|    |         |
|____________|    ----- *tmp &lsqb;partendo da questo&rsqb;
 
</verb></p><sect1>Sleep 
<sect2>Codice di Sleep
<p>Files:
</p><p><itemize><item>kernel/sched.c
<item>include/linux/sched.h
<item>include/linux/wait.h
<item>include/linux/list.h
</itemize></p><p>Funzioni:
</p><p><itemize><item>interruptible_sleep_on
<item>interruptible_sleep_on_timeout
<item>sleep_on
<item>sleep_on_timeout
</itemize></p><p>Funzioni chiamate:
</p><p><itemize><item>init_waitqueue_entry
<item>__add_wait_queue
<item>list_add
<item>__list_add
<item>__remove_wait_queue
</itemize></p><p>InterCallings Analysis:
</p><p><verb>|sleep_on
   |init_waitqueue_entry  --
   |__add_wait_queue        |   Accodamento della richiesta sulla lista della risorsa
      |list_add              |
         |__list_add        -- 
   |schedule              ---     Attesa che la richiesta venga eseguita
      |__remove_wait_queue --   
      |list_del              |   Disaccodamento richiesta dalla lista della risorsa
         |__list_del        -- 
 

</verb></p><p>Descrizione:
</p><p>Ogni risorsa (in teoria ogni oggetto condiviso tra piu' utenti
 e piu' processi), ha una cosa per gestire TUTTI i Tasks che la richiedono.
</p><p>Questo metodo di accodamento viene chiamato "wait queue" e consiste
 di molti elementi chiamati"wait queue element":
</p><p><verb>***   struttura wait queue &lsqb;include/linux/wait.h&rsqb;  ***


struct __wait_queue &lcub;
   unsigned int flags; 
   struct task_struct * task; 
   struct list_head task_list;
&rcub;
struct list_head &lcub; 
   struct list_head *next, *prev; 
&rcub;;
</verb></p><p>Rappresentazione grafica:
</p><p><verb>        ***  elemento wait queue  ***

                             /|&bsol;
                              |
       &lt;--&lsqb;prev *, flags, task *, next *&rsqb;--&gt;
 
                     


                 ***  Lista wait queue ***  
 
          /|&bsol;           /|&bsol;           /|&bsol;                /|&bsol;
           |             |             |                  |
--&gt; &lt;--&lsqb;task1&rsqb;--&gt; &lt;--&lsqb;task2&rsqb;--&gt; &lt;--&lsqb;task3&rsqb;--&gt; .... &lt;--&lsqb;taskN&rsqb;--&gt; &lt;--
|                                                                  |
|__________________________________________________________________|
          

           
              ***   Testa wait queue ***

       task1 &lt;--&lsqb;prev *, lock, next *&rsqb;--&gt; taskN
   
 
</verb></p><p>La Testa "wait queue" punta al primo (con next *) e last (com prev
 *) all'ultimo della lista "wait queue".
</p><p>Quando deve venire inserito un nuovo elemento viene chiamata
 la "__add_wait_queue" &lsqb;include/linux/wait.h&rsqb;, dopo di che
 verra' eseguita la generica routine "list_add" &lsqb;include/linux/wait.h&rsqb;:
</p><p><verb>***   funzione list_add &lsqb;include/linux/list.h&rsqb;  ***

// classico inserimento doppio linkato
static __inline__ void __list_add (struct list_head * new,  &bsol;
                                   struct list_head * prev, &bsol;
                                   struct list_head * next) &lcub; 
   next-&gt;prev = new; 
   new-&gt;next = next; 
   new-&gt;prev = prev; 
   prev-&gt;next = new; 
&rcub;
</verb></p><p>Per completare la descrizione vediamo anche la "__list_del" &lsqb;include/linux/list.h&rsqb;
 chiamata da "list_del" &lsqb;include/linux/list.h&rsqb; all'interno
 di "remove_wait_queue" &lsqb;include/linux/wait.h&rsqb;:
</p><p><verb>***   funzione list_del &lsqb;include/linux/list.h&rsqb;  ***


// classica cancellazione doppio linkato
static __inline__ void __list_del (struct list_head * prev, struct list_head * next) &lcub; 
   next-&gt;prev = prev; 
   prev-&gt;next = next; 
&rcub;
</verb></p><sect2>Considerazioni sullo Stack
<p>Una lista tipica (o coda) viene normalmente gestita allocandola
 nello Heap (si veda il Cap.10 per definizioni e gestione delle variabili
 nello Heap e nello Stack). 
</p><p>Qui invece, allochiamo ''Wait Queue'' in una variabile locale
 (quindi nello Stack), dopo di che la funzione viene interrotta dalla
 schedulazione e, al risveglio, la variabile locale verra' cancellata
</p><p><verb>  new task &lt;----|          task1 &lt;------|          task2 &lt;------|
                |                       |                       |
                |                       |                       | 
|..........|    |       |..........|    |       |..........|    | 
|wait.flags|    |       |wait.flags|    |       |wait.flags|    |
|wait.task_|____|       |wait.task_|____|       |wait.task_|____|   
|wait.prev |--&gt;         |wait.prev |--&gt;         |wait.prev |--&gt;
|wait.next |--&gt;         |wait.next |--&gt;         |wait.next |--&gt;   
|..        |            |..        |            |..        |    
|schedule()|            |schedule()|            |schedule()|     
|..........|            |..........|            |..........|    
|__________|            |__________|            |__________|     
 
   Stack                   Stack                   Stack
</verb></p><sect>Variabili Statiche
<sect1>Introduzione
<p>Linux e' scritto in linguaggio ''C'', e come ogni applicazione
 usa:
</p><p><enum><item>Variabili Locali
<item>Variabili di Modulo (all'interno del file sorgente e relative
 soltanto al modulo)
<item>Variabili Globali/Statiche presenti soltanto in una copia nell'intero
 Kernel (la stessa per tutti i moduli)
</enum></p><p>Quando una variabile Statica viene modificata da un modulo, tutti
 gli altri moduli potranno avere a disposizione il nuovo valore.
</p><p>Le variabili Statiche sotto Linux sono molto importanti, perche'
 rappresentano l'unico metodo per aggiungere nuove funzionalita' al
 Kernel: tipicamente puntano alla testa di liste dove sono memorizzati
 elementi ''registrati'', che possono essere:
</p><p><itemize><item>aggiunti
<item>cancellati
<item>anche modificati
</itemize><p><verb>                           _______      _______      _______
Variabile Globale ------&gt; |Item(1)| -&gt; |Item(2)| -&gt; |Item(3)|  ..
                          |_______|    |_______|    |_______|
</verb></p><sect1>Variabili principali
<sect2>Current
<p><verb>                           __________________
Current ----------------&gt; | Processo attuale |
                          |__________________|
</verb></p><p>Current punta alla struttura ''task_struct'', che contiene tutte
 le informazioni relative ad un Processo:
</p><p><itemize><item>pid, nome, stato, contatori, politica di scheduling
<item>puntatori ad altre strutture dati come: files, vfs, altri processes,
 signals...
</itemize></p><p>Current non e' una vera variabile, piuttosto una macro: 
</p><p><verb>static inline struct task_struct * get_current(void) &lcub; 
   struct task_struct *current; 
   __asm__(&quot;andl &percnt;&percnt;esp,&percnt;0; &quot;:&quot;=r&quot; (current) : &quot;0&quot; (&tilde;8191UL)); 
   return current; 
&rcub;
&num;define current get_current()
</verb></p><p>Le linee sopra prendono il valore di ''ESP'' (stack pointer)
 e lo rendono disponibile come una variabile, che poi verra' usata
 per puntare ad una struttura task_struct.
</p><p>Dall'elemento ''current'' possiamo accedere quindi direttamente
 ad ogni struttura dati Kernel del processo (ready, stopped o in qualunque
 altro stato), ad esempio possiamo cambiargli lo stato (come fa ad
 esempio un driver di I/O), il PID, la presenza o meno nella Ready
 List o nella Blocked List, ecc.
</p><sect2>FileSystem Registrati
<p><verb>                       ______      _______      ______
file_systems  ------&gt; | ext2 | -&gt; | msdos | -&gt; | ntfs |
 &lsqb;fs/super.c&rsqb;         |______|    |_______|    |______|
</verb></p><p>Quando digitiamo il comando ''modprobe some_fs'' verra' aggiunta
 una nuova entry alla lista dei file systems, mentre con il comando
 ''rmmod'' la andremo a rimuovere.
</p><sect2>FileSystem ''montati''
<p><verb>                        ______      _______      ______
mount_hash_table  ----&gt;|   /  | -&gt; | /usr  | -&gt; | /var |
&lsqb;fs/namespace.c&rsqb;       |______|    |_______|    |______|
</verb></p><p>Il comando ''mount'' permette di aggiungere un file system alla
 lista dei fs gia' montati nel sistema, mentre la umount cancella
 la relativa voce.
</p><sect2>Network Packet Type ''Registrati''
<p><verb>                        ______      _______      ______ 
     ptype_all  ------&gt;|  ip  | -&gt; |  x25  | -&gt; | ipv6 |
&lsqb;net/core/dev.c&rsqb;       |______|    |_______|    |______|
</verb></p><p>Ad esempio, se si vuole aggiungere l'IPv6 (caricando il modulo
 relativo) sara' necessario inserire la voce del Network Packet Type
 nella lista.
</p><sect2>Network Internet Protocol Registrati
<p><verb>                          ______      _______      _______ 
inet_protocol_base -----&gt;| icmp | -&gt; |  tcp  | -&gt; |  udp  |
&lsqb;net/ipv4/protocol.c&rsqb;    |______|    |_______|    |_______|
</verb></p><p>Ogni Network Packet Type puo' avere all'interno una serie di
 protocolli che si possono aggiungere alla lista (come IPv6 che ha
 i protocolli TCPv6).
</p><p><verb>                          ______      _______      _______ 
inet6_protos -----------&gt;|icmpv6| -&gt; | tcpv6 | -&gt; | udpv6 |
&lsqb;net/ipv6/protocol.c&rsqb;    |______|    |_______|    |_______|
</verb></p><sect2>Network Device registrati 
<p><verb>                          ______      _______      _______ 
dev_base ---------------&gt;|  lo  | -&gt; |  eth0 | -&gt; |  ppp0 |
&lsqb;drivers/core/Space.c&rsqb;   |______|    |_______|    |_______|
</verb></p><sect2>Char Device Registrati 
<p><verb>                          ______      _______      ________ 
chrdevs ----------------&gt;|  lp  | -&gt; | keyb  | -&gt; | serial |
&lsqb;fs/devices.c&rsqb;           |______|    |_______|    |________|
</verb></p><p>''chrdevs'' non e' in realta' un vero puntatore ad una lista,
 piuttosto un vettore standard.
</p><sect2>Block Device Registrati
<p><verb>                          ______      ______      ________ 
bdev_hashtable ---------&gt;|  fd  | -&gt; |  hd  | -&gt; |  scsi  |
&lsqb;fs/block_dev.c&rsqb;         |______|    |______|    |________|
</verb></p><p>''bdev_hashtable'' e' un vettore di hash
</p><sect>Glossario
<sect>Links
<p><url url="http://www.kernel.org" name="Sorgenti Ufficiali del Kernel e Patches">
</p><p><url url="http://jungla.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html" name="Ottima documentazione sul Kernel">
</p><p><url url="http://www.uwsg.indiana.edu/hypermail/linux/kernel/index.html" name="Mailing List Ufficiale dello sviluppo del Linux Kernel">
</p><p><url url="http://www.tldp.org/guides.html" name="Guide Linux Documentation Project per il Linux Kernel">
</p>

</article>