The Unix and Internet Fundamentals HOWTO | Cerca per titolo, autore, parola chiave | ||||||||
The Unix and Internet Fundamentals HOWTO The Unix and Internet Fundamentals HOWTO, di Eric Raymond, Revision 2.11, maggio 2010. Questo manuale vuole essere d'aiuto agli utenti Linux e Internet che tendono ad imparare facendo. Pur essendo un ottimo metodo di apprendimento, soprattutto se riferito a specifiche competenze, questo approccio può lasciare alcuni vuoti, nelle conoscenze del lettore, soprattutto nella comprensione del quadro complessivo, di come realmente funziona il tutto e di cosa realmente accada dietro le quinte. In queste pagine tenterò di descrivere, in modo chiaro, con un linguaggio semplice, come tutto questo funziona. Il vostro computer contiene un chip processore che è la parte che realmente esegue i calcoli. Il processore, a sua volta, contiene una memoria interna (la "RAM" o, come la chiamano alcuni propgrammatori Unix il "core", in memoria dei primi sistemi di memorizzazione, chiamati Magnetic-core memory, spesso consistenti in elementi in ferrite (ferrite-core donut). Processore e memoria risiedono nella scheda madre (motherboard), che è il cuore del vostro computer. Il vostro computer avrà anche uno schermo ed una tastiera, uno o più dischi fissi (hard drive), un CD-ROM, un DVD e, magari, anche un floppy disk. Alcuni di questi dispositivi sono gestiti da schede apposite, o controller card, alloggiate nella scheda madre, che aiutano il computer a utilizzarli correttamente; altri dispositivi, invece, vengono gestiti da chipset specializzati, installati direttamente nella scheda madre, che svolgono la stessa funzione dei controller. La tastiera è così semplice da non richiedere alcun controller separato; il controller viene quindi installato direttamente all'interno dello chassis della tasiera stessa. Tutti i componenti del vostro computer sono collegati al processore da un bus. Il bus, in realtà, è proprio ciò che viene installato nella scheda madre per ciascuno dei controller (video card, disk controller, sound card etc.). Il bus è l'autostrada attraverso la quale i dati viaggiano dal processore allo schermo, alla tastiera, al disco fisso, e viceversa. Se vi è capitato di incorrere in nomi quali ISA, PCI, PCMCIA, sappiate che si tratta di tipi diversi di bus. ISA è, a parte qualche dettaglio minore, lo stesso bus utilizzato nei primi PC IBM degli anni 80; oggi non viene più utilizzato. PCI, che sta per Peripheral Component Interconnection, è il bus utilizzato nei moderni computer, così come nei Macintosh. PCMCIA è una variante di ISA che, utilizzando connettori fisici più piccoli, viene usato nei computer portatili o nei laptop. Il processore, che comanda tutto ciò che accade nel sistema, in realtà, non è in grado di vedere direttamente i componenti del sistema, ma può solo parlare con loro attraverso il bus. Il solo altro sottosistema a cui il processore può accedere velocemente, istantaneamente, è la memoria (core). Quindi, affinchè i programmi possano essere eseguiti, devono risiedere nella memoria. Quando il vostro computer legge un programma o dei dati dal disco fisso, ciò che realmente accade è che il processore invia sul bus una richiesta di lettura al controller del disco fisso. Qualche tempo dopo, il controller del disco utilizza il bus per informare il processore di aver letto i dati e di averli trasferiti in una determinata locazione di memoria (RAM). A questo punto, il processore può cercare i dati, sempre utilizzando il bus. Anche la tastiera e lo schermo comunicano con il processore attraverso il bus, ma in maniera più semplice. Cosa succede quando accendiamo un computer? Senza un programma in esecuzione, un computer è solo un pezzo inerte di componenti elettronici. La prima cosa che un computer deve fare quando viene acceso, è di eseguire un programma speciale chiamato sistema operativo. Il compito del sistema operativo è di permettere ad altri programmi di funzionare, occupandosi di tutti i dettagli correlati alla gestione dell'hardware. Il processo di esecuzione del sistema operativo viene chiamato booting (originariamente chiamato bootstrapping, dalla locuzione inglese "pull yourself up by your bootstraps", farcela da soli). Il vostro computer sa come eseguire il boot del sistema operativo, perchè trova le istruzioni direttamente in uno dei suoi chip, il BIOS (Basic Input/Output System). Il chip del BIOS dice al vostro computer di cercare, in un punto del disco fisso, solitamente il disco fisso con il numero più basso, il disco di boot, un programma speciale chiamato "boot loader" (in Linux, Grub o LILO). Il boot loader viene trasferito in memoria e, quindi, eseguito. Il compito del boot loader è di lanciare il sistema operativo vero e proprio. Il loader lancia il sistema operativo cercando un kernel, caricandolo in memoria e, quindi, dando il via alla sua esecuzione. Potreste chiedervi perchè il BIOS non carichi direttamente il kernel. Perchè sono necessari due differenti stadi, con l'intervento del boot loader? Bene, il BIOS è un programma davvero stupido e Linux non se ne servirà più, una volta lanciato il processo di boot. Fu scritto originariamente per i computer a 8-bit, dotati di dischi fissi molto piccoli: inoltre, il BIOS non è in grado di accedere a tante posizoni del disco fisso, da permettere di caricare il kernel. Inoltre, l'utilizzo del boot loader permette di scegliere il sistema operativo da eseguire, tra i diversi sistemi operativi eventualmente installati sul disco fisso. Una volta lanciato, il kernel cerca l'hardware collegato al sistema e si prepara ad eseguire i programmi. L'hardware trovato non viene collegato a indirizzi di memoria ordinari, ma a indirizzi speciali di memoria, destinati al bus, chiamati I/O port (porte di Input/Output), ai quali si presume siano connessi i controller delle periferiche, in attesa di eventuali comandi. Il kernel non punta a locazioni di memoria a caso, poichè ha già una certa conoscenza su cosa probabilmente trovare e dove trovarla, oltre a sapere già quali risposte attendersi nel caso i controller fossero presenti. Questo processo è chiamato autoprobing. Potreste essere in grado di vedere tutto quello che sta accadendo, durante il processo di boot. Quando i sistemi Unix usavano le console testuali (lo schermo nero), si potevano vedere scorrere sullo schermo tutti i messaggi di boot. Ai giorni nostri, invece, Unix nasconde spesso i messaggi di boot, dietro ad un'immagine di caricamento (graphical splash screen). In alcune distribuzioni, è possibile passare dall'immagine grafica alla console testuale, premendo i tasti: Ctrl-Shift-F1 Nel caso funzionasse, potrete tornare alla schermata grafica di boot, provando una delle sequenze seguenti: Ctrl-Shift-F7 Ctrl-Shift-F8 Ctrl-Shift-F9 La gran parte dei messaggi di boot riguardano l'attività di riconoscimento dell'hardware (autoprobing), da parte del kernel, attraverso le porte I/O, utile alla corretta configurazione del sistema. Ma, caricare ed eseguire il kernel non rappresenta l'intero processo di boot, ma ne rappresenta solo il primo passo (chiamato a volte level 1). Dopo questa prima fase, il kernel trasferisce il controllo ad uno speciale processo, chiamato "init", che genera una serie di processi di amministrazione. Alcune distribuzioni Linux recenti utilizzano un diverso programma, chiamato "upstart", che fa più o meno le stesse cose. Il primo compito di init è, normalmente, eseguire un check del disco fisso. I file system, infatti, sono cose molto fragili, che potrebbero danneggiarsi in caso di interruzioni hardware, oppure di improvvise interruzioni di corrente. Il compito successivo di init è di eseguire diversi demoni (daemon). Un demone è un programma come lo spooler di stampa, un programma di mail (che resti in ascolto), un server WWW, che resta in attesa, in background, di qualcosa da fare. Sono demoni perchè sono sempre in esecuzione e gestiscono tutte le richieste in arrivo, senza costringere il sistema ad eseguire più istanze dello stesso programma, contemporaneamente, assegnando a ciascuna una singola richiesta, con il rischio di collisioni. Il compito successivo di init è la preparazione per gli utenti (user). Il programma init lancia una copia del comando getty per controllare lo schermo e la tastiera (probabilmente, anche altre copie per controllare le porte seriali di dial-in). In realtà, ai giorni nostri, init esegue più istanze di getty, così da rendere disponibili più console virtuali (normalmente, 7 o 8), con lo schermo e la tastiera connessi ad una di esse. Probabilmente, voi non vi accorgerete di tutte queste console virtuali, perchè una di esse verrà occupata dal vostro X server (di cui parleremo tra poco). Ma non è ancora finita. Il passo successivo è l'esecuzione di altri demoni, che supportano i servizi di rete e altri servizi, tra i quali il più importante è il vostro X server. X è un demone che gestisce il vostro schermo, la tastiera ed il mouse. Il suo principale compito è produrre la grafica a colori a cui siamo ormai abituati. Quando lo X server appare, nelle ultime fasi di boot, rileva il controllo dell'hardware dalla console che lo aveva in gestione fino a quel momento. A quel punto, voi vedrete uno schermo grafico di login, prodotto da un programma chiamato display manager. Cosa succede quando si effettua il login? Effettuare il login significa identificarsi. Nelle distribuzioni moderne, solitamente l'identificazione avviene attraverso un display manager grafico. Ma è sempre possibile passare alla console virtuale, premendo i tasti: Ctrl-Shift-F1 aprendo così un terminale testuale. In questo caso, viene eseguita una istanza di getty, che a sua volta chiamerà il programma: login L'identificazione richiede l'inserimento di un nome di login (user) e di una password. Il nome di login è memorizzato in un file: /etc/passwd che è composto da più righe, ciascuna delle quali rappresenta un utente. carlo:x:1000:1000:carlo,,,:/home/carlo:/bin/bash Uno di questi campi è la versione criptata della password. Nelle versioni più recenti, il campo criptato viene salvato in un secondo file: /etc/shadow con permessi molto più ristretti, in modo da rendere ancora più difficile scovare la password. In questo caso, il campo password del file passwd conterrà una x, come nel nostro esempio. La password che voi scrivete a terminale viene criptata nello stesso modo, così da permettere al programma login di verificare se le due versioni criptate coincidono. La sicurezza di questo sistema risiede nel fatto che è facile passare dalla versione in chiaro della password alla sua versione criptata, ma il passaggio contrario (dalla versione criptata alla versione in chiaro) è molto, ma molto più complessa. Quindi, anche se qualcuno intercetta la vostra password criptata, non sarà comunque in grado di utilizzarla, accedendo al vostro account. Attenzione: questo significa anche che se voi dimenticate la vostra password di accesso, non ci sarà alcuna possibilità di recuperarla. Una volta entrati nel sistema (logged in), vi vedrete assegnati, dal sistema stesso, tutti i privilegi associati al vostro account. Il vostro account, inoltre, potrebbe essere associato ad un gruppo di utenti. Ogni gruppo ha un nome e viene creato, solitamente, dall'amministratore del sistema. I gruppi possono vedersi assegnare privilegi diversi, dal sistema, indipendentemente dai privilegi detenuti dagli utenti membri. Un utente può essere membro di più gruppi. Nonostante si utilizzino i nomi degli utenti e dei gruppi, internamente essi vengono memorizzati con ID numerici. Il file: /etc/passwd mappa il nome di ogni account al suo ID numerico, mentre il file: /etc/group mappa il nome di ogni gruppo al suo ID numerico. Nella riga del file passwd, viene anche riportata la home directory assegnata all'account, la directory in cui verranno salvati i file personali dell'utente, nel nostro esempio: /home/carlo Oltre alla home directory, nella riga del file passwd troviamo anche la shell assegnata all'utente, vale a dire l'interprete dei comandi che il programma login lancia per permettervi di inserire i vostri comandi. Nel nostro esempio: /bin/bash Quello che accade dopo il log, dipende da come avete ottenuto l'accesso. In una console testuale, il programma login lancia una shell che vi permetterà di eseguire i vostri comandi. Se, invece, siete entrati usando un display manager, il server X vi servirà il vostro desktop grafico e voi sarete in grado di eseguire programmi da quell'ambiente, o direttamente dal menu, oppure dalle icone presenti nel desktop, oppure da una emulatore di terminale che, a sua volta, eseguirà una shell. Una volta terminato il boot del sistema, cosa accade quando si esegue un programma? Terminato il boot, prima di eseguire un qualsiasi altro programma, il computer è come uno zoo di processi che restano in attesa di qualcosa da fare. Quello che aspettano, in realtà, è un evento. Premere un tasto è un evento. Muovere il mouse è un evento. Un pacchetto di dati in arrivo dalla rete è un evento. Il kernel è uno di questi processi. È un processo speciale, naturalmente, poichè è lui a controllare quando i processi utente possono essere eseguiti ed è, normalmente, il solo processo ad avere un accesso diretto all'hardware della macchina. Infatti, i processi utente, quando vogliono catturare l'input della tastiera, scrivere sullo schermo o leggere dal disco (o scrivere sul disco), fare qualsiasi altra cosa che non sia muovere bit nella memoria, devono fare una richiesta al kernel. Queste richieste sono note come system call (chiamate di sistema). Normalmente, tutte le operazioni di I/O passano dal kernel, così che il kernel possa programmarle, evitando che i diversi processi si intralcino l'uno con l'altro. Un pugno di processi utente speciali è autorizzato ad aggirare il kernel, normalmente vedendosi assegnato l'accesso diretto alle porte I/O. I server X sono l'esempio più comune di ciò. I programmi possono essere eseguiti in due modi: attraverso il server grafico X oppure tramite shell. Spesso, in realtà, facciamo entrambe le cose, perchè apriamo un emulatore di terminale dall'ambiente grafico X. L'emulatore di terminale imita le vecchie console testuali, aprendo una shell per permetterci di inserire i comandi. I'll describe what happens when you do that, then I'll return to what happens when you run a program through an X menu or desktop icon. La shell si chiama shell (guscio) proprio perchè chiude e nasconde il kernel del sistema operativo. È importante notare che la shell ed il kernel sono programmi separati, in Unix, che comunicano tra loro grazie ad una serie di chiamate di sistema. La shell è solo un processo utente. Non è un processo speciale. La shell resta in attesa dei dati in arrivo dalla vostra tastiera, ascoltando (attraverso il kernel) la sua porta di I/O. Non appena il kernel vede i caratteri battuti sulla tastiera, li invia alla vostra console virtuale o al vostro emulatore di terminale X. Quando il kernel vede un "Enter", trasmette la vostra riga testuale alla shell, che cercherà di interpretare quelle battute come comandi. Ipotizziamo che voi scriviate: ls e Enter (list). La shell verifica le sue regole interne e intuisce che voi desiderate eseguire il file: /bin/ls La shell, quindi, esegue una chiamata di sistema chiedendo al kernel di eseguire quel file come nuovo processo child, dandogli accesso allo schermo ed alla tastiera, tramite il kernel. A questo punto, la shell entra in stato di sleep (dormiente), in attesa che il comando ls termini. Una volta terminato, il comando ls invia al kernel un avviso di uscita dalla chiamata di sistema. Il kernel, a questo punto, fa uscire dallo stato di sleep la shell. La shell produce un altro prompt e resta in attesa della prossima riga di input. Durante l'esecuzione di ls, possono accadere altre cose. È possibile aprire una nuova console virtuale, eseguire il login, iniziare, per esempio, un gioco. Oppure, il vostro computer potrebbe essere in fase di invio o ricezione di una mail. Quando lanciamo un'applicazione dal server X e non dalla shell (scegliendo un'applicazione da un menu grafico, oppure facendo doppio click su un'icona del desktop), ciascuno dei programmi associati al vostro server X può comportarsi come una shell, lanciando l'applicazione. Il server X, tuttavia, non va in stato di sleep, quando un programma è in esecuzione, bensì si interpone tra voi ed il programma in esecuzione, trasmettendogli i movimenti del vostro mouse o della vostra tastiera e rispondendo alle sue richieste di mostrare aree di pixel del vostro schermo. Come funzionano le periferiche di input e gli interrupt? La vostra tastiera è una periferica di input davvero semplice, poichè genera poche quantità di dati e a velocità molto basse. Quando premete o rilasciate un tasto della tastiera, l'evento viene inviato al cavo della tastiera stessa, generando un interrupt hardware. È compito del sistema operativo tenere d'occhio questi interrupt. A ciascun interrupt, viene associato un gestore di interrupt (interrupt handler), una parte del sistema operativo stesso che conserva i dati associati al particolare interrupt, fino al momento in cui potranno essere elaborati. Ciò che l'interrupt handler della vostra tastiera fa, è mettere il valore del tasto premuto in un'area di memoria di sistema, vicina al fondo della memoria, dove resterà fino a quando il sistema operativo passerà il controllo ad un qualsiasi programma che sia in grado di leggere i dati della tastiera. Periferiche di input molto più complesse, quali i dischi fissi o le schede di rete, funzionano più o meno in modo simile. Abbiamo detto, poco prima, che il controller del disco fisso utilizza il bus per segnalare che una richiesta (di dati) è stata completata. Ciò che realmente accade è che il disco invia un segnale di interrupt. L'interrupt handler del disco fisso, quindi, copia i dati estratti dal disco in memoria, affinchè l'applicazione che li aveva richiesti li possa, in seguito, recuperare. Ogni interrupt si vede associato un livello di priorità. Gli interrupt di bassa priorità (come l'interrupt della tastiera), ovviamente, devono aspettare che prima siano soddisfatti gli interrupt di maggiore priorità (come i clock tick o gli eventi del disco). Tra i messaggi di log del sistema operativo, relativi al processo di boot, potreste trovare messaggi riguardanti i numeri IRQ. Fate attenzione, poichè uno degli errori più comuni è di assegnare lo stesso numero IRQ a due diverse periferiche, senza un vero motivo. IRQ sta per "Interrupt Request". Il sistema operativo deve sapere, al momento dello startup, a quali interrupt sono associate le singole periferiche, così da poter assegnare a ciascuna di esse il corretto interrupt handler. Se due differenti periferiche utilizzano lo stesso IRQ, l'interrupt potrebbe attivare un errato interrupt handler. Questo comporta, di solito, il blocco della periferica e, a volte, il crash del sistema. Come può, il mio computer, fare più cose alla volta? In realtà, non lo fa! Un computer può svolgere solo un compito alla volta (o processo). Ma un computer può cambiare attività in modo rapidissimo e illudere l'uomo che stia svolgendo più compiti allo stesso tempo. Questo si chiama timesharing. Uno dei compiti del kernel è gestire il timesharing. Al suo interno, il kernel ha uno scheduler (to schedule, letteralmente "mettere in lista", ovvero "pianificare", schedulatore o gestore di processi) che conserva le informazioni su tutti i processi diversi dal kernel, presenti nel vostro zoo. Ogni sessantesimo di secondo (1/60), scatta un timer del kernel, che genera un clock interrupt. Lo scheduler blocca il processo che stava eseguendo, lo salva in una posizione di memoria, sospendendolo, e passa il controllo ad un altro processo. Un sessantesimo di secondo potrebbe non sembrare molto tempo, ma, con i processori di oggi, quel tempo è sufficiente ad eseguire decine di migliaia di istruzioni macchina. Quindi, ciascun processo può completare un bel po' di lavoro nel suo timeslice (fetta di tempo). E non è sempre detto che un processo possa davvero utilizzare l'intero timeslice a lui assegnato. Se una periferica di I/O invia un interrupt, il kernel interrompe il processo corrente, esegue l'interrupt handler, per poi tornare al processo appena interrotto. Quindi, un serie di interrupt ad alta priorità può sconvolgere la normale esecuzione dei processi; questo tipo di fenomeno viene chiamato thrashing ed è, fortunatamente molto raro, nei moderni sistemi Unix. È molto più facile che i ritardi nei processi siano determinati dalle attese di dati dal disco fisso o dalla rete. Lo scheduler si preoccupa di distribuire il tempo ai vari processi. Ma il sistema operativo deve anche preoccuparsi di tenere separati gli spazi di memoria dei vari processi. Le cose che il vostro sistema operativo fa per risolvere questo problema vengono chiamate "gestione della memoria" (memory management). Ciascun processo ha bisogno della sua area di memoria, dove ci sia il codice (istruzioni) da eseguire e dove memorizzare le variabili e i risultati. Quindi, ciascun processo avrà un read-only code segment (segmento di codice di sola lettura, contenente le istruzioni al processo) e un writeable data segment (segmento dati sovrascrivibile, contenente le variabili generate dal processo). Il data segment è realmente unico per ciascun processo, ma due processi potrebbero dover eseguire lo stesso codice. In questo caso, Unix automaticamente permette ai due processi di condividere lo stesso code segment. L'efficienza è molto importante, visto che la memoria è molto cara. A volte, non avete sufficiente memoria da distribuire tra tutti i programmi in esecuzione, specialmente quando utilizzate il server grafico X. In questo caso, Unix usa una tecnica chiamata virtual memory. Invece di tenere codice e dati di un processo in memoria, Unix ne tiene solo la quantità strettamente necessaria per lavorare, salvando il resto in un'area speciale di swap (scambio), presente sul disco fisso. Oggi, in realtà, la memoria è meno costosa di un tempo, così che anche i computer più limitati ne hanno a sufficienza. Nei computer di oggi, anche quelli domestici, single-user, con 64 MB di memoria o più, è possibile eseguire il server grafico X e tutta una serie di altri programmi, senza nemmeno aver bisogno dell'area di swap. In realtà, fino ad ora abbiamo semplificato eccessivamente le cose. Ok, i programmi vedono la vostra memoria come una lunga fila di indirizzi, fila molto più grande della vostra memoria fisica, grazie al meccanismo di swap. Ma, il vostro hardware, in realtà, contiene non più di cinque tipi differenti di memoria:
Perchè ben 5 tipi di memoria differenti? Per un problema di costi. La velocità costa. Abbiamo elencato i tipi di memoria in ordine crescente di tempi di accesso e decrescente di costo. I registri del processore sono i più veloci, in termini di accesso, ma sono anche i più costosi: è possibile accedere ad essi circa un miliardo di volte al secondo, mentre al disco fisso, il meno veloce ed il meno caro tra gli apparecchi, si può accedere più o meno 100 volte al secondo. Ecco un elenco dei mezzi e delle velocità riscontrabili nei prima anni 2000, per un computer desktop tipico. Capacità e velocità possono sicuramente migliorare, mentre i prezzi, probabilmente, scenderanno, ma c'è da aspettarsi che i rapporti tra le grandezze resteranno sempre le stesse:
Naturalmente, non è possibile fondare un sistema esclusivamente sui tipi di memoria più veloci: sarebbe troppo costoso, oltre ad essere poco consigliabile, visto che le memorie più veloci sono tutte volatili: se manca la corrente, perdono i dati, tutti! Per questo motivo, i computer devono dotarsi di una qualche memoria non volatile, quale quella dei dischi fissi, in cui memorizzare i dati in modo permanente, anche quando non alimentate dalla corrente elettrica. Inoltre, tra la velocità dei processori e la velocità dei dischi fissi esiste una enorme differenza, che rende i due tipi di dispositivo incompatibili tra loro: le tre memorie intermedie (cache interna, cache esterna, RAM, o main memory) vengono utilizzate proprio per colmare questa differenza. Linux ed i sistemi operativi Unix hanno una funzionalità chiamata memoria virtuale (virtual memory), che permette loro di comportarsi come se avessero a disposizione molta più memoria RAM di quanta, in effetti, dispongano. La vostra memoria fisica si comporta come se fosse suddivisa in una serie di finestre, o cache, inserite in uno spazio virtuale di memoria molto più grande, la gran parte del quale risiede, in ogni dato momemto, in un'area del disco fisso chiamata swap area. Il sistema operativo, nascondendo il processo ai programmi utente, muove continuamente questi blocchi di dati, (chiamati "pagine" o "page") spostandoli dalla memoria al disco fisso e viceversa. Grazie a questo meccanismo, la vostra memoria virtuale è molto più grande, ma non molto più lenta della memoria reale. Quanto più lenta sia la memoria virtuale dipende anche dagli algoritmi di swapping adottati dal vostro sistema operativo e da quanto questi algoritmi si adattino all'utilizzo della memoria virtuale messa in pratica dai vostri programmi. Fortunatamente, le operazioni di lettura e di scrittura vicine nel tempo tendono ad essere raggruppate nello stesso spazio di memoria. Questa tendenza è chiamata locality, o, più formalmente, locality of reference (oppure principle of locality, principio di località) ed è una buona cosa. Reference è il riferimento ad un'altra locazione di memoria che potrebbe contenere, per esempio, l'operando su cui l'istruzione dovrà agire. Il principio della locality parte dal presupposto che un'applicazione non avrà la necessità di accedere a tutti i suoi dati contemporaneamente, ma solo ad una piccola porzione di essi, per ogni dato momento. Se un dato è referenziato, c'è una buona probabilità che verrà referenziato anche nell'immediato futuro. Questo tipo di locality viene chiamata temporal locality. Inoltre, se un dato è referenziato, c'è una buona probabilità che verranno referenziati anche i dati nelle immediate vicinanze, sempre nell'immediato futuro. Questo tipo di locality viene chiamata spatial locality. Un'applicazione può avvantaggiarsi della Locality, salvando in una memoria più veloce quei dati utilizzati più spesso oppure i dati appena utilizzati. Questo è il meccanismo del “caching”. Se, invece, le referenze a indirizzi di memoria venissero riferite a locazioni sparse nell'intera memoria virtuale, si rischierebbe di dover effettuare operazioni di lettura e scrittura direttamente sul disco fisso per ogni nuova referenza, costringendo il sistema ai bassi tempi di accesso al disco fisso. Il sistema della locality permette di ridurre, quindi, il ricorso allo scambio (swap) tra memorie veloci e dischi fissi. Uno degli schemi più usati e più efficaci di gestione della memoria virtuale è quello chiamato LRU ("least recently used"). Il sistema di memoria virtuale prende dal disco fisso i blocchi di dati di cui ha bisogno. Quando la memoria fisica tende a terminare, il sistema si libera del blocco di dati utilizzato meno recentemente. La memoria virtuale è il primo anello del ponte ideale che accorda la velocità del disco fisso e quella del processore. È gestita dal sistema operativo. Resta anche un'altra differenza di prestazioni: il processore, infatti, può accedere ai propri registri di memoria a velocità notevolmente superiori a quelle supportate dalla RAM (main memory). Le due cache, interna ed esterna, ovviano a questo problema, utilizzando una tecnica simile alla memoria virtuale appena descritta. Se la memoria fisica (RAM) si struttura come una serie di finestre (o cache) salvate nell'area di swap del disco fisso, la cache esterna agisce come una serie di finestre salvate in RAM. La cache esterna è più veloce (250M di accessi al secondo, contro i 100M della RAM) e più piccola. L'hardware (il controller della memoria) esegue l'algoritmo LRU nella cache esterna, su blocchi di dati estratti dalla main memory (RAM). Per ragioni storiche, l'unità di cache nello swapping viene chiamata line, piuttosto che page. E non è ancora finita. La cache interna, a sua volta, più veloce e ancora più piccola, esegue il caching di porzioni della cache esterna. La cache interna è installata direttamente nel chip del processore. Anche quando avete sufficiente memoria fisica da evitare lo swapping, il gestore della memoria (memory manager) ha ancora un compito da assolvere: assicurarsi che i programmi possano alterare solo il loro segmento di memoria. Per farlo, crea una tabella contenente i segmenti dati e i segmenti codice (data e code segment). La tabella viene aggiornata ogni volta in cui un processo chiede più memoria, oppure rilascia memoria (solitamente all'uscita del processo stesso). Questa tabella viene usata per passare i comandi ad una unità hardware specializzata, chiamata MMU (memory management unit), installata direttamente nei chip del processore. La MMU ha la capacità di disegnare confini intorno a specifiche aree di memoria, così da poter respingere i tentativi di accedere ad aree di memoria non assegnate, generando, contemporaneamente, uno speciale interrupt. Quando ricevete un messaggio del tipo: "Segmentation fault", "core dumped" o qualcosa del genere, significa che un processo ha tentato di accedere ad una locazione di memoria esterna al suo segmento, generando un interrupt fatale.
|
|||||||||
The Unix and Internet Fundamentals HOWTO | Disclaimer: questo è un link a contenuti ospitati su server esterni. |