Il Vantaggio di Linux (by Linus Torvalds)

Linux si trova oggi ad avere milioni di utenti, migliaia di sviluppatori e un mercato in espansione. È presente in sistemi integrati, è usato per il controllo di dispositivi robotizzati e ha volato a bordo dello Shuttle. Sarebbe bello poter dire che avevo immaginato quello che sarebbe successo, che era tutto parte di un mio piano per la conquista del mondo: ma, onestamente, sono stato colto di sorpresa. Ho avvertito, sì, la transizione dall'essere l'unico utente di Linux all'essercene un centinaio, ma quella da cento utenti ai milioni mi è sfuggita. Linux ha avuto successo non tanto grazie al suo intento originario - essere largamente portabile e disponibile - quanto perché è basato su solidi principi di progettazione e su un solido modello di sviluppo. La portabilità e la disponibilità risultano da queste robusta fondamenta.
Provate per un attimo a confrontare Linux con progetti dotati di una forte base commerciale, quali Java o Windows NT. L'entusiasmo per Java ha convinto molti che il motto "write once, run anywhere" ("lo scrivi una volta, lo esegui dappertutto") fosse un obiettivo in sé sufficiente. Siamo prossimi a un momento in cui i compiti di elaborazione saranno demandati a una gamma sempre più vasta e differenziata di hardware: dunque, non c'è dubbio che quello sia certo un valore importante. Diciamo però che "write once, run anywhere" non è un'invenzione della Sun. La portabilità ha rappresentato a lungo il Sacro Graal dell'industria informatica. La Microsoft, per esempio, confidava originariamente in Windows NT come in un sistema portabile, che girasse su macchine Intel ma anche sulle macchine RISC diffuse nell'ambiente workstation. Linux non ha mai nutrito ambizioni così grandi. È dunque un'ironia della sorte che Linux sia diventato un ambiente di tale fortuna per il codice multipiattaforma.

All'inizio Linux era indirizzato a un'unica architettura: l'Intel 386. Oggi Linux gira su qualunque cosa, dai PalmPilot alle workstation Alpha, ed è il sistema operativo più soggetto a porting fra quelli disponibili per PC. Se dunque si scrive un programma che gira su Linux, per un'ampia gamma di macchine, quel programma sarà effettivamente "scritto una volta ed eseguito dappertutto". È interessante allora ripercorrere le decisioni che portarono al progetto di Linux, vedere come lo sforzo di sviluppo di Linux si sia evoluto, e come Linux abbia finito per diventare un qualcosa che l'intuizione originale non aveva affatto contemplato.

Amiga e il porting su Motorola
Per quanto Linux, come sistema operativo, sia affine a Unix, non si tratta di una versione di Unix; questo significa che la sua portata ereditaria è altro da quella, per esempio, di Free BSD. Per capirci: i creatori di Free BSD partirono dal codice sorgente dello Unix di Berkeley, e il loro kernel discende in linea retta da quel sorgente. Quindi, Free BSD è una versione di Unix, al cui albero genealogico appartiene. Di contro, Linux, pur proponendosi di fornire un'interfaccia compatibile con Unix, ha un kernel completamente originale, senza riferimenti al codice sorgente di Unix. Dunque, Linux stesso non è un porting di Unix ma un sistema operativo nuovo.
All'inizio non pensavo affatto a portare questo nuovo sistema operativo su altre piattaforme: mi bastava qualcosa che funzionasse sul mio 386.
Lo sforzo vero per rendere portabile il codice del kernel di Linux cominciò con quello fatto per portare Linux sull'Alpha di DEC. Ma neanche quello su Alpha fu il primo porting.
Il primo porting fu opera di un team che portò il kernel di Linux sulla serie 68K della Motorola, cioè sui processori dei primi computer Sun, Apple e Amiga. Era nelle intenzioni dei realizzatori del porting su Motorola fare qualche una cosa di basso livello, e in Europa si trovava una nutrita comunità di utenti Amiga che non vedevano con favore la prospettiva di dover passare a usare DOS o Windows.

In quel modo, gli utenti Amiga si trovarono in effetti un sistema che girava sul 68K. Ma io esiterei a definirlo un porting di Linux riuscito. L'approccio era più o meno quello che avevo adottato io quando mi ero messo a creare Linux: scrivere del codice radicalmente nuovo, che supportasse un'interfaccia di un certo tipo. Possiamo allora definire meglio il porting su 68K come un sistema operativo affine a Linux, che ha portato a una biforcazione della struttura di base del codice.

Se, da una parte, questo primo Linux per 68K non aiutò veramente a creare un Linux portabile, fu utile in un altro senso. Quando cominciai a pensare al porting su Alpha, l'esperienza del 68K mi diede pure da pensare. Se avessi adottato su Alpha quel medesimo approccio, mi sarei ritrovato con tre diverse strutture di base del codice che avrei dovuto supportare per mantenere in vita Linux. Se anche la cosa fosse stata fattibile quanto alla programmazione, non lo sarebbe stata in termini di gestione. Come avrei potuto gestire lo sviluppo di Linux, se per farlo avessi dovuto tener traccia di una struttura di base del codice nuova di zecca ogni volta che qualcuno avesse richiesto Linux su una nuova architettura? Quello che invece io volevo fare era un sistema in cui poter avere uno specifico albero Alpha, uno specifico albero 68K e uno specifico albero x86, ma tutto su una sola e comune struttura di base del codice.
Fu allora che sottoposi il codice a una riscrittura sostanziale. Ma quella riscrittura fu motivata dalla necessità di lavorare con una comunità crescente di sviluppatori.

 

Microkernel
Quando cominciai a scrivere il kernel di Linux, vigeva un'autorevole scuola di pensiero a proposito di come dovesse essere scritto un sistema portabile. Il senso comune prescriveva che si usasse un'architettura a microkernel.
Con un kernel monolitico com'è quello di Linux, la memoria è divisa fra spazio dell'utente e spazio del kernel. Lo spazio del kernel è dove si trova caricato effettivamente il codice del kernel, e dove è allocata la memoria per le operazioni a livello del kernel. Le operazioni del kernel comprendono la pianificazione degli eventi (scheduling), la gestione dei processi, la segnalazione, i dispositivi I/O, la paginazione e lo swapping: le operazioni fondamentali su cui si basano gli altri programmi. Dal momento che il codice del kernel include interazione a basso livello con l'hardware, i kernel monolitici devono evidentemente essere specifici per una specifica architettura.

Un microkernel esegue un insieme di operazioni assai più ridotto, e in forma più ristretta: comunicazione dei processi interni, limitata gestione e scheduling dei processi e qualche I/O di basso livello. È chiaro che i microkernel sono meno specificamente collegati all'hardware, dato che molte delle peculiarità del sistema sono demandate allo spazio dell'utente. Un'architettura a microkernel è fondamentalmente una maniera per astrarre i dettagli del controllo dei processi, dell'allocazione di memoria e dell'allocazione delle risorse, in modo che il porting su un chipset diverso non richieda che modifiche minime.

È per questo che, al tempo in cui mi misi al lavoro su Linux (1991), si dava per assodato che la portabilità dovesse derivare da un approccio in stile microkernel (si ricordi che in quell'epoca questo era una specie di chiodo fisso per gli informatici). Ma io, che sono un pragmatico, mi ero reso conto che i microkernel a) erano sperimentali, b) erano palesemente più complessi dei kernel monolitici, e c) rispetto a quelli, avevano una velocità d'esecuzione notevolmente inferiore. La velocità è, nella pratica, un fattore-chiave per qualunque sistema operativo: dunque gran parte del denaro per la ricerca veniva allora speso per escogitare un'ottimizzazione dei microkernel, tale che essi potessero eseguirsi alla velocità dei kernel normali. La cosa buffa è che, se si ha la pazienza di leggere quelle relazioni, si scopre che i trucchi adottati dai ricercatori per ottimizzare i microkernel avrebbero potuto agevolmente essere applicati ai kernel tradizionali, per accelerarne l'esecuzione.

Fu proprio questo a farmi pensare che l'approccio a microkernel non fosse che un espediente disonesto, volto a ottenere più fondi per le ricerche. Non voglio dire che quei ricercatori fossero disonesti intenzionalmente: può darsi che fossero solo stupidi, o illusi - nel vero senso della parola. La disonestà era il risultato della pressione ossessiva sull'argomento "microkernel" che allora pesava sulla comunità dei ricercatori. Nei laboratori di ricerca informatica, o si studiavano i microkernel oppure i kernel non si studiavano affatto. Restare onesti e resistere alla pressione era dunque difficile per tutti, anche per chi stava realizzando Windows NT. Il team di sviluppo di NT sapeva benissimo che il risultato finale sarebbe stato ben lontano da un microkernel, ma si rassegnò a pagare il suo tributo formale a quell'idea.

Devo dire che, per mia fortuna, non dovetti mai subire simili pressioni. L'Università di Helsinki svolgeva ricerca sui sistemi operativi fin dai tardi anni Sessanta, e il kernel di un sistema operativo non più era da noi considerato un soggetto degno di particolari studi. Era giusto, in un certo senso: i fondamenti dei sistemi operativi, e per estensione il kernel di Linux, potevano considerarsi sviscerati già dai primi anni Settanta; quanto è venuto dopo allora è stato, entro certi limiti, un esercizio di autogratificazione.

Se si vuole che un codice sia portabile, non è necessario creare un livello astratto per raggiungere la portabilità: bisogna piuttosto programmare con intelligenza. In parole povere, cercare di rendere portabili i microkernel è una perdita di tempo: come costruire un'automobile velocissima e montarvi pneumatici quadrati. L'idea di rendere astratta proprio quella parte che dovrebbe essere veloce come il fulmine - il kernel - è intrinsecamente controproducente. Non è con questo che io voglia dire che la ricerca sui microkernel si esaurisce lì, ma gran parte del problema è una discrepanza negli obiettivi. Quello della maggior parte delle ricerche sui microkernel era la produzione di un ideale teorico, l'invenzione di un progetto che fosse portabile al massimo su ogni possibile architettura. Con Linux, io miravo molto più in basso; m'interessava la portabilità non su sistemi teorici, ma su sistemi reali.

Da Alpha alla portabilità
Il porting su Alpha s'iniziò nel 1993 e richiese circa un anno, dopo il quale il lavoro non era ancora completato, ma le fondamenta erano gettate. Le difficoltà di questo primo porting furono ripagate dalla formulazione di alcuni principi progettuali che Linux ha seguito da allora e che hanno reso più semplici i porting successivi.

Il kernel di Linux non è concepito per essere portato su qualsiasi architettura. Stabilii che, se l'architettura di destinazione è salda abbastanza nelle sue basi e si attiene a certe regole fondamentali, Linux sarà in grado di supportare quel modello. Per esempio, la gestione della memoria può variare molto da una macchina a un'altra. Dopo aver letto con cura le specifiche sulla gestione della memoria nel 68K, nello Sparc, nell'Alpha e nel PowerPC, scoprii che, pur fra molti dettagli differenti, c'era molto in comune nell'uso della paginazione, nel caching e così via. Queste architetture avevano un denominatore comune su cui scrivere la gestione della memoria del kernel di Linux; in seguito, non sarebbe stato troppo arduo modificare il codice di gestione della memoria secondo i dettagli di un'architettura specifica.

Alcuni punti fermi aiutano immensamente a semplificare il problema del porting. Per esempio, se una CPU ha funzioni di paginazione, deve per estensione avere un TLB (Translation Lookup Buffer, area di memoria temporanea di traduzione) che indichi alla CPU come mappare la memoria virtuale perché la CPU possa usarla. Naturalmente, non si può sapere con certezza quale forma debba prendere il TLB: va detto però che la sola cosa che importi sapere è come riempirlo e come svuotarlo quando si decide che debba scomparire. Così, in quest'architettura ben congegnata, si saprà che il kernel dovrà avere alcune parti specifiche per la macchina, ma che la gran parte del codice sarà basato sul meccanismo generale col quale funziona qualcosa come il TLB. Un'altra regola pratica che io seguo è che è sempre meglio usare una costante di tempo di compilazione piuttosto che una variabile: seguendo questa regola, il compilatore ottimizzerà assai meglio il codice. L'astuzia è evidente, dato che permette di scrivere il codice così che sia definito con flessibilità ma ottimizzato facilmente.

La cosa interessante di quest'approccio - tentare di definire una solida architettura comune - è che, così facendo, si presenta al sistema operativo un'architettura migliore di quella effettivamente disponibile sulla piattaforma hardware. Questo non è molto intuitivo, ma è importante. Le generalizzazioni che si ricercano quando si fa la ricognizione di un sistema corrispondono spesso alle ottimizzazioni che si desiderano operare per migliorare la prestazione del kernel.

Quando si fa una ricognizione abbastanza approfondita di cose quali l'implementazione delle tabelle (o matrici) di pagina e sulla base delle osservazioni fatte si decide, per esempio, che l'albero di pagina dev'essere profondo solo tre livelli, si scopre più tardi che ciò poteva essere fatto solo sulla base di un interesse autentico per alte prestazioni. In altre parole, avendo come meta non la portabilità ma l'ottimizzazione del kernel su una certa architettura, si raggiungerà spesso la medesima conclusione: ovvero, la profondità ideale per il kernel per rappresentare l'albero della pagina è di tre.

Questo non è semplicemente un caso fortunato. Spesso, quando un'architettura si scosta in alcuni particolari da un progetto generale solido, è perché si tratta di un cattivo progetto. Così, lo stesso pincipio che porta ad aggirare nella programmazione le specifiche di progetto per raggiungere la portabilità, conduce anche ad aggirare le caratteristiche di progetto difettose e ad attenersi a un progetto generale più ottimizzato. Fondamentalmente, ho cercato di raggiungere un compromesso mischiando il meglio della teoria a una considerazione realistica delle architetture odierne.

Spazio del kernel e spazio dell'utente
Con un kernel monolitico come quello di Linux è vitale la cautela nell'ammettere nel kernel nuovo codice e nuove funzioni. Queste decisioni possono riflettersi più tardi su tanti aspetti del ciclo di sviluppo, ben oltre il lavoro specifico sul kernel.
La prima e fondamentale regola è di evitare interfacce. Se qualcuno vuole aggiungere qualcosa che comporti una nuova interfaccia di sistema, bisogna stare in guardia. Una volta che gli utenti abbiano a disposizione un'interfaccia, cominceranno ad aggiungervi codice: e quando questo sia avvenuto, ci si trova intrappolati. Si vuole davvero supportare la stessa identica interfaccia per tutta la vita del sistema?
Altro codice presenta meno problemi. Se non ha un'interfaccia, per esempio un driver del disco, non c'è da pensarci molto: si può aggiungere un nuovo driver con poco rischio. Se Linux non aveva quel driver, l'aggiunta non danneggerà nessuno che già lo usasse e anzi aprirà Linux a nuovi utenti.

Altri campi richiedono equilibro. È una buona implementazione? La funzione aggiunta è davvero buona? A volte, anche quando è buona, salta poi fuori che è cattiva l'interfaccia o che l'implementazione di quella funzione implica in qualche modo che non si potrà mai più fare una cosa diversa, né ora né in futuro.

Per esempio - anche se questa, per la verità, è una questione d'interfaccia - si supponga che qualcuno abbia implementato sconsideratamente un file system così che i nomi non possano avere di più di quattordici caratteri: la tipica cosa da evitare, dal momento che questa limitazione ha luogo in un'interfaccia che è scolpita nella roccia. Diversamente, quando si cerca di estendere il file system, si resta con un palmo di naso, perché toccherà trovare il modo di inserirvi in quest'interfaccia inferiore, che era bloccata. Ancora peggio, ogni programma che richieda un nome di file potrà aver spazio in una variabile solo per, diciamo, tredici caratteri: se si vuole passargli un nome di file più lungo, lo si manderebbe in crash.

Al momento il solo produttore che faccia una scemenza simile è Microsoft. Essenzialmente, per leggere file DOS/Windows c'è questa ridicola interfaccia in cui ogni file aveva undici caratteri, otto più tre. Con NT, che consente nomi più lunghi, Microsoft ha dovuto aggiungere un set completo di nuove routine per fare la stessa cosa che facevano altre routine: solo che queste nuove gestiscono anche nomi di file più lunghi. Ecco un esempio di cattiva interfaccia, che inquina anche i lavori a venire.

Un altro esempio lo vediamo nel sistema operativo Plan 9. Qui si trovava una chiamata al sistema veramente ben fatta per migliorare una biforcazione di un processo (process fork): una maniera semplice perché un programma si divida in due e continui a elaborare lungo entrambi i rami. Questo nuovo ramo, che Plan 9 ha chiamato R-Fork (e SGI, più tardi, S-Proc) in pratica crea due spazi separati di processo che condividono uno spazio d'indirizzamento. Questo risulta utile soprattutto nel threading.

Linux fa la stessa cosa con la sua chiamata di sistema clone, che qui è però implementata correttamente. Tuttavia, con le routine di SGI e Plan 9 si decise che i programmi con due rami potessero condividere lo stesso spazio d'indirizzamento ma usando stack separati. Di solito, se si usa lo stesso indirizzamento in entrambi i thread, si ottiene la stessa zona di memoria. Ma si ha un segmento di stack che è specifico, così, se si adopera un indirizzo di memoria basato su stack si ottengono in pratica due diverse zone di memoria che possono condividere un puntatore di stack senza sovrapporsi all'altro stack.

È un risultato intelligente, ma con un rovescio, e cioè che il dispendio complessivo per mantenere gli stack rende il tutto, all'atto pratico, una vera stupidaggine. Troppo tardi si è capito che le prestazioni andavano in malora, e dal momento che i programmi in uso erano dotati d'interfaccia, non si poté porvi rimedio. Si dovette invece introdurre un'interfaccia aggiuntiva, meglio scritta, perché si potesse alla fine fare un uso sensato dello spazio dello stack. Mentre a un produttore con un marchio è possibile, talvolta, dar la colpa all'architettura, Linux non ha questa libertà.

È questo un altro campo in cui gestire lo sviluppo di Linux e prendere decisioni su di esso suggeriscono il medesimo approccio. Da un punto di vista pratico, non mi era possibile gestire un gran numero di sviluppatori che fornivano interfacce al kernel. Avrei perso il controllo sul kernel. Ma dal punto di vista del progetto questa è la cosa giusta da fare: tenere il kernel piccolo e limitare al minimo il numero d'interfacce e di altri vincoli allo sviluppo futuro. Va da sé che nemmeno Linux è esente da peccati. Ha ereditato una quantità d'interfacce orripilanti da implementazioni precedenti di Unix. Dunque, in alcuni casi sarei stato ben felice di non dover mantenere la stessa interfaccia di Unix. Ma Linux è tanto "pulito" quanto può esserlo un sistema operativo che non nasce dal nulla: e se si desidera poter eseguire applicazioni Unix, bisogna accettare il fardello di Unix come conseguenza. Ma ne è valsa la pena, perché poter eseguire quelle applicazioni si è rivelato vitale per la popolarità di Linux GCC

Unix stesso è un eccellente esempio di portabilità operata con successo. Il kernel Unix, come molti altri, conta sull'esistenza del C per gran parte della portabilità che gli serve; lo stesso è per Linux. Per Unix, la grande disponibilità di compilatori per C su molte architetture diverse ha reso possibile portarvi Unix. Unix sottolinea dunque l'importanza dei compilatori, che è una della ragioni per cui ho scelto porre Linux sotto la GNU Public License (GPL). La GPL era la licenza del compilatore GCC. Penso che ogni altro progetto del gruppo GNU scompaia di fronte a Linux: l'unico che io tenga in qualche conto è GCC. Molti degli altri li odio con passione: l'editor Emacs, per dirne uno, è orribile. Linux è più grosso di Emacs, ma almeno ha la scusa di esserlo per buoni motivi.

Ma, fondamentalmente, i compilatori sono davvero una necessità primaria.
Adesso che il kernel Linux segue un progetto in genere portabile (almeno su architetture ragionevolmente solide), la portabilità dovrebbe essere possibile finché si potrà far uso di un buon compilatore. Se penso al kernel, i chip a venire non mi preoccupano più molto quanto alla portabilità fra architetture: mi preoccupo piuttosto dei compilatori. Il chip a 64-bit di Intel, il Merced, spiega bene perché, dal momento che per un compilatore esso è qualcosa di molto diverso.
Dunque, la portabilità di Linux è strettamente legata al fatto che GCC venga portato sulle principali architetture di chip.

 

Moduli del kernel
Il kernel Linux ha subito mostrato chiaramente che quanto ci serve è un sistema il più possibile modulare. Il modello di sviluppo open source lo vuole, per non avere gente che lavori simultaneamente sugli stessi punti. È doloroso dover spesso constatare l'incompatibilità del lavoro di due persone sulla stessa parte del kernel.
Senza modularità mi toccherebbe controllare ogni file modificato - e sarebbero moltissimi - per accertarmi che non sia cambiato nulla che potrebbe riflettersi su qualcos'altro. Con la modularità, quando ricevo le patches per un nuovo file system e non mi fido, mi tranquillizzo pensando che, se nessuno usa quel file system, non ci saranno danni.

Per esempio, Hans Reiser è al lavoro su un nuovo file system che ha appena cominciato a funzionare. È mia opinione che, a questo punto, non valga la pena accingersi alla versione 2.2 del kernel: ma grazie alla sua modularità, se volessi potrei farlo, e non sarebbe nemmeno difficile. La cosa difficile è far sì che i programmatori non si pestino i piedi l'un l'altro. Col kernel 2.0 Linux è veramente cresciuto. È in questa versione che abbiamo aggiunto dei moduli caricabili del kernel, il che ha comprensibilmente migliorato la modularità producendo una struttura esplicita per scrivere i moduli. I programmatori potrebbero lavorare su moduli diversi senza rischio d'interferenza e io potrei mantenere il controllo su quanto viene scritto all'interno del kernel vero e proprio. Dunque, una volta di più, la gestione dell'elemento umano e quella del codice hanno condotto alle medesime decisioni progettuali. Per mantenere la coordinazione fra le persone al lavoro su Linux ci serviva qualcosa come i moduli del kernel: e questa si è rivelata una decisione savia anche dal punto di vista del progetto.

L'altra faccia della modularità è meno ovvia e più problematica. Concerne il caricamento del run-time, riconosciuto da tutti come cosa buona ma non di meno fonte di nuovi problemi. Il primo è tecnico, e come (quasi) tutti i problemi tecnici, non difficile da risolvere. Più scottanti le questioni extra-tecniche, per esempio: fino a che punto un modulo è un derivato di Linux, e quindi soggetto a GPL?

Quando uscì il primo modulo d'interfaccia, ci fu chi aveva scritto driver per SCO e che rifiutò di rilasciare il sorgente, come la GPL prevede, dichiarandosi invece disposto a fornire i binari per Linux. Fu allora che conclusi, per ragioni morali, che la GPL non era in casi simili applicabile.

La GPL richiede che prodotti "derivati da" un altro prodotto brevettato sotto GPL ricadano pure sotto la GPL. Purtroppo, la nozione di "derivazione" è vaga. Nel momento in cui si tenta di tracciare un confine che contenga i lavori derivati, subito sorge il problema di dove tracciarlo. Abbiamo finito per decidere (o, forse meglio: ho finito per decretare) che le chiamate al sistema non venissero considerate come collegamenti al kernel: quanto a dire che ogni programma eseguito sotto Linux non sarebbe stato coperto da GPL. La decisione è stata presa molto per tempo e a tale scopo ho perfino aggiunto uno speciale file read-me (v. appendice B) perché non sfuggisse a nessuno. Questo perché l'industria possa scrivere programmi per Linux senza temere la GPL.

Il risultato, per gli autori dei moduli, è stato che era possibile scrivere un modulo proprietario qualora si fosse usata per il caricamento solo l'interfaccia normale. Tuttavia questa è ancora una zona grigia del kernel. Queste zone grigie lasciano forse agio a qualcuno di approfittare dello stato di cose, e questo accade perché la GPL è poco chiara su argomenti come l'interfaccia a moduli. Se qualcuno violasse la normativa usando i simboli esportati in modo tale che sia chiaro che lo fa solo per aggirare la GPL, credo che ci sarebbe materia per perseguire penalmente quella persona. Ritengo, tuttavia, che nessuno intenda abusare del kernel; chi ha mostrato interesse commerciale nel kernel lo ha fatto solo perché interessato ai benefici del modello di sviluppo.

La potenza di Linux risiede nella cooperazione comunitaria che c'è dietro non meno che nel codice stesso. Se Linux venisse dirottato, se si cercasse di produrne e distribuirne una versione proprietaria, svanirebbero da quella versione le ragioni del suo richiamo di Linux, che sono le stesse del modello di sviluppo open source.

La portabilità oggi
Oggi Linux ha colto molti di quei traguardi che un tempo si ritenevano riservati solo a un'architettura a microkernel.
Edificando un modello generale a partire da elementi comuni di un'architettura tipica, il kernel Linux vanta molti vantaggi di portabilità che richiederebbero altrimenti un livello d'astrazione, e questo senza pagar pegno in termini di prestazioni, come invece accade ai microkernel.
Con l'introduzione dei moduli del kernel, il codice specifico per un hardware può spesso essere confinato in un modulo, mantenendo ampiamente portabile il nucleo del kernel. I driver di periferiche sono un buon esempio di uso efficace di moduli del kernel per confinare entro moduli le specificità hardware; si tratta di un ottimo compromesso fra l'inserire nel nucleo del kernel tutte le specificità hardware (kernel veloce ma non portabile) e collocarle invece nello spazio dell'utente (kernel lento o inutilizzabile, o entrambe le cose).

Ma l'approccio di Linux alla portabilità è stato benefico anche per la comunità di sviluppatori che gli si è raccolta intorno. Le decisioni che motivano la portabilità consentono anche a un ampio gruppo il lavoro simultaneo su parti di Linux senza ch'io perda il controllo sul kernel. Le generalizzazioni d'architettura su cui Linux si basa mi danno un quadro di riferimento sul quale controllarlo e forniscono un'astrazione sufficiente perché io non debba mantenere biforcazioni del codice rigidamente separate per diverse architetture. Così, nonostante il gran numero di persone che lavorano su Linux, riesco a tener dietro al nucleo del kernel. E i moduli del kernel forniscono un modo ovvio perché i programmatori lavorino indipendentemente su parti del sistema che dovrebbero effettivamente essere indipendenti.

Il futuro di Linux
La decisione di fare il meno possibile nello spazio del kernel continua a sembrarmi ottima. La verità sacrosanta è che, a questo punto, non riesco a figurarmi aggiornamenti decisivi del kernel. C'è un momento in cui un progetto software di successo è maturo, ed è allora che il ritmo delle modifiche rallenta. Non ci sono grandi novità in serbo per il kernel: si tratta più che altro di supportare una gamma più ampia di sistemi, di approfittare della portabilità di Linux per portarlo su nuovi sistemi.

Vedremo nuove interfacce, ma - io credo - in gran parte legate ai nuovi sistemi supportati. Per esempio, quando si comincerà a fare clustering, si vorrà dire allo scheduler di pianificare certi gruppi di processi come pianificazioni di gruppo (gang secheduling), e cose simili. Ma, allo stesso tempo, non vorrei vedere una concentrazione esclusiva sul clustering o sulle applicazioni di calcolo intensivo: gran parte del futuro risiede nei computer laptop, o in schede da inserire dovunque ci si trovi. Mi piacerebbe che Linux esplorasse anche queste strade.

Ci sono poi i sistemi integrati (embedded), dove non esiste letteralmente interfaccia. Il sistema è accessibile solo per l'upgrade del kernel, forse, ma per il resto rimane invisibile: e questa è un'altra direzione per Linux. Non penso che Java o Inferno (il sistema operativo integrato di Lucent) si affermeranno nel campo dei dispositivi integrati, perché non hanno colto il succo della legge di Moore. Può sembrare giusta, sulle prime, l'idea di progettare un sistema ottimizzato specifico per un particolare dispositivo integrato, ma al momento in cui il progetto sarà praticamente maneggiabile, la legge di Moore avrà abbassato il prezzo dell'hardware più potente, svalutando il potenziale insito nello sviluppo per un dispositivo specifico. I prezzi calano così velocemente che conviene avere sulla scrivania lo stesso sistema che si ha in un dispositivo integrato; la vita sarà più semplice per tutti.

Il multi-processing simmetrico (SMP) è un'area che avrà sviluppo. Il kernel 2.2 di Linux gestirà a dovere quattro processori e verrà sviluppato fino a supportarne otto o sedici. Il supporto per più di quattro processori esiste già, ma non è ancora usabile. Avere più di quattro processori, in questo momento, è tempo sprecato. Ci sono insomma ampi margini di miglioramento.
Ma chi vorrà supporto per sessantaquattro processori, dovrà usare un versione speciale del kernel, perché dotare il kernel di un supporto simile causerebbe un decremento delle prestazioni per l'utente comune.

Alcune aree specifiche d'applicazione continueranno a stimolare sviluppi del kernel. I server Web sono stati sempre una sfida interessante, in quanto applicazioni molto impegnative per il kernel. Potrei dire che per me sono un pericolo, perché tale è il riscontro che ho da chi usa Linux come piattaforma di Web serving, che sarei tentato di ottimizzare il kernel a quel solo scopo. Così, mi devo sforzare di ricordare che il server Web è un'applicazione importante, ma non è tutto. Naturalmente, i server Web attuali non impiegano Linux alla sua massima potenza; Apache stesso, per esempio, non gestisce i thread nel modo migliore.

Questo tipo di ottimizzazione è stata rallentata dai limiti dell'ampiezza di banda in rete. Al presente, una rete da 10 Mbit viene saturata con tanta facilità che non c'è ragione di ottimizzare oltre. L'unico modo per non saturare reti da 10 Mbit è di avere un'infinità di CGI che lavorino a pieno regime: ma qui il kernel non può aiutare. Ciò che potrebbe fare, piuttosto, è rispondere direttamente alle richieste di pagine statiche e passare le richieste più complicate ad Apache. Quando le reti veloci saranno più diffuse la cosa si farà più interessante, ma al momento ci manca la massa critica di hardware per il testing e lo sviluppo.

Cosa ricavare da tutto quanto precede? Che io voglio che Linux sia sempre sulla breccia, e se possibile un po' oltre, perché quanto oggi è "oltre la breccia", domani sarà sulle vostre scrivanie.
Ma gli sviluppi più eccitanti per Linux avverranno non nello spazio del kernel, ma in quello degli utenti. I cambiamenti nel kernel sembreranno poca cosa paragonati a quelli presto in atto nelle zone decentrate del sistema: da questo punto di vista interessa meno chiedersi dove sarà il kernel di Linux che non immaginare le funzioni che saranno contenute in Red Hat 17.5, o dove arriverà Wine (l'emulatore di Windows) fra pochi anni.

Fra quindici anni mi aspetto di veder arrivare qualcuno che dirà "Ehi, faccio tutto quello che fa Linux ma sono più leggero e più scattante, perché il mio sistema non ha un bagaglio di vent'anni sulle spalle". Dirà che Linux è stato creato con il 386 in mente e che le nuove CPU fanno in modo diverso delle cose molto interessanti, e insomma di lasciar perdere quel ferrovecchio di Linux. È più o meno quanto ho fatto io quando ho scritto Linux. E in futuro si potrà esaminare il nostro codice, usare le nostre interfacce e fornire la compatibilità binaria, e quando tutto questo succederà, io ne sarò felice.

Free Joomla templates by Ltheme