Introduzione a Unix: un po’ di storia C. Baroglio∗ a.a. 2002–2003 Il sistema operativo utilizzato nella parte di laboratorio del corso intitolato “Sistemi Operativi e Sperimentazioni” è Unix. Unix è nato nel 1969, questo significa che noi lo studieremo nel corso del suo trentaquattresimo anno di esistenza e di evoluzione. Studiare uno strumento che esiste da tanto tempo, sopravvivendo a molti antagonisti, e ritrovando negli ultimi anni un nuovo impulso di affermazione (grazie alle varie distribuzioni Linux e al lavoro di molti affezionati sparsi un po’ ovunque nel mondo), è un compito interessante sia per motivi tecnico-scientifici (quali caratteristiche rendono Unix cosı̀ popolare come sistema operativo?) sia perché qualcuno potrebbe un giorno di questi farvi la domanda “ma tu usi già Linux?” (come è successo a me pochi mesi fa) ed allora voi potrete divertirvi a raccontare alla persona in questione l’interessante storia di questo strumento. Spesso nella storia una cosa importante nasce a seguito di un evento che di per sè non sarebbe mai assurto agli onori della cronaca. Nel caso del nostro sistema operativo, nella seconda metà degli anni sessanta del secolo scorso, il signor Ken Thompson –ricercatore presso i Bell Labs della AT&T– scrisse il programma di un gioco: Space Travel. A quell’epoca i Bell Labs erano coinvolti in un progetto finalizzato alla realizzazione di un sistema operativo chiamato Multics, per un mainframe GE645 (della General Electrics). L’esecuzione del gioco sul GE645 avveniva però a scatti ed era molto costosa. Di qui l’esigenza di sviluppare un supporto diverso. Thompson e alcuni suoi colleghi (fra cui Dennis Ritchie) svilupparono un nuovo sistema operativo multi-tasking, che sfruttava una gestione del file system innovativa e comprendeva un interprete di comandi ed alcune utility, per un altro tipo di computer: il DEC PDP-7. Brian Kernighan lo battezzò UNICS (Uniplexed Information and Computing System), poco dopo sintetizzato in Unix. Molte idee implementate in quella versione si sono rivelate vincenti, tanto da essersi mantenute inalterate fino ai giorni nostri. Un po’ in sordina e con scarsi finanziamenti, l’avventura continuò. L’anno successivo venne sviluppato un programma di formattazione del testo (antesignano da un lato dei moderni pacchetti Office, dall’altro di linguaggi di formattazione come LaTeX –usato per esempio per scrivere queste note– e HTML) ∗ Fonte: UNIX la guida completa, di K. Rosen, D. Host, J. Farber e R. Rosinski, McGrawHill, 1999. 1 chiamato runoff. Il successore di runoff, troff, fu il primo programma mai sviluppato per la pubblicazione elettronica. Altra data importante nella storia di Unix è il 1973: in quell’anno Thompson e Ritchie riscrissero il kernel in un linguaggio di programmazione di alto livello, il C (un linguaggio ideato dallo stesso Ritchie e da Kernighan). Un’idea innovativa, in quanto fino ad allora tutti i sistemi operativi erano scritti in linguaggio assembly, e al contempo geniale in quanto rese questo sistema operativo facilmente mantenibile e, soprattutto, ampiamente portabile. Un parallelo più recente può essere fatto con il linguaggio Java, che avete studiato, il cui successo sta in parte proprio nella sua portabilità. L’infanzia di Unix comprende un’ultima innovazione portante: l’introduzione di uno strumento che consente di collegare più utility, inviando l’output dell’una in input all’altra. Tale strumento (che studieremo) si chiama pipe. Nel 1982, all’età di tredici anni Unix, fino ad allora utilizzato solo in ambiente universitario, divenne un prodotto commerciale, regolarmente distribuito da At&T. L’unico problema era che a quel punto esistevano diverse versioni di Unix, sviluppate a partire dal codice originario da centri di ricerca indipendenti. At&T sviluppò nel 1983 Unix System V, Release 1 impegnandosi da quel momento in avanti a mantenere la compatibilità. In altri termini tutto ciò che girava in System V, Release 1 avrebbe dovuto rimanere eseguibile per sempre nelle versioni successive di Unix. Fra gli spin-off prodotti da questa avventura occorre ricordare BSD (Berckley Software Distribution) e XENIX. BSD è un figlio di Unix nato all’Università di Berckley, su richiesta del DARPA (Dipartimento della Difesa) che lo adottò quale proprio sistema operativo. William Joy, uno degli autori di BSD, fondò di lı̀ a poco Sun Microsystems, dove venne realizzata la versione di Unix nota come Sun OS e poi sviluppatasi in Solaris. XENIX nacque nel 1980 ad opera di Microsoft. Il maggior contributo di questa distribuzione fu l’introduzione di Unix nel mondo dei desktop. Fino ad allora, infatti, questo sistema operativo era rimasto prerogativa di computer più grandi. In tempi più recenti si è sviluppato Linux, la cui storia merita però un capitolo a sè. Come conclusione a questa breve introduzione ricordiamo che se possiamo utilizzare certi strumenti è perché ci sono persone che hanno avuto sia le idee sia la costanza di portarle a frutto. Fra i molti che hanno contribuito a Unix alcuni dei nomi più illustri sono: K. Thompson (iniziatore dell’avventura), D. Ritchie (autore del linguaggio C), B. Kernighan (coatore di C e di awk), R. Canady (coautore del file system), J. Ossanna (autore di troff), W. Joy (autore di vi, della C shell –csh– e fondatore di Sun), D. Korn e S. Bourne (autore della korn shell –ksh– e della bourne shell –bsh– rispettivamente), R. Stallman (autore di emacs e fondatore della Free Software Foundation). 2 1 Caratteristiche principali Il principio cardine che ha da sempre guidato lo sviluppo di Unix è che un sistema deve essere semplice, generale ed estendibile. La semplicità è fondamentale perché semplifica la vita agli utilizzatori (e ottimizza il tempo necessario a svolgere i compiti che si prefiggono). La generalità è fondamentale perché conduce a non legarsi mani e piedi ad una nicchia e consente di realizzare strumenti che divengono una ricchezza condivisa da una popolazione ampia. L’estendibilità è fondamentale perché base dell’evoluzione, e senza evoluzione ai sistemi non rimane che l’oblio o al più una nota nei libri di storia dell’informatica. Questo principio rifrasa una lezione molto importante di quella disciplina nota come software engeneering, nella quale si insegna che nella realizzazione di programmi occorre evitare di sviluppare sistemi monolitici, per tanti motivi: occupano molto spazio, generalmente li si sfrutta per un minimo delle loro potenzialità e, cosa peggiore, non possiamo riutilizzarne delle parti per risolvere problemi di tipo diverso (noto problema del riuso del software). Ecco alcuni esempi di come queste lezioni sono state applicate alla realizzazione di Unix. Un esempio di attenzione alla semplicità della soluzione adottata è dato dal file system: l’utente non ha bisogno di sapere su quale unità disco è contenuto un file in quanto gli viene proposto un modello (una visione) più astratto, che prescinde dalle complicazioni dovute all’hardware (uno dei compiti per i quali i sistemi operativi vengono sviluppati). Inoltre le periferiche (tastiera, monitor, ...) sono gestite alla stregua di file attraverso il meccanismo dei flussi di dati, fra i quali standard input e standard output. Per quel che riguarda la modularità l’utente ha a disposizione una vasta libreria di piccoli programmi in grado di assolvere compiti molto specifici; tali programmi possono essere composti per realizzare strumenti ad hoc tramite il meccanismo dello standard input, standard output connessi dalle già citate pipe (tutti argomenti che affronteremo in una lezione successiva). Oltre ad applicativi specifici sono a disposizione anche linguaggi e interpreti specifici (per lavorare su file di dati –awk, nawk–, per lanciare compilazioni complesse –make–, ...). Per quel che riguarda l’interazione fra utente e sistema, possiamo immaginare un computer sul quale è installato Unix come organizzato a cipolla. L’utente lavora in un ambiente chiamato shell (conchiglia o guscio). La shell funge da interfaccia fra l’utente e il nucleo (kernel) del sistema operativo, il quale a sua volta nasconde l’hardware. In Unix sono presenti diversi tipi di shell, fra questi: sh, bsh, ksh, zsh, csh, tcsh, bash. Una shell è un ambiente di lavoro e un interprete di comandi ed è anche un linguaggio di programmazione, come vedremo. La gestione degli utenti e del meccanismo delle shell è tale che è possible per più utenti lavorare contemporaneamente su di una stessa macchina, addirittura utilizzando finestre diverse aperte sullo stesso monitor. Questa è una caratteristica che differenzia Unix da altri sistemi operativi che sono multiutente però non consentono a più utenti di utilizzare contemporaneamente (da remoto) un computer oppure ad un singolo utente di utilizzare contemporaneamente account 3 diversi su di uno stesso computer. Il tipo interazione privilegiato fra utente e sistema è quello attraverso linea di comando. Questa caratteristica porta in sè alcuni vantaggi e alcuni svantaggi. Il vantaggio è l’alto grado di flessibilità d’uso del sistema. Lo svantaggio è che per utilizzare questo sistema operativo occorre imparare a scrivere quel che vogliamo che faccia per noi nel linguaggio che sa interpretare, non sempre intuitivo. Questo è uno dei motivi che ha frenato la diffusione di Unix in ambienti non tecnologici (uffici, amministrazioni, imprese commerciali, ...). Negli ultimi anni è stato compiuto un grosso sforzo per realizzare interfacce grafiche più intuitive e gradevoli (gnome e kde in primis), che consentano un’interazione più agevole, tuttavia questo sviluppo non è avvenuto nell’ottica di sostituire la linea di comando (e quindi perdere quella flessibilità di cui si parlava). Il kernel di Unix svolge le funzionalità tipiche di un sistema operativo, come la gestione del file system, della memoria, l’interfacciamento con le periferiche attraverso driver, la gestione degli utenti e cosı̀ via. L’utente può invocare l’esecuzione di funzionalità di sistema operativo utilizzando un insieme di system call quali fossero funzioni di libreria. Noi studieremo alcune di esse soffermandoci in particolar modo sulle call per la gestione dei processi e sul pacchetto IPC (inter-process communication). Infine uno dei motivi del successo, dell’efficienza e dello sviluppo di questo sistema operativo è che, per lo meno nella prima fase della sua vita, i codici sorgenti sono stati resi disponibili a chiunque volesse realizzare una nuova versione del medesimo o semplicemente volesse realizzare una nuova funzionalità. Il fatto di distribuire il codici sorgente dei programmi era prassi negli anni in cui Unix si è sviluppato; è solo in tempi più recenti, con la diffusione capillare dei computer negli uffici e nelle case e con il conseguente incremento di concorrenza fra i produttori di software, che è nata l’idea di distribuire i soli eseguibili. 4