scrax Posted August 22, 2010 Share Posted August 22, 2010 IN PREPARAZIONE L'avvio del sistema In queste pagine vengono descritte le prime fasi della procedura di avvio di un sistema GNU/Linux, con riferimento al codice sorgente del kernel 2.6.1. Riprodotto con il permesso di Linux Magazine, Edizioni Master. Introduzione L'avvio del sistema operativo è il delicato insieme di operazioni che trasformano un groviglio di metallo e silicio in una potente macchina da calcolo multiutente e multiprocesso. Nonostante non sia necessario conoscere cosa succeda alla partenza della macchina per poterla successivamente usare, ho sempre trovato molto interessante vedere come i programmatori hanno affrontato le vari fasi di inizializzazione del sistema. Conoscere la divisione dei compiti tra i vari programmi e i vari file del kernel, può anche essere utile; per esempio per diagnosticare cosa succede quando la propria macchina si rifiuta di partire o quando si deve far girare il kernel sulla scheda personalizzata che si è appena finito di costruire. Il segnale di reset Quando la macchina viene alimentata, viene attivato il segnale di reset (lo stesso che talvolta è attivabile direttamente dall'utente premendo un pulsante). In risposta al reset, il processore inizia ad eseguire le istruzioni in un modo predeterminato nella CPU stessa. La maggior parte dei processori moderni eseguono le istruzioni a partire dall'indirizzo fisico 0, oppure caricano un puntatore a 32-bit da tale indirizzo e lo usano come vettore del codice di avvio. Il PC, invece, si comporta diversamente. In risposta al reset la CPU si trasforma in un 8086, un processore a 16 bit che può indirizzare solo 1MB di memoria, ed esegue le istruzioni all'indirizzo 0xffff0, cioè appena sotto il limite di 1MB. In entrambi i casi, le prime istruzioni eseguite dal processore risiedono su memoria non volatile, spesso memoria cosiddetta "flash". Nel caso del PC il codice di avvio è parte del famigerato BIOS, con cui tutti abbiamo imparato a convivere; su altre macchine si può trattare di "open firmware" o altro codice generico; sui sistemi embedded spesso il codice eseguito al reset è direttamente il boot loader, riprogrammabile tramite interfaccia JTAG o BDM. Riquadro 1 - La memoria flash La memoria flash è memoria non volatile che si può cancellare a blocchi senza rimuoverla dal circuito; tipicamente un blocco è di 32kB, 64kB o 128kB. È l'evoluzione di una storia fatta di ROM ("Read-Only Memory"), PROM ("Programmable ROM", non cancellabile), EPROM ("Erasable PROM", cancellabile tramite esposizione ai raggi ultravioletti), EEPROM ("Electrically Erasable PROM", cancellabile senza rimuoverla dal circuito). Il principale problema della memoria flash, come degli altri dispositivi cancellabili, è l'usura provocata dalle operazioni di cancellazione; un blocco di flash non può essere cancellato più di qualche migliaio o qualche decina di migliaia di volte. La flash è il componente che si trova all'interno di tutte le schede di memoria oggi disponibili sul mercato: Compact Flash, MMC, Secure Digital, chiavette USB. In questi dispositivi la flash è associata ad un microcontrollore che gestisce l'allocazione e la cancellazione dei blocchi, oltre al protocollo di alto livello -- IDE, USB o altro -- usato per comunicare con la macchina ospite. In un dispositivo embedded, come un palmare o una macchina industriale non-x86, la flash è saldata sulla motherboard e direttamente visibile sul bus di memoria del processore, come la RAM e le periferiche. Può quindi ospitare direttamente il boot loader, il kernel e il filesystem della macchina, senza bisogno di trasferire i dati in RAM. La gestione di alto livello delle aree di memoria ROM o flash nel kernel Linux è implementata dal sottosistema MTD (Memory Technology Device), che coordina l'accesso agli spazi di indirizzi associati a memoria non-RAM e gestisce allocazioni e cancellazioni dei blocchi di flash. È importante ricordare che sulla memoria flash non è possibile ospitare direttamente un filesystem come ext2 o FAT, sia a causa dell'elevata dimensione dei blocchi, sia per l'elevato numero di riscritture del "superblocco" da parte di questi filesystem. Sulla flash deve essere ospitato un filesystem apposito (come JFFS2) oppure un filesystem read-only (per esempio ROMFS o CRAMFS). Il ruolo del microcontrollore nei dispositivi elencati in precedenza, infatti, è anche quello di disaccoppiare l'utilizzo reale della flash dalla struttura dati visibile dalla macchina ospite, che spesso è proprio un filesystem FAT. Riquadro 2 - JTAG e BDM L'interfaccia JTAG (Joint Test Action Group, IEEE Std 1149.1) è uno standard elettrico e un protocollo di comunicazione per la verifica della funzionalità dei circuiti integrati. Molti circuiti integrati oggi contengono un modulo di supervisione e di controllo remoto accessibile tramite interfaccia JTAG, cui di solito si accede con un cavo apposito dalla porta parallela di un PC. Nel caso di sistemi a microprocessore, l'interfaccia JTAG permette tra le altre cose di pilotare il bus dati e il bus indirizzi della CPU, per cui è sempre possibile riprogrammare la memoria flash di un dispositivo il cui processore è dotato di JTAG. BDM (Background Debug Mode) è un'interfaccia sviluppata da Motorola per il controllo remoto. Offre funzionalità simili a JTAG ed è generalmente disponibile sui processori ColdFire e su alcuni PowerPC. Alcuni processori sono dotati sia di interfaccia JTAG sia di interfaccia BDM. Il BIOS Su PC, il BIOS effettua una generica inizializzazione della macchina e rende disponibile ai programmi una libreria di funzioni che effettuano operazioni di base come caricare in memoria settori di dati dal floppy o dal disco rigido; il nome BIOS significa infatti "Basic Input-Output System". Una volta determinata la periferica da usare per il boot, il BIOS carica un settore (512 byte) da tale periferica all'indirizzo 0x7c00 ed esegue il codice ivi contenuto. Il BIOS si preoccupa anche di chiamare il codice di auto-inizializzazione delle periferiche PCI o ISA, ove presente; questo permette, per esempio, alle schede di rete di modificare la procedura di boot e caricare dalla rete il passo successivo della procedura di avvio, invece che da un disco locale. In base ai protocolli usati dalla scheda di rete e alla configurazione scelta, tale passo successivo può essere il boot loader o direttamente il kernel del sistema operativo. Uno standard frequentemente usato per il boot via rete su x86 si chiama PXE, "Pre-eXecution Environment". Le schede di rete conformi a tale standard utilizzano i protocolli DHCP e TFTP per caricare dalla rete un programma eseguibile. È possibile in questo modo caricare un boot loader che permetta all'utente interattivo di scegliere tra varie opzioni di boot o invocare comandi di diagnostica del sistema. GRUB, per esempio, è un boot loader in grado di girare in ambiente PXE oltre che in ambiente BIOS. Il boot loader Qualunque sia la piattaforma hardware e la modalità di avvio, a un certo punto il controllo passa ad un programma che si preoccupa di caricare il kernel Linux in memoria e predisporre alcuni parametri hardware necessari al suo funzionamento, si tratta del cosiddetto "boot loader". Il boot loader può variare in dimensione da pochi byte ad alcuni megabyte. Si tratta di pochi byte quando il controllo viene passato direttamente ad una copia del kernel residente in un banco di memoria flash mappata nello spazio di indirizzamento del processore (come succede su alcune macchine con processore ColdFire, per esempio). Si può trattare di alcuni megabyte quando, per esempio, il boot loader è una copia del kernel Linux in qualche modo adattata a questo nuovo ruolo; su alcune macchine infatti si è scelto di usare il kernel come boot loader per beneficiare del supporto già presente in Linux per molte periferiche, senza doverlo reimplementare nel loader. Questo e' l'approccio usato per il boot del NetWinder (una macchina ARM) e per la costruzione di MILO (il boot loader usato su molte macchine Alpha); alcune macchine per usi specifici contengono addirittura un sistema GNU/Linux completo residente in flash, usato come boot loader per una nuova versione di kernel e di filesystem. La maggior parte dei boot loader permettono all'utente di interagire su porta seriale; quelli per PC (come GRUB e LILO) normalmente interagiscono su VGA ma possono essere anch'essi configurati per usare la porta seriale verso un'altra macchina, permettendo quindi il controllo remoto delle opzioni di avvio, per esempio da una sessione ssh su questa altra macchina. Il boot loader termina l'esecuzione nel momento in cui ha caricato in memoria un kernel e lo ha eseguito. 13.2 Dal Kernel a Init Una volta effettuate tutte queste verifiche il kernel carica in memoria RAM un altro programma e gli cede il controllo del sistema. Il kernel (il cuore del sistema operativo) e' operativo, pero' occorre ancora configurare l'ambiente utente. Per far cio' il kernel carica un altro programma in memoria. Tale programma si chiama /sbin/init. Il programma init e', come si intuisce dal nome, il programma che si occupa di inizializzare il sistema operativo vero e proprio (INITialize significa inizializza). Tutto cio' che e' stato fatto fino ad ora infatti, e' stata solamente una preparazione dell'ambiente hardware (memoria, processore, dischi etc). Init e' il padre di tutti i processi di un sistema operativo Unix/Linux. Poiche' init e' il primo processo che viene attivato, il suo PID e' uguale a 1 (il PID, cioe' Process IDentifier e' il numero progressivo assegnato ad ogni processo). Le prime operazioni eseguite da init sono solitamente standard, ma le directory interessate possono variare leggermente da distribuzione a distribuzione. Secondo alcune scuole di pensiero infatti, la directory /etc dovrebbe contenere solo file di configurazione e nessuno script eseguibile, secondo altre alcuni script sono pur sempre inerenti alla configurazione del sistema e possono risiedere nella directory /etc. Ad esempio, su un sistema Red Hat, init esegue lo script /etc/rc.d/rc.sysinit che tra le altre cose imposta lo swapping e controlla il filesystem. Questo script in sostanza si occupa di tutti quei processi che devono essere eseguiti in fase di inizializzazione del sistema (come ad esempio l'impostazione dell'orologio). Successivamente init esamina il file /etc/inittab e lo esegue. Prima di analizzare cio' che e' contenuto all'interno di questo file, occorre definire il concetto di runlevel. Il runlevel (livello di esecuzione) e' uno stato, una modalita' del sistema. Esistono solitamente 6 runlevel o stati del sistema: il runlevel 0 corrisponde all'arresto del sistema, il runlevel 6 corrisponde al riavvio del sistema, il runlevel 1 corrisponde alla modalita' utente singolo (usato di solito per operazioni di manutenzione del sistema), il runlevel 3 corrisponde alla modalita' multiutente da console (cioe' con terminale testuale e senza grafica), il runlevel 5 corrisponde alla modalita' multiutente con ambiente grafico (ad esempio xdm, gdm, kdm etc). I runlevel 2 e 4 non sono predefiniti e possono essere personalizzati dall'utente. E' sempre possibile passare da un runlevel all'altro usando il programma init, ad esempio: http://www.linux.it/~rubini/docs/boot26/boot26.html http://www.wowarea.com/italiano/linux/faseboot.htm Init: http://en.wikipedia.org/wiki/Init Mac OS X System Startup: http://osxbook.com/book/bonus/ancient/what...ch_startup.html Link to comment Share on other sites More sharing options...
Recommended Posts