I componenti principali di un elaboratore per quanto riguarda la componente Hardware, come gia’ detto, sono :
CPU
MC
Unita’ di Output “Video o Monitor … ”
Unita’ di input “Tastiera , Mouse…”
MS “Dischi fissi o Hard Disk , Dischetti floppy, nastri magnetici, CD-ROM, DVD,…”
BUS “cavetti”
Ha lo scopo di memorizzare grandi quantita’ di dati in modo persistente; i dati memorizzati nella MS sopravvivono alla esecuzione dei programmi ed alla terminazione della alimentazione dell’elaboratore. La capacita’ ( capienza di INFO ) di queste memorie varia molto da dispositivo a dispositivo;
Per i floppy disk la capacita’ e’ di 1,44 MB, mentre per gli Hard Disk, nei personal computer domestici e’ dell’ordine delle decine di GigaByte “ GB ”.
Il tempo di accesso per i floppy e’ dell’ ordine dei 100 msec, mentre per i dischi fissi e’ dell’ordine delle unita’ di millisecondo.
Distinguiamo tra due classi fondamentali di dispositivi, in base al metodo di accesso consentito.
1) Accesso diretto ( Dischi )
2) Accesso sequenziale ( Nastri magnetici )
In questi dispositivi magnetici, nastri o dischi abbiamo che l’area del dispositivo viene suddivisa in micro-zone ed in ogni micro-zona si puo' memorizzare una INFO elementare. Una micro-zona puo’ essere magnetizzata oppure non magnetizzata ed in base al fatto di avere uno stato magnetizzato o non magnetizzato gli vengono associate le due cifre binarie 0 od 1. In una micro-zona si memorizza quindi un solo bit.
Nei nastri magnetici vengono tracciate sul nastro delle piste orizzontali e parallele di solito 9 piste parallele 8 bit + 1 bit di parita’.
Gli Hard Disk sono formati da una pila di piatti con due superfici per piatto, che ruotano attorno ad un asse centrale.
Fig. 12.1
Ogni superficie dei piatti dispone di una testina di lettura /scrittura e sono organizzate in un certo numero di cerchi concentrici detti TRACCE ed in spicchi di grandezza uguale detti SETTORI. Le tracce equidistanti dal centro formano un CILINDRO. I cilindri sono numerati dall’ esterno verso l’ interno , iniziando da cilindro 0 .
Un Hard Disk nei dati di targa contiene il numero di tracce, il numero di settori ed il numero di cilindri.
Le INFO sono scritte su posizioni successive lungo le tracce, come sequenze binarie, corrispondente il singolo bit ad uno stato di magnetizzazione positivo o negativo del materiale con cui e’ costruito il piatto del disco.
I floppy disk sono dischi magnetici di piccola capacita; sono portabili e vengono usati per trasferire informazioni (file) tra computer diversi o per archiviare piccole informazioni di piccola capacita’; il diametro degli attuali floppy disk e’ di 3,5 pollici (inch). Per poter essere utilizzati, i floppy richiedono una operazione detta formattazione, ossia il sistema operativo tramite un dispositivo chiamato floppy driver suddivide i dischetti in tracce e settori. Notare che la formattazione dei dischetti distrugge eventuali informazioni precedentemente registrate, per cui quando la si esegue si dovrebbe prima controllare il contenuto del floppy disk, affinche’ non vengano cancellate informazioni importanti. In commercio oggi i floppy sono gia formattati.
I CD-ROM (Compact Disk - Read Only Memory ) che hanno una capacita’ di 650 MB .
I lettori di CD-ROM sono dispositivi ottici (a laser ) che hanno una velocita’ di trasferimento elevata; in origine era di 150KB/sec identificata come (“1X”);
oggi i lettori di CD-ROM possono leggere i CD-ROM a velocita’ di 24X,32X,40X,50X (150KB * 50 ) rispetto alla velocita’ di trasferimento iniziale (anche se c’e’ da considerare che queste velocita’, sono un po’ teoriche in quanto la velocita’ di trasferimento e’ influenzata da come e’ scritta l’ informazione sul CD: 2X, 4X ,12X, 24X,….).
In ultima analisi l’hardware contiene INFO + esegue ISTRUZIONI MACCHINA; mentre il SOFTWARE coincide con le sequenze di istruzioni ( un insieme complesso di programmi ).
Il bootstrap e’ la procedura di avvio e predisposizione al funzionamento del computer.
All’accensione del computer alcuni programmi base, che risiedono nella ROM, vengono caricati in MC e poi eseguiti. Si controlla il funzionamento hardware ( tutto connesso ) e viene caricato il nucleo (Kernel) del sistema operativo ( S.O. ) . E poi il computer si predispone in stato di PRONTO.
Il sistema operativo e’ un programma di grande dimensioni e di notevole complessita’ senza il quale nessun elaboratore potrebbe funzionare; il sistema operativo e’ quell’insieme di programmi dell’elaboratore che consentono di governare il controllo delle risorse fisiche e non. In pratica fornisce la interfaccia utente di qualsiasi sistema di elaborazione.
Il S.O. gestisce le risorse hardware e software del sistema di elaborazione;
le funzioni messe a disposizione dal S.O. dipendono dalla complessita’ del sistema di elaborazione e sono :
gestione delle risorse complessive disponibili
gestione della MC “copiare, trasferire”
organizzazione e gestione della MS “vedere cosa e’ registrato in MS (HD, Floppy..) ”
interpretazione ed esecuzione di comandi elementari “ eseguire altri programmi tipo word processor ( elaborazione di testi) ”
gestione di un sistema multi-utente
Un utente comunica con l’elaboratore solo tramite il S.O. ; con il S.O. il livello di interazione utente/elaboratore viene elevato; senza di esso l’interazione sarebbe a livello di bit.
Esempi di sistemi operativi sono : DOS, WINDOWS 95/98/2000/NT/ME, UNIX , LINUX .
S. base e’ il software di base che coincide con {programmi del S.O.} È {compilatori per linguaggi ad alto livello}
S. applicativo e’ il software applicativo che coincide con {programmi specifici che usano il software di base per problemi specifici}
F.S. e’ il file system (“gestore dei file”), che ha il compito di gestire le informazioni memorizzate sulla memoria secondaria . Si occupa in particolare della organizzazione gerarchica in file e directory.
Il FILE e’ un blocco di MS contenente un insieme di INFO omogenee, classificate sotto un unico nome ( nome del file). Nella programmazione come certo sara’ noto il file e’ formato da:
nomefile.estensione esempi di nomi di file sono: nomefile.c , nomefile.txt , nomefile.exe ecc. Rispettivamente file di tipo c, file di testo , file eseguibile.
Le unita’ di memoria secondaria HD, FLOPPY hanno un nome logico nel FILE SYSTEM tipo C: A:
piu’ unita’ à piu’ nomi C: D: E: …
La DIRECTORY ( direttorio ) e’ una tabella ( contenitore ) che raggruppa piu’ file sotto di una stessa unita’ sotto un nome comune. Le directory sono organizzate secondo una struttura gerarchica ad albero, la cui radice e’ C: . Questa directory radice puo’ contenere a sua volta directory e file.
Nella Fig. 12.3 notiamo che tutti i nodi foglia sono dei file effettivi, mentre i nodi non foglia sono delle directory. Oltre che una notevole flessibilita’ si garantisce in questo modo un meccanismo molto efficace per la identificazione univoca dei file; infatti un file e’ identificato univocamente dal suo path-name, cioe’ dalla successione dei nomi dei direttori che si incontrano lungo il cammino dalla radice al file stesso.
Cosi’ per identificare il file3.txt scriveremo C:\file3.txt mentre per identificare file2.exe dobbiamo passare nella directory TC in cui esso e’ contenuto e scriveremo C:\TC\file2.exe .
C:\> prompt
dir seguito da invio . Elenca la lista di file e directory <DIR> della directory in cui si e’ posizionati
cd TC seguito da invio , change directoy ( cambia directory e vai alla sottodirectoy TC dove sono visibili i file file1.c e file2.exe)
cd.. seguito da invio, ( sali su di una directory salita di un livello verso la radice )
a: seguito da invio ,( comando per posizionarsi sul floppy disk che si trova nell’ unita’ A )
se si e’ nell’ unita’ C si puo’ visionare il contenuto dell’ unita’ A con il comando
dir a: seguito dal tasto invio.
file2.exe seguito da invio, provoca l’ esecuzione di file2.exe se la directory corrente e’ quella che contiene file2.exe ( a meno che non vi sia nel file autoexec.bat il path di tale file )
edit c:\file3.txt seguito da invio, carica nell’editor del dos il file3.txt indipendentemente da quale directory ci si trova, in quanto abbiamo fornito il path completo per raggiungere file3.txt .
md TC (make directory ) comando per costruire una directory di nome TC
copy *.* a: comando per copiare tutti i file della directory corrente nel dischetto A
rd TC (remove directory) rimuove la directory TC; affinche’ una directory si possa rimuovere deve, in DOS, essere priva di alcun file.
del file2.exe (delete) cancella il file di nome file2.exe
del *.exe cancella tutti i file di estensione exe ( il carattere * riveste il ruolo di jolly )
xcopy *.* /e/s a: copia tutti i file, le directory e sottodirectory in A
All'interno del direttorio di lavoro C:
1. Generare il seguente file system
FONDINF_ODL
/ \
/ \
PROVE LAVORO_C
2. Usare il programma EDIT per produrre un testo di almeno
20 righe, salvare il file nella direttory FONDINF_ODL con
nome prova.txt
3. Posizionarsi nella directory LAVORO_C e visualizzare il file
prova.txt nei due modi: percorso assoluto e relativo
4. Copiare il file prova.txt nella directory PROVE
5. Spostare il file prova.txt nella directory LAVORO_C
6. Dal direttorio FONDINF_ODL modificare il file prova.txt
che si trova nella directory PROVE
7. Visualizzare la struttura del file system a partire da
FONDINF_ODL. Quanti file con nome prova.txt compaiono?
8. Confrontare le versioni di prova.txt
9. Stampare il file prova.txt che si trova nella directory PROVE
10. Copiare l'intera directory FONDINF_ODL nel dischetto
Fasi della
compilazione di un programma
Il compilatore traduce un programma ad alto livello (programma sorgente) nel corrispondente programma in linguaggio macchina (programma oggetto)
“ prog.c à prog.obj ” .
Prima di tradurre prog.c, bisogna verificare che esso rispetti la grammatica del C
GC :
1)
sia scritto con simboli appartenenti a VC
2) sia riconoscibile mediante PC da SC
3) verifichi le regole di “ sintassi contestuale ” cioe’ quelle regole non espresse da GC, ma senza verificare le quali il programma non funziona: es. prima di usare una variabile bisogna dichiararla; non usare lo stesso identificatore per due variabili diverse; rispettare i tipi delle variabili.
La rigorosa e non ambigua definizione della sintassi e della semantica del linguaggio C, puo’ garantire una compilazione corretta dei programmi.
La prima funzione realizzata dal compilatore e’ l’analisi lessicale svolta dal modulo analizzatore lessicale, la seconda funzione e’ l’analisi sintattica svolta dall’ analizzatore sintattico , la terza e’ l’analisi di semantica statica.
1) ANALIZZATORE LESSICALE
Il programma sorgente viene scandito lettera per lettera riconoscendo le parole (simboli) che lo formano : parole chiave del linguaggio , operatori , costanti, identificatori . Se tutti i simboli sono riconosciuti allora vengono trasformati in simboli standard e questi inseriti in una tavola detta tavola dei simboli. In questa fase si possono individuare gli errori lessicali.
Esempio la seguente espressione
BETA1 = 10 + X * Y viene trasformata in id1= id2 + id3 * id4 e costruita la tavola dei simboli:
id1 |
BETA1 |
id2 |
10 |
id3 |
X |
id4 |
Y |
id1 , id2, id3, id4 sono nomi simbolici cioe’ le chiavi della tavola dei simboli
2)ANALIZZATORE SINTATTICO
Analizza la struttura sintattica delle istruzioni ; se quest’ultime sono riconoscibili anche il programma e’ riconoscibile.
L’ analisi sintattica nell’ esempio precedente , analizza la frase
id1 = id2 + id3 * id4 ; se la stringa a destra del simbolo = “operatore di assegnazione”
viene riconosciuta come < espressione> e la stringa nel suo complesso come <assegnazione>, in accordo con la regola sintattica dell’istruzione di assegnazione che e’ :
< assegnazione > ::= <variabile> = <espressione> , allora la stringa e’ sintatticamente corretta.
L’analizzatore sintattico
a) accede alla tavola dei simboli
b) per ogni istruzione costruisce l’albero di derivazione sintattica ( per riconoscere la frase)
Questo albero di derivazione servira’ per generare il corrispondente codice oggetto.
Dall’albero di derivazione viene costruita la rappresentazione tabellare delle istruzioni :
Considerando come esempio l’assegnazione e la seguente istruzione:
C = V * ( T - F) + (2 * 8 ) * ((T-F) - 50)
Avremo che l’albero sintattico che la riconosce e’ il seguente:
La rappresentazione tabellare e:
Simbolo |
Codice |
Operando1 |
Operando 2 |
|
|
M1 |
- |
T |
F |
|
|
M2 |
* |
V |
M1 |
|
|
M3 |
* |
2 |
8 |
ß |
16 |
M4 |
- |
T |
F |
ß |
= M1 |
M5 |
- |
M4
|
50 |
|
M1 |
M6 |
* |
M3
|
M5 |
|
16 |
M7 |
+ |
M2 |
M6 |
|
|
M8 |
= |
C |
M7 |
|
|
Sul codice intermedio e’ possibile eseguire delle ottimizzazioni ( ad esempio calcolare T - F una sola volta )
M1 ßà M4 eliminare M4 e mettere M1 in M5
M3 ßà 16 eliminare M3 e mettere 16 in M6
3)ANALIZZATORE DI SEMANTICA STATICA
Durante i passi di generazione del codice e di ottimizzazione si effettua anche l’analisi di semantica procedendo al controllo delle regole di semantica statica; l’analizzatore di semantica statica scopre quelle che sono potenziali situazioni di errore a RUN TIME (a tempo di esecuzione) precisamente quelle individuabili a COMPILE TIME ( a tempo di compilazione statico; non in esecuzione).
Ad esempio
int i;
char c;
k= 100;
k e’ una variabile non dichiarata ; l’errore viene segnalato dall’analizzatore di semantica statica.
In ognuna delle tre fasi di analisi si generano eventuali messaggi di errore ed informazioni che ci aiutano ad eliminarli.
In generale, per ogni tipo di istruzione avremo il seguente schema:
Funzione |
Input |
Output |
Analisi lessicale |
Programma sorgente come sequenza di simboli |
Codice intermedio, tavola dei simboli, errori lessicali |
Analisi sintattica |
Codice intermedio, tavola dei simboli |
Albero sintattico, codice intermedio, tavola dei simboli , errori sintattici |
Analisi semantica |
Codice intermedio, tavola dei simboli |
Programma oggetto, tavole varie , errori semantici |
Grafo per ottenere un programma funzionante
Fig. 12.5
L’ambiente di programmazione e’ un insieme di programmi che permette di seguire lo schema di Fig. 12.5
SCRIVERE il programma ( con un programma editore di testi )
COMPILARE
ESEGUIRE
(ESEGUIRE passo passo controllando il contenuto delle variabili ) = Debug
Queste operazioni sono dedicate ad un preciso linguaggio.
Scrittura di un
programma
Scriviamo un programma che: dato n e dati n numeri interi in input, stampa la somma dei positivi e quella dei negativi :
Ragionamento da fare :
Ci serve una variabile sommapos in cui
memorizzare la quantita’ dei numeri positivi; serve una variabile sommaneg in cui
memorizzare la quantita’ dei numeri negativi;
serve una variabile n inserita da input,
con la quale indichiamo al computer quanti sono i numeri che vogliamo sommare ;
serve una variabile numero in cui
memorizzare il valore del numero letto da input ;
serve una variabile i da usare come
variabile di controllo , in prospettiva di usare un ciclo while, per far
terminale la lettura dei numeri da input, ed uscire dal ciclo while.
Adesso iniziamo la scrittura del programma dentro l’editor del compilatore che stiamo usando ad esempio Rhide del DJGPP:
stesura 1 )
#include<stdio.h>
main ()
{
int sommapos, sommaneg, n , i ;
scanf (“\n%d”,&n);
i = 1;
while ( i <= n)
{
sommapos=0; sommaneg=0;
scanf (“\n%d”,
&numero );
if (numero < 0 )
sommapos = sommapos + numero ;
else
sommaneg = sommaneg +numero ;
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
}
Salvare il programma, ossia copiare il testo in un file su MS attraverso il comando SAVE (salva) o meglio SAVEAS (salva con nome ), presenti sul menu’ del compilatore alla voce FILE.
Compilazione
Compilando il programma alla voce compile del menu’ compile
avremo come risposta dal compilatore : errore numero non dichiarato ( error undefined symbol ‘numero’ );
allora passiamo di nuovo nella finestra di EDIT ed eseguiamo la
correzione; cioe’ inserire la variabile di nome numero
salvare
compilare
ok!
Stesura 2)
main ()
{
int sommapos, sommaneg, n , i, numero ;
scanf (“\n%d”,&n);
i = 1;
while ( i <= n)
{
sommapos=0; sommaneg=0;
scanf (“\n%d”,
&numero );
if (numero < 0 )
sommapos = sommapos + numero ;
else
sommaneg = sommaneg +numero ;
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
}
Esecuzione
Dal menu alla voce RUN clicchiamo sul comando run
Schermo con l’ esecuzione
appare soltanto il cursore lampeggiante - perche’ il programma sta’ eseguendo la istruzione scanf(“\n %d”,&n); cioe’ attende l’inserimento di un numero, da parte dell’utente, dalla tastiera. Per cui una buona idea e’:
andiamo nell’ EDIT ed inserire
printf (“\n, Quanti numeri vuole sommare ? ); (b)
Stesura 3)
main ()
{
int sommapos, sommaneg, n , i, numero ;
printf (“\n, Quanti numeri
vuole sommare ? ); (b)
scanf (“\n%d”,&n);
i = 1;
while ( i <= n)
{
sommapos=0;
sommaneg=0;
scanf (“\n%d”,
&numero );
if (numero < 0 )
sommapos = sommapos + numero ;
else
sommaneg = sommaneg +numero ;
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
}
Adesso in esecuzione compare la scritta Quanti numeri vuole sommare ?
Inseriamo un numero ad esempio 3 e poi ancora compare - il cursore lampeggiante
Conviene andare nell‘EDIT ed inserire la riga
printf (“ scrivete un numero ” ); (c);
Stesura 4)
main ()
{
int sommapos, sommaneg, n , i, numero ;
printf (“\n,
Quanti numeri vuole sommare ? ); (b)
scanf (“\n%d”,&n);
i = 1;
while ( i <= n)
{
sommapos=0; sommaneg=0;
printf (“ scrivete un numero ” ); (c)
scanf (“\n%d”, &numero );
if (numero < 0 )
sommapos = sommapos + numero ;
else sommaneg =
sommaneg +numero ;
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
}
salvare , compilare , eseguire.
Avremo in esecuzione
Quanti numeri vuole sommare ?
3 (invio)
scrivete un numero
2 (invio)
scrivete un numero
-4 (invio )
scrivete un numero
5(invio)
il risultato dato dal computer sara’ :
totale dei positivi = 0
totale dei negativi = 5
il risultato dato dal computer sara’ :
totale dei positivi = 0
totale dei negativi = 5
Evidentemente il programma e’ sbagliato in quanto avrebbe dovuto dare come
totale dei positivi = 7
totale dei negativi =-4
Come vedere dove e perche’ il programma e’ sbagliato?
con il DEBUG .
Nel programma vi sono errori logici che possono essere evidenziati con il metodo delle stampe di controllo per cui andiamo nell’EDIT ed inseriamo
(d) printf (“\n iterazione
%d numero scritto = %d ”, i , numero );
(d) printf (“\n somma
positivi = %d somma negativi = %d ”,
sommapos , sommaneg);
Stesura 5)
main ()
{
int sommapos, sommaneg, n , i, numero ;
printf (“\n,
Quanti numeri vuole sommare ? ); (b)
scanf (“\n%d”,&n);
i = 1;
while ( i <= n)
{
sommapos=0; sommaneg=0;
printf (“ scrivete un numero ” ); (c)
scanf (“\n%d”, &numero );
if (numero < 0 )
sommapos = sommapos + numero ;
else sommaneg =
sommaneg +numero ;
(d) printf (“\n iterazione
%d numero scritto = %d ”, i , numero );
(d) printf (“\n somma positivi = %d somma negativi = %d ”, sommapos , sommaneg);
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
}
compilazione ed esecuzione;
avremo :
Quanti numeri vuole sommare ?
3 (invio)
scrivete un numero
2 (invio)
(iterazione 1 numero scritto 2: sommapos=0; sommaneg=2 ; quest’ultimo e’ un errore!! in quanto il 2 doveva essere in sommapos )
scrivete un numero
-4 (invio)
(iterazione 2 numero scritto -4 : sommapos=-4; “doveva stare in sommaneg” sommaneg=0; “ abbiamo azzerato sommaneg”
scrivete un numero
5 (invio)
(iterazione 3 numero scritto 5 : sommapos=0; sommaneg=5 )
Possiamo notare che vi sono due tipi di errore :
Errore 1) inizializzazione nel ciclo !! l’inizializzazione delle variabili va fatta fuori
dal ciclo while
Errore 2) if sbagliato !! l’if corretto e’ if(numero >0)
Stesura 6)
main ()
{
int sommapos, sommaneg, n , i, numero ;
printf (“\n,
Quanti numeri vuole sommare ? ); (b)
scanf (“\n%d”,&n);
i = 1;
sommapos=0; sommaneg=0; (e)
while ( i <= n)
{
printf (“ scrivete un numero ” );
(c)
scanf (“\n%d”, &numero );
if (numero > 0 ) (f)
sommapos = sommapos + numero ;
else sommaneg =
sommaneg +numero ;
(d) printf (“\n iterazione %d
numero scritto = %d ”, i ,
numero );
(d) printf (“\n
somma positivi = %d somma negativi = %d
”, sommapos , sommaneg);
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
}
Compilazione, esecuzione
Ok! Il programma e’ corretto.
Esercizio provare con diversi insiemi di dati.
N.B. subito dopo la fine dell’esecuzione, lo schermo di esecuzione scompare e torna la finestra di EDIT.
Ossia non vediamo piu’ la parte finale dell’ esecuzione ; per risolvere tale problema usiamo uno fra tanti metodi, ad esempio mettiamo il codice in (g) , cosi’ il programma rimane in esecuzione in attesa di un input (nel nostro caso inserimento di un qualunque carattere diverso da ? per poter uscire ).
Stesura 7 finale)
main ()
{
int sommapos, sommaneg, n , i, numero ;
char car; (g)
printf (“\n,
Quanti numeri vuole sommare ? ); (b)
scanf (“\n%d”,&n);
i = 1;
sommapos=0; sommaneg=0; (e)
while ( i <= n)
{
printf (“ scrivete un numero ” );
(c)
scanf (“\n%d”, &numero );
if (numero > 0 )
(f)
sommapos = sommapos + numero ;
else sommaneg =
sommaneg +numero ;
(d) printf (“\n iterazione %d
numero scritto = %d ”, i ,
numero );
(d) printf (“\n
somma positivi = %d somma negativi = %d
”, sommapos , sommaneg);
i = i +1;
} /* fine ciclo
while */
printf (“\n totale dei positivi = %d”, sommapos);
printf (“\n totale dei negativi = %d”, sommaneg);
printf (“\n digitate un carattere e premere invio per uscire”); (g)
scanf (“\n%c”,&car); (g)
if ( car !=’?’) (g)
exit(1) ; (g)
}
Una altra soluzione esatta simile, dell’esercizio proposto la si trova al seguente link:
somponeg .