Evoluzione del software

I grandi computer degli anni 40, come Mark I od ENIAC, si programmavano collegando cavi e regolando interruttori su appositi pannelli; successivamente furono utilizzati per l'input/output di dati e programmi nastri di carta perforata, poi schede perforate. Nell'immagine a fianco si vede come veniva programmato il computer ENIAC.

Un utente per volta utilizzava il computer ed i programmi erano scritti in linguaggio macchina: una lunga sequenza di 1 e 0, adatta a pilotare direttamente i circuiti elettrici del computer. Solo negli anni 50, con l'aumentare della complessita' e prestazioni delle macchine, le cose cambiarono, ed il lavoro fu in parte reso automatico. Si iniziarono anche ad usare librerie di programmi: parti di programma di uso comune venivano scritti una volta per tutte, messe in queste librerie e poi riutilizzate.

Al linguaggio macchina si sostituiti' l'assembler. In assembler si utilizzano termini simbolici per indicare le operazioni da effettuare, con termini come: ADD, MULT, JUMP etc.; analogamente vengono indicati con termini simbolici e registri ed in decimale od esadecimale gli indirizzi in memoria. E' molto piu' facile scrivere in assembler che non in binario, ed anche correggere i programmi e leggerli.

Programmazione di ENIAC

Un apposito programma, l'assemblatore, traduce poi l'assembler in linguaggio macchina, collega fra loro le varie parti ed eventualmente include funzioni prese dalle librerie.

Un altro programma ancora: il loader, carica quanto tradotto nel computer per l'esecuzione.

I primi assembler furono scritti per EDSAC all'universita' di Cambridge attorno al 1950, a Cambridge si inizio' anche a scrivere subroutines, cioe' parti di programmi riutilizzabili, e si introdusse la microprogrammazione: le istruzioni assembler piu' complicate venivano tradotte in operazioni piu' semplici da un apposito programma per essere eseguite nella CPU.

Negli anni 60 le cose cambiarono ed entrarono in uso i sistemi "batch", i programmi (jobs) di diversi utenti, scritti su schede, ed eventualmente copiati su nastro, venivano raggruppati in lotti omogenei (batch) per tipo di calcolo o compilatori richiesti. Gli operatori della macchina facevano eseguire i jobs in sequenza, in modo da ridurre i tempi morti e poi consegnavano i  risultati. Questa organizzazione riduceva i tempi morti, ma il lavoro di messa a punto dei programmi era molto lento, poiche' il tempo fra la consegna del job e l'esame dei risultati era lungo.

Il sistema del lavoro "batch" fu poi automatizzato. Un programma sempre residente in memoria (il batch monitor), in base a schede di controllo aggiunte al job, eseguiva in sequenza i programmi e stampava i risultati. Si aveva quindi un inizio di sistema operativo: un programma che gestiva la macchina. Un esempio di sistema batch e' l'IBM 709, del 1958, che aveva il sistema operativo FMS (Fortran Monitor System).

Nelle immagini seguenti una macchina per perforare le schede, un lettore di schede ed una scheda per dati.

scheda da perforare
perforatrice di schede
lettore di schede, da corestore.org

Alla fine degli anni 50 si iniziarono a sviluppare i linguaggi di programmazione: l'assembler e' praticamente il linguaggio macchina, scritto in forma simbolica; e' diverso da macchina a macchina ed un programma assembler non e' "portabile" cioe' e scritto per una macchina specifica e se si cambia macchina si deve riscrivere. Inoltre e' complicato e poco adatto ai non specialisti. Con i linguaggi di programmazione le cose cambiano, i programmi sono scritti in un linguaggio simile al linguaggio matematico, relativamente facile da imparare, un apposito programma: il compilatore, provvede poi alla traduzione in linguaggio binario, creando quello che si chiama "modulo oggetto". C'e' poi il programma di "link" che collega il modulo oggetto alle librerie e crea un "eseguibile". Infine il "loader" carica il programma in memoria e lo esegue.
Si indica con il termine "source code" o codice sorgente, il programma scritto in un linguaggio di compilazione, per distinguerlo dal linguaggio binario della macchina.

I primi compilatori furono prodotti da Grace Hopper, per Univac, nel 1952, ma il primo compilatore di successo fu il FORTRAN (FORmula TRANslation), sviluppato da John Backus per l'IBM nel 1956. Come dice il nome, il FORTRAN e' un linguaggio adatto ad esprimere formule matematiche a trattare vettori e matrici e le sue prime versioni avevano poche istruzioni: c'era il GOTO per salti nella sequenza delle istruzioni, IF: per salti condizionati, DO: per operazioni cicliche e poco altro.

Il linguaggio non era adatto a trattare problemi con poca elaborazione numerica, ma molti dati da leggere e scrivere; per queste esigenze fu sviluppato il COBOL che prevede l'organizzazione di numeri e testi in unita', dette "records" ed "array", per cui l'input e l'output ( I/O ) sono ottimizzati.
A fine anni 50 furono prodotti molti altri linguaggi:

Questo entusiasmo e fiorire di linguaggi era dovuto all'idea, non sempre esplicita, che la logica del programma formulata nel modo giusto, potesse in qualche modo riprodurre i meccanismi dell'intelligenza. Si aveva quindi l'illusione di poter, con il linguaggio di programmazione, creare come una nuova forma di intelligenza.

Negli anni 60 erano in uso grandi sitemi batch, come l'IBM 7090 ed il 7094, questi avevano il sistema operativo IBSYS, che prevedeva che i lavori fossero copiati da schede su nastro per l'esecuzione ed era corredato da librerie di programmi e compilatori FORTRAN e COBOL.

Intanto l'evoluzione della tecnologia apriva nuove prospettive per i sistemi batch. L'evoluzione della tecnologia dei dischi porto' allo "spooling", l'evoluzione delle memorie alle memorie virtuali, alla paginazione ed ai meccanismi di protezione della memoria.

Con l'uso dei dischi magnetici fu introdotto lo "spooling" (Simultaneous Peripheral Operation On Line): i programmi venivano copiati da schede o nastri su disco, ed il sistema operativo per eseguirli li prelevava dal disco (molto piu' veloce dei nastri). Quando un programma doveva fare una operazione di input-output (I/O), relativamente lenta, veniva sospeso, e, mentre veniva eseguita l'operazione di I/O, un altro programma veniva prelevato dal disco ed impegnava la CPU, che quindi era sfruttata al massimo. Anche l'output finiva su disco in attesa di essere stampato. I computer batch erano spesso affiancati da elaboratori ausiliari per l' I/O, che gestivano lettori di schede e stampanti.

In figura unita' disco e nastro IBM, degli anni 70. ed un mainframe IBM 3090 (1985).


IBM disk 1311 IBM 3090

La memoria "a pagine" fu introdotta negli anni 60, quando si iniziarono ad usare memorie a nuclei di ferrite, veloci, ma relativamente costose, accanto alle piu' lente, ma meno costose, memorie a tamburo magnetico, come nell' IBM 360.

La memoria era divisa in pagine, e solo quelle che in un certo momento erano in uso stavano nelle memorie di ferrite, mentre quelle non usate erano trasferite al tamburo. Quando il sistema doveva accedere a una pagina che non era nelle memorie di ferrite si verificava un'interruzione (page fault) il programma veniva interrotto ed interveniva il sistema operativo, che recuperava la pagina richiesta.

Anche il concetto di memoria virtuale viene introdotto in questo periodo: nei primi computer i programmi utilizzano gli indirizzi veri delle celle di memoria, se parti del programma devono essere spostate da una zona di memoria all'altra questa gestione degli indirizzi non e' piu' possibile. I programmi usano quindi indirizzi relativi, ed un apposito hardware viene utilizzato dal sistema operativo per aggiunger un "offset" agli indirizzi relativi ed ottenere quelli veri. Ogni programma "vede" quindi una memoria che non e' quella vera, ma e' "virtuale", e puo' anche essere piu' grande di quella fisicamente presente nel computer, parte di questa memoria puo' essere simulata con spazi sul disco (aree di swap).

Con la presenza di pagine di diversi programmi in memoria contemporaneamente, occorre evitare che un programma possa, per errore, scrivere nelle pagine di un altro. Vengono quindi implementati sistemi per la protezione della memoria, come elenchi di pagine ammesse e proibite, od introdotti bits nella memoria che contengono un codice unico per ogni programma. Un tentativo di scrivere fuori della zona di memoria ammessa crea un errore di "access violation" e l'interruzione del programma.

Un esempio di sistema di questo tipo e' il sistema operativo IBM OS/360, del 1966, usato per l'IBM 360 e modificato poi per l'IBM 370, con l'aggiunta della memoria virtuale. Questo sistema operativo era un enorme programma scritto in assembler, contenente milioni di istruzioni, la memoria era divisa in partizioni, dedicate a programmi diversi, contemporaneamente attivi, il sistema operativo gestiva le partizioni ed i turni per l'uso della CPU, l'I/O era gestito da appositi dispositivi (canali) che lavoravano contemporaneamente alla CPU ed in modo indipendente.

A meta' degli anni 60 ci fu un'altra novita': l'interattivita'. Al computer potevano collegarsi contemporaneamente piu' utenti, usando diversi terminali,  il sistema operativo gli assegnava a turno  la CPU . Siccome il computer e' molto piu' veloce degli umani alla tastiera, ognuno ha l'impressione di avere il computer tutto per se, e non si accorge che nell'intervallo fra un tasto e l'altro la CPU e' utilizzata da un altro utente. Questo nuovo modo di lavorare permette di abbreviare molto i tempi di messa a punto dei programmi, poiche' si vede subito il risultato e si possono correggere subito gli errori, senza aspettare i lunghi tempi dei sistemi batch.

Il primo sistema di questo tipo fu il CTSS (Compatible Time Sharing Systemy) di John McCarthy ed altri, che lo implementarono su un IBM 704 modificato, e poi su un IBM 7090 anche questo modificato, attorno al 1961, al MIT. Negli anni 70 molti costruttori offrivano, per i loro sistemi batch, queste opzioni di "time sharing" che pero' divennero di uso comune solo con l'avvento dei terminali con video, alla fine degli anni 70. Dal sistema CTSS derivo' poi MULTICS ( 1965 ) e da MULTICS Unix.

In figura un IBM 360 (1964) ed un Vax 11-780 (1978), dotato di terminali grafici.

IBM 360 Vax 11-780

Questi concetti nati negli anni 60 e 70 sono ora standard per tutti i sistemi moderni, anche se le cose sono diventate piu' complicate e tutto e' strutturato per ottimizzare l'uso delle veloci CPU ed eliminare i tempi morti: ogni processo vede una sua memoria virtuale di circa 4 Gbyte (per macchine Intel 386), che non ha nulla a che fare con quella vera. Il sistema operativo divide la memoria in pagine, e mantiene una tabella di pagine di ogni processo, per la corrispondenza fra memoria virtuale e fisica (RAM). Per fare questo utilizza  appositi circuiti  elettronici. Le pagine sono portate dentro e fuori della memoria reale quando necessario.
Per ottimizzare l'uso della CPU ci sono anche memorie veloci, le cache, ove sono copiati i dati perche' la CPU possa trovarli piu' velocemente. Ci sono cache di diversa velocita' (e costo), ed i dati sono copiati dalle une alle altre, cercando di far stare nelle cache piu' veloci quelli che saranno utilizzati poco dopo. Si chiamano cache di livello 1 (L1) le piu' veloci, di livello 2 (L2) le piu' lente; a volte c'e' anche una cache di livello 3. Oggi (2006) le cache di livello 1 e 2 tendono ad essere integrate nella CPU. L'architettura delle cache e' abbastanza complicata ed e' uno dei fattori piu' importanti nel determinare le prestazioni di un computer.

I calcoli vengono sempre fatti utilizzando i registri (memorie molto veloci che sono nella CPU), i dati quindi passano dal disco alle pagine della RAM, poi nella cache di livello 2, poi in quella di livello 1, poi nei registri, cercando di indovinare quali copiare nelle cache per ridurre al massimo gli accessi alle memorie piu' lente. Anche le istruzioni del programma hanno le loro cache ed anche loro viaggiano fra il disco, la RAM e le cache.

Si parla di "cache miss" quando si cerca un dato, non e' nella cache e va recuperato dalla memoria RAM; di page fault, quando una pagina non e' nella RAM e va recuperata dal disco.

Sul disco c'e' poi un'area apposita detta di "swap" in cui vengono parcheggiate le pagine quando la RAM e' piena. Si parla di "swapping" quando ci sono troppe pagine in uso e viene usata l'area di swap. L'uso di questa area permette di far finta di avere molta piu' RAM di quanta ce ne sia in realta', ma, siccome l'accesso al disco e' molto piu' lento dell'accesso alla RAM, "swappare" significa degradare molto le performance del computer. In Windows questa area e' il file: pagefile.sys, in Linux in genere una partizione del disco.

Tutto questo via vai di dati fra disco, RAM, cache e CPU e' gestito dal sistema operativo ed e' trasparente all'utente, che si accorge di qualcosa solo di quando usa lo swap (la macchina diventa lentissima), Questa gestione della memoria fa si che se la macchina viene spenta in modo irregolare le parti dei files che sono in quel momento in RAM e nell'area di swap vengano perse, ed i files rovinati.

I computer moderni sono multi-processing, cioe' nello stesso tempo sono attivi molti processi, ogni processo vede una sua memoria virtuale, inaccessibile agli altri processi (protezione della memoria) e se un processo cerca di scrivere fuori della sua memoria si verifica un errore ed il processo viene interrotto. I processi possono essere divisi in tasks (multi-tasking), che sono eseguiti in modo indipendente, ma condividono la stessa memoria virtuale.

La CPU e' utilizzata dai processi a turno, i turni sono decisi in base a complicati algoritmi di scheduling, ed ad una priorita', decisa dal sistema o dall'utente. Quando un processo fa I/O, oppure il suo turno scade, viene interrotto, i dati che aveva nei registri sono salvati ed il processo e' messo in attesa, mentre un altro prende il suo posto. Questo cambiamento di stato della CPU e' detto: "context switch".

I sistemi Unix ( e Linux) sono multi-user, cioe' piu' utenti, ognuno con tutti i suoi processi, possono usare contemporaneamente la macchina; per far questo si possono usare finestre diverse dello schermo, oppure collegarsi via rete da un altra macchina, o da un terminale remoto. Programmi possono anche essere eseguiti in modo indipendente dal terminale (o da video e tastiera), mettendoli in code di esecuzione, che lavorano un po' come i vecchi sistemi batch. Oltre gli utenti possono essere attivi sulla macchina vari servizi, come server web, servizi di posta etc.

I sistemi Windows hanno una ridotta capacita' di multi utenza, ma in XP e' possibile sospendere un utente e far lavorare un'altro, o permettere il collegamento remoto al computer, via rete.




Unix

Il sistema operativo Unix riassume un po' tutte le idee che si erano sviluppate negli anni 50 e 60 sui sistemi operativi e le implementa in modo semplice ed elegante. Unix e' la base dei sistemi operativi moderni.

Al MIT, negli anni 60 viene sviluppato MULTICS, in collaborazione con Bell Labs e General Electric, sistema multi utente, multy process, con file system gerarchico. Nel 1969 AT&T e General Electric si ritirano dal progetto,

Ai Bell Labs nel 1969 Ken Thompson e poi Dennis Ritchie, che aveva lavorato su Multics, curano una versione ridotta da laboratorio di MULTICS, su un PDP-7 Digital, poi su un PDP-11, scritta in assembler, e la chiamano UNIX (qualcosa come mini-multics), Nel 1972 Unix e' riscritto in B ( linguaggio sviluppato ai Bell Labs) nel 73 e' riscritto in C (restano in assembler poche routines). Il C ed Unix vengono sviluppati nello stesso periodo dalle stesse persone, facendo del C un linguaggio adatto a scrivere software di sistema, e di Unix un sistema portabile su computer diversi. La portabilita' di Unix e' una delle chiavi del suo successo.

Questa prima versione di UNIX viene usata entro i Bell Labs , e siccome, per questioni legali, la AT&T non puo' venderla, viene data in licenza, assieme ai sorgenti, a prezzo scontato alle universita'. (1974), unix veniva anche distribuito assieme ai PDP-11 DEC.

Nella foto Ken Thompson e Dennis Ritchie lavorano su Unix con il PDP-11

Ken Thompson e Dennis Ritchie portano Unix su un PDP-11

UNIX si diffonde molto in ambito accademico, nel 1977 ci sono 500 macchine Unix in giro, nell'84 ce ne sono gia' 100.000. Questa diffusione di Unix in ambito accademico e' dovuta alla disponibilita' dei source in linguaggio C ed in assembler, che permettono di usare Unix per lo studio dei sistemi operativi e di cambiarne i codici liberamente. Questo fara' di Unix un punto di riferimento ed un laboratorio per lo studio dei sistemi operativi; tutte le nuove idee e le nuove tendenze vengono inserite subito in Unix che diviene sempre piu' ricco di funzionalita', e tutti i sistemi operativi del periodo successivo finiranno per somigliare ad Unix.


Il fatto che Unix sia stato sviluppato nelle universita', da tanta gente indipendentemente e' evidente anche all'utente distratto: le varie utilities risultano spesso disomogenee nella sintassi, ridondanti; in cambio c'e' molta varieta'. Unix e' sviluppato prima che iniziasse la grafica nei computers, ed e' inteso per interfacce a caratteri; fatto dagli stessi utenti, tende ad essere poco user-friend ed a favorire una sintassi essenziale, (per battere meno tasti possibili), ma criptica. In cambio tutto il sistema ha una struttura chiara, in cui l'utente esperto riesce facilmente ad intervenire per il set-up ed il maintenence del sistema. La documentazione e' ampia ma spesso frammentaria.

L'universita' della California a Berkeley sviluppa la sua versione di Unix (unix BSD), diffusa soprattutto nelle universita'. A Berkeley introducono memoria virtuale a pagine, supporto di rete, l'interfaccia a socket per le applicazioni che usano la rete, l'uso di nomi lunghi nel file system, vengono scritti gli editors ex e vi, compilatori Pascal e Lisp , la C-shell.

Le prime reti si svilupparono in ambito accademico, su macchine Unix BSD, nel 1977 ai Bell Labs erano usate reti di tipo UUCP. I protocolli di rete finiscono per essere integrati in Unix. ( TCP-IP a Berkeley)

Attorno al 1985 la At&T riorganizza Unix e lo trasforma in un prodotto commerciale, smette di distribuire i source e vieta la loro diffusione nelle universita'. A partire dalla Versione 7 di Unix la AT&T crea l'Unix System V, che differisce un po' dall'Unix BSD.
Negli anni 80 Unix e' quindi diviso in 2 filoni, ed i sources non sono piu' disponibili.
Il sistema ha pero' proprio in questi anni enorme diffusione. E' il periodo del declino dei maniframes in favore dei mini e poi delle workstation grafiche; le workstation grafiche RISC utilizzano Unix e questo sistema di diffonde con esse. Gli stessi costruttori finiscono per utilizzare Unix anche sui grossi server.

I produttori usano ognuno una sua variante proprietaria di Unix, varianti solo parzialmente compatibili:

Negli anni 90 si cerca di i standardizzare Unix e permettere la portabilita' delle applicazioni.

Del 1988 e' lo standard POSIX, cui dovrebbero uniformarsi tutte le varianti di Unix, Posix definisce il formato di certi comandi e di certe chiamate di sistema. Per essere Posix compliant anche Windows ha certi comandi di Unix, opzionali, sono nel Windows resource kit di w89 ed altre versioni di Windows.

Poi si formano 2 consorzi in lotta per standardizzare Unix, At&T e Sun hanno Unix International, mentre IBM,HP,DEC hanno OSF (Open software fundation ), non possono chiamarsi Unix, perche' il marchio unix e' della At&T . per questo l'unix delle workstation alpha per un certo periodo si e' chiamato OSF-1. Poi ha cambiato in DU ( Digital Unix)

A meta' anni 90 le diatribe legali su Unix si affievoliscono, mentre lentamente il mercato delle workstation perde di importanza a favore dei PC, e restano tutte queste varianti incompatibili di Unix, ogni fornitore ha il suo Unix.

Nel 1993 la Novell compera Unix dall'AT&T, e chiama i suoi prodotti di rete Unixware. Attorno al 1995 Unixware e' venduto a SCO , mentre il marchio e' passato al gruppo X/Open ( www.unix-systems.org) in cui confluisce anche OSF/1. Questo gruppo cerca di produrre specifiche "aperte" per Unix che siano anche coerenti con lo standard Posix. Al gruppo afferiscono tutti i maggiori fornitori di sistemi Unix.

Nel frattempo, con il crescere della potenza dei PC e della loro importanza commerciale, viene fuori un nuovo Unix per PC, Linux. Tecnicamente Linux non e' Unix, in quanto e' riscritto completamente da capo, ma l'utente non se ne accorge, perche' Linux re-implementa in pratica tutte le caratteristiche di Unix. Questo nuovo Unix si diffonde come sistema operativo sulle macchine basate su processori Intel , e similari. E' anche utilizzato sui PC desktop, ed in ambiente accademico.

Ci sono anche altre varianti di Unix in circolazione, derivate dall'Unix di Berkeley , come: "Net BSD", "Open BSD" e "Free BSD"; in queste varianti di Unix tutto il codice della At&T e' stato tolto dai source e sono quindi distribuite liberamente. Questi sistemi operativi sono usati da appassionati oppure sui server, perche' giudicati stabili e sicuri.

Un tentativo di superare le idee di Unix e' dato dallo sviluppo del kernel Mach, della Carneige Mellon University, dal 1985 al 1994. Questo lavoro ha suscitato grande interesse. Questo kernel e' un micro-kernel, cioe' un kernel molto piccolo, che fa poche cose, affiancato da processi esterni, con cui comunica con un sistema di messaggi, in teoria molto flessibile ed efficiente ed adatto anche a macchine con molte CPU. Purtroppo il kernel Mach non ha mostrato buone prestazioni per cui non e' stato adottato da molti costruttori; il Mac OS X, il nuovo sistema operativo della Apple, e' un ibrido fra il BSD Unix e la versione del kernel Mach utilizzata dal sistema Next, di Steve Jobs.


Tendenze attuali

Con l'evoluzione delle prestazioni delle macchine una ottimizzazione elevata del software, che comporta lunghi tempi di sviluppo, diviene non conveniente; del resto le maggiori prestazioni delle macchine permettono di devolvere parte del lavoro di ottimizzazione al compilatore.
Contemporaneamente c'e' un l'aumento del numero degli utenti dei computers, spesso non specializzati, ed un aumento della complessita' dei programmi, che si ritrovano ad avere anche centinaia di migliaia di linee di codice, con enormi problemi di gestione e correzione degli errori; i tempi di sviluppo del software, imposti dall'evoluzione rapida dell'informatica sono molto stretti, e c'e' necessita' di produrre codice riutilizzabile.

La crescente complessita' del software e l'eterogeneita' dell'hardware fanno si che il software moderno sia organizzato in componenti indipendenti, che comunicano in modi standardizzati; i modelli sono principalmente 2:

Tutti questi fattori provocano una evoluzione dei compilatori, che si si allontanano sempre piu' dal linguaggio macchina, per avvicinarsi al punto di vista dell'utente ed al problema; si pone l'enfasi sulla necessita' di avere linguaggi strutturati, e poi su linguaggi ad oggetti.

Contemporaneamente si sviluppano ambienti di programmazione facili, come Visual Basic della Microsoft ed ambienti di programmazione specializzati, come SAS od IDL, che forniscono all'utente un ambiente che integra tutti gli strumenti di cui necessita, compresa la grafica, ed un linguaggio di programmazione specializzato, relativamente semplice ed indipendente dalla piattaforma.

I linguaggi "object oriented" vengono introdotti nel 1965, col Simula, di Nygaard e Dahl, ma prenderanno piede solo piu' tardi, il C++ ( o C con classi ) e' introdotto negli anni 80, da Bjarne Stroustrup, della AT & T.

La programmazione ad oggetti permette di affrontare progetti software complessi, sviluppati da gruppi numerosi di programmatori. Il linguaggio pone l'accento sulla struttura dei dati, invece che sul flusso delle istruzioni che la macchina deve eseguire, e permette di descrivere, in modo astratto, le interfacce del sistema (cioe' come il sistema viene visto dall'esterno) e le interazioni fra le parti, senza entrare in dettagli implementativi che possono essere sviluppati in un secondo momento. Permette inoltre di descriver una sola volta funzionalita' di oggetti simili e di riutilizzare parti di software senza doversi preoccupare della loro struttura interna. Viene anche devoluto al compilatore parte del lavoro che solitamente viene fatto dal programmatore.

Per quanto riguarda i sistemi operativi, attualmente sono usate: