Sei sulla pagina 1di 14

CAPITOLO 6

IL SOFTWARE

PREMESSA

In questo capitolo, dopo una breve introduzione, verranno presentati i linguaggi di


programmazione, strumenti essenziali per la realizzazione dei prodotti software. La
"scrittura" degli algoritmi sotto forma di programmi è, infatti, un passo necessario per la
soluzione dei problemi 1. Il terzo paragrafo è dedicato alla classificazione dei linguaggi,
mentre il quarto conclude l’argomento con esempi relativi ad alcuni linguaggi di
programmazione. I paragrafi successivi sono dedicati ad una breve presentazione dei
sistemi operativi.

1. GENERALITA' SUL SOFTWARE

Tutti gli utenti hanno bisogno di una "interfaccia logica" con la macchina, visto che
un elaboratore elettronico, in quanto sola circuiteria, non ha la possibilità di svolgere, in
pratica, nessun lavoro utile all'uomo 2. Sorge quindi la necessità di dotare ogni sistema
elettronico di programmi che ne permettano un uso effettivo da parte degli utilizzatori.
Il software è costituito dall'insieme dei programmi che un utente ha a disposizione.
I programmi che costituiscono il software possono essere suddivisi nei seguenti due
grandi gruppi:
1) software di base,
2) software applicativo.
Il primo gruppo, software di base, è costituito da tutti quei programmi che permettono,
oltre ad una gestione efficiente della macchina ed un semplice colloquio uomo-elaboratore,
lo sviluppo di applicazioni.
Un elemento essenziale del software di base, è il sistema operativo, cui sono dedicati
gli ultimi paragrafi di questo capitolo. Un tempo, per il funzionamento degli elaboratori, era
necessario l'intervento di una specifica figura professionale, chiamata operatore, che
utilizzava "fisicamente" la macchina, compiendo manualmente tutta una serie di operazioni
molto semplici. Con l'avvento delle unità a nastro magnetico e, soprattutto, con l'utilizzo via
via crescente dei dischi, molte funzioni manuali sono state sostituite con un insieme di
procedure automatizzate che hanno ridotto notevolmente gli interventi manuali. Ai nostri
giorni, l'uso di personale operativo specializzato è limitato a macchine di notevoli

1 Abbiamo precedentemente visto che la risoluzione di un problema mediante elaboratore comporta una serie di fasi, fra
le quali una molto importante è la programmazione, e cioè la scrittura di un programma in un determinato linguaggio,
dopo averlo analizzato ed aver descritto una sua possibile soluzione in forma di diagramma a blocchi.
2 Abbiamo già visto, in figura 4.2, una schematizzazione di possibili interfacce uomo/macchina. In pratica, ogni tipo di
utente interagisce con la macchina a livelli diversi, a seconda delle sue conoscenze e dei suoi scopi.

1
dimensioni e ad operazioni che non possono, in nessun caso, essere effettuate dalla
macchina. Le procedure "di base" di cui sopra sono venute a formare nel tempo qualcosa di
organizzato che ha preso il nome di sistema operativo.
In una accezione estensiva, tutto il software di base può essere identificato con il
termine "sistema operativo", anche se assai spesso il termine ora citato serve ad identificare
solo quella parte del software di base che ha il compito specifico di gestire la macchina.
Infatti, alla funzione iniziale di migliorare il rapporto uomo-macchina per semplificare le
operazioni da compiere, si è presto aggiunta, ed ha assunto un ruolo prevalente, quella della
gestione ed ottimizzazione di tutte le risorse, hardware e software, di un sistema elettronico.
Questo insieme di programmi, come tutto il software di base, si interpone fra uomo
e macchina e si posiziona, come abbiamo visto nella figura 4.2, ad un livello più vicino
all'hardware, sia per la funzione che esso svolge, sia per il fatto che ogni elaboratore ha la
necessità di uno specifico software di base.
Si è avuta una grossa evoluzione nel software di base, tanto da poter parlare di
generazioni. Anche se, generalmente, è più consueto parlare di generazioni riferendoci
all'hardware degli elaboratori 3. Le varie generazioni del software di base si riferiscono
sempre all'evoluzione che è avvenuta nel tempo, ma, più precisamente, al modo di gestire
un sistema di elaborazione; sui vari modi di attuare tale gestione ritorneremo in seguito.
Il secondo gruppo, software applicativo, è costituito da tutti quei programmi che
servono per specifiche applicazioni e che sono utilizzati dagli utenti finali. Riferendosi
sempre alla figura sopra richiamata, ricordiamo che tali prodotti vanno ad occupare il livello
più vicino all'uomo.

2. I LINGUAGGI DI PROGRAMMAZIONE

Per introdurre l'argomento dei linguaggi di programmazione abbiamo ritenuto


opportuno ricordare, nel paragrafo precedente, i diversi livelli ai quali i vari utilizzatori
interagiscono con la macchina, a seconda che operino a livello hardware, a quello del
software di base o, infine, a livello di software applicativo. Nel capitolo 4, abbiamo parlato
dello studio di un problema attraverso l’ideazione dell'algoritmo e la stesura del diagramma
a blocchi e abbiamo visto esempi di linguaggi in grado di trascriverlo in una forma di
compromesso fra uomo e macchina.
I "linguaggi di programmazione" sono, appunto, un mezzo di comunicazione fra
l'uomo e la macchina e possono essere considerati strumenti fondamentali per la
realizzazione del software.
Quando parliamo di linguaggi, in generale, sappiamo che esistono quelli naturali,
che vengono utilizzati dall'uomo e che non sono, almeno per ora, utilizzabili direttamente
dalla macchina, poi ci sono quelli speciali, come ad esempio l’algebra, che è più facile
assimilare a quello degli elaboratori; sono comunque necessari linguaggi artificiali mediante
i quali è possibile descrivere algoritmi in una forma trattabile direttamente dall'elaboratore,
dopo processi di traduzione automatica.
I linguaggi di programmazione sono strumenti intermedi, che si pongono fra l'uomo

3 Come abbiamo visto al capitolo 1, l’evoluzione tecnologica degli elaboratori ha segnato il passaggio dalle valvole
termoioniche, ai transistori, ai circuiti integrati, via via con livelli di integrazione più elevati; questa evoluzione ha portato
a notevoli aumenti di capacità e velocità della memoria e della circuiteria nel suo complesso, oltre a diminuzioni di costo
ai quali siamo ormai abituati.

2
(ossia fra il linguaggio naturale) e il linguaggio con il quale la macchina lavora, che si
denomina appunto linguaggio macchina. Ricordiamo che il linguaggio macchina è legato
alla struttura fisica dell'elaboratore. Le differenze strutturali degli elaboratori portano a
definire, per ogni tipo di unità centrale di elaborazione (processore), linguaggi macchina
diversi, rimanendo, sempre, come carattere comune, la rappresentazione delle
informazioni, compresi i comandi, per mezzo dei simboli binari 0 e 1.
Nel tempo si è cercato di facilitare il lavoro di programmazione creando linguaggi
intermedi, diversi da quelli naturali e da quelli macchina, pur rimanendo, questi ultimi, gli
strumenti più diretti per comunicare con la macchina, i soli che permettano l'accesso
immediato a tutte le risorse, con la conseguente operatività immediata delle stesse.
Il linguaggio macchina, o meglio i linguaggi macchina, visto che per ogni tipo di
elaboratore abbiamo uno specifico linguaggio, sono di difficile utilizzo da parte dell'uomo,
per una serie di motivi. I principali sono i seguenti:

1) usano una simbologia complicata e affatto mnemonica per esprimere i codici operativi,
2) fanno riferimento a veri e propri indirizzi fisici di memoria, con la conseguenza di una
difficile gestione della memoria stessa; i programmi realizzati divengono così di
difficile messa a punto, in quanto ogni correzione, sia pur lieve, comporta una serie di
cambiamenti a catena negli indirizzi di memoria,
3) i programmi così realizzati sono eccessivamente lunghi,
4) e non sono portabili, cioè trasferibili da una macchina all'altra, se esse sono di tipo
diverso.

I linguaggi di programmazione artificiali usano dei formalismi più vicini al


linguaggio naturale dell'uomo, o quantomeno alle notazioni da lui usate in determinati
ambiti; sono comunque capaci di descrivere rigorosamente quasi ogni tipo di algoritmo. Tali
linguaggi si dicono linguaggi simbolici; data la loro evoluzione nel tempo.
Infatti, partendo dal linguaggio macchina si passa, verso la metà degli anni 50, ai
linguaggi simbolici di tipo "assembler" che permettono di definire gli indirizzi di memoria
tramite variabili, ed i codici operativi con dei codici mnemonici4; tali tipi di linguaggi,
orientati alla macchina, ancora usati per applicazioni specialistiche, non possono comunque
prescindere da uno specifico modello di macchina.
Successivamente, si cercò di orientarsi verso i problemi, svincolandosi dalla
particolare macchina a disposizione; furono così disponibili i linguaggi simbolici ad alto
livello; la IBM, già dal 1954 aveva studiato e realizzato un primo linguaggio di questo tipo:
il FORTRAN. Fra i primi ad apparire, oltre al FORTRAN, del quale parleremo meglio più
avanti, ci fu il COBOL, basato su di una simbologia del tutto differente (a dimostrazione che
con i linguaggi simbolici ad alto livello ci si avvicina abbastanza ai modi di pensare e di
lavorare dell'uomo, che sono molteplici e variabili anche in base al campo di applicazione).
Mentre il primo linguaggio era destinato a risolvere problematiche di tipo scientifico, il
secondo era rivolto ad elaborazioni di tipo commerciale; la loro evoluzione è stata notevole
e tale da estendere notevolmente le loro potenzialità. Seguirono poi centinaia di linguaggi,
più o meno diffusi, più o meno specializzati, come ad esempio: il PL/1, il Basic, l'APL, il
Pascal, il C, ecc..
Una ulteriore evoluzione, senza tuttavia che i linguaggi precedenti venissero
accantonati, si è avuta con i linguaggi non procedurali, basati sulla definizione di predicati

4 Si tenga presente l’esempio in figura 4.1.

3
e su funzioni da eseguire. Nelle intenzioni degli ideatori, questi linguaggi mirano a
semplificare, sia in tempo che in costi, le esigenze applicative. Fanno parte di questa
categoria un numero crescente di linguaggi, come, ad esempio, i linguaggi: PROLOG, LISP,
ecc..
Prima di esaminare alcuni tipi di linguaggi simbolici, dobbiamo ripetere ancora una
volta che essi, non essendo direttamente interpretabili dall'elaboratore, devono prima essere
"tradotti"; tale traduzione sarà fatta da un "programma traduttore" che accetterà, come
informazioni da elaborare, le istruzioni del programma in linguaggio simbolico, chiamato
"programma sorgente" e darà, come risultato, un programma, in linguaggio macchina, detto
"programma oggetto", che solo dopo la successiva fase di "linkeditaggio" 5 è direttamente
interpretabile dall'elaboratore e quindi eseguibile.

3. CLASSIFICAZIONE DEI LINGUAGGI SIMBOLICI

Dei linguaggi simbolici si possono effettuare varie classificazioni: in base alla


maggiore o minore corrispondenza con il linguaggio dell'elaboratore, per quanto riguarda
l'aderenza del linguaggio al particolare problema, in relazione al modo con il quale avviene
la "traduzione" del linguaggio, tenendo conto del diverso modo con il quale l'utente può
interagire con la macchina e così via.
Una prima distinzione è quella che si può effettuare fra linguaggi simbolici a basso
livello e linguaggi simbolici ad alto livello, a seconda della vicinanza del linguaggio alle
istruzioni macchina e, quindi, alla macchina stessa. Infatti i linguaggi a basso livello, detti
anche assemblativi, si dicono anche orientati alla macchina, non potendo prescindere dalla
macchina disponibile; tali linguaggi permettono di definire gli indirizzi di memoria con dei
simboli 6 ed i codici operativi con dei codici mnemonici 7. Quelli ad alto livello, invece, sono
detti anche orientati al problema, universali, portabili o algoritmici e sono quelli che
veramente ovviano alle principali difficoltà di uso dei linguaggi macchina: con essi siamo
svincolati dalla struttura dell'elaboratore e ci troviamo di fronte a strumenti standardizzati;
questo fa sì che i programmi scritti in un linguaggio ad alto livello siano generalmente
portabili: eseguibili cioè su qualsiasi macchina, purché sia disponibile un programma di
traduzione delle istruzioni standard del linguaggio simbolico nelle istruzioni della
macchina.
Un'altra distinzione possibile, anche se sempre meno attuale, è quella fra linguaggi
orientati a problemi di tipo scientifico, quali, ad esempio, il FORTRAN e l'APL, e linguaggi
orientati a problemi di tipo commerciale, come il COBOL e l'RPG. La differenza sostanziale
fra queste due categorie di applicazioni deriva, soprattutto, dal fatto di dover gestire, o
meno, grandi masse di dati; ricordiamo che con il COBOL si intesero affrontare i problemi
che comportavano l'ingresso/uscita ed il trattamento di ingenti masse di dati, mentre il
FORTRAN e l'APL sono stati sviluppati per effettuare operazioni complesse su di un numero
abbastanza ridotto di dati.

5 Fase che lo collegherà ad altri moduli oggetto (tipicamente sottoprogrammi contenuti in librerie o realizzati dall’utente)
e che lo renderà disponibile per essere caricato in memoria centrale per essere eseguito.
6 Si introducono variabili e costanti (in modo molto simile a quello dell’algebra) anziché usare indirizzi numerici binari.
7 Si tratta di linguaggi detti anche 1 a 1, in quanto ad ogni istruzione simbolica verrà a corrispondere, dopo la traduzione,
una istruzione in linguaggio macchina (è anche per questo che il programmatore deve avere una chiara conoscenza del
funzionamento dell’elaboratore).

4
Per quanto concerne, poi, le diverse modalità di "traduzione" 8 di un programma in
linguaggio simbolico ad alto livello, possiamo parlare di due diverse strategie, e cioè:

1) compilativa,
2) interpretativa.

I linguaggi simbolici ad alto livello che vengono tradotti usando la strategia


compilativa hanno bisogno di un particolare programma, detto "compilatore", che legge il
"programma sorgente", scritto cioè nello specifico linguaggio simbolico e lo traduce in un
"programma oggetto" (in linguaggio macchina); dopo l'uso del "linker", per mezzo di un
apposito programma di sistema operativo, detto caricatore o "loader", il modulo eseguibile
sarà caricato in memoria centrale, pronto per la sua esecuzione.
Nella figura 1 sono schematizzate le fasi da seguire per la soluzione di un problema
usando la strategia compilativa (che, ad esempio, per il FORTRAN è quella di gran lunga
prevalente).

Figura 1 - Iter di un programma usando un compilatore9

Abbiamo già parlato delle prime due fasi; la terza è quella che permette l'immissione
da tastiera, attraverso un "editore", del programma scritto nel linguaggio scelto.
In questo caso, il programma sorgente viene tradotto (o meglio, “compilato”)
attraverso un programma detto, appunto, compilatore; per far ciò tale programma, che è

8 Alcuni linguaggi di programmazione possono essere tradotti utilizzando entrambe le strategie; questo dipende, quasi
esclusivamente, dal programma di traduzione di cui disponiamo.
9 In questa figura non appare l'utilizzo del programma "linker", quello che trasforma il programma tradotto con il
compilatore in un programma eseguibile unendolo, come abbiamo detto, ad altri codici oggetto (contenuti in librerie di
sistema o creati ad hoc dall’utente).

5
registrato in una memoria ausiliaria in forma eseguibile, viene inserito in memoria centrale;
dalla fase di traduzione, la fase 4, si ottiene sia un programma scritto in linguaggio
macchina, che viene registrato in una nuova zona di memoria non volatile, sia una lista con
evidenziati eventuali errori di tipo sintattico. Alla fine della fase di traduzione il compilatore
fornisce oltre al programma in linguaggio macchina anche la lista degli errori commessi
dall'utente con indicazione del tipo di errore e dell'istruzione errata 10. Se non si verificano
errori si può passare alla esecuzione del programma, fase 5, con i dati relativi, per ottenere
i risultati desiderati. Qualora vi siano errori, questi saranno corretti, passando poi
nuovamente dalla fase 4, finché siano stati tolti tutti gli errori formali.
E' bene precisare che quando si parla di errori, si parla di errori di tipo sintattico, cioè
di errori secondo le regole del particolare linguaggio usato; gli errori di tipo semantico, nella
logica dell'algoritmo, non vengono rilevati, né evidenziati in questa fase: l'unica possibilità
per scoprirli è quella di una prova con dati test nella fase 5.
Anche usando la strategia interpretativa i programmi hanno ugualmente bisogno di
essere tradotti, ma ciò avverrà per mezzo di un "interprete". Tale programma opera in
maniera diversa dal compilatore, perché non si limita alla sola traduzione, ma procede
anche all'esecuzione, istruzione per istruzione. In tal caso non viene effettuata una
preliminare traduzione di tutto il programma: le istruzioni vengono tradotte ed eseguite
una alla volta, dalla prima fino all'ultima, fino a quella, cioè, che indica la fine logica del
programma.
Nella figura 2 sono evidenziate le fasi dell'iter di un programma, nel caso si utilizzi
una strategia di tipo interpretativo. Le fasi 1, 2 e 3 sono simili a quelle del caso precedente,
mentre quelli che cambiano sono i passi successivi, relativi alla traduzione e all'esecuzione:
in questo caso c'è un'unica fase. Infatti, dal momento in cui si immette in memoria il
programma, le singole istruzioni, una alla volta o in blocchi, vengono tradotte ed eseguite
in un solo passo. In memoria, durante questa fase, ci sarà, quindi, sia il programma sorgente,
che l'interprete, che le istruzioni tradotte. Naturalmente, in questa fase, si rilevano subito sia
gli errori sintattici, che semantici, istruzione per istruzione o blocco per blocco.

Figura 2 - Iter di un programma utilizzando un interprete

10 A volte, se richiesta, può essere possibile avere una versione del programma scritta in linguaggio simbolico a basso
livello.

6
Utilizzando un compilatore, durante la fase di esecuzione avremo in memoria
centrale il programma tradotto in linguaggio macchina e reso eseguibile.
Usando un interprete abbiamo minor "efficienza" 11 nel trattamento dei cicli, in quanto
le istruzioni inserite in un ciclo vengono spesso tradotte ed eseguite, tante volte quanto
indicato dall'indice del ciclo stesso, o da altre condizioni previste dall'algoritmo.
Con i compilatori, nella fase della traduzione, vengono rilevati soltanto gli errori
sintattici ed ogni correzione degli stessi comporta una nuova traduzione di tutto il
programma, con evidente spreco di tempo. Inoltre, anche gli errori semantici, rilevati
successivamente nella fase di esecuzione, sia pure di minimo livello, comporteranno una
nuova compilazione di tutto il programma.
Si comprende da quanto detto a proposito degli errori, che la messa a punto di un
programma, cioè l'insieme di tutte quelle operazioni iniziali e preliminari alla esecuzione
definitiva a regime, è facilitata nel caso di uso di un interprete, a scapito dell'efficienza, che
si ha in misura maggiore, durante la fase di esecuzione, utilizzando i compilatori.
L'ideale sarebbe poter adoperare un interprete per la correzione degli errori, ossia per
la fase di messa a punto, e un compilatore, invece, per preparare il programma per l'esecu-
zione a regime, ossia per la fase di gestione della procedura. Questa ultima esigenza si fa
sentire, notevolmente, nei casi in cui un programma debba essere eseguito in maniera
ricorrente; in tal caso, una volta corretto e compilato, il programma può essere mandato in
esecuzione più volte senza doverlo tradurre ogni volta.
In figura 3 viene riassunta la classificazione dei linguaggi di programmazione
presentata in questo paragrafo.

Figura 3 - Classificazione dei linguaggi di programmazione 12

4. ESEMPI DI LINGUAGGI DI PROGRAMMAZIONE

In questo paragrafo vengono date sommarie informazioni su alcuni linguaggi di


programmazione, cominciando dal primo linguaggio simbolico ad alto livello realizzato: il
FORTRAN.
Il FORTRAN (Mathematical Formula Translation System), è uno dei linguaggi che
meglio si adattano a problemi di tipo scientifico. Varie sono state, nel tempo, le versioni di
questo linguaggio, versioni che si sono sempre più evolute, a partire dal 1954, anno in cui

11 Un criterio per misurare l'efficienza di un programma è normalmente il tempo di esecuzione su una determinata
macchina rispetto ad un altro programma sempre sulla stessa macchina, o la sua occupazione di memoria relativa.
12 Ricordiamo che di per sé un linguaggio di programmazione non è necessariamente legato ad una specifica forma di
traduzione (spesso per un determinato linguaggio sono disponibili entrambe le strategie: compilativa e interpretativa fra
cui scegliere in base alle esigenze).

7
inizio, come abbiamo accennato, la sua realizzazione grazie ad un progetto della IBM per
l'allora nuovo elaboratore 704. L'iniziativa ebbe subito un notevole successo; intorno anni
intorno al 1960, si è assistito ad una proliferazione di versioni FORTRAN per i vari elaboratori,
tanto da arrivare alla necessità di una versione standard. Tale prima definizione fu
effettuata, sempre dalla IBM, con la versione FORTRAN IV del 1962, e dall'ANSI (American
National Standards Institute) nel 1966, con la versione FORTRAN 66 (ANSI, 1966). Oggi, a
seguito di numerose estensioni, siamo arrivati al FORTRAN 77 (ANSI, 1977) e al FORTRAN 90,
versioni che permettono al linguaggio, nato prevalentemente per problemi di tipo
scientifico, di adattarsi bene anche a problemi di tipo diverso.
Il linguaggio è comunque basato su una notazione di tipo algebrico, sia pure adattata
alle necessità degli elaboratori. Come tutti i linguaggi di programmazione tutte le
espressioni sono costituite da linee su di un unico livello.
Una tipica istruzione di assegnazione è scritta in FORTRAN con una notazione di
questo tipo:

C=A+B

Le istruzioni FORTRAN, fino alla versione 77, avevano un tracciato fisso e tre
caratteristiche importanti che differenziavano questo linguaggio da quasi tutti gli altri:

1) gli spazi bianchi non erano rilevanti ai fini del linguaggio (non servivano cioè da
separatori fra elementi) e potevano essere inseriti anche all'interno di espressioni (escluse le
costanti carattere) senza avere alcun effetto;
2) non esistevano parole chiavi riservate (le parole che identificano le varie istruzioni, come
IF, GOTO, DO, ecc., potevano quindi essere liberamente usate come variabili;
3) non veniva utilizzato alcun simbolo "terminatore" per individuare la fine di una
istruzione (mentre altri linguaggi usano, ad esempio, il punto od il punto e virgola.

Il COBOL (COmmon Business Oriented Language) è stato progettato su specifiche del


Pentagono. Sono stati molto curati gli strumenti di definizione degli archivi, sacrificando,
soprattutto nelle prime versioni gli aspetti "computazionali". Le istruzioni sono state basate
su frasi in lingua inglese; una tipica operazione equivalente a quella sopra riportata per il
FORTRAN assume questa sintassi:

ADD A TO B GIVING C.

Si noti il carattere "terminatore", rappresentato dal ".", che, nelle prime versioni,
serviva a far riconoscere al compilatore il termine dell'istruzione.
Il BASIC è nato come versione interattiva del FORTRAN, per sfruttare le opportunità di
utilizzare i terminali che, a partire da fine anni sessanta, permettevano un uso "diretto" degli
elaboratori da parte di più utenti, anche per sviluppo di procedure e a fini di "elaborazione
personale", soprattutto in campo scientifico (anche se le versioni successive hanno ampliato
notevolmente le capacità di trattamento di archivi e stringhe tanto da essere largamente
utilizzato anche per elaborazioni di tipo aziendale). L'avvento dei personal computer,
permettendo una larga diffusione di elaborazioni interattive ha fatto diffondere molto
questo linguaggio. Sin dalle origini in quasi tutte le implementazioni è stata scelta la logica
interpretativa. Solo in un secondo momento l'uso del linguaggio per realizzare applicazioni
di uso ricorrente ha incentivato la realizzazioni di compilatori.
8
La notazione del BASIC, viste anche le sue origini, è di tipo algebrico e molto vicina a
quella del FORTRAN. L'istruzione sopra utilizzata come esempio appariva nelle prime
versioni BASIC con questa sintassi:

LET C=A+B

In seguito la parola LET non è più divenuta necessaria per distinguere le istruzioni
di assegnazione. Ricordiamo che, come accennato, la numerazione delle istruzioni ne
sospende l'esecuzione fino a quando non venga comandata con la frase RUN.
Per finire, anche per ribadire che le istruzioni dei linguaggi simbolici ad alto livello
sono destinate ad essere tradotte in più istruzioni in linguaggio macchina, riportiamo una
esemplificazione di quella che potrebbe essere la sintassi delle istruzioni di un linguaggio
simbolico a basso livello per effettuare la stessa operazione sopra indicata:

LW 1,A
AD 1,B
STW 1,C

Come si può vedere, sono state necessarie tre istruzioni:


la prima serve per caricare l'accumulatore (registro) 1 con il valore memorizzato in memoria
centrale nella posizione simbolicamente indicata con la variabile A; la seconda serve ad ag-
giungere nello stesso accumulatore il valore contenuto in B; la terza a portare nella variabile
(o, meglio, posizione di memoria) C il risultato della addizione. Anche gli operandi sono
espressi attraverso simboli che ne facilitano il ricordo.
Come confronto, riportiamo qui di seguito le stesse istruzioni tradotte in un ipotetico
linguaggio macchina, con istruzioni lunghe 32 bit; per ragioni di spazio usiamo la notazione
esadecimale (ricordiamo che ad ogni carattere esadecimale vengono a corrisponde quattro
valori binari).

504B17AD
5A4B17A1
5F4B17A2

Il linguaggio utilizzato è molto simile ad uno reale. Possiamo notare la maggiore


complessità di questa notazione rispetto anche a quella simbolica a basso livello (detta anche
1 a 1, visto che per ogni istruzione simbolica viene a corrispondere una istruzione in
linguaggio macchina). Come si vede, in questo ultimo esempio gli operandi non sono più
rappresentati in modo simbolico e non vengono adoperate variabili per riferirsi a posizioni
di memoria su cui operare.

5. EVOLUZIONE DEI SISTEMI OPERATIVI

La nascita dei sistemi operativi e la loro evoluzione nel tempo è avvenuta


contemporaneamente e conseguentemente all'evoluzione dell'hardware. Infatti i computer
si sono dotati di periferiche (prima nastri, poi dischi magnetici ad accesso diretto) e di unità
di canale che hanno reso più complessa la realizzazione del software di base. A questo
proposito sono stati messi a punto dei programmi che agiscono da interfaccia fra utente e

9
computer, rendono più semplice la programmazione e liberano l'utente dalla necessità di
conoscere caratteristiche tecniche specifiche e difficoltose. Oltre a questa funzione di
interfaccia, un sistema operativo controlla e supervisiona l'esecuzione dei vari lavori in un
sistema di elaborazione ottimizzandone l’utilizzo delle risorse (memorie e processori).
L'evoluzione di questi programmi attraverso il tempo, ci porta ad analizzare le varie
modalità di elaborazione che si sono via via avvicendate dando luogo a sistemi operativi
diversamente complessi e strutturati.

I primi elaboratori elettronici con supporti non magnetici venivano utilizzati


dall'utente che svolgeva spesso sia le mansioni di programmatore che di operatore. Così
provvedeva a caricare il suo programma in memoria e ad eseguirlo. Se il programma si
interrompeva rimuoveva le cause dell'interruzione e di nuovo lanciava l'esecuzione. Per cui
l'interazione fra uomo e computer era completa e non era nemmeno necessario un
programma di supervisione dei lavori (al massimo venivano preventivamente caricate in
memoria centrale alcune informazioni utili per effettuare le elaborazioni, come, ad esempio,
le tabelle di somma e moltiplicazione).
L'inconveniente della precedente modalità consisteva nei tempi di attesa del sistema
per operazioni di correzione o di messa a punto dell'utente. La successiva modalità di
elaborazione ha eliminato l'interazione diretta fra l'utente ed elaboratore migliorando
l'utilizzo del sistema. Si procedeva con la sequenza:

- l'utente presentava il lavoro, (consistente in genere in un pacco di schede) all'operatore;


- il lavoro veniva immesso in macchina, non appena essa risultava disponibile;
- il risultato dell'esecuzione veniva restituito all'utente.

Con questo procedimento, detto anche “batch” o “a lotti” l'elaboratore eseguiva un


programma alla volta, e serviva un utente alla volta, il quale rimaneva distaccato dal
computer. Il suo compito consisteva nel consegnare un lavoro e nel riprendere il risultato.
Per lavoro si può intendere la traduzione di un programma in linguaggio simbolico, un
lavoro gestionale, una elaborazione scientifica, ecc..
Nascevano così i primi sistemi operativi, vale a dire programmi che si incaricavano
di leggere i vari lavori degli utenti, di caricarli in memoria, assegnare le periferiche richieste,
curare l'emissione dei risultati. A questo proposito l'utente, insieme alle schede dei suoi
lavori, doveva apporre particolari schede di controllo che indicavano al sistema operativo il
linguaggio utilizzato, le periferiche contenenti gli archivi ed ogni altra informazione utile
per l'esecuzione del programma. Il sistema operativo, poi, interagiva con l'operatore tramite
messaggi inviati sulla console per indicare la necessità di rendere disponibili periferiche,
fornire notizie sull'esito dell'esecuzione, ecc..

La modalità di elaborazione precedentemente esaminata, a blocchi sequenziali di


programmi, ha trovato un miglioramento notevole nell'efficienza di utilizzo dell'unità di
elaborazione con l'introduzione delle unità di canale, che, come abbiamo visto, sono
particolari dispositivi in grado di eseguire le operazioni di immissione-emissione in modo
autonoma dall’unità centrale di elaborazione. A questo punto è nata una nuova modalità di
elaborazione: la multiprogrammazione che consiste nell'esecuzione di più lavori

10
“contemporaneamente”13:
• la memoria dell'elaboratore viene ripartita in più partizioni;
• ai vari lavori da eseguire viene assegnata una priorità;
• quando un programma si interrompe, automaticamente l'unità di elaborazione passa
ad eseguire le istruzioni di un altro programma. Se le interruzioni sono causate da
istruzioni di input-output, mentre il canale si occupa di eseguirle, l'unità di
elaborazione serve un nuovo programma finché non è stata eseguita l'istruzione di
input-output.
Si può dunque dire che la multiprogrammazione consista nell'esecuzione
“contemporanea” di più lavori in memoria. L'unità di elaborazione esegue alternativamente
i vari programmi e dopo ogni interruzione riprende ad eseguire il programma con priorità
più alta.
Tutto questo meccanismo viene gestito dal sistema operativo che diviene ovviamente
più complesso per consentire a più utenti di usare contemporaneamente i tempi morti
dell'unità di elaborazione.
L'evoluzione della tecnologia informatica ha determinato lo sviluppo
dell'elaborazione interattiva, vale a dire l'elaborazione tramite un colloquio diretto fra
utente (ad esempio, tramite tastiera, mouse e video) ed elaboratore (non necessariamente un
personal computer né necessariamente disponibile localmente) per usufruire di vari servizi.
Il nascere dell’elaborazione interattiva si accompagna ad un'evoluzione generale di
hardware e software. Anche i sistemi operativi divengono più complessi perché spesso si
trovano ad operare in un ambiente in cui il computer collega decine o anche centinaia di
utenti e quindi di potenziali richieste di elaborazione. L'utente, in questo scenario,
interagisce con il sistema ignorando le contemporanee richieste di elaborazione degli altri
utenti, perché in genere il sistema è così veloce da rispondere non appena l'utente ha
terminato la sua richiesta. Di conseguenza il miglior contributo dell'interattività è stata
l'eliminazione, per l'utente, dei tempi di attesa dei risultati.
Esaminando la multiprogrammazione abbiamo visto che il supervisore cede il
controllo ad un nuovo processo quando si verifica un'interruzione. Se, per la natura dei
programmi (di carattere scientifico o di prova, con errori eventuali di loop, ossia esecuzione
ciclica di un gruppo di istruzioni da cui non può uscire) non si hanno interruzioni, il
meccanismo della multiprogrammazione va in crisi poiché un solo processo può
monopolizzare le risorse di calcolo. Per ovviare a questo inconveniente si è pensato di
ripartire in parti uguali fra gli utenti il tempo di unità di elaborazione introducendo una
nuova causa di interruzione: l'orologio di sistema. Questa modalità di elaborazione è detta
time-sharing 14. Gli utenti appaiono tutti come equi-prioritari e se, trascorso il tempo di unità
di elaborazione assegnato al processo, questo non si è ancora interrotto, il supervisore,
consultando l'orologio di sistema, cede il controllo al processo successivo.
Questa modalità è utilizzata principalmente in ambienti scientifici o di sviluppo di
applicazioni (messa a punto di programmi) quando, invece di singoli personal indipendenti,

13 Tale contemporaneità è solo apparente (per l’utente il sistema sembra funzionare in modo “parallelo” anche quando
non è dotato di un hardware che comprende più processori e/o unità di elaborazione vettoriale).
14 Questa denominazione si utilizza in relazione ad ambienti interattivi, con più utenti collegati; nel caso di applicazioni
batch concorrenti, senza assegnazione di priorità, ma basate sull’assegnazione di un determinato tempo di utilizzo del
processore, si parla di time slicing.

11
si utilizza un sistema centrale cui sono collegati i vari posti di lavoro.

Con l'avvento dell'elaboratore l'utente ha diminuito notevolmente i tempi dedicati al


calcolo. Tuttavia, all'epoca dell'elaborazione batch, specialmente con l'utilizzo di schede
perforate, nessun vantaggio era stato apportato alla fase di raccolta dati e consegna dei
risultati. L'immissione e l'emissione rimanevano fasi pesanti, causa di molti errori (di
codifica, trascrizione, perforazione) e tutto ciò aveva spento molti iniziali entusiasmi
sull'utilizzo dell'elaborazione automatica.
Una svolta fondamentale è stata ottenuta con l'utilizzo dell’elaborazione interattiva,
quando l'utente ha potuto immettere senza intermediari i dati nell'elaboratore ed ottenere
direttamente i risultati o le risposte dal sistema.
Quando il sistema risponde in tempi “brevissimi” all'utente, dandogli la possibilità
di influenzare un processo in corso si parla di tempo reale (real time). Questa modalità di
utilizzo del sistema presuppone di avere macchine veloci e archivi ad accesso diretto, per
poter leggere o scrivere i singoli record in tempi brevi. Gli archivi devono essere aggiornati
costantemente senza ritardi ponendo, quando possibile, i terminali (o meglio i personal
collegati al sistema centrale) nei luoghi in cui si origina l'informazione per poterla introdurre
immediatamente nel sistema. Un sistema tempo reale deve garantire un funzionamento
continuativo ed ininterrotto, con salvataggio e protezione delle informazioni.

6. COMPONENTI DI UN SISTEMA OPERATIVO

Nel descrivere un sistema operativo è necessario far riferimento alle sue componenti
che, come abbiamo detto, sono costituite da programmi, che si avvicendano o risiedono
permanentemente in memoria per gestire l’elaboratore. Fra le componenti principali ci sono,
oltre al supervisore, il gestore della memoria, alcuni moduli che ordinano i programmi in
vista della loro esecuzione, il gestore dell’input-output. Se si dà una interpretazione
estensiva al termine “sistema operativo”, vi possiamo includere anche i traduttori e i
programmi di utilità (ad esempio, di ordinamento e di fusione degli archivi).
Il supervisore è il programma principale del sistema operativo e risiede
costantemente (in tutto o in parte) in memoria. Ha lo scopo di seguire le varie applicazioni,
via via che passano da uno stato di avanzamento ad un altro. Per far questo si serve di un
registro chiamato PSW (Program Status Word, costituita ad esempio da 64 bit) che
memorizza lo stato del programma (l'istruzione che sta eseguendo, l’indirizzo della
successiva, ecc.).
Poiché l'unità di elaborazione è unica, in ogni momento l'elaboratore può essere in
stato utente se esegue istruzioni di programmi degli utenti, o in stato supervisore se esegue
programmi del sistema operativo.
Le interruzioni di un sistema di elaborazione possono essere:
- malfunzionamento hardware,
- errori di programma,
- chiamata al supervisore,
- interruzioni esterne,
- interruzioni di ingresso-uscita.

12
Per accedere ad un sistema di calcolo, non è sufficiente che un utente conosca un
linguaggio di programmazione; deve sapere anche i comandi necessari per comunicare con
il sistema operativo.
Infatti, oltre al lavoro, l'utente deve immettere nel sistema una serie di informazioni
concernenti il tipo di lavoro, il linguaggio di programmazione utilizzato, gli archivi usati, le
caratteristiche degli archivi in ingresso e in uscita. Tutti questi dati sono inseriti nel lavoro
inviato o immessi direttamente da tastiera (a seconda che si utilizzi una modalità batch o
interattiva). Questi comandi fanno parte di un apposito linguaggio di descrizione dei lavori
che viene tradotto da un apposito interprete, ulteriore componente del sistema operativo.

Per quanto riguarda la gestione dei file è forse opportuno accennare al fatto che ciò
che appare all’utente rispetto al contenuto di una memoria ausiliaria, soprattutto quando si
utilizzano interfacce grafiche, è una struttura gerarchica virtuale. Dal punto di vista fisico
non abbiamo invece niente di diverso da ciò che abbiamo presentato in precedenza: la
gestione a settori o a cilindri
Il sistema gerarchico di archiviazione viene anche chiamato sistema ad albero perché
presenta una struttura ramificata che ricorda un albero.
Per rappresentare un file inserito in una struttura gerarchica si deve descrivere il
cammino da compiere per raggiungerlo. Il cammino si esprime, partendo dalla radice
(indicazione dell’unità di memoria ausiliaria), e procedendo via via per livello: in questo
caso si parla di indirizzo assoluto (sempre valido a prescindere dalla directory in cui ci si
trovi). Si tratta invece di un indirizzo relativo quello che parte dalla directory nella quale
siamo posizionati: ad esempio, se il file (contenete dati o un programma) si trova
virtualmente in tale posizione è sufficiente utilizzarne il solo nome.

DIRECTORY RADICE

FILE1 FILE2 DIRECTORY X FILE3 DIRECTORY Y

FILE4 FILE5 DIRECTORY Z FILE6

FILE7 FILE8

Figura 4 – Schematizzazione di una struttura gerarchica.

7. ALCUNI SISTEMI OPERATIVI

Adesso prenderemo sommariamente in esame alcuni sistemi operativi, considerando


in pratica quelli utilizzati per gestire i personal computer.
Cominciamo con l'MS-DOS 15 che permetteva di lavorare con una modalità single-
user: consentiva l’accesso alle risorse dell’elaboratore solamente ad un utente; questo utente
poteva usare le risorse solamente per far funzionare un’applicazione alla volta.

15 In un primo momento, per i personal computer IBM la versione utilizzata era detta PC-DOS.

13
Questo sistema operativo nato nei primi anni ottanta in quanto commissionato per i
primi PC di IBM a Bill Gates, ideatore anche di GW-BASIC. Bill Gates sviluppa la prima
versione di MS-DOS e diventa fondatore e presidente di una delle software-house più
importanti del mondo: la Microsoft.
L'MS-DOS (MicroSoft Disk Operating System) però non nasce dal nulla, infatti si basa
su un sistema operativo già conosciuto e diffuso allora sui PC: il CP/M. Dalla sua nascita ne
sono seguite varie versioni per adattarsi all'aggiornamento della tecnologia dei computer.
Era un sistema operativo molto semplice anche perché applicato, come accennato, in
ambiente di monoprogrammazione, salvo specifiche eccezioni.
Un sistema operativo che risale a molti anni fa, ma che è ancora attuale, nelle sue
ultime versioni, è UNIX. UNIX è stato sviluppato nel 1969 da Ritchie e Thompson nei
laboratori della Dell, si rivolgeva ad utenti di mainframe o mini-computer. Infatti il sistema,
fin dalle sue prime versioni, funzionava in modo multi-user e multi-tasking, per le esigenze
degli utenti di tali calcolatori che spesso avevano un'architettura composta da un computer
e più terminali.
Nel tempo UNIX si è evoluto ed anche i programmi applicativi che funzionano in
questo ambiente. Alcune importanti caratteristiche sono costituite dalla protezione del si-
stema, dalla sua portabilità (possibilità di funzionare su elaboratori diversi senza grandi
modifiche).
La portabilità è stata ottenuta sviluppando un linguaggio di programmazione
apposito: il C, creato dallo stesso Ritchie. UNIX fornito non come programma eseguibile ma
come codice scritto in C, dovrà essere tradotto con appositi compilatori al momento
dell’installazione. La portabilità ha un altro vantaggio a favore degli utenti, su ogni
computer si può lavorare immediatamente senza dover imparare un'interfaccia utente
completamente nuova e con comandi differenti.
Attualmente versioni UNIX (anche se con denominazioni diverse) funzionano su
moderne workstation di molte case costruttrici.
Alcune versioni dello UNIX, come per esempio XENIX e MINIX furono sviluppate
per essere installate sui primi personal, fino ad arrivare ad un altro sistema unix-like:
LINUX, a partire dal 1991, progettato per i nuovi processori 80386 INTEL a 32 bit,
attualmente molto diffuso, soprattutto su macchine di notevole potenza di calcolo 16.
Alla fine del 1988, è stato presentato Windows; inizialmente, fino alla versione 3.1
non si poteva considerare un sistema operativo: era più appropriato parlare di ambiente
operativo perché in effetti era un programma che si aggiungeva al sistema operativo vero e
proprio che rimaneva il DOS. Windows aggiungeva solamente funzioni e potenzialità
nuove, soprattutto nella visualizzazione grafica delle risorse (anche tramite icone, dette
“cartelle”).
Versioni successive lo vedono diventare un sistema operativo sempre più autonomo
dal DOS, su cui era stato in parte progettato. Denominazioni come Windows NT, 2000, XP,
Vista, 7, 8, e così via, fanno la storia recente dell’elaborazione personale e non solo. Tali
versioni, sempre più complesse e con maggiori funzionalità prevedono anche il multi-
tasking, la multi-utenza, una migliore gestione della memoria, ecc..
Si può dire che Windows insieme a Linux e ai sistemi OS della Apple costituisca la
quasi totalità del software di base degli attuali personal computer.

16 Elaboratori di questo tipo vengono anche collegate fra loro per costituire dei cluster e ottenere così prestazioni tali da
poter sostituire grossi sistemi di calcolo.

14