Universita' degli Studi di Roma - La Sapienza Corso di laurea in Fisica DISPENSE DEL CORSO DI CALCOLATORI ELETTRONICI (Modulo B) ======================================================== Tenuto dal Prof. Roberto Biancastelli N.B. Per ridurre le dimensioni del file tutta la grafica e' stata realizzata in modalita? testo, usando i caratteri grafici che devono essere disponibili nel programma di visualizzazione. Si puo' usare il programma NotePad, con caratteri Terminal. Anno Accademico 2003-2004 Copyright 1988-2004 !----------------------------------------------------------------------- PREFAZIONE ══════════ Il corso e quindi anche le dispense sono suddivisi in tre 'moduli' A, B e C, organizzati come indicato nel programma. I primi due sono per la laurea quadriennale, mentre il modulo C (capitoli 6-14) e' per la laurea triennale (richiede la propedeuticita' dei corsi di Laboratorio di Calcolo e di Comunicazione Scientifica e Tecnologica). La relativa didattica e' stata organizzata in una serie di dispense in vista di un'informatizzazione completa del corso, con tecnologie multimediali e telematiche. Queste dispense, con tutti i programmi delle esercitazioni pratiche, sono distribuite gratuitamente su floppy disk in copia unica e per uso personale esclusivo, ai soli studenti regolarmente iscritti al corso del prof.Roberto Biancastelli. Contenendo dei caratteri grafici, le dispense sono visualizzabili e stampabili con i programmi editor standard (in MS-DOS con EDIT o in Windows con NotePad, caratteri 'Terminal' e Formato senza "A capo automatico"). Se la dimensione di un file risultasse 'troppo estesa', occorre modificare le impostazioni di memoria della finestra DOS, cliccando prima con il pulsante destro sull'icona DOS o sulla barra del titolo della finestra DOS e poi con il pulsante sinistro su Proprieta' ed impostando quindi tutte le opzioni in 'Automatico'). I files scritti in formato Word invece possono essere letti dagli studenti, che non possiedono il programma MS-Word, scaricando gratuitamente dal sito della Microsoft ( www.microsoft.com --> Office --> Word Downloads ) il programma WordViewer97/2000 (wd97vwr32.exe). E' opportuno precisare che, essendo il resto del corso di laurea in fisica piuttosto povero di insegnamenti informatici, il compito che attende lo studente del corso di Calcolatori Elettronici e' piuttosto impegnativo. Cio' perche' questa materia, a differenza di altre, e' soggetta ad un continuo e rapido progresso tecnologico, che ha ampliato sempre di piu' negli ultimi anni il campo di insegnamento. Poiche' molti studenti arrivano a questo corso con una conoscenza iniziale della materia praticamente nulla, il docente deve risolvere due problemi di non poco conto: insegnare sempre di piu' nello stesso numero di lezioni e gestire didatticamente la crescente complessita' delle nuove tecnologie informatiche. La soluzione finora adottata e' stata quella di inserire gradualmente nel programma una parte su tecnologie avanzate e programmazione ad oggetti ed in rete. Gli studenti che, non avendo avuto una conoscenza iniziale della materia, si trovassero in difficolta' nella preparazione all'esame su quest'ultima parte non dovranno preoccuparsi, perche' nell'esame finale non verra' loro richiesta in modo fiscale una conoscenza approfondita di questi argomenti. Si tenga inoltre presente che in questo corso l'apprendimento non deve essere finalizzato al voto, ma all'acquisizione di una professionalita'in questa materia, che sia spendibile poi nel mondo del lavoro e della ricerca). Tuttavia gli studenti arrivati a questo corso senza precedenti conoscenze propedeutiche all'informatica di base (algebra di Boole, elettronica digitale, ecc.) sono invitati a studiare dall'inizio tutti gli argomenti esposti nel programma e spiegati nelle lezioni, tralasciando eventualmente gli ultimi argomenti riguardanti la programmazione in rete, applet java, ecc. Invece gli studenti arrivati a questo corso, con le conoscenze di base ed una certa esperienza di programmazione in un qualche linguaggio di alto livello precedentemente acquisita, potranno rinfrescare velocemente le loro conoscenze di base, esposte nella prima parte del programma e nelle lezioni, dedicando poi un tempo maggiore sulle ultime parti del programma riguardanti la programmazione ad oggetti, in rete, Internet, ecc. La modalita' di svolgimento delle esercitazioni e' stata organizzata in modo da permettere anche il lavoro parallelo di gruppi di studenti, che trattano argomenti diversificati in funzione della loro preparazione e degli obiettivi proposti (nell'ambito degli argomenti trattati nel corso). Ove possibile, con le cautele sotto indicate per impedire la diffusione di virus informatici, la prosecuzione del lavoro pratico sul computer di casa e' vivamente incoraggiata. !----------------------------------------------------------------------INDICE GENERALE ═══════════════ INDICE PROGRAMMA DEL CORSO DI CALCOLATORI ELETTRONICI PROGRAMMA LOGICO DEL CORSO INTRODUZIONE Capitolo 1 - Elementi del linguaggio Pascal Capitolo 2 - Sistemi di numerazione e rappresentazione delle informazioni Capitolo 3 - Strutture di dati Capitolo 4 - Introduzione all'algebre di Boole Capitolo 5 - Circuiti logici digitali Capitolo Capitolo Capitolo Capitolo Capitolo Capitolo Capitolo Capitolo Capitolo Modulo C 6 - Architettura di un calcolatore elettronico <──────────┐ │ 7 - Sottoprogrammi e passaggio dei parametri │ │ │ 8 - Programmazione Assembly │ Fine Modulo A │ 9 - Sistema di Input/Output <───────────────> │ Inizio Modulo B │ 10 - Programmazione concorrente │ │ 11 - Sistema di memoria │ │ 12 - Sistemi operativi │ │ 13 - Sistemi in rete │ │ 14 - Programmazione ad oggetti e linguaggio Java <─────────┘ Capitolo 15 - Programmazione sul Web: Apache, HTML, CGI, Java apple Capitolo 16 - Architetture avanzate Capitolo 17 - Acquisizione di dati sperimentali APPENDICE A - Sigle e definizioni utili APPENDICE B - Il simulatore H6809 APPENDICE C - Programma delle esercitazioni !----------------------------------------------------------------------Programma del Corso di CALCOLATORI ELETTRONICI ══════════════════════════════════════════════ Prof.R.Biancastelli - Dipartimento di Fisica Universita' di Roma - La Sapienza ────────── MODULO A ─────────── RAPPRESENTAZIONE DELLE INFORMAZIONI (D,W): Sistemi numerici - Rappresentazioni posizionali - Conversioni di base - Rappresentazioni dei negativi Aritmetica binaria in complemento a due -Carry e Overflow - Rappresentazione dei numeri frazionari - Conversioni in virgola fissa e virgola mobile - Rappresentazione BCD - Codici binari. ELEMENTI DI PASCAL (W,D,G): Intestazioni, tipi e dichiarazioni di variabili, istruzioni semplici e composte, diagrammi sintattici - Procedure e funzioni - Parametri formali e passaggio dei parametri. STRUTTURE DI DATI (D,W): Vettori e matrici - Pile (Stack) - Code - Liste (a singolo e doppio 'link') - Gestione delle liste: free list e garbage collection. INTRODUZIONE ALL'ALGEBRA DI BOOLE (C,D): Operazioni e loro proprieta' Teorema di De Morgan e legge di dualita' - Teorema di sviluppo: Mintermini, Maxtermini e forma canonica - Minimizzazione: il metodo delle mappe di Karnaugh - Operatori universali - Operatore OR-esclusivo (coincidenza e anticoincidenza). │ CIRCUITI LOGICI DIGITALI (C,D): Circuiti combinatori SSI: Analisi e sintesi - Circuiti combinatori MSI, LSI - Sintesi a ROM e PLA Circuiti sequenziali - Memorie ROM, RAM statiche, dinamiche e associative. Modulo C <────────┐ ARCHITETTURA DI UN ELABORATORE (D,W): Organizzazione interna - Bus │ di memoria - Registri - Contatori di programma e unita' di controllo │ Unita' aritmetica - I/O bus - Istruzioni - Modi di indirizzamento: a │ registro , a registro indiretto , assoluto , assoluto indiretto, imme│ diato, autoincremento, relativo, a registro base e indexato - Sistema │ di I/O: sistemi a bus dedicato e memory mapped - Porte di I/O - Proto│ colli Handshake - Interruzioni - Microcomputer H6809 e sua simulazione │ in Pascal. │ │ PROGRAMMAZIONE ASSEMBLY (D,W) - Pseudo-istruzioni - Compilazione: PLC, │ tabella dei simboli e modulo oggetto - Correlatore (Link Editor) │ Caricatore (Loader) - Rilocazione - Macro-istruzioni. │ │ ─────────── MODULO B ─────────── │ │ ELABORATORI NUMERICI (D,W): Organizzazione interna dell'unita' centrale │ Microcomputer H6809 e sua simulazione in Pascal - Unita' di controllo │ canalizzate - Gestione della memoria - Memory Management Unit │ Paginazione (memory mapping): mappa di traduzione degli indirizzi │ Classificazione delle macchine: ad accumulatori (H6809), a registri │ generali (H8000) ed a stack (H11) - Processori RISC. │ │ SISTEMA DI INPUT/OUTPUT (W,D): Sistemi a bus dedicato e memory mapped │ - Porte di I/O - Protocolli Handshake - Interfacciamenti: Paralleli │ (Centronix, IEEE 488) e seriali (asincrono RS232C e sincroni BSC) │ Programmazione I/O e sovrapposizione - I/O drivers - Sistema di inter│ ruzione - Gestione delle priorita': geografica, daisy chain, mask bits, │ round-robin, polling e interruzioni vettorizzate - Interruzioni interne │ (traps) e software (system calls) - Programmazione delle interruzioni │ Accesso diretto alla memoria (DMA) - Sottosistemi a dischi - Canali di │ I/O controllati da processori. │ │ PROGRAMMAZIONE CONCORRENTE (D,W): Processi e loro stati - Concorrenza │ con I/O in sovrapposizione - Variabili condivise e sezioni critiche │ Errori timing-dependent e stallo (dead-lock) - Protezione delle sezioni │ critiche - Semafori - Bloccaggio di strutture di dati - Condivisione di │ struzioni. │ │ SISTEMA DI MEMORIA (D,P): Organizzazione gerarchica - Memoria a banchi, │ interleaved, multiport - Fetch multiplo - Registro di istruzioni a │ buffer - Memoria cache - Memoria virtuale - Gestione della memoria: │ partizioni fisse e variabili, paginazione, memoria virtuale. │ │ SISTEMI OPERATIVI E RETI (C,D,I): Dedicati, a lotti (batch) e interattivi │ (time-sharing) - Multitasking e multiprogrammazione - Reti locali │ Software di rete │ │ ELEMENTI DEL LINGUAGGIO JAVA (D,J): Programmazione ad oggetti - Fondamenti │ del linguaggio Java: classi, oggetti, variabili e metodi - I/O in Java │ Interfaccia grafica Swing (GUI) - Gestione degli eventi e delle eccezioni - │ Applicazioni e applet - Connessioni in rete e programmazione client-server. │ <─────────┘ INTERCONNESSIONE DI RETI E INTERNET (D,I,H) - Indirizzamento IP - Protocolli TCP - Inter-reti - Internet - Browser e linguaggio HTML - Web server Script CGI e Javascript ARCHITETTURE AVANZATE (C,D): Classificazione dei sistemi di elaborazione parallela: MISD, SIMD,MIMD - Tipi di accoppiamento: Multiproces- sor, multicomputer e reti locali - Esempi ACQUISIZIONE DI DATI SPERIMENTALI (D): Sistemi standard di acquisizione di dati (NIM, CAMAC, VME e FASTBUS) - Organizzazione di un sistema multitask in tempo reale - Supervisione dell'elaborazione parallela (centralizzata e distribuita) - Multiprocessing (esempi su VME e FASTBUS). ─────────── ESERCITAZIONI ─────────── Modulo A: Programmazione in Pascal - Simulatore del computer H6809 Uso pratico del linguaggio binario e mnemonico Assembly del computer H6809 con simulatore e debugger. Modulo B: Programmazione concorrente su rete locale di PC (farm per la simulazione Montecarlo dello scattering coulombiano) - Uso pratico del linguaggio Java - Istallazione ed uso di Web server con uso di applet Java, Javascript e script CGI - Programmazione concorrente (client-server) su rete Intranet. Modulo C: Uso pratico del linguaggio binario e mnemonico Assembly del computer H6809 con simulatore e debugger - Programmazione concorrente su rete locale di PC (farm per la simulazione Montecarlo dello scattering coulombiano) - Uso pratico del linguaggio Java - Programmazione concorrente (client-server) su rete Intranet. PRINCIPALI RIFERIMENTI BIBLIOGRAFICI: ──────────────────────────────────── Testo base: D: Dispense, esercizi e fotocopie di appunti/trasparenze distribuite gratuitamente dal docente su floppy disk. Testi per consultazione: W: J.Wakerly - Microcomputer Architecture and Programming - Ed. Wiley G: M.Gori et al. - Pascal e C - Ed. Mc Graw Hill C: G.Cioffi,V.Falzone - Manuale di Informatica - Ed. Calderini P: D.A.Patterson et al.- Struttura e progetto...- I/F Hard/Softw. J: Cay Horstmann - JAVA 2, i fondamenti - Ed. Mc Graw Hill I: D.Comer - Internet e reti di calcolatori - Ed. Addison-Wesley H: G.B.Shelly et al. - HTML imparare per progetti Per informazioni, spiegazioni e chiarimenti e' possibile concordare durante tutto l'anno accademico incontri con il professore al 349.5553010 anche al di fuori degli orari di ricevimento (studenti lavoratori). Si puo' comunicare con il professore anche all'indirizzo di posta elettronica [email protected] !----------------------------------------------------------------------PROGRAMMA LOGICO DEL CORSO ========================== (Grafo che evidenzia le connessioni logiche tra i vari argomenti) RAPPRESENTAZIONE INFORMAZIONI ──────┬────── │ ┴ STRUTTURE DATI ───────┬────── │ ┴ ALGEBRA DI BOOLE ────────┬─────── │ ┴ ELETTRONICA DIGITALE ──────────┬───────── │ ┴ ARCHITETTURA ELABORATORE ^ ┌──────────────────────────┐ │ │ UNITA' CENTRALE │ │ │ ┬ │ ┴ ┌───│─< SISTEMA │ SISTEMA >──│───┐ Modulo A │ │ I/O │ MEMORIA │ │ │ └────────────┼─────────────┘ │ ......... │ │ │ │ PROGRAMMAZIONE │ Modulo B │ (ASSEMBLY,PASCAL,JAVA...) │ ┬ │ │ │ │ │ │ │ │ SISTEMA ────> PROGRAMMAZIONE MEMORY │ D'INTERRUZIONE CONCORRENTE MANAGEMENT │ │ │ │ │ │ │ │ v │ ┌─────────┴────────┐ │ └─────>│ SISTEMI OPERATIVI│<───────┘ │ MONOUTENTE │ └─────────┬────────┘ │ │ SPECIALI ACCORGIMENTI HARDWARE │ │ ┌───────────┴───────────┐ │ SISTEMI OPERATIVI │ ┌───────────────│ MULTIUTENTE,MULTITASK │────────────┐ │ │ SOFTWARE DI RETE │ │ │ └───────────┬───────────┘ │ │ │ │ │ │ │ │ MULTIPROGRAMMAZIONE <──┼──> MULTIPROCESSING │ │ ─┬ │ ┬─ │ │ │ │ │ │ ────┴──────────────── │ │ │ ──────────────┴────────────── ARCHITETTURE AVANZATE │ │ │ RETI LOCALI/INTERNET/INTRANET │ ─┬── │ │ │ ─┬─ │ │ │ │ │ │ │ ┌─┴─────┴───────┴──────┴─────────┴──┐─────> CAMAC │ │ ACQUISIZIONE DI DATI SPERIMENTALI │─────> VME │ │ ( ed altre applicazioni ) │─────> FASTBUS . └───────────────────────────────────┘─────> ECC. . . !----------------------------------------------------------------------INTRODUZIONE ════════════ Il campo dei calcolatori elettronici e' forse quello che piu' di tutti negli ultimi decenni ha mostrato un progresso sfrenato. Infatti non solo ha raccolto i frutti del rapidissimo progresso della microelettronica, ma ha anche contribuito al progresso della nostra societa' con le grandi innovazioni nel campo del software e della telematica. Questo enorme progresso, unito alla grande rapidita' di evoluzione di idee e sistemi, condiziona a nostro parere le modalita' di una didattica istituzionale in questa materia. Infatti, mentre da un lato gli studenti sono desiderosi di arrivare rapidamente all'apprendimento delle macchine e sistemi piu' moderni, dall'altro questi non risultano adatti ad un efficiente apprendimento di base della materia, poiche' mentre sono destinati ad una rapida obsolescenza, inevitabilmente la loro complessita' pone degli ostacoli, complicando e rallentando inutilmente la comprensione dei concetti di base, la cui acquisizione, anche se fatta in maniera piu' semplice, permette poi di capire meglio e piu' rapidamente il funzionamento non solo dei sistemi attuali (che hanno una VITA LIMITATA) ma anche e soprattutto di quelli futuri. Per questi motivi nel nostro corso adotteremo come macchina di riferimento il miglior processore tra quelli piu' semplici ad 8 bit prodotto alla fine degli anni settanta e addirittura lo semplificheremo ulteriormente riducendone il numero dei registri allo stretto indispensabile. In questo modo, per il nostro corso, il processore ideale H6809 che ne deriva assume lo stesso ruolo che il punto materiale svolge in Meccanica o che il gas perfetto svolge in Termodinamica: un'idealizzazione che non ha riscontro nella realta' ma che ci aiuta a comprendere i meccanismi che stanno alla base della materia. ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ Per meglio comprendere i meccanismi di funzionamento impiegati nei moderni calcolatori elettronici, pur nella loro complessita', seguiremo nella nostra trattazione un criterio di evoluzione 'storica', con cui progrediremo con gradualita', dai sistemi uniprocessor con sistema d'interruzione alla programmazione concorrente ed alla multiprogrammazione per arrivare infine alle architetture avanzate di elaborazione parallela multiprocessore. Essendo nostra convinzione che un apprendimento di questa materia non puo' essere disgiunto dall'acquisizione di capacita' operative sull'uso dei computers, abbiamo associato alle lezioni teoriche delle esercitazioni pratiche svolte su personal computer dotati di un programma simulatore che fa funzionare il PC come se dentro ci fosse un processore H6809. In questo modo lo studente puo' meglio comprendere i principi della programmazione dei calcolatori elettronici che studia a lezione. Un'ultima osservazione riguarda gli studenti del corso di laurea ║ in fisica, ai quali questo corso e' particolarmente diretto: come ║ deve essere gia' loro noto, la fisica si occupa di scoprire e stu- ║ diare le leggi fisiche della Natura ovvero i rapporti quantitativi ║ che intercorrono tra le grandezze osservabili, cioe' suscettibili ║ di misurazione e quindi quantificabili con numeri: per questo, ║ come la matematica ed i calcoli numerici hanno un ruolo fondamentale║ in fisica, cosi' anche i calcolatori elettronici sono fondamentali ║ per i fisici, come strumenti di calcolo (OFF-LINE). ║ Inoltre i calcolatori elettronici sono anche l'elemento centrale di ║ analisi e controllo in linea (ON-LINE) di tutti i moderni apparati ║ sperimentali e vanno quindi visti come parte essenziale dello stru- ║ mento con cui il fisico misura le grandezze fisiche osservabili, ║ che sono oggetto della fisica sperimentale. ║ Cosi' lo studio dei calcolatori elettronici e dei loro campi e modalita' di impiego va associato allo studio dei trasduttori della informazione fisica (per esempio i rivelatori di particelle), ed alle metodologie di acquisizione ed analisi dei dati sperimentali, che concorrono a formare lo strumento con cui indaghiamo il mondo fisico. !----------------------------------------------------------------------AVVERTENZE IMPORTANTI PER LE ESERCITAZIONI ========================================== Licenza d'uso: Le dispense per loro natura devono fare riferimento ai libri di testo consigliati, sui quali lo studente puo' cosi' trovare, in modo omogeneo, le informazioni per gli ulteriori approfondimenti. Cio' e' stato fatto dal docente con la massima attenzione al rispetto delle disposizioni di legge sul diritto di autore e pertanto il materiale didattico consegnato allo studente non deve essere in nessun caso alterato ne' duplicato o diffuso senza specifica autorizzazione scritta dell'autore. Il software contenuto nei dischetti consegnati dal docente e' di proprieta' dei rispettivi fornitori e concesso in licenza all'Universita' di Roma-La Sapienza. Puo' essere usato solo presso questa Universita' nel rispetto delle relative licenze, sui computer assegnati per le esercitazioni, per fini didattici e non puo' essere ne' asportato ne' copiato con qualsiasi mezzo. Lo stesso vale per il testo delle dispense, la cui circolazione deve essere limitata agli studenti iscritti al corso del prof.R.Biancastelli. Eventuali responsabilita' civili e penali previste dalle leggi vigenti, conseguenti al mancato rispetto delle disposizioni impartite al riguardo dal docente e dagli organi universitari, sono solo dei trasgressori. Per il materiale scaricato dalla rete devono essere sempre rispettate le restrizioni di copyright indicate dai relativi fornitori. Poiche' spesso tra gli studenti si subiscono questi doveri come ingiuste vessazioni, il docente ritiene utile esprimere la propria opinione al riguardo: nei Paesi in cui e' consentito a liberi imprenditori di investire nella ricerca sul software, ripagandosi poi degli investimenti fatti con i GIUSTI proventi della vendita, una efficace tutela giuridica del software e dei diritti di autore permette, al Paese che la pratica, di sviluppare l'industria del software e di creare con essa numerosi posti di lavoro, che in prospettiva valgono molto di piu' del software trafugato (purche' le Autorita' sappiano contrastare le tendenze ai monopoli nel software per salvaguardare, con la concorrenza, il livello e la giustezza dei prezzi). Virus: Le esercitazioni si svolgeranno usando un dischetto personale per ogni studente: su questo dischetto vi saranno sia il file contenente le dispense del corso, che appunti (LEZ??.PAS) e programmi per le esercitazioni. Lo studente dovra' riconsegnare questo dischetto al docente al termine delle esercitazioni e non usare dischetti propri per ragioni di sicurezza e protezione dai 'virus'. Un virus e' un programma nascosto all'interno di un altro programma usato dell'utente. Quando viene lanciato in esecuzione esso modifica gli altri programmi eseguibili presenti sul dischetto e nel computer installandosi in maniera invisibile nella memoria e nel sistema operativo, in modo da rimanere stabilmente installato nella macchina, che risulta cosi' 'infettata' ed in grado di infettare tutti i programmi eseguibili presenti sui dischetti di utenti ignari. Questo processo di contagio dei dischetti del nostro corso e' possibile anche nei vari computers dei nostri laboratori, poiche' sono aperti a tutti e qualcuno puo' portare dischetti infetti dall'esterno. I vostri dischetti, una volta infettati, se portati fuori del laboratorio possono infettare il vostro computer dell'ufficio, di casa, ecc. Per questi motivi lo studente deve riconsegnare il suo dischetto al docente al termine delle esercitazioni e non portare mai dischetti propri in laboratorio. I tecnici di laboratorio sono i custodi responsabili del funzionamento e del coordinamento nell'uso dei computer. Gli studenti devono pertanto attenersi alle disposizioni da essi impartite e segnalare loro eventuali anomalie riscontrate. PROGRAMMA DI LAVORO DURANTE LE ESERCITAZIONI PRATICHE: -----------------------------------------------------Lo svolgimento delle esercitazioni e' basato sul materiale didattico nel dischetto consegnato dal professore e su software scaricabile gratuitamente da Internet (dopo essersi registrati indicando il proprio indirizzo di E.mail: se non lo avete potete richiederlo ad un fornitore di servizio gratuito di posta elettronica, per esempio Netscape). Bisognera' quindi, al momento stabilito dal professore, eseguire il download dei programmi seguenti, dai siti indicati: Delphi www.borland.com (eserc. Pascal) Java Development Kit http://java.sun.com (eserc. Java) Perl www.activestate.com (eserc. CGI) Apache Web-Server www.apache.org/httpd (eserc. Web) vedi http://httpd.apache.org/docs/windows.html#req (su Win95 potrebbe essere necessario scaricare anche l'upgrade Winsock2 da http://www.microsoft.com/windows95/downloads) Il programma di lavoro, configurato in base alla preparazione dello studente, dovra' contenere, nell'ordine, i seguenti argomenti: 1) Uso dei comandi elementari del Sistema Operativo MS-DOS 2) Uso dell'Editor del TURBOPASCAL 3) Prove di uso delle istruzioni Pascal in semplici programmi (xxxDEMO.PAS) 4) Comprensione ed ampliamento del programma ADDING.PAS 5) Studio del programma Monitor 6) Studio del programma di simulazione H6809BUG.PAS (con BINPROG.PAS); ricerca ed eliminazione degli errori ivi contenuti 7) Studio del microcomputer H6809 simulato su IBM-PC, scrivendo ed eseguendo un semplice programma in linguaggio macchina 8) Ampliamento del simulatore H6809 introducendovi altre istruzioni come quelle di branch condizionato o di I/O o il sistema d'interruzione 9) Uso dell'Editor dell'ambiente JDK1.3 (Java Developmenti Kit) 10) Prove di uso delle istruzioni Java in semplici programmi (xxxdemo.java): FirstSample, Welcome, BigDebt, Test0, Root, NewRoot, SwapTest, SwapTest2, ShellSort, VirusLook, LotteryOdds: esempio con input da tastiera e chiamata a metodo EmployeeTest, H6809g: Monitor H6809 scritto in java Root, RootApplet: esempio di applet Prova: programma contenente un errore da trovare 11) Studio del programma Monitor H6809g.java, con particolare attenzione all'uso dell' oggetto Byte (esempio di programmazione ad oggetti) 12) Realizzazione in Basic di un simulatore dello scattering coulombiano con il metodo di Montecarlo 13) Programmazione concorrente del Montecarlo su una LAN in elaborazione parallela ('farm' di computer). 14) Istallazione ed uso del Web server Apache con uso di applet Java e script CGI. 15) Programmazione concorrente su rete Intranet (facoltativo). AVVERTENZA IMPORTANTE ===================== Le esercitazioni si svolgeranno usando un dischetto personale per ogni studente: su questo dischetto vi saranno sia il file contenente le dispense del corso, che appunti (LEZ??.PAS) e programmi per le esercitazioni. Gli esercizi e le dispense su Internet, sulla programmazione ad oggetti e sul linguaggio Java saranno consegnati agli studenti all'inizio del modulo B (subito dopo Pasqua). Lo studente dovr? riconsegnare questo dischetto al docente al termine delle esercitazioni e non portare dischetti propri per ragioni di sicurezza e protezione dai 'virus'. Un virus e' un programma nascosto all'interno di un altro programma usato dell'utente. Quando viene lanciato in esecuzione esso modifica gli altri programmi eseguibili presenti sul dischetto e nel computer, installandosi in maniera invisibile nella memoria e nel sistema operativo in modo da rimanere stabilmente installato nella macchina. Questa risulta così 'infettata' ed in grado di infettare tutti i programmi eseguibili, presenti sui dischetti in essa inseriti da utenti ignari . Questo potenziale processo di contagio dei dischetti del nostro corso e' possibile sia nei computers del nostro laboratorio che in quelli del CATTID, poiche' sono centri aperti a tutti e qualcuno puo' portare dischetti infetti dall'esterno. I vostri dischetti, una volta infettati, se portati fuori dall'Universita' possono infettare il vostro computer di casa, dell'ufficio, ecc. Per questi motivi lo studente deve riconsegnare il suo dischetto al docente al termine delle esercitazioni e non portare mai dischetti propri al laboratorio o al CATTID. !------------------------------------------------------------------Per le esercitazioni si puo' utilizzare anche la sala computers del CATTID, dotata di 20 PC-IBM, ubicata come indicato nella piantina seguente: ═════════════════════════════════════════════════════════════════ ══ VIALE DEL POLICLINICO ═════════════════════════════════ ══════════════════════════════ ....─────────────────┐ ╔═════════════════════════╗ │ ║ EDIFICIO ║ │ ║ CATTID ║ │ ║ <--- ingresso ║ ...──────────────────┘ ╚═════════════════════════╝ ...──────────────────┐ ┌───────────────────────... │ │ │ │ MINERALOGIA GEOLOGIA │ │ SCIENZE │ │ │ POLITICHE └────────────────────────────┘ │ │ ───┐ ┌───────────┘ F │ │ I │ │ GIURISPRUDENZA S │ │ I │ └────┐ C │ Piazza │ A │ della │ RETTORATO ───┘ Minerva │ !-----------------------------------------------------------I primi 9 capitoli sono contenuti nel file Corso_A.TXT (Modulo A) !------------------------------------------------------------------CAPITOLO 10 - Programmazione concorrente ══════════ Riassunto: Scopo essenziale di questo capitolo e' quello di introdurre le problematiche connesse con l'elaborazione simultanea di piu' compiti (processi) da parte di uno stesso elaboratore, quale si incontra per la prima volta usando il sistema di interruzione. ⌡ 10.1 - Processi e loro stati Il concetto astratto di 'processo' viene introdotto perche' aiuta a capire il funzionamento dei programmi in ambiente di interruzioni multiple. Definiamo inizialmente un processo come un compito che l'elaboratore svolge eseguendo le istruzioni di un apposito programma. Vedremo in seguito (Java) che un processo puo' anche avere piu' sottoprocessi (thread), funzionanti in concorrenza nella stessa area di memoria virtuale del processo, ognuno con un compito diverso, come per esempio quello di attendere il verificarsi di un certo evento o di eseguire un'elaborazione compatibile con la prosecuzione dell'elaborazione del programma (elaborazione in background). Quando un thread termina il suo compito, viene sospeso o eliminato. Nel capitolo precedente abbiamo visto come sia possibile, con il pieno impiego del sistema d'interruzione, far compiere al calcolatore elettronico piu' processi simultaneamente, come elaborazione, input, output, acquisizione di dati sperimentali, ecc. Con il termine 'simultaneamente' intendiamo dire che il computer esegue l'elaborazione contemporaneamente ad altri compiti, come quelli relativi all'I/O, nel senso che, mentre l'elaborazione procede, vengono serviti (durante le interruzioni) i vari dispositivi che inviano le interruzioni (input, output, acquisizione-dati, ecc.), in modo da farli funzionare tutti insieme alla loro massima velocita' (cosa resa possibile dalla maggior velocita' della CPU), e cio' 'contemporaneamente' al procedere dell'elaborazione principale. Nell'esempio citato diremo cosi' che i processi di elaborazione principale, input, output ed acquisizione-dati (identificabili come attivita' distinte della CPU) sono eseguiti in concorrenza tra loro, nel senso che la CPU esegue le istruzioni necessarie alla loro esecuzione, saltando dall'un processo all'altro, in modo pilotato dal succedersi delle interruzioni (servite con le priorita' stabilite dal programmatore). Cosi' si riesce ad ottenere due risultati importanti: 1) far lavorare ogni dispositivo alla sua massima velocita' 2) ridurre i tempi morti solo a quelli necessari al salto da un processo all'altro ed al salvataggio e ripristino dello stato della macchina all'interruzione. Dopo aver definito un processo, vediamo di definire i suoi tre stati possibili. STATI DI UN PROCESSO: 1) inattivo (=inactive) 2) attivo sveglio (=active awake) 3) attivo dormiente (=active asleep). Consideriamo per esempio il processo di output di un messaggio sulla console dell'operatore (i relativi programmi Assembly e Pascal sono stati discussi nel capitolo precedente). Prima che il processo di elaborazione principale (main program) generi il messaggio, il processo di output e' nello stato inattivo. Quando la CPU genera il messaggio, attiva anche il processo di output, inviando (con il driver di output) il primo carattere alla console ed attivando l'interruzione relativa (vedi ⌡ 9.7.2, dove la variabile BUSY=1/0 indica lo stato attivo/inattivo del processo di output). La CPU ritorna poi al processo di elaborazione principale. Da quel momento il processo di output e' nello stato attivo e dormiente (in attesa di risveglio, quando arrivera' il segnale d'interruzione, dopo il completamento della stampa del primo carattere). Successivamente, quando l'interruzione arriva (dopo che il primo carattere e' stato stampato), la CPU interrompe il processo di elaborazione principale, che va cosi' nello stato attivo dormiente, e salta alla routine di servizio dell'interruzione, cominciando ad eseguire le istruzioni del processo di output: in questo momento diremo che il processo di output e' nello stato attivo e sveglio. Quando poi la routine di servizio dismette l'interruzione, eseguendo l'apposita istruzione RTI (return from interrupt), il processo di output torna nello stato attivo dormiente, mentre il processo a cui si ritorna va nello stato attivo sveglio. In conclusione abbiamo definito i tre stati di un processo nel modo seguente: ┌───> DORMIENTE ---┐ │ (esecuzione sospesa) | INATTIVO <───> ATTIVO <───┤ | interrupt │ | └───> SVEGLIO <---┘ (in esecuzione) INATTIVO: quando un processo non e' ancora iniziato; ATTIVO E SVEGLIO : un processo iniziato ed in esecuzione (cioe' la CPU ne sta eseguendo le istruzioni); ATTIVO E DORMIENTE: un processo iniziato, ma in attesa di essere continuato al momento dell'arrivo dell'interruzione (la CPU sta eseguendo le istruzioni di un altro processo). In ambiente multiprocessor possono essere svegli piu' processi contemporaneamente (con un solo processor un solo processo e' sveglio: quello su cui la CPU sta lavorando). MULTITASKING: Si possono anche attivare piu' processi di elaborazione, anziche' di I/O, ed averne l'elaborazione 'simultanea', nel modo sopra descritto, se si crea un apposito programma supervisore (chiamato sistema operativo multitask o real time executive per le applicazioni in tempo reale), appositamente progettato per coordinare l'esecuzione dei vari processi, gestendone eventualmente anche le relative priorita'. In questo caso le interruzioni che fanno passare la CPU da un processo all'altro, possono essere non solo quelle generate dalle unita' di I/O, ma anche quelle generate da: - un 'real time clock' interno al calcolatore che, ad intervalli di tempo prefissati, invia segnali di interruzione (time sharing); - chiamate di sistema che sono inserite nel processo corrente per evitare che questo faccia perdere tempo alla CPU restando in un loop, in attesa che un altro processo si compia (vedi prossimo esempio). Si dice pre-emptive (cooperativo) un sistema operativo multitasking a partizioni di tempo (time-sharing), in cui la CPU assegna il controllo in ogni intervallo di tempo (time-slice) ad un task diverso e puo' interrompere un processo in corso, salvandone lo stato, per eseguirne uno piu' prioritario (riprendendo poi quello interrotto da punto in cui era). Nel multitasking non pre-emptive invece, quando un processo ottiene il controllo della CPU non puo' piu' essere interrotto e tiene il controllo finche' non e' terminato. ⌡ 10.2 - Programmazione dei processi - Sezioni critiche Per poter programmare i processi abbiamo bisogno di apposite istruzioni per dichiarare, risvegliare o mettere a dormire un processo. In Pascal queste possono essere indicate con le seguenti parole chiave (in maiuscolo): PROCESS nome_del_processo; (Dichiarazione di processo) WAKEUP nome_del_processo; (Risveglio di un processo) SLEEP UNTIL condizione; (Messa a dormire condizionata del processo) SLEEP; (Messa a dormire incondizionata del processo) In tal caso si parla di Pseudo-Pascal o Pascal concorrente. Come esempio di programmazione che usa queste nuove istruzioni esaminiamo quello che riporta i driver di I/O sotto interruzione gia' studiati nel ⌡ 9.7.2 (Programmazione delle interruzioni): PROGRAM ElaboraRighe (input,output); ....... <--- Dichiarazione variabili PROCESS RigaIn; ...... BEGIN InputAvailable:=false; ClearInBuffer; REPEAT BEGIN KbIEN:=true; KbGO:=true; KbRDY:=false; SLEEP UNTIL KbRDY=true; InBuffer(KbData) END UNTIL KbData=tappo; InputAvailable:=true; SLEEP END; Istruzioni nuove: ================ <---- PROCESS RigaOut; ...... BEGIN OutPrinted:=false; WHILE Buffer=pieno DO BEGIN DisplayData:=NextChar; DisplayIEN:=true; DisplayGO:=true; DisplayRDY:=false; PROCESS SLEEP UNTIL SLEEP PROCESS SLEEP UNTIL DisplayRDY:=true END; DisplayIEN:=false; OutputPrinted:=true; SLEEP END; END; PROCESS RigaElab; ...... BEGIN WAKEUP RigaIn; SLEEP UNTIL InputAvailable; ...... Elabora riga ...... SLEEP UNTIL OutputPrinted; WAKEUP RigaOut END; BEGIN <--- Inizio Main Program OutputPrinted:=true; <--- Inizializzazione WAKEUP RigaElab; <--- Input+Elabora+Print ........ END. SLEEP UNTIL SLEEP PROCESS WAKEUP SLEEP UNTIL SLEEP UNTIL WAKEUP WAKEUP In questo esempio i processi funzionano come coroutines, in quanto quando sono risvegliati riprendono l'elaborazione dal punto in cui erano stati sospesi (con SLEEP). Questo fa capire come il compilatore deve tradurre in linguaggio macchina le parti di programma dichiarate come processi. ⌡ 10.3 - Sezioni critiche, errori timing-dependent La programmazione concorrente, in cui la CPU ripartisce il tempo di elaborazione tra piu' processi contemporaneamente attivi, presenta una problematica nuova. Per coprenderne l'origine, consideriamo un esempio: un processo in corso di elaborazione (attivo), tra un risveglio ed un altro puo' trovarsi inopinatamente variati i valori di alcune variabili memorizzate, perche' condivise con altri processi che sono entrati in elaborazione mentre lui era dormiente ed hanno usato quelle stesse variabili, cambiandone il valore. Le aree di programma dove sono utilizzate le variabili (o piu' in generale le risorse) condivise si chiamano 'sezioni critiche'. Questi problemi non solo possono creare errori di elaborazione, ma possono anche paralizzare l'attivita' del calcolatore: in tal caso si dice che si e' verificata una situazione di 'stallo' (dead-lock), in cui due processi si arrestano, ognuno in attesa che l'altro compia una certa azione. Per capire come cio' possa succedere, consideriamo il seguente esempio, in cui un processo MAIN preleva dati tramite un altro processo INPUT e li elabora: PROCESSO MAIN PROCESSO INPUT ──────┬────── ────────────── │ ─ ─ ─> interrupt │ ┌─────┴─────┐ ┌────────────────────────────────>┤ │ Legge dati│ │ ┌──┴──┐ └─────┬─────┘ │ no┌──────┘ e' └──────┐ ┌────────┴───────────┐ │ ┌──────────┤InputAvailable=true│ │InputAvailable:=true│ │ │ └──────┐ ? ┌──────┘ └────────┬───────────┘ │ ┌──────┴─────┐ └──┬──┘ ┌──┴──┐ │ │Preleva dati│ si │ ┌────┘ e' └─────┐ no │ └──────┬─────┘ │ │MainWaiting=true├───>┐ │ ┌──────────┴──────────┐ ┌───────┴─────────┐ └────┐ ? ┌─────┘ │ │ │InputAvailable:=false│ │MainWaiting:=true│ └──┬──┘si │ │ └──────────┬──────────┘ └───────┬─────────┘ ┌────────┴─────────┐ │ │ ┌──────┴─────┐ ┌----->│ │MainWaiting:=false│ │ │ │Elabora dati│ | ┌────┴─────┐ └────────┬─────────┘ │ │ └──────┬─────┘ | │SLEEP Main│ ┌─────┴─────┐ │ └<───────────┘ | └──────────┘ │WAKEUP Main│ │ | └─────┬─────┘ │ Se interrupt avviene -----┘ ├<────────────┘ qui', Main va in SLEEP con ┌─────┴─────┐ MainWaiting cambiato in false │SLEEP Input│ da Input; cosi' Input non └───────────┘ eseguira' mai piu' WAKEUP Main ed entrambi i processi dormiranno per sempre (situazione di stallo)! Il punto indicato e' una sezione critica perche' usa una variabile condivisa (MainWaiting). E' istruttivo esaminare anche altri casi in cui si puo' verificare lo stallo. Un primo esempio e' quello in cui due processi per lavorare si condividono le stesse pagine di memoria, richiedendole, di volta in volta, al sistema di allocazione dinamica del sistema operativo (non ci sono pagine di memoria sufficienti da allocare ad entrambi i processi). In questo caso si verifica uno stallo quando il processo B non puo' procedere se non termina il processo A, il quale pero' non puo' terminare perche' la memoria che gli sarebbe necessaria e' stata gia' allocata a B (e rimane percio' per sempre in attesa che venga rilasciata). Si puo' verificare uno stallo anche nell'uso incorretto del bloccaggio di risorse condivise tramite semafori. Per esempio, due processi di compilazione di ordini, composti da articoli contenuti in records di un file random, da parte di due distinti operatori, possono andare in stallo se: - il primo processo accede al record dell'articolo A bloccandolo (perche' ne dovra' cambiare la giacenza all'invio dell'ordine); - il secondo processo accede al record dell'articolo B bloccandolo; - successivamente il primo processo chiede di accedere anche lui all'articolo B (bloccato) ed il secondo processo chiede poi di accedere all'articolo A, bloccato anch'esso (perche' li devono inserire nei rispettivi ordini); Ovviamente essendo gli articoli A e B gia' bloccati, l'accesso non viene concesso ed i due processi vanno a dormire per sempre perche' nessuno dei due rilascera' il bloccaggio del record che ha impegnato. La procedura corretta sarebbe quella in cui ogni processo compila l'ordine, bloccando poi i records o addirittura l'intero file (per un tempo brevissimo, durante l'aggiornamento delle giacenze), con un'unica operazione non interrompibile, solo all'invio dell'ordine. Questo tipo di errori sono assolutamente da evitare (con un'attenta progettazione e conoscendo i casi in cui possono verificarsi), perche' essendo timing-dependent, producono effetti non riproducibili. Cio' accade in quanto essi dipendono dall'istante di tempo in cui arriva l'interruzione che mette a dormire un processo e ne risveglia un altro: questo e' un evento non riproducibile nel senso che ripetendo la procedura l'interruzione non ricapitera' nello stesso microsecondo della volta precedente. Puo' cosi' capitare che un programma sembra funzionare correttamente; una volta reso operativo pero' mostra un'anomalia sporadica, che compare casualmente nel senso che, ripetendo le operazioni con le stesse modalita', l'anomalia non si ripete (perche' e' molto poco probabile che l'interruzione responsabile dell'anomalia interrompa il processo esattamente nello stesso istante, in cui la CPU sta elaborando le istruzioni della sezione critica; anzi piu' breve e' la sezione critica tanto meno sara' questa probabilita' e quindi la riproducibilita' dell'anomalia). ⌡ 10.4 - Controllo delle sezioni critiche: bloccaggio e semafori Abbiamo definito 'sezioni critiche' quelle parti del programma, che fanno riferimento a variabili condivise con altri processi e che pertanto possono variare in modo incontrollato in conseguenza di interruzioni che avvengono durante l'esecuzione delle istruzioni della sezione critica. Nella programmazione dei processi concorrenti e' percio' necessaria un'accurata analisi in fase progettuale di tutti gli eventi che possono verificarsi. Un primo modo di risolvere questi problemi, evitando stalli ed altri errori timing-dependent (molto difficili da diagnosticare), e' il BLOCCAGGIO ovvero l'accorgimento di impedire le interruzioni nelle sezioni critiche (per questo basta disabilitare temporaneamente il sistema d'interruzione). Questo rimedio, benche' semplice da attuare, non e' pero' privo di rischi: infatti e' possibile che la stessa disabilitazione del sistema di interruzione in una sezione critica possa generare stallo, se nella sezione critica c'e' un'istruzione di SLEEP, per esempio per l'attesa di un'input. Lo stallo si verificherebbe perche' il processo andato a dormire non si risveglierebbe mai piu', essendo l'interrupt disabilitato; cosi', non uscendo piu' dalla sezione critica, non si riabiliterebbe piu' il sistema d'interruzione disabilitato prima di entrare nella sezione critica. Un altro modo di proteggere le sezioni critiche del programma e' quello di regolare con SEMAFORI l'accesso alle risorse condivise, in modo che un solo processo alla volta possa usarle. Un semaforo e' una variabile condivisa che puo' assumere due valori (1=rosso e 0=verde). Basta fare un test su questa variabile prima di entrare nella sezione critica: se e' rosso non si entra (si attende o si mette in SLEEP il processo), se e' verde lo si mette rosso e si entra nella sezione critica impegnando la risorsa condivisa. La procedura e' descritta nel seguente diagramma: ┌──────────────>┤ Risorsa bloccata │ ┌──┴──┐ da altro processo │ no ┌─────┘ e' └─────┐ └──────┤ SEMAFORO=verde │ └─────┐ ? ┌─────┘ si└──┬──┘ │ <-------- interrupt ? ┌───────┴───────┐ │SEMAFORO:=rosso│ └───────┬───────┘ ─┐ ┌───────┴───────┐ │ │Uso risorsa │ │ └───────┬───────┘ │ Risorsa bloccata ┌───────┴───────┐ │ da questo processo │SEMAFORO:=verde│ │ └───────┬───────┘ ─┘ ecc. Attenzione pero': un interrupt, che arrivasse mentre l'elaborazione e' nel punto indicato dalla freccia, consentirebbe ad un altro processo di impegnare la risorsa (perche' SEMAFORO non e' ancora stato posto rosso) e ritornare dall'interruzione mantenendola ancora impegnata, cosicche' entrambi i processi la userebbero contemporaneamente. Per evitare questo, Test e Set del SEMAFORO devono essere fatti con un'unica istruzione indivisibile (come TSET, ISZ, DSZ, ecc.), oppure con un software interrupt, in cui si lascia disabilitato il sistema d'interruzione (senza mettere ovviamente istruzioni di SLEEP nel servizio dell'interruzione). ⌡ 10.5 - Condivisione di strutture-dati Le stesse problematiche si incontrano se le risorse condivise sono strutture di dati, scritte su disco. E' necessario evitare i seguenti errori: 1) Mentre il processo A sta leggendo, il processo B interrompe e aggiorna la struttura-dati; 2) Mentre il processo B sta scrivendo, il processo A interrompe per leggere; 3) Mentre un processo sta scrivendo, l'altro interrompe e scrive anche lui. N.B. Nessun errore avviene invece se i due processi leggono simultaneamente una stessa struttura-dati. Il bloccaggio in lettura o scrittura a livello di file o record, dev'essere messo a disposizione dal sistema operativo nei comandi di accesso ai dischi (file system) e serve a proteggere la strutturadati. Criteri analoghi si applicano a strutture-dati condivise su reti locali LAN o in ambiente di multiprocessing (un'apposita esercitazione mostrera' l'uso di questo bloccaggio nella realizzazione di una farm di computers per elaborazione parallela in rete locale). ⌡ 10.6 - Condivisione di istruzioni. Programmi rientranti. La condivisione di istruzioni e' possibile, senza che produca errori, se il codice del programma e' scritto in maniera rientrante. Per essere rientrante un sottoprogramma deve usare soltanto i registri e lo stack per tutti i parametri e le variabili locali (come fa il Pascal, ma non il Fortran, per esempio). Devono inoltre essere evitate istruzioni automodificantesi durante la elaborazione (ovvero il programma non deve costruirsi con istruzioni di Store le successive istruzioni da eseguire): in altre parole il codice del programma dev'essere 'read-only'. Va inoltre evitata la memorizzazione di variabili in locazioni fisse di memoria (perche' sarebbero sovrascritte da un secondo processo che rientra nel sottoprogramma prima che il primo processo, che dorme perche' e' stato sospeso da un'interruzione, ne sia uscito). Il problema della generazione di codice rientrante e' gia' stata discussa nel capitolo riguardante la descrizione dei modi di indirizzamento. I programmi rientranti sono importanti in ambienti multiprogrammati perche' consentono di caricare nella memoria una sola copia delle routines di utilita', che grazie alla rientranza risultano disponibili simultaneamente a tutti gli utenti (per esempio: compilatori, text editor, ecc.). La programmazione concorrente e' facilitata dai moderni sistemi operativi e compilatori che prevedono apposite facilities e restrizioni che riducono il rischio di errori timing-dependent. !----------------------------------------------------------------------CAPITOLO 11 - Sistema di memoria ═══════════ Riassunto: Scopo essenziale di questo capitolo e' quello di descrivere gli accorgimenti messi in atto per migliorare le prestazioni del sistema di memoria di un calcolatore elettronico e come si possano poi risolvere i problemi connessi con il caricamento e l'elaborazione simultanea di piu' programmi nella memoria dell'elaboratore. ⌡ 11.1 - Organizzazione gerarchica del sistema di memoria Riportiamo in un'unico schema i vari elementi di memorizzazione usati in un'elaboratore per confrontarne le caratteristiche (i valori indicano solo gli ordini di grandezza): COSTO ALTO │ VELOCITA' ALTA ( $/Mb) ┌────────┴────────┐ │ CPU (registri) │ 1 ns └────────┬────────┘ ┌────────┴────────┐ 5 $ │ CACHE MEMORY │ 10 ns └────────┬────────┘ ┌────────┴────────┐ 1 $ │ MEMORIA PRIMARIA│ 100 ns └────────┬────────┘ ┌────────┴────────┐ 0.1 $ │MEMORIA DI MASSA │ 10.000 ns └────────┬────────┘ ┌────────┴────────┐ 0 $ │ MEMORIA IN RETE │ 1.000.000 ns └────────┬────────┘ COSTO BASSO │ VELOCITA' BASSA Il divario tra la velocita' che si puo' rilevare tra la CPU e la memoria rende determinanti le prestazioni del sistema di memoria. Basta pensare che per ogni istruzione eseguita viene fatto almeno un accesso alla memoria (fetch): per questo le prestazioni dell'intero sistema di elaborazione vengono a dipendere dalle prestazioni del sistema di memoria ed e' quindi molto importante adottare tutti gli accorgimenti possibili per migliorarne le prestazioni. ⌡ 11.2 - Tecniche di miglioramento delle prestazioni del sistema di memoria Poiche' per ogni istruzione eseguita viene fatto almeno un accesso alla memoria (fetch), le prestazioni del sistema di memoria determinano le prestazioni dell'intero calcolatore elettronico. Per questo motivo vale la pena esaminare dapprima alcuni semplici accorgimenti che si possono adottare per velocizzare l'accesso alla memoria. 1) Memory banking: MSB LSB Bank 0 Bank 1 Bank 2 Bank 3 ┌──────┬────────┐ 0┌────────┐ 4┌────────┐ 8┌────────┐ 12┌────────┐ │ Bank │ Offset │ 1├────────┤ 5├────────┤ 9├────────┤ 13├────────┤ └──────┴────────┘ 2├────────┤ 6├────────┤ 10├────────┤ 14├────────┤ Address 3└────────┘ 7└────────┘ 11└────────┘ 15└────────┘ Dividendo la memoria in banchi elettronici diversi (banking), si puo' avviare la lettura del fetch dell'istruzione seguente su un banco, mentre un altro banco sta completando (per esempio) il ciclo di scrittura che termina l'istruzione precedente. Naturalmente il vantaggio scompare se il banco interessato dalle due operazioni e' lo stesso (sarebbe come in assenza di banking). In questo caso pero' ci viene incontro la tecnica dell'interleaving. 2) Memory interleaving: si ottiene dal memory banking semplicemente collegando in maniera diversa le linee di indirizzamento: MSB LSB Bank 0 Bank 1 Bank 2 Bank 3 ┌──────┬────────┐ 0┌────────┐ 1┌────────┐ 2┌────────┐ 3┌────────┐ │Offset│ Bank │ 4├────────┤ 5├────────┤ 6├────────┤ 7├────────┤ └──────┴────────┘ 8├────────┤ 9├────────┤ 10├────────┤ 11├────────┤ Address 12└────────┘ 13└────────┘ 14└────────┘ 15└────────┘ Usando i bit meno significativi come indirizzo del banco si ottiene la distribuzione degli indirizzi indicata in figura. Siccome locazioni di memoria contigue vengono ora a trovarsi su banchi distinti, risulta molto piu' frequente che i cicli di memoria capitino su banchi diversi. In questo modo si esalta il guadagno del memory banking. 3) Multiport memory: e' una tecnica costruttiva che consente all'elettronica del sistema di memoria di gestire piu' di un accesso simultaneo. E' una tecnica particolarmente utile nei sistemi multiprocessore o nei sistemi con I/O assistito da canale. 4) Multiword fetch: Siccome la fase di fetch dell'istruzione e' molto onerosa, in quanto comporta un ciclo di lettura della memoria per ogni istruzione eseguita, si puo' usare la tecnica di consentire la lettura di 2 locazioni contigue per ogni ciclo di memoria. Per esempio il computer IBM 370 eseguiva il fetch di 2 parole di 32 bit per ogni ciclo. In questo modo, se il successivo fetch era in sequenza (cioe' se non veniva eseguito un salto), l'istruzione era gia' pronta nei registri elettronici, senza bisogno di leggerla dalla memoria con un ciclo di lettura. 5) Buffered Instruction Register: La tecnica del multiword fetch, che mira a far trovare gia' pronta nei registri elettronici (molto piu' veloci della memoria) l'istruzione da eseguire, puo' essere spinta oltre, fino a prevedere un banco di molti registri elettronici che vengono caricati con le istruzioni via via lette dalla memoria. In questo modo nell'evenienza non improbabile di loop stretti si possono trovare per molte volte le istruzioni gia' pronte nel banco di registri, evitando cosi' di eseguire i cicli di lettura della memoria, ben piu' lenti. Lo schema di funzionamento, che prevede la funzione di 'freeze', che congela il contenuto del buffer nel caso dei loop stretti, e' il seguente: ┌─────────┐ Prefetch ┌────────┐ │ MEMORIA ├───────────────>│ BUFFER ├───>┐ └────┬────┘ ┌───────>└────┬───┘ │ │ si│ │ │Fetch │ ╔══╗ Freeze│ │ │ no╔═══╝PC╚══╗ ┌────┴───┐ │ └<─────║in Buffer║<───┤ CPU │<───┘ ╚═══╗ ?╔══╝ └────────┘ ╚══╝ 6) Tra gli accorgimenti hardware introdotti per migliorare le prestazioni del calcolatore elettronico, riportiamo la tecnica del pipelining (canalizzazione), anche se non riguarda direttamente il sistema di memoria. Si tratta di una tecnica che aumenta la velocita' di elaborazione, introducendo un parallelismo interno alla CPU nell'esecuzione delle istruzioni. Per comprendere l'idea di base consideriamo il caso semplificato in cui per compiere un'istruzione il calcolatore debba eseguire le tre fasi seguenti, che hanno una certa durata temporale: 1) Fetch, che preleva l'istruzione dalla memoria; 2) Decode, che decodifica l'istruzione ed imposta gli organi interni del calcolatore per eseguire l'istruzione; 3) Execute, che esegue l'istruzione. Normalmente queste fasi andrebbero eseguite sequenzialmente (in serie nel tempo), per cui per eseguire quattro istruzioni sono necessari dodici passi: Passo: 1 2 3 4 5 6 7 8 9 10 11 12 Fase : F1 + D1 + E1 + F2 + D2 + E2 + F3 + D3 + E3 + F3 + D3 + E3 Supponiamo ora di aver costruito l'elettronica del nostro calcolatore in modo che le tre fasi possano essere eseguite indipendentemente. E' allora possibile far lavorare le tre sezioni che eseguono Fetch, Decode ed Execute in parallelo secondo lo schema seguente: Passo : 1 2 3 4 5 6 Fetch : F1 F2 F3 F4 F5 F6 Decode : D1 D2 D3 D4 D5 Execute: E1 E2 E3 E4 7 8 ecc. 9 E' evidente come il parallelismo di funzionamento delle tre sezioni del calcolatore permette di compiere il lavoro in un numero minore di passi e quindi di far funzionare piu' velocemente il calcolatore. Poiche' questo avviene facendo eseguire alla CPU il Fetch e Decode dell'istruzione seguente prima della fine dell'Execute dell'istruzione precedente, e' possibile (nei casi di istruzioni di salto) che questo lavoro fatto, in cui la CPU cerca di avvantaggiarsi compiendo prima il lavoro che si prevede di dover fare subito dopo, si inutile. In questi casi (salti) il parallelismo del pipelining non funziona, la 'pipe' si svuota e l'elaborazione procede con la solita velocita' di una CPU non canalizzata per alcuni passi, finche' la 'pipe' si riempie di nuovo. Questa situazione e' mostrata nello schema seguente, in cui l'esecuzione dell'istruzione 3 esegue un salto: Passo : 1 Fetch : F1 Decode : Execute: 2 F2 D1 3 F3 D2 E1 4 * D3 E2 5 * * E3 6 F4 * * 7 F5 D4 * 8 F6 D5 E4 9 Come si puo' osservare dagli asterischi, lo svuotamento della 'pipe', prodotto dall'istruzione di salto, provoca una sospensione temporanea del funzionamento parallelo, con un conseguente rallentamento della elaborazione. ⌡ 11.3 - Cache memory (ad indirizzamento diretto) Per migliorare le prestazioni del sistema di memoria si possono sfruttare le caratteristiche di localita' spaziale e temporale dell'elaborazione: 1) Localita' spaziale: in assenza di salti a lungo range, l'elaborazione tende a ripetere gli accessi alle stesse locazioni entro un range limitato (basti pensare ai loop). 2) Localita' temporale: per gli stessi motivi il riutilizzo delle stesse memorie avviene con alta probabilita' a breve distanza di tempo. Queste caratteristiche sono alla base del successo della tecnica di accesso alla memoria con 'cache'. L'idea e' quella di trattenere, caricandolo in una memoria ausiliaria, non visibile al programmatore (cache=nascosta), che sia piccola ma molto veloce, il contenuto (istruzioni e dati) delle locazioni lette nella memoria principale, al fine di accedervi piu' rapidamente le volte successive, se il dato e' ancora nella memoria cache. La localita' spaziale e temporale rende molto frequente il successo di questo accesso alla memoria cache. Vediamo di comprenderne il funzionamento con un esempio di realizzazione pratica (DEC Station 3100). Lo schema seguente mostra il funzionamento una memoria cache da 16K, organizzata in blocchi di 1 parola da 32 bit: 31 16 15 2 1 0 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │ IDENTIF │ I N D I C E │ │ INDIRIZZO (sul memory bus) └──┴──┴┬─┴──┴──┴──┴─┬─┴────┴──┴──┘ \16 \14 Byte ┌──────────┘ │ Addr. │ ┌────────────────┘ │ │ │ │ IDENTIF DATO │ │ FILL<--16 bit--> <---- 32 bit ----> │ │ ┌──┬────────────┬─────────────────┐ <─┐ │ │ │ │ │ │ │ │ │ ├──┼────────────┼─────────────────┤ │ │ │ │ │ │ │ │ │ │ ├──┼────────────┼─────────────────┤ │ CACHE │ └───>│X │ * │ * │ │16Kword │ Se X=0 non├|─┼─────|──────┼────────|────────┤ │ MEMORY │ c'e' DATO │| │ | │ | │ │ │ in Cache ├|─┼─────|──────┼────────|────────┤ │ │ │| │ | │ | │ │ │ └|─┴─────|──────┴────────|────────┘ <─┘ │ | \16 \32 │ | │ | │ | ╔══╩══╗ | └────────────────>╣ = ║ └────> DATO | ╚══╦══╝ │ └───>╔═════╗ └───────────>║ AND ╠───> OK (INDIRIZZO presente in cache) ╚═════╝ Ad ogni indirizzo corrisponde una locazione nella cache, individuata dai 14 bit dell'INDICE. Questa corrispondenza non puo' essere biunivoca, poiche' con i 32 bit dell'indirizzo possiamo indirizzare fino a 4Gword, mentre la cache contiene solo 16Kword. Poiche' 2 bit sono riservati per indirizzare i bytes, restano 16 bit per l'identificatore e quindi esistono 2^16=64Kword indirizzi della memoria, che si condividono una stessa locazione della cache. Avendo scelto i bit piu' significativi come identificatore, questi indirizzi della memoria disteranno tra loro di 64Kword: questo rende possibile sfruttare la localita' spaziale dell'elaborazione, poiche' ci garantisce un'adeguata permanenza del DATO nella cache. Infatti funziona nel modo seguente: - In lettura, se Presenza=True, si legge il DATO nella cache, altrimenti si legge in memoria principale e si aggiorna il DATO nella cache; - In scrittura si scrive il DATO sia nella cache che in memoria principale (modalita' Write-through). E' di La di anche possibile organizzare la memoria cache in gruppi di 4 parole 32 bit, caricabili con un solo ciclo di accesso alla memoria. figura seguente mostra una cache memory di 64K organizzata in blocchi 4 parole da 32 bit: 31 ...... 16 15 ........ 4 3 2 1 0 ┌──┬──┬──┬──┬──┬──┬──┬────┬──┬──┬──┬──┐ │ IDENTIF │ I N D I C E │ │ │ INDIRIZZO (sul memory bus) └──┴──┴┬─┴──┴──┴──┴┬─┴────┴──┼──┴──┴──┘ │ │ Word Byte \16 \12 Addr. Addr. ┌──────────┘ │ │ │ ┌───────────────┘ └───────────────────────────────>─┐ │ │ │ │ │ IDENTIF DATI │ │ │ FILL<--16 bit--> <--------- 128 bit -----------> │ │ │ ┌──┬────────────┬───────┬───────┬───────┬───────┐ │ │ │ 0 │ │ │ │ │ │ │ │ │ │ ├──┼────────────┼───────┼───────┼───────┼───────┤ │ │ │ 1 │ │ │ │ │ │ │ │ │ │ : ├──┼────────────┼───────┼───────┼───────┼───────┤ 4K │ │ └───>│X │ * │ * │ * │ * │ * │CACHE │ │ : ├|─┼─────|──────┼───|───┼───|───┼───|───┼───|───┤MEMORY│ │ : │| │ | │ | │ | │ | │ | │ │ │ : ├|─┼─────|──────┼───|───┼───|───┼───|───┼───|───┤ │ │ 4095│| │ | │ | │ | │ | │ | │ │ │ └|─┴─────|──────┴───|───┴───|───┴───|───┴───|───┘ │ │ | | | | | | │ │ | \16 \32 \32 \32 \32 \2 │ | │ │ │ │ │ │ │ | ╔══╩══╗ ╔═╩═══════╩═══════╩═══════╩═╗ │ └────────────────>╣ = ║ ║ M U X ║<───────┘ | ╚══╦══╝ ╚═════════════╦═════════════╝ | │ │ | │ \32 | │ └──────>DATO │ │ │ └───>╔═════╗ └───────────>║ AND ╠───> OK (INDIRIZZO presente in cache) ╚═════╝ Il funzionamento e' analogo alla cache precedente, pero' si caricano 4 parole (16 byte) per volta per sfruttare maggiormente la localita' spaziale (per approfondimenti su questo argomento si veda D.Patterson, J.Hennessy - Struttura e progetto dei calcolatori). ⌡ 11.4 - Memory Mapping & Management (Hardware) Consideriamo un caso semplice con indirizzi a 16 bit. Il MMU e' un dispositivo hardware che si inserisce sul bus degli indirizzi per aumentarne il numero di linee e quindi il campo di memoria indirizzabile. Per esempio si puo' estendere un address bus da 16 bit a 20 bit, in modo da indirizzare 1M (20 bit) di memoria fisica anziche' solo 64K (16 bit). La conversione degli indirizzi da 16 a 20 bit dev'essere fatta senza rallentare apprezzabilmente il ciclo di memoria (i tempi di propagazione dei segnali elettronici nella MMU possono essere trascurabili rispetto alla durata del ciclo di memoria). Lo schema di principio e' il seguente: ┌───────┐ Data Bus ┌───────┐ │ │<══════════════════════════════════════════>│ │ │ │ Control Bus │Memoria│ │ │═════════════════════════╦═════════════════>│ │ │ │ ┌──┴──┐ │ Fisica│ │ CPU │ Address Bus │ MMU │ │ │ │ │═══════════════/16═══>│(Map)│═══/20════════>│ ( 1M )│ │ │ Indir.Logico└──┬──┘Indir.Fisico └───────┘ │ │ I/O Bus ║ │ │<════════════════════════╩═════════════════════════>... └───────┘ (usato per caricare Map, come per I/O port) La conversione degli indirizzi avviene prendendo i 4 bit da aggiungere da uno degli 8 registri della MMU (Map): quello indirizzato dai 3 bit piu' significativi dell'indirizzo logico. Lo schema seguente mostra come avviene questa conversione: 15 13 12 .... .... 2 1 0 ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ CPU ═══════> Indirizzo Logico │0 1 1│0 1 0 1 1 1 0 1 1 0 1 1 0│ (16 bit) └─┴┬┴─┴─┴─┴─┴─┴─┴┬┴─┴─┴─┴─┴─┴─┴─┘ │ │ Map │ │ ┌─┬─┬─┬─┬─┬─┬─┬─┐ │ │ 0 │W│0 0 0 0 1 1 0│ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ │ 1 │W│1 1 0 1 1 0 1│ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ │ 2 │W│0 0 0 1 1 1 0│ │ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ │ 3 │W│0 0 0 1 1 1 1│<────────────┘ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ 4 │W│1 1 1 0 1 1 0│ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ 5 │W│0 0 0 0 1 1 0│ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ 6 │W│0 0 0 0 0 1 1│ │ ├─┼─┼─┼─┼─┼─┼─┼─┤ │ 7 │W│0 0 0 0 0 0 0│ │ └─┴─┴─┴─┴┬┴─┴─┴─┘ ┌<───────────────┘ │ │ 19... │ ...13 12.... │ ....0 ┌─┬─┬─┬─┬┴┬─┬─┬─┬─┬─┬─┬─┬─┬┴┬─┬─┬─┬─┬─┬─┐Indirizzo MEMORIA │0 0 0 1 1 1 1│0 1 0 1 1 1 0 1 1 0 1 1 0│ Fisico ════> FISICA └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ (20 bit) Con la MMU si ottengono quindi 2 vantaggi: 1) Con il Memory Mapping si puo' montare una memoria fisica piu' grande (1Mb) di quella massima indirizzabile (64K); 2) Con il Memory Management si possono caricare contemporaneamente piu' programmi diversi (es. di 64K ciascuno) ed eseguirli senza che interferiscano tra loro; infatti una volta caricati i registri della Map, la CPU (attraverso gli indirizzi logici di 16 bit) puo' accedere solo a quegli 8 blocchi di 8K ciascuno, individuati dai contenuti caricati negli 8 registri della Map e non agli altri (1024-64)K di memoria fisica. Perche' la CPU possa elaborare un programma caricato in questa memoria fisica non accessibile, basta che il sistema operativo supervisore dell'elaborazione carichi nei registri della Map (con le opportune istruzioni di I/O) gli indirizzi di altri 8 blocchi di 8K (quelli dove sono istruzioni e dati del nuovo programma da elaborare). ⌡ 11.5 - Verso il multitasking e la multiprogrammazione L'introduzione della MMU, insieme al sistema d'interruzione, apre la porta alla possibilita' del multitasking (piu' programmi dello stesso utente elaborati simultaneamente, come in Windows 95/98) e della multiprogrammazione (piu' programmi di utenti diversi elaborati simultaneamente, come in Unix/Linux/Solaris, ecc.). Vedremo tra poco, pero', come sara' necessario aggiungere degli appositi accorgimenti hardware per permettere tutto cio' senza che accadano anomalie di funzionamento. Per esempio, dev'essere impedito che un utente possa compromettere il lavoro degli altri utenti, come nel caso in cui uno studente che, esercitandosi nella programmazione Assembly, esegua un programma che per errore cambia i registri della MMU Map, cancellando cosi' la memoria usata da un altro utente. In breve i problemi che bisogna risolvere e gli accorgimenti adottati per risolverli sono i seguenti: 1) PROBLEMA: Impedire mutue interferenze tra utenti diversi: ----> Bisogna impedire che il programma di un utente possa variare il contenuto dei registri di Map della MMU, mentre il programma supervisore deve poterlo fare per impostare il controllo della macchina sul lavoro di un altro utente. Per ottenere questo si deve migliorare il progetto hardware della CPU, aggiungentovi un flip-flop che definisce due stati della CPU: - SUPERVISOR MODE (flip-flop messo a 1): abilita l'accesso ai registri della Map; la CPU e' in questo stato quando sta eseguendo le istruzioni del programma supervisore. - USER MODE (flip-flop messo a 0): la CPU viene mantenuta in questo stato quando esegue le istruzioni del programma utente; in tali condizioni la Control Unit impedisce alle istruzioni del programma dell'utente di modificare i registri della MMU. Poiche' non si puo' impedire che l'utente salti ad un'istruzione che, se eseguita, modificherebbe il contenuto dei registri di Map della MMU, bisogna anche modificare il funzionamento della Unita' di Controllo, facendole produrre un'interruzione hardware (Trap) se incontra l'esecuzione di una tale istruzione in User mode (mentre non la deve produrre se la CPU e' in Supervisor mode. Un analogo tipo di restrizione dev'essere imposto all'utente per impedirgli di accedere direttamente alle unita' periferiche (perche' non si possano mescolare input/output di utenti diversi: vedremo poi come i sistemi operativi multiutente risolvono questo problema con il sistema di SPOOL). Tutto cio' significa che: RIMEDIO: -----> La CPU deve avere due modi di funzionamento (User e Supervisor mode) e l'Unita' di Controllo deve riconoscere un set privilegiato di istruzioni di I/O, a cui consentire l'accesso soltanto in Supervisor mode (se la CPU e' in User mode deve generare invece un trap al tentativo di eseguirle). 2) PROBLEMA: Come vengono impostati il flip-flop di stato ed i registri della MMU quando si accende il computer? RIMEDIO : -----> Nella costruzione del computer bisogna prevedere un metodo di inizializzazione hardware in Supervisor mode e della Map della MMU perche' si possa procedere al bootstrap (fase iniziale del caricamento del sistema operativo). 3) PROBLEMA: Chi gestisce le interruzioni? RIMEDIO : -----> Abbiamo gia' detto che l'I/O dev'essere controllato in modo esclusivo dal sistema operativo, per cui sara' prevista la commutazione automatica in Supervisor mode all'arrivo di un'interruzione ed il controllo passera' al sistema supervisore. 4) PROBLEMA: Restituzione del controllo al sistema operativo al termine del lavoro di un utente (User mode), con commutazione dello stato in Supervisor mode. RIMEDIO : -----> Apposite istruzioni generatrici di interruzioni software (che per questo si chiamano SYSTEM CALLS, cioe' chiamate di sistema). 5) PROBLEMA: Cosa succede in caso di violazione delle regole da parte del programma di un utente (per esempio: uso di istruzioni di I/O o di altre che potrebbero minacciare l'integrita' del sistema operativo)? RIMEDIO : -----> Generazione da parte della CPU o della MMU di hardware interrupts (=Trap). 6) PROBLEMA: Puo' anche accadere che l'utente faccia un uso non corretto dello stack, restituendo al supervisore uno stack pointer errato. RIMEDIO : -----> Due stack separati per supervisore ed utenti (con due registri di stack pointers diversi), per salvaguardare l'integrita' dello stack del supervisore. 7) PROBLEMA: Come si puo' realizzare la condivisione di dati in memoria tra utenti diversi, garantendo l'integrita' del sistema (per farlo bisogna accedere alla Map della MMU)? RIMEDIO : -----> Bisogna predisporre apposite utilities di sistema che assolvono questo compito in maniera garantita. 8) PROBLEMA: Come possono gli utenti usare il DMA, in presenza della MMU? RIMEDIO : -----> Ci sono due soluzioni possibili: A) il DMA ignora la MMU, lavorando con indirizzi fisici; B) nei cicli DMA viene selezionata automaticamente un'apposita Map, specificamente impostata per l'attivita' di DMA. Inoltre citiamo l'opportunita' di inserimento nella memoria di appositi bit per ottenere gli scopi seguenti: WRITE-PROTECT bit: permette di leggere ma non di scrivere nella locazione di memoria (usate per proteggere il codice read-only del sistema operativo) EXECUTE-ONLY bit: permette di eseguire ma non di leggere la locazione di memoria (leggibile solo durante la fase FETCH; usabile per proteggere la segretezza del codice del sistema operativo). ⌡ 11.6 - Gestione della memoria (Software) PAGINAZIONE: La MMU permette di gestire la memoria in pagine che possono essere assegnate ad un processo che ne ha bisogno e recuperate quando il processo ha terminato di utilizzarle. Questo presuppone un'attivita' di allocazione dinamica della memoria, suddivisa in pagine, da parte del sistema operativo. Per questo un'apposito modulo software viene predisposto, che gestendo apposite tabelle, come quelle sotto riportate, che rappresentano lo stato di allocazione delle pagine di memoria, svolge questa funzione. Blocchi disponibili: BLOCCO│STATO FISICO│ALLOCAZ. ─────┼───── Blocchi allocati ai processi: BLOCCO│BLOCCO│ LOGICO│FISICO│PROTEZ. ───────┼──────┼────── <---- Processo A 0 1 2 3 4 5 6 │ │ │ │ │ │ │ B 0 <---Blocco libero 0 <---Blocco libero A A B B 1 0 1 │ │ 3 4 │ R/W │ R BLOCCO│BLOCCO│ LOGICO│FISICO│PROTEZ. <---- Processo B ───────┼──────┼────── 0 │ 0 │ E (Execute only) │ 5 │ R/W (Read/Write) 2 │ 6 │ R (Read only) I processi lavorano con indirizzi logici contigui, mentre la traduzione in indirizzi fisici viene eseguita dai circuiti della MMU. Quando un processo dev'essere attivato il sistema operativo guarda se ci sono i blocchi di memoria richiesti: se ci sono, li alloca aggiornando le tabelle ed impostando la Map della MMU, altrimenti risponde con 'allocazione negata' ed il processo non viene attivato. L'allocazione puo' essere fatta in due modi: a partizioni fisse o vaariabili. PARTIZIONI FISSE: I blocchi di lunghezza prefissata vengono allocati fino al termine del processo che li ha richiesti. PARTIZIONI VARIABILI: Come sopra, ma la frazione del blocco in eccesso viene ridefinita come un nuovo blocco riusabile. In questo modo aumenta continuamente il livello di frammentazione dei blocchi, per cui dev'essere prevista una procedura di ricompattamento che eviti la loro eccessiva frammentazione. ⌡ 11.7 - Memoria virtuale Sviluppando applicazioni si arriva non di rado ad aver bisogno di una quantita' di memoria fisica di gran lunga superiore alla massima disponibile sul nostro calcolatore elettronico. La strada percorribile e' allora quella di segmentare l'applicazione in piu' programmi, ognuno dei quali entra nella memoria disponibile, e caricarli automaticamente in memoria, con apposite istruzioni messe nel programma, quando occorre. Queste intruzioni di CHAIN caricano solo il codice delle istruzioni lasciando inalterato lo stato dei dati in memoria. Questa procedura e' meno complicata di quanto possa sembrare, poiche' le applicazioni in genere constano di un menu' con tanti comandi, ad ognuno dei quali il programmatore puo' far corrispondere un modulo software diverso, gestito sotto CHAIN (segmentazione del programma). Questo tipo di soluzione e' pero' insoddisfacente perche' lascia al programmatore l'onere della gestione della memoria (inserendo le istruzioni di CHAIN nel proprio programma). Molto meglio sarebbe se il programmatore potesse vedere una memoria praticamente illimitata (reperendo lo spazio sui dischi), lasciando al sistema operativo l'onere di gestire automaticamente i processi di roll-in e roll-out di programmi e dati da memoria a disco e viceversa. Per ottenere questo viene inserita nel sistema operativo una nuova funzionalita' (Memoria Virtuale), che consente di elaborare programmi che richiedono una quantita' di memoria superiore a quella fisica disponibile (ma inferiore alla memoria di massa disponibile). Con questa tecnica tutti i blocchi necessari ad un processo in esecuzione sono su disco e solo una parte di loro e' caricata in memoria fisica. Quando un nuovo blocco X e' indirizzato dal programma, se esso non e' nella memoria fisica, viene individuato il blocco meno usato, tra quelli presenti in memoria, che viene salvato su disco per lasciare posto al blocco X che dev'essere caricato in memoria. Il processo che aveva bisogno del blocco viene sospeso (SLEEP) durante il caricamento. Questa tecnica permette di: 1) Tenere durante l'elaborazione dati e programmi su disco, usando la memoria principale (RAM) come una 'cache' della memoria secondaria (su disco); vedremo in seguito come questa a sua volta potra' fungere da 'cache' della memoria costituita dai file condivisi in rete, per evitare la ripetizione dello scaricamento da rete (lento) di un file gia' memorizzato sull'hard disk locale (HD cache). 2) Superare i limiti della memoria fisica istallata (senza dover ricor- rere a procedure di CHAIN gestite dall'utente). Ripetiamo qui' lo schema di organizzazione gerarchica del sistema di memoria presentato nel ⌡11.1: COSTO ALTO │ VELOCITA' ALTA ( $/Mb) ┌────────┴────────┐ │ CPU (registri) │ 1 ns └────────┬────────┘ ┌────────┴────────┐ 5 $ │ CACHE MEMORY │ 10 ns └────────┬────────┘ ┌────────┴────────┐ 1 $ │ MEMORIA PRIMARIA│ 100 ns └────────┬────────┘ ┌────────┴────────┐ Memoria Virtuale ---> 0.1 $ │MEMORIA DI MASSA │ 10.000 ns **************** └────────┬────────┘ ┌────────┴────────┐ 0 $ │ MEMORIA IN RETE │ 1.000.000 ns └────────┬────────┘ COSTO BASSO │ VELOCITA' BASSA Nello schema logico di funzionamento della memoria virtuale, gli indirizzi (virtuali) su cui lavora il programma stanno parte in RAM e parte su disco, come mostrato nello schema seguente: FILL Indirizzi Virtuali Indirizzi fisici ┌───┬──────────────────┐ ┌───────────────┐ │ 0 -------------------│-----─┐ ┌--->│ │ ├───┼──────────────────┤ | | ├───────────────┤ │ 1 -------------------│------|---|--->│ │ ├───┼──────────────────┤ | | ├───────────────┤ Memoria │ 1 -------------------│------|--─┘ ├───┼──────────────────┤ | │ Memoria RAM │ Fisica ├───────────────┤ (RAM) MEMORIA │ 1 -------------------│------|------->│ │ VIRTUALE ├───┼──────────────────┤ | ├───────────────┤ (indirizzata│ 0 -------------------│---─┐ | ┌--->│ │ dal ├───┼──────────────────┤ | | | └───────────────┘ programma) │ 1 -------------------│----|-|--─┘ ╔════════════╗ ├───┼──────────────────┤ | └--------->║ ║ │ 0 -------------------│--┐ └----------->║ HARD ║ Memoria ├───┼──────────────────┤ └------------->║ DISK ║ su disco │ 0 -------------------│---------------->║ ║ └───┴──────────────────┘ ╚════════════╝ Si noti come memoria virtuale e memoria fisica siano suddivise in pagine. Non tutte le pagine virtuali sono caricate in memoria RAM, ma solo quelle indicate con FILL=1. Nulla vieta che due o piu' indirizzi virtuali corrispondano allo stesso indirizzo fisico: in questo modo e' possibile condividere dati e istruzioni tra programmi diversi (assumendo che se ne faccia poi un uso corretto, in base ai vincoli imposti dalla programmazione concorrente). Per la mappatura delle pagine tra virtuali e fisiche si puo' seguire uno schema di traduzione degli indirizzi analogo a quello gia' incontrato nel memory management della MMU: la differenza e' nel fatto che in questo caso la memoria fisica e' supposta essere piu' piccola (4Gb) di quella indirizzabile con l'indirizzo virtuale di 32 bit (16Gb). 31 .... .... 12 11... ... 0 ┌──────────────────────────┬────────────┐ │ NUMERO DI PAGINA VIRTUALE│ADDR.IN PAG.│ INDIRIZZO VIRTUALE └─────────────┬────────────┴─────┬──────┘ (2^32x32bit = 16Gb) \20 \12 │ │ ╔════════════╗ │ ║ TRADUZIONE ║ │ ╚════════════╝ │ │ │ \18 │ 29 .... │ .... 12 11...│ ... 0 ┌───────────┴────────────┬─────┴──────┐ │ NUMERO DI PAGINA FISICA│ADDR.IN PAG.│ INDIRIZZO FISICO └────────────────────────┴────────────┘ (2^30x32bit = 4Gb) 256K pagine Pagine di (2^18=256K) 4K parole (2^12=16Kb) La gestione della memoria virtuale funziona quindi nel modo seguente: quando la CPU invia un indirizzo (virtuale) per accedere ad un dato nella memoria, possono verificarsi due casi. 1° caso: La pagina indirizzata e' in memoria principale: il meccanismo di traduzione trasforma immediatamente l'indirizzo virtuale in indirizzo fisico e la CPU accede al dato. 2° caso: La pagina indirizzata non e' presente nella memoria principale: possono allora verificarsi due sottocasi: a) Esiste una pagina di memoria libera e quindi disponibile al processo. Allora il sistema operativo: - genera una SYSTEM CALL "Pag.assente"; - blocca il processo (SLEEP); - identifica, nel modo che descriveremo, la pagina sul disco e la trasferisce da disco nella pagina disponibile della memoria principale; - al termine del trasferimento il processo potra' essere risvegliato e lavorera' come descritto nel 1° caso (trovando ora la pagina in memoria principale). b) Non esiste una pagina di memoria libera, disponibile al processo. Allora il sistema operativo, con un "Algoritmo di sostituzione" appropriato, individua il blocco RAM meno usato (magari con hardware aggiuntivo in grado di sfruttare la localita' temporale), lo salva su disco (modalita' Write-back) e trasferisce poi la pagina richiesta dal processo in queste locazioni della memoria principale (RAM). Al termine, il processo potra' essere risvegliato e lavorera' come descritto nel 1° caso. Nella memoria cache avevamo adottato il metodo di scrittura detto 'Writethrough' (sul livello di gerarchia di memoria inferiore, cioe' in memoria principale). Nella memoria virtuale dobbiamo utilizzare invece sempre il metodo 'Writeback', cioe' il salvataggio operato al momento della sostituzione di pagina nella memoria fisica) per la scrittura sul livello di gerarchia di memoria inferiore (in questo caso i dischi), per l'eccessivo tempo di accesso. Vediamo ora di capire con un esempio come puo' funzionare il meccanismo di traduzione dell'indirizzo virtuale in indirizzo fisico. Quando si avvia l'elaborazione di un processo, il sistema operativo alloca al processo lo spazio di memoria necessario, sotto forma di un certo numero di pagine di memoria virtuale. La mappatura, degli indirizzi virtuali sulle pagine del disco, viene in quel momento definita e scritta sul disco, sotto forma di una tabella (TPD, Tabella delle Pagine su Disco), che memorizza la corrispondenza tra gli indirizzi delle pagine virtuali ed gli indirizzi delle corrispondenti pagine sul disco. Un'analoga tabella (TPF, Tabella delle Pagine Fisiche) viene allocata su RAM per memorizzare le corrispondenze tra gli indirizzi di tutte le pagine virtuali indirizzabili dalla CPU ed i corrispondenti indirizzi fisici delle pagine virtuali presenti su RAM, che saranno meno di quelle indirizzabili (16Gb) perche' la memoria fisica nel nostro esempio e' solo di 4Gb. Inizialmente TPF e' vuota, cioe' ha tutti FILL=0. Quando la CPU comincia ad indirizzare la memoria virtuale, accede in modo diretto ad una locazione della tabella TPF, utilizzando i 20 bit piu' significativi dell'indirizzo virtuale (vedi lo schema seguente). Trovando FILL=0, esegue le operazioni descritte al precedente punto 2a), cioe' identifica sul disco, usando la tabella TPD scritta su disco, la pagina richiesta e la trasferisce nella pagina disponibile della memoria principale, scrivendo il relativo indirizzo fisico di pagina nei 18 bit della tabella TPF e ponendo FILL=1. Da quel momento, finche' in quella locazione di TPF rimarra' FILL=1, la tabella TPF fungera' da tabella di mappatura di quella pagina virtuale nella corrispondente pagina fisica, realizzando cosi' la traduzione degli indirizzi da virtuali a fisici (per tutte le pagine caricate in RAM, con FILL=1), tramite un semplice accesso di lettura alla tabella TPF in RAM. In altre parole il blocco di memoria di 3Mb RAM, che abbiamo chiamato TPF (Tabella delle Pagine Fisiche), svolge lo stesso ruolo che la 'Mappa' della MMU svolgeva per la traduzione degli indirizzi: soltanto che ora la traduzione e' meno veloce, perche' la mappa non e' su registri elettronici ma su RAM (essendo di 3 Mb). Ogni programma deve avere un proprio spazio di indirizzi virtuali e quindi una sua tabella TPF. Si introduce allora anche un apposito registro hardware (Registro Tabella Pagine), che consente di individuare l'inizio dell'area TPF della memoria RAM dove, con i 20 bit del numero di pagina virtuale, memorizziamo il numero di pagina fisica (di 18 bit), in cui e' stata caricata quella pagina virtuale (vedasi lo schema seguente). Cosi' alle interruzioni (commutazione di processo) non si dovra' salvare tutta la tabella di 3 Mb, ma solo il relativo Registro, che indica l'indirizzo iniziale della tabella TPF. Al risveglio, ogni processo trovera' cosi' caricato in questo registro l'indirizzo d'inizio della propria 'Tabella delle pagine' TPF e potra' lavorare correttamente, usando la propria mappatura della memoria virtuale nella memoria fisica. Il sistema operativo provvede al caricamento delle pagine da disco su RAM mettendo FILL=1 per indicare che la pagina virtuale indirizzata e' presente in memoria RAM. ┌─────────────────────────┐ │ REGISTRO TABELLA PAGINE │ (contiene l'indirizzo iniziale di TP) └─┬───────────────────────┘ │ │ 31 .... .... 12 11... ... 0 │ ┌──────────────────────────┬────────────┐ │ │ NUMERO DI PAGINA VIRTUALE│ADDR.IN PAG.│ INDIRIZZO VIRTUALE │ └────────┬─────────────────┴─────┬──────┘ (2^32x32bit =16Gb) │ \20 \12 │ FILL │ Num.di pag.fisica │ │ ┌────┬───│───────────────────┐ │ └───>│ │ │ │ │ ├────┼───│───────────────────┤ │ │ X │ * * │ │ TABELLA DELLE PAGINE (TPF) ├─|──┼─────────│─────────────┤ │ (2^20x19bit = 3Mb) │ | │ │ │ │ └─|──┴─────────│─────────────┘ │ N.B. Se X=0 la pag. │ │ non e' in RAM \18 │ │ │ 29 .... │ .... 12 11... │... 0 ┌─────────────┴──────────┬──────┴─────┐ │ NUMERO DI PAGINA FISICA│ADDR.IN PAG.│ INDIRIZZO FISICO └────────────────────────┴────────────┘( 2^30x32bit = 4Gb ) 256K pagine (2^18=256K) Pagine di 4K parole (2^12=4Kb) Il prezzo da pagare per i benefici forniti dalla memoria virtuale e' un rallentamento dell'accesso alla memoria, perche' sono necessari due accessi alla memoria principale anziche' uno: 1) Un primo ciclo (lettura della 'Tabella delle pagine' TPF) per ottenere l'indirizzo fisico (=traduzione); 2) Un secondo ciclo per ottenere il dato, leggendo l'indirizzo fisico. Essendo la velocita' di accesso alla memoria un fattore determinante per le prestazioni dell'intero sistema di elaborazione, vediamo come si possono ridurre gli effetti di questo rallentamento. Per velocizzare l'accesso in memoria virtuale si possono conservare i risultati delle traduzioni degli indirizzi (da virtuale a fisico) in una speciale memoria cache, chiamata TLB (Translation Lookaside Buffer). Siccome il numero di pagina cercato puo' essere in una posizione qualsiasi del TLB, la ricerca della LABEL (tipica della cache) conviene che sia fatta con una memoria di tipo "associativo". Se questa ricerca fallisce vuol dire che l'indirizzo della pagina fisica non e' nel TLB e si deve quindi procedere alla lettura della relativa locazione della TPF (tabella delle pagine fisiche) in memoria principale. Il TLB (Transl. Lookaside Buffer) funziona come una cache della tabella delle pagine, ma solo per gli indirizzi delle pagine fisiche. Riportiamo lo schema di funzionamento di una memoria virtuale con TLB: FILL LABEL INDIRIZZO PAGINA FISICA ┌───────────────┐ ┌────┬─────┬───────────────────────┐ │ N.PAG.VIRTUALE│ │ 0 │ │ │ └───┬───────────┘ ├────┼─────┼───────────────────────┤ TLB ├───────────────>│ 1 │ │ * │ Es. 4Kb di │ ├────┼─────┼────────────────|──────┤ memoria veloce │ │ 1 │ │ | * │ (associativa) │ └────┴─────┴────────────────|─|────┘ │ | | │ | | Memoria Fisica │ FILL Ind.Pag.Fisica/Disco | | (es. 4Gb di DRAM) │ ┌────┬────────────────────┐ | | ┌──────────────┐ │ │ 0 │ o --------│--------─┐ | └--->│ │ │ ├────┼────────────────────┤ | | ├──────────────┤ │ │ 1 │ * --------│---------|-─┐ └----->│ │ │ ├────┼────────────────────┤ | | ├──────────────┤ └──>│ 1 │ * --------│---------|--|------->│ │ ├────┼────────────────────┤ | | ├──────────────┤ TPF │ 0 │ o --------│------─┐ | └------->│ │ (Tabella├────┼────────────────────┤ | | ├──────────────┤ Pagine │ 1 │ * --------│-------|-|---------->│ │ Fisiche├────┼────────────────────┤ | | └──────────────┘ es.3Mb │ 0 │ o --------│----─┐ | | DRAM)├────┼────────────────────┤ | | | │ 0 │ o --------│--─┐ | | | └────┴────────────────────┘ | | | | ╔═╩═╩═╩═╩═╗ ║ DISCO ║ Indirizzi su disco ╚═════════╝ Volendo si potrebbe segmentare in pagine anche lo spazio occupato dalla tabella delle pagine TPF e gestire anche questa in memoria virtuale. Questa soluzione risulta conveniente quando si riservano piu' di 20 bit per l'indirizzo di pagina virtuale e quindi aumenta oltre 3 Mb lo spazio richiesto per la Tabella delle pagine TPF oppure in quei sistemi che, dovendo gestire centinaia di processi attivi contemporaneamente, avrebbero gran parte della memoria fisica impegnata dalle centinaia di tabelle TPF dei vari processi attivi. Ovviamente anche per gli indirizzi fisici ricavati con lo schema della memoria virtuale (e non solo per gli accessi alla 'Tabella delle pagine') si puo' applicare la tecnica della memoria cache per velocizzare ulteriormente l'accesso. Quello che segue e' lo schema di memoria virtuale con TLB e Cache Memory realizzato nella DecStation 3100: 31 .... .... 12 11... ...0 ┌──────────────────────────┬───────────┐ │ NUMERO DI PAGINA VIRTUALE│ PAGE ADDR.│ INDIRIZZO VIRTUALE └──────────┬───────────────┴─────┬─────┘ (2^32x32bit =16Gb) \20 \12 ┌────────────────────┘ │ │ FILL MODIF IDENT IND.PAG.FISICA │ │ ┌────┬─────┬─────┬──────────────┐ │ │ │ │ │ │ │ │ │ ├────┼─────┼─────┼──────────────┤ │ ├───>│ * │ │ * │ * │TLB │ │ ├─|──┼─────┼──|──┼────────|─────┤ │ │ │ | │ │ | │ | │ │ │ └─|──┴─────┴──|──┴────────|─────┘ │ │ | ╔═╩═╗ \20 │ └──────|────────>║ = ║ Assoc. | │ | ╚═╦═╝ | │ | ┌─────────┘ | │ ╔═╩═╩═╗ ┌─────┬───┴───────────┴───┐ ║ AND ║ │IDENT│ INDICE │ INDIRIZZO FISICO ╚══╦══╝ └──┬──┴─────────┬─────────┘ Addr in TLB │ \14 │ ┌──────────┘ │ │ FILL IDENT DATI │ │ ┌────┬─────┬──────────────┐ │ │ │ │ │ │ │ │ ├────┼─────┼──────────────┤ │ └──>│ * │ * │ * │ Cache Memory │ ├─|──┼──|──┼───────|──────┤ │ │ | │ | │ | │ │ └─|──┴──|──┴───────|──────┘ │ | ╔═╩═╗ | └───────|──>║ = ║ | | ╚═╦═╝ | | ┌───┘ | ╔═╩═╩═╗ | ║ AND ║ | ╚══╦══╝ | Cache grant DATO (per ulteriori approfondimenti su questi argomenti si veda D.Patterson, J.Hennessy - Struttura e progetto dei calcolatori: l'interfaccia hardware software). !----------------------------------------------------------------------CAPITOLO 12 - Sistemi operativi ═══════════ Riassunto: Scopo essenziale di questo capitolo e' quello di spiegare i principi di base del funzionamento dei sistemi operativi. ⌡ 12.1 - Origine e requisiti necessari ----------------------------Dal grafo con cui nell'introduzione abbiamo presentato il collegamento logico tra i vari argomenti trattati nel nostro corso, che riportiamo qui' per comodita', possiamo vedere come tutti gli argomenti fin qui' trattati conducano all'introduzione dei sistemi operativi (e di rete, trattati nel prossimo capitolo): ARCHITETTURA ELABORATORE ┌──────────────────────────┐ │ UNITA' CENTRALE │ │ ─────────────── │ ┌───│<─ SISTEMA │ SISTEMA ──>│───┐ │ │ I/O │ MEMORIA │ │ │ └────────────┼─────────────┘ │ │ │ │ │ PROGRAMMAZIONE │ │ (ASSEMBLY,PASCAL,JAVA...) │ │ │ │ │ │ │ SISTEMA ────> PROGRAMMAZIONE MEMORY D'INTERRUZIONE CONCORRENTE MANAGEMENT │ │ │ │ │ │ └─────> SISTEMI OPERATIVI <────────┘ MONOUTENTE │ │ SPECIALI ACCORGIMENTI HARDWARE │ │ ┌───────────┴───────────┐ │ SISTEMI OPERATIVI │ │ MULTIUTENTE,MULTITASK │ <---- Punto in cui siamo │ SOFTWARE DI RETE │ ****************** └───────────────────────┘ Un sistema operativo e' un programma scritto per fornire alle applicazioni dell'utente un facile accesso a vari servizi, come quelli necessari per eseguire operazioni di Input/Output (interfaccia di sistema). Il sistema operativo fa quindi 'vedere' all'utente una macchina virtuale piu' semplice e facile da usare. I sistemi operativi possono essere di vari tipi: - Dedicati: sono quelli concepiti per essere usati da un solo utente. Anche quelli dei PC possono rientrare in questa categoria. - Dedicati in rete: un'interfaccia software particolare puo' consentire di facilitare l'accesso anche ai servizi di rete (locale o geografica). - A lotti (batch): fin dai primi sistemi operativi degli anni '60, si automatizzavano le varie operazioni richieste al sistema per eseguire un lavoro (lettura programma, compilazione, link-editaggio, caricamento, lettura dati, esecuzione) con dei macro-comandi che si inserivano come schede o come righe di comando fornite all'interprete dei comandi da un terminale di input. Il risultato e' il funzionamento di un centro di calcolo in cui l'utente consegna un lavoro, che viene poi letto ed eseguito come stabilito da uno 'scheduler'. I risultati sono disponibili all'utente al termine. - Interattivi (time-sharing): consentono a piu' utenti di lavorare contemporaneamente sul computer dai terminali collegati anche via modem, condividendosi il tempo della CPU in piccoli intervalli di tempo (time slice=50msec, per esempio); - Controllo di processo (Real-time Executive): sono quelli dedicati alle applicazioni in tempo reale, cioe' quelle in cui l'utente vuole che siano soddisfatti dei vincoli sui tempi di esecuzione dei vari task (non superiore a ....msec). Campi di applicazione tipici sono il controllo on-line di apparati industriali o sperimentali (come quelli usati dai fisici). - Transazionali: sono quelli concepiti per essere usati con un'applicazione particolare. Non potendo addentrarci nello studio dei sistemi operativi nel nostro corso, cercheremo di capire la loro origine e l'organizzazione funzionale dei vari moduli che li compongono. ⌡ 12.1.1 - Multitasking -----------Abbiamo visto come con l'introduzione del sistema d'interruzione diventa possibile sviluppare la programmazione concorrente, con cui possiamo pensare di eseguire 'contemporaneamente' su un processore piu' lavori (task) di uno stesso utente, saltando da uno all'altro con delle interruzioni software (chiamate di sistema) inserite nei punti in cui e' opportuno mettere in 'Slip' il task o interruzioni hardware prodotte dai dispositivi di I/O (tra cui, per esempio, quelle di un real-time clock, prodotte a cadenza regolare per dividere il tempo della CPU tra i vari task attivi). Naturalmente occorre mantenere separate le aree di memoria in cui lavorano i vari task, ma a questo puo' provvedere la tecnica della paginazione: Interruzioni + Paginazione ───────> (Progr.concorrente) (MMU, Tab.pagine) Multitasking (Multiutenza) Questo modo di operare puo' essere conveniente sia per dividere la capacita' elaborativa del processore tra piu' compiti che si vuole svolgere contemporaneamente, sia per evitare attese al processore, in corrispondenza di operazioni lente, come quelle di I/O. Per operare in questo modo (multitasking) basta caricare contemporaneamente, in aree distinte della memoria, il codice eseguibile dei vari task come processi concorrenti e coordinare, con un programma supervisore (executive), la condivisione del tempo di elaborazione. Questo puo' essere fatto in due modi: 1) Multitasking preemptive: e' il caso di un sistema operativo multitasking in time-sharing (partizioni di tempo), in cui la CPU assegna il controllo in ogni time-slice ad un task diverso e puo' interrompere un processo in corso, salvandone lo stato, per eseguirne uno piu' prioritario (riprendendo poi quello interrotto da punto in cui era). 2) Multitasking non pre-emptive (cooperativo): in questo caso quando un processo ottiene il controllo della CPU non puo' piu' essere interrotto e tiene il controllo finche' non e' terminato. Gli studenti che hanno seguito gli argomenti trattati nei capitoli precedenti non dovrebbero avere difficolta' a capire come un tale supervisore potrebbe essere sviluppato, usando il sistema di interruzione del computer. Questo e' sufficiente per gli obiettivi del nostro corso, poiche' i dettagli implementativi di un particolare sistema operativo ci interessano meno, in quanto il nozionismo diventerebbe prevalente sui concetti di base. ⌡ 12.1.2 - Multiprogrammazione ------------------Un ulteriore passo avanti si avrebbe, se i vari task potessero essere addirittura programmi di utenti diversi (useremo in questo caso il termine multiprogrammazione). La gestione della memoria tramite MMU (Memory Management Unit) in effetti consente di attribuire pagine diverse ai programmi dei vari utenti, permet- tendo cosi' di gestire la loro allocazione con un programma supervisore, attraverso l'uso dei registri della MMU. Il funzionamento di un sistema del genere sarebbe pero' precario, perche' un utente sarebbe in grado di compiere azioni che potrebbero compromettere il funzionamento dei programmi degli altri utenti (per esempio accedendo alle loro pagine di memoria, dopo aver variato i registri della MMU con le apposite istruzioni). L'obiettivo di realizzare un sistema multiprogrammato e' pero' talmente attraente, che risulta senza'altro conveniente procedere alla costruzione di processori specificamente dotati di specifici accorgimenti hardware e software, necessari per impedire che il programma di un utente possa minacciare l'integrita' degli altri programmi in esecuzione (sia di altri utenti, che del sistema operativo stesso). Alcuni di questi accorgimenti da introdurre nel computer sono stati gia' discussi nel ⌡ 11.5 e sono: 1) Un flip-flop di stato del processore (0/1=Supervisor/User mode). Se il processore e' in 'User mode' e' impedito l'uso delle istruzioni di sistema (quelle di I/O, quelle che permettono di modificare le mappe della MMU e quelle che potrebbero minacciare l'integrita' del sistema operativo); 2) Un Write-Protect-Bit nelle memorie che permette di leggere ma non di scrivere nelle memorie; 3) Un Execute-Only-Bit nelle memorie che permette di fare il fetch ma non di scrivere nelle memorie; 4) Commutazione automatica in Supervisor mode agli interrupts per farli gestire dal sistema operativo in via esclusiva; 5) Interrupt automatico (trap) se l'utente viola le regole (tentando per esempio di eseguire istruzioni proibite, come quelle di I/O); 6) Due stack pointers distinti per Supervisor e User mode per poter salvaguardare lo stack del sistema operativo; 7) Utilities di sistema per poter trasferire dati tra utenti diversi (altrimenti reso impossibile dalla MMU, che impedisce mutue interferenze tra utenti diversi); 8) Inizializzazione automatica al bootstrap del flip-flop di stato e dei registri della MMU; 9) Istruzioni apposite (system calls) per poter trasferire il controllo al sistema operativo. All'utente risulta in genere molto difficile usare direttamente le chiamate di sistema, sia per carenza di documentazione fornita sia perche' possono cambiare nelle versioni successive del sistema operativo. Per risolvere questi problemi vengono fornite delle apposite librerie (API, Application Programming Interface), che sono ben documentate e molto piu' stabili nel tempo: sono loro che operano con le chiamate di sistema, presentando l'uso del servizio in modo standard all'utente. Si rende cosi' possibile anche il trasporto delle applicazioni da un sistema operativo ad un altro, come nel caso di Windows NT/2000 e di Windows 95/98 con le API Win32. Molte delle chiamate alle procedure API creano oggetti del nucleo (kernel) del sistema operativo (come file, thread, processi, ecc.). In tali casi viene restituito dalla chiamata un insieme di parametri chiamato handle (=maniglia), descrittore dell'oggetto e specifico del processo che ha creato l'oggetto. Questo handle puo' poi essere usato dall'utente per eseguire operazioni sull'oggetto, invocando i metodi dell'oggetto (funzioni API) sulla sua 'handle'. ⌡ 12.2 - Organizzazione funzionale ------------------------La struttura di un sistema operativo puo' essere rappresentata con una struttura gerarchica, nel senso che ogni livello genera nuove funzionalita' per i livelli superiori, usando le funzionalita' messe a disposizione dai livelli inferiori (non di quelli superiori): Livello 6: Applicazioni dell'utente Livello 5: Gestione delle applicazioni Livello 4: Gestione dei flussi dei dati (gestione logica dei files, ecc.) Livello 3: Gestione periferiche (fornisce periferiche virtuali, risolve i conflitti di accesso, I/O spooling, ecc.) Livello 2: Gestione della memoria (fornisce memoria virtuale ai livelli superiori) Livello 1: Nucleo del sistema operativo (o supervisor o kernel): gestione unita' centrale, gestione delle interruzioni, ecc.) Livello 0: Macchina fisica Un'altra rappresentazione puo' essere fatta con lo schema seguente: ┌─ │ │ │ │ │ │ │ Sistema │ │ Operativo │ │ │ │ │ │ └─ ┌───────────────────────┐ │ APPLICAZIONI UTENTE │ └───────────┬───────────┘ ┌───────────┴────────────┐ ─┐ │ Shell Comandi │ │ └───────────┬────────────┘ │ User Mode ┌───────────┴────────────┐ │ │ System Calls I/F (API) │ │ └───────────┬────────────┘ ─┘ ┌────────────────┴──────────────────┐ ─┐ │ I/O HANDLER SUPERVISOR │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─┬─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ Spooler | Gestione Processi│ │ │ File System | Scheduler │ │ Supervisor │ (HD cache) | IP communication │ │ │ Drivers | Allocaz.memoria │ │ Mode └────────────────┬──────────────────┘ │ ┌────────────────┴──────────────────┐ │ │NUCLEO (Interrupt service/Priority)│ │ └────────────────┬──────────────────┘ ─┘ ┌───────────┴──────────┐ │ Macchina Fisica │ │ ( Hardware ) │ └──────────────────────┘ Nei paragrafi seguenti cercheremo di descriverne brevemente i relativi moduli componenti. ⌡ 12.2.1 - Supervisione -----------Il compito del modulo supervisore del sistema operativo e' la gestione della sincronizzazione dei processi (scheduling a breve) e l'uso dell'hardware, aggiornando delle tabelle di stato delle varie risorse. Questo viene fatto rispondendo alle chiamate di sistema (system calls, interruzioni generate da apposite istruzioni), prodotte dalle chiamate alle funzioni F (primitive di sincronizzazione). Esempi di funzioni F sono: LOCK/UNLOCK SEND/RECEIVE CREA/ELIMINA PROCESSO ALLOCA MEMORIA ecc. Una funzione F puo' essere eseguita tramite processo o tramite monitor. L'esecuzione tramite processo avviene nel modo seguente: Task che invoca la funzione F ============================= (per esempio F=allocaz.memoria) ................ SEND (Pf,Parametri) Processo di sistema Pf ====================== System call -------------> WHILE coda NOT empty DO <-- legge coda (invocaz.processo BEGIN dei messaggi di sistema Pf) RECEIVE (mittente,richiesta) RECEIVE (Pf,risposta) IF richiesta valida THEN (attende risposta) BEGIN ...... ecc. ...... <-----+ esegui richiesta | | | | +<------ SEND (mittente,risposta=ok) END ELSE SEND (mittente,risposta=errore) END. L'esecuzione tramite programma monitor invece puo' essere rappresentata nel modo seguente: Task che invoca la funzione F ============================= (per esempio F=allocaz.memoria) ................ MONITORCALL (F,Parametri) ..... ecc. ..... Processo di sistema Pf ====================== System call -------------> (invocaz.funzione di sistema F) <-------------- LOCK (Rf) <-- lock risorsa modifica MMU di Pf esegue funzione F ripristina MMU di Pf UNLOCK (Rf) In questo caso il nucleo consente a Pf di accedere a programmi e dati necessari per eseguire F (in modo protetto). ⌡ 12.2.2 - I/O Spooler ----------In un sistema multiprogrammato non e' possibile concedere agli utenti di eseguire direttamente operazioni di I/O. Si pensi per esempio a due programmi di due utenti diversi che si mettano a stampare contemporaneamente: si avrebbe un mescolamento delle righe di stampa dell'uno con quelle dell'altro. La soluzione a questo problema e' semplice: basta assegnare un diverso file su disco per ogni programma che deve stampare, in modo che gli output vengano costruiti separatamente su disco. Si dovra' poi avere uno specifico modulo (spooler) del sistema operativo, che provveda a scaricare i files sul dispositivo fisico di output separatamente. In questo modo i programmi utente possono lavorare come se avessero ognuno una diversa unita' periferica virtuale in-linea, anche se il sistema dispone di una sola periferica reale, sulla quale il sistema di Spool scarica off-line i vari file di output (di qui' il nome di SPOOL=Simultaneous Peripherals On Line). La stessa tecnica si usa per l'input. Un pacco di schede, per esempio, viene letto immediatamente dal sistema di Spool e trascritto in un file su disco, specifico di un utente. Quando l'applicazione dovra' leggere i dati, anziche' da schede lo leggera' dal file sul disco. Cosi' ogni utente vede un suo lettore di schede (virtuale), mentre il sistema dispone di un solo lettore di schede fisico. Lo schema di funzionamento della funzione di Spool e' il seguente: ┌─────────┐ ( n utenti ) ┌─────────┐ │ PROGR.1 │ ........... │ PROGR.n │ Applicazioni └────┬────┘ └────┬────┘ └───────────>─┬─<───────────┘ ┌─────────┴──────────┐ │ GESTORE DEI DISCHI │ File system └─────────┬──────────┘ ┌────────────┴─────────────┐ │ DISCO FISICO │ Unita' fisiche │Input files Output files│ └─────┬──────────────┬─────┘ ┌───┴───┐ ┌───┴───┐ │ INPUT │ │ OUTPUT│ <---- Spooling system │ SPOOL │ │ SPOOL │ *************** └───┬───┘ └───┬───┘ ┌───────┴──────┐ ┌────┴────┐ │LETTORE SCHEDE│ │STAMPANTE│ Unita' fisiche └──────────────┘ └─────────┘ Il risultato dell'uso di un sistema operativo multiprogrammato puo' quindi essere rappresentato nello schema seguente: ( 1 utente ) ┌────────────────────┐ ┌─────────┐ ( n utenti ) ┌─────────┐ │ PROGRAMMA UTENTE │ │ PROGR.1 │ ........... │ PROGR.n │ └─────────┬──────────┘ └────┬────┘ └────┬────┘ ┌─────────┴──────────┐ ┌───────────┴─────────┐ ┌──────────┴──────────┐ │ Sistema Operativo │ │ Macchina virtuale 1 │.....│ Macchina virtuale n │ │ (Uniprogr.) │ └──────────┬──────────┘ └──────────┬──────────┘ └─────────┬──────────┘ └───────────>─┬─<───────────┘ ┌─────────┴──────────┐ ┌─────────┴──────────┐ │ Macchina Fisica │ │ Sistema Operativo │ │ ( Hardware ) │ │ (Multiprogr.) │ └────────────────────┘ └─────────┬──────────┘ ┌─────────┴──────────┐ │ Macchina Fisica │ │ ( Hardware ) │ └────────────────────┘ ⌡ 12.2.3 - File system ----------Lo scopo principale del modulo chiamato 'File System' e' di consentire ai livelli superiori l'accesso logico ai dischi. La memorizzazione dei dati sui dischi viene fatta da piu' TESTINE di lettura, ognuna su un diverso piatto magnetico. Su un piatto la registrazione avviene su piu' TRACCE concentriche, corrispondenti alle varie posizioni del braccio dove e' montata la testina di lettura. La pista magnetica di una traccia (circolare su 360 gradi) viene ulteriormente suddivisa in settori, in ognuno dei quali si puo' memorizzare un blocco di dati. Quindi su disco non si memorizza un singolo dato, ma un blocco (per esempio di 512 bytes). E' possibile cosi' individuare un blocco di dati fornendo al File System l'indirizzo fisico di TESTINA, TRACCIA e SETTORE, nel modo descritto nel ⌡ 9.6 (sottosistemi a dischi). Poiche', in seguito al susseguirsi di scritture e cancellazioni, e' possibile che lo spazio rimasto disponibile su disco sia frammentato, cioe' non in settori contigui nel campo di indirizzamento, risulta complicato all'utente gestire direttamente l'accesso al disco. Il File System risolve questo problema consentendo all'utente di accedere ai dati su disco, usando nomi i logici dei files anziche' gli indirizzi fisici e di ritrovare i dati del file, anche se scritto in maniera frammentata. Questo viene fatto creando sul disco una directory (come l'indice di un libro) contenente i nomi logici dei files memorizzati, i loro attributi ed il link iniziale per accedere alla lista dei blocchi, o cluster, che compongono il file su disco (un cluster puo' essere costituito da uno o piu' settori e puo' avere dimensione, per esempio, da 512 a 64K bytes). Se per la scrittura di un file su disco sono stati usati piu' cluster, non e' detto che questi risultino contigui su disco. Dobbiamo percio' usare una struttura dati che ci permetta di recuperare tutti i dati nei cluster, a partire dal primo, nel giusto ordine. La struttura idonea allo scopo e' una una lista. Poiche' si deve anche poter reperire spazio libero per memorizzare nuovi files e' necessario poter sapere se un cluster su disco e' libero o no. Per questo bisogna che sul disco ci sia scritta anche una mappa dei cluster, dalla quale poter dedurre se un cluster e' libero. Abbiamo da risolvere quindi due problemi: collocare sul disco una mappa di tutti i cluster ed una tabella contenente i link di ogni file scritto su disco (lista). Entrambi i problemi possono essere risolti con un'unica tabella (FAT, File Allocation Table), come fatto nei PC-IBM, nel modo seguente. La FAT deve contenere un elemento (2 byte) per ogni cluster del disco: - se questo elemento e' zero il corrispondente cluster e' libero (e cosi' abbiamo la mappa del disco); - se e' diverso da 0 rappresenta il link al prossimo cluster della lista di un file (convenzionalmente 0FFFH sara' il terminatore della lista, mentre 0FF7H indichera' un cluster difettoso, non utilizzabile). Quindi per estrarre dal disco un file con il nome logico DATI, il File System leggera' la directory e cerchera' il nome DATI; se non c'e' scrive 'FILE NOT FOUND' ed esce; se c'e', ricava il cluster iniziale da cui avviare la lettura del file; a partire da quel cluster nella FAT trovera' poi la sequenza di tutti i link con cui leggere, nel giusto ordine, tutti gli altri cluster del file, fino al link terminatore 0FFFH. Il processo inverso si fara' nella scrittura, andando a reperire i cluster liberi nella FAT, scrivendoci i dati del file e registrando nella FAT i link della lista risultante (con il terminatore finale 0FFFH). Altri metodi leggermente diversi sono usati nei File System di UNIX o di Windows NT/2000, ma tutti sono riconducibili alla gestione di una mappa del disco e delle liste dei blocchi con cui sono stati scritti i files. Analogia con la gestione delle basi di dati: ------------------------------------------Citiamo, come stimolo di riflessione sulle procedure di gestione dei dati all'interno di un calcolatore elettronico, l'analogia di funzionamento tra un File System ed un semplice programma di gestione di dati (esempio primitivo di database, cioe' di un insieme di dati organizzati e gestiti da un apposito software DBMS, DataBase Management System). Possiamo usare la stessa strategia gestionale per organizzare un nostro archivio. Consideriamo per esempio un archivio di dati, composto dalle prenotazioni dei posti sui voli di un vettore aereo, che archiviamo su record diversi di un file ad accesso casuale (random). Le principali operazioni da compiere per la loro gestione possono essere programmate in grandi linee nel modo seguente (prevediamo per semplicita' una sola chiave di ricerca, costituita dal nome del cliente): - Inserimento di una nuova prenotazione: 1) si legge la mappa e si individua un record libero; 2) si scrive la prenotazione sul record libero; 3) si aggiorna la lista nominativa (legge, aggiorna e riscrive il relativo file); 4) si riscrive la mappa aggiornata. - Modifica di una prenotazione: 1) si cerca il nome del cliente fornito dall'operatore nella lista nominativa (dopo aver letto il relativo file), che e' ordinata alfabeticamente (vedi il capitolo 2 sulle strutture dei dati); 2) accanto al nome (se lo trova) c'e' il numero di record dove e' stata scritta la prenotazione (se non lo trova risponde con un messaggio di errore appropriato); 3) legge il record e presenta i dati all'operatore per la modifica (editing); 4) dopo la modifica introdotta dall'operatore viene riscritto il record ed aggiornata la lista nominativa (riscrivendo il file aggiornato) se e' stato variato il nome del cliente; la mappa non viene variata perche' il record rimane scritto nella stessa posizione (stesso numero di record). - Cancellazione di una prenotazione: 1) si cerca il nome del cliente fornito dall'operatore nella lista nominativa (dopo aver letto il relativo file); 2) accanto al nome (se lo trova) c'e' il numero di record dove e' stata scritta la prenotazione (se non lo trova risponde con un messaggio di errore appropriato); 3) legge il record e presenta i dati all'operatore per la conferma della cancellazione; 4) se confermata elimina il nome dalla lista nominativa (legge, modifica e riscrive il relativo file); 5) aggiorna la mappa liberando il record (legge, aggiorna e riscrive il file della mappa); i dati sul record possono rimanere, tanto saranno sovrascritti da una prossima memorizzazione perche' nella mappa il record risulta libero. E' interessante osservare la seguente corrispondenza: GESTIONE PRENOTAZIONI GESTIONE DISCO (FILE SYSTEM) -----------------------------------------------Mappa dei record liberi ---> FAT Lista nominativa clienti ---> Directory File random (contenitore) ---> Area dati su disco Cio' in quanto in grandi linee sia un file system che un database aiutano entrambi l'utente alla memorizzazione ed al recupero dei dati su disco. !----------------------------------------------------------------------CAPITOLO 13 - Sistemi in rete ═══════════ Riassunto: In questo capitolo una rete locale di Si parlera' poi di reti ed i relativi Internet. viene spiegato come e' possibile realizzare computer, sia sul piano hardware che software. reti geografiche e si descriveranno le interprotocolli, con particolare riferimento ad ⌡ 13.1 - RETI LOCALI ----------Fino agli anni '70 la connessione di computer, a parte l'uso dei front-end, connessi ai mainframe nei centri di calcolo per l'organizzazione dell'I/O e pochi progetti pilota, era limitata essenzialmente alle esigenze del trasferimento di files. Con l'avvento dei primi personal computer, alla fine degli anni '70, si comincio' a concepire una rete locale di PC, composta da piu' personal computer collegati tra loro via cavo e confinati in un'area limitata, tipicamente l'edificio di un'azienda, come un sistema 'general purpose' in grado di gestire applicazioni in multiutenza, in concorrenza ai sistemi multiprogrammati (composti da un computer con piu' terminali) usati fino ad allora. Il successo di quest'idea fu reso possibile dal basso costo dei personal computer e dalla maggiore affidabilita' di funzionamento raggiungibile con piu' unita' di elaborazione. Le prime reti, basate sull'architettura client-server, dedicavano un computer (server) alla gestione della base-dati su disco ed alla condivisione delle funzioni di output. Infatti per un'applicazione multiutente in cui piu' utenti possono eseguire operazioni concorrenti sulla stessa base-dati, e' opportuno detenere un unico archivio dei dati su un computer che ne assicuri la condivisione degli accessi. La stessa esigenza di condivisione si manifesta quando si vuole consentire a tutti gli utenti l'uso una stessa stampante, per esempio per ragioni di economicita'. L'architettura client-server, tuttora molto usata soprattutto nell'uso di basi-dati, prevede che il server manipoli i dati su richiesta del client e che invii al client richiedente solo le poche informazioni relative al risultato dell'elaborazione (e non tutti i dati dei files necessari per far compiere l'elaborazione al client). Si ottiene cosi' una notevole riduzione della quantita' di dati trasferiti sulla rete. La soluzione adottata fu fin dall'inizio quella ovvia di programmare un computer con un apposito programma 'server', capace di soddisfare le richieste degli altri computer ad esso collegati, che (grazie ad un'apposita programmazione 'client') sottoponevano via cavo richieste di accesso ai dati su disco e di stampa. Trattandosi di applicazioni con condivisione di dati e di output, era necessario realizzare i necessari accorgimenti, come semafori e spool dell'I/O. Oggi le reti si stanno sviluppando sempre di piu' come elemento strutturale e strategico delle economie mondiali perche' offrono la possibilita' di estendere la potenzialita' di elaborazione oltre i limiti imposti sul singolo calcolatore elettronico dalla velocita' della luce che, imponendo limiti fisici alla propagazione dei segnali elettronici all'interno dei calcolatori, ne limita di fatto le massime prestazioni raggiungibili. L'ulteriore vantaggio delle reti e' cosi' quello di permettere una maggiore potenzialita' elaborativa tramite lo sfruttamento dell'elaborazione parallela e distribuita in tutte le diverse applicazioni che gli utenti devono gestire contemporaneamente. Seguendo il nostro solito approccio didattico di spiegare i principi di funzionamento con gli esempi piu' semplici possibili, cominciamo a descrivere la prima rete di questo tipo che realizzammo nel 1980 in Italia sui personal computer allora disponibili (gli Apple II, dotati di 48K RAM e 143K su floppy disk: gli hard disk per PC ancora non erano disponibili). Questa rete, non pubblicata in letteratura, non e' solo un esercizio per la didattica, ma e' stata effettivamente utilizzata per alcuni anni nella realizzazione di sistemi di elaborazione in multiutenza in aziende commerciali e turistico/alberghiere italiane, fino all'avvento delle prime reti locali basate su PC-IBM. Per l'esiguita' delle risorse richieste (pochi kilobytes di memoria e nessuna scheda di rete), potrebbe addirittura essere usata anche oggi in sistemi integrati a controllo automatico distribuito con microprocessori a 8 bit. Con questa semplice rete spiegheremo, evitando per ora le complessita' delle reti attuali, non essenziali ai nostri fini, quali nuovi elementi bisogna introdurre per realizzare un rete locale: 1) Elementi hardware: cavo e schede di rete; 2) Elementi software: driver per la scheda di rete e software di rete nelle due versioni Client e Server; 3) Modalita' di sincronizzazione e comunicazione: protocolli. Potremo anche capire funzionalita' avanzate come la programmazione della elaborazione distribuita ovvero la possibilita' di inviare, per esempio, da un computer Client un comando ad un altro computer Server per pilotare l'esecuzione di programmi su di esso residenti. ⌡ 13.2 - COLLEGAMENTO IN RETE -------------------Per realizzare la connessione tra i computer nella rete, chiamata MicroNet, occorsero i seguenti elementi: 1) Hardware: il supporto fisico della comunicazione era costituito in questo caso da un cavo a tre fili e dalle interfacce seriali (che svolgevano il ruolo che oggi viene svolto dalle schede di rete) ai due capi del collegamento tra i computer Client e Server (collegamento punto-punto): TX ┌──────┐──────────────────>┌──────┐ │Client│───── Ground────── │Server│ └──────┘<───────────────── └──────┘ RX 2) Driver (software): l'Apple II non era dotato di UART, ma gestiva la comunicazione seriale asincrona con un apposito loop software, che alzava od abbassava il livello sulla linea di trasmissione (o leggeva lo stato della linea in ricezione) con la cadenza stabilita dal baudrate, che era di 19200 bit/sec. E' stato possibile quindi, programmando con poche istruzioni Assembly il processore Rockwell 9502 (8 bit e 1MHz, equivalente ad un Motorola 6800), usare le due linee RX e TX anche in modo non standard RS232, semplicemente come linee di stato alto/basso per attivare le funzioni di controllo, come la 'Richiesta' di servizio (Look-At-Me) usata in modalita' polling dai computer Client o la 'Risposta' del computer Server, quando si mette in ricezione del comando trasmesso dal Client. Essendo il Server dedicato, cioe' non assolveva altre funzioni oltre quella di rispondere alle richieste dei client, lo si e' programmato con un polling continuo sullo stato delle linee seriali ad esso collegate (una per ogni computer Client). Il driver era costituito da una piccola routine (poche decine di istruzioni assembly), presente nei due computer, con la quale: - uno trasmetteva un blocco di 256 bytes presenti in una pagina di memoria (buffer) dove il programma aveva scritto i dati; - l'altro riceveva nella sua pagina di buffer, dove il programma li andava a leggere dopo la ricezione. 3) Protocollo: e' l'insieme delle regole comuni con cui i computer comunicanti gestiscono la comunicazione (chi riceve deve acquisire i dati con la stessa sequenza, con cui l'altro computer li sta trasmettendo), prevedeva vari formati dei comandi possibili. Un pacchetto scambiato poteva avere un formato del tipo: ┌────┬────┬────┬────┬────┬────┬────┬────┐ │Funzione │ .....Dati....... │CheckSum │ └────┴────┴────┴────┴────┴────┴────┴────┘ Come esempio elenchiamo alcuni comandi richiesti dal Client al Server con la relativa sequenza di dati scambiati (il protocollo non prevedeva soltanto il pacchetto iniziale di comando, ma anche gli eventuali altri scambi successivi, necessari al completamento del comando): A) Lettura file sequenziale sul disco del Server: ---------------------------------------------Passo 1: Client alza livello di richiesta sulla linea seriale TX Passo 2: Server risponde alzando l'altra linea RX e si mette in ricezione seriale quando TX si abbassa (=procedura di connessione). Passo 3: Client trasmette il pacchetto di comando con Funzione=Lettura file sequenziale (1 byte) Dati=Nome del file e poi si mette in ricezione seriale a 19200 baud su RX. Passo 4: Server riceve il pacchetto, riconosce la funzione da eseguire ed avvia la routine appropriata, leggendo il file sul proprio disco; al termine inizia a trasmettere i dati a 19200 baud sulla linea RX, in blocchi di lunghezza prefissata ┌────┬────┬────┬────┬────┬────┐ │ .....Dati..... │CheckSum │ └────┴────┴────┴────┴────┴────┘ Passo 5: Client riceve il pacchetto dei dati e li inserisce nel buffer, come se provenissero dal proprio hard disk (cosi' i programmi scritti in linguaggi di alto livello procederanno senza accorgersi che i dati sono stati letti dalla rete). La ricezione di tutti i pacchetti procede in rapida successione e solo alla fine il client trasmette al server il messaggio di acknowledgement o di errore (scelta fatta perche' la trasmissione risulta molto affidabile). Passo 6: Server dopo la trasmissione dell'ultimo pacchetto si mette in ricezione sull'altra linea del messaggio di acknowledgement o di errore inviato dal client: in caso di errore si ripete il ciclo di trasmissione altrimenti il comando e' terminato. B) Scrittura file sequenziale sul disco del server: ----------------------------------------------Il protocollo e' del tutto analogo a quello della lettura. Anche per lettura/scrittura di record di file random si procede in modo analogo, con l'apposita funzione ed aggiungendo al nome del file il numero del record (la lunghezza dei record e' gia' nota al server). C) Stampa: ------Due sono i modi possibili: - Inviando un pacchetto di dati corrispondente ad una singola riga di stampa (senza processo di spool): ┌────┬────┬────┬────┬────┬────┬────┬────┐ │F=Stampa │ .....Dati..... │CheckSum │ └────┴────┴────┴────┴────┴────┴────┴────┘ - Inviando un pacchetto con il comando di avvio in stampa di dati che il server legge sui suoi dischi: in questo caso si inserisce nel server la programmazione necessaria a svolgere questo compito (come si fa oggi per esempio con i servlet sui server Web). Per ogni routine attivabile dal client sul server veniva inviato un pacchetto di comando con apposito codice funzione ed i dati necessari all'espletamento del comando. Per esempio nell'applicazione in questione erano predisposti dai client sul server tanti ordini (commerciali) che al completamento venivano fatturati con un solo comando che identificava la funzione da svolgere (fatturazione) e forniva come dati il codice del cliente (con cui il server poteva individuare l'ordine su disco). Il vantaggio di questa procedura e' evidente: con l'invio di un semplice comando si avviava una procedura complessa, che comportava numerose registrazioni sui dischi del server e sulla stampante. Questa procedura, pilotata direttamente dal server, poteva procedere alla massima velocita' senza problemi di concorrenza da parte degli altri computer client e senza bisogno di un sistema di spooling, che in sistemi senza hard disk risulterebbe problematico. Con questi esempi si puo' capire bene cosa si intende con il termine di protocollo: le regole comuni (realizzate nel 'software di protocollo') che i due computer comunicanti devono rispettare per potersi scambiare comandi ed informazioni in modo sincronizzato, ben al di la del singolo pacchetto scambiato. ⌡ 13.3 - SOFTWARE DI RETE ---------------Rimane ora da capire come si possono progettare i due componenti del software di rete, che chiameremo ClientWare e ServerWare per non fare confusione con Client e Server (con cui abbiamo indicato i computer). OBIETTIVO: La funzionalita' consentire ad un utente che di accedere ad un hard disk la stessa facilita' con cui realizzata dal software di rete deve programma in un linguaggio ad alto livello o ad una stampante del computer Server con accede al disco o stampante locale. STRATEGIA: Poiche' l'utente programma in un linguaggio ad alto livello, questo accede ai dischi con una chiamata di sistema, che puo' essere intercettata, modificando il relativo vettore d'interruzione, con una procedura analoga a quella seguita dagli hacker per compiere un'infezione virale: il nuovo vettore dell'interruzione puntera' alla routine 'Quale drive?' (vedi figura), che inviera' le chiamate dirette al drive C: sull'indirizzo del driver locale (il vettore d'interruzione originale) e dirottera' invece sul software di rete (quello indicato con tratti doppi nello schema) le chiamate dirette al drive condiviso sul server (E: nell'esempio). SOLUZIONE: Il software di rete, mostrato con un doppio trattino nel diagramma seguente, gestisce le operazioni come nella precedente descrizione del protocollo: ┌───────────────────┐ │Chiamata di sistema│ └──────────┬────────┘ COMPUTER CLIENT │ --------------- COMPUTER SERVER (Vettore interruz.) --------------║ ╔══════╩═════╗ ClientWare ServerWare ║Quale drive?║ ╔═══════╗ ╔═══════╗ ┌────────┐ ║ C: ║ E: ╠══╣NetWare║ Software di ║NetWare╠═══════┤ Server │ ╚══╦═══╩═════╝ ╚═══╦═══╝ protocollo ╚═══╦═══╝ └───┬────┘ │ ║ (in comune) ║ │ ┌──────┴───────┐ ╔════╩════╗ ╔════╩════╗ ┌──────┴───────┐ │Driver lettura│ ║Driver SK╠═----------═╣Driver SK║ │Driver lettura│ └──────┬───────┘ ╚═════════╝ Cavo ╚═════════╝ └──────┬───────┘ ┌──────┴───────┐ ┌──────┴───────┐ │ disco locale │ (I componenti del software di rete │ disco server │ └──────────────┘ sono quelli a tratto doppio) └──────────────┘ Nello schema di funzionamento, che anche nei software di rete usati oggi ha la stessa impostazione, occorrono quindi i seguenti elementi: 1) Una coppia di programmi ClientWare e ServerWare (chiamati per esempio da Microsoft: Client per reti e Condivisore di risorse); 2) Una coppia di schede di rete con relativi programmi driver; 3) Un software di protocollo, che puo' essere diverso per i vari tipi di reti (Microsoft, Novell, ecc.). I moderni sistemi operativi (OS) inglobano il software di rete. Si parla allora di SISTEMI OPERATIVI DI RETE (NOS, Network Operating System), come Netware della Novell o Windows NT. Nelle reti Microsoft presenti nel nostro laboratorio questi componenti si possono vedere e selezionare nella finestra Rete del Pannello di controllo, che si apre cliccando su Risorse del sistema. ⌡ 13.3.1 - PROTOCOLLI DI COMUNICAZIONE --------------------------Il protocollo di comunicazione e' l'insieme delle regole scelte per lo scambio dei messaggi (formato dei pacchetti di dati e significato dei campi) e trova relizzazione pratica nel software di protocollo. Le applicazioni non interagiscono direttamente con l'hardware della rete, ma usano come interfaccia il software di protocollo, che implementa le regole del protocollo usato. I protocolli di interesse per il nostro corso sono: NetBEUI (NetBios Enhanced User Interface): E' il protocollo di rete sviluppato da IBM e Microsoft come implementazione dello standard NetBIOS (Network Basic I/O System, l'interfaccia API che fornisce i servizi per la gestione di reti locali LAN). NetBEUI fornisce i servizi di trasporto per piccole reti locali (fino a 200 computer), che gestiscono le comunicazioni tra i vari computer tramite il software NetBIOS. E' un protocollo semplice e veloce, ma non e' istradabile tra reti diverse (attraverso i router), a differenza di TCP/IP. Quest'ultimo pero', essendo piu' ricco di funzionalita', ha un maggiore sovraccarico (overhead) sul sistema e puo' quindi risultare troppo gravoso per alcuni sistemi o applicazioni. E' usato nella rete locale del laboratorio delle esercitazioni pratiche. TCP/IP : E' il pacchetto di protocolli piu' usato. Fornisce capacita' di istradamento e supporto per molte piattaforme e servizi come SNMP (Simple Network Management Protocol, il protocollo standard di Internet per il controllo ed amministrazione remota della rete), DHCP (Dynamic Host Configuration Protocol, permette ai computer di ottenere gli indirizzi IP dal server DHCP), WINS (Windows Internet Name Service), DNS (Domain Name Service), ecc. ⌡ 13.4 - INTERCONNESSIONE DELLE RETI --------------------------Le reti esistenti sono diverse, realizzate con tecnologie che le rendono tra loro incompatibili. Esiste pero' un metodo (internetworking), basato sia su componenti software che su dispositivi fisici, che permette comunque di interconnettere tra loro reti diverse, fino a realizzare cosi' un servizio universale (Internet, la rete delle reti), attraverso l'uso di reti eterogenee. ⌡ 13.4.1 - DISPOSITIVI DI CONNESSIONE -------------------------I dispositivi fisici usati per le interconnessioni sono diversi a secondo del tipo di servizio richiesto: 1) Ripetitori e/o amplificatori di segnale: si usano nelle connessioni su distanze lunghe (per esempio oltre 150 metri) per eliminare i problemi dovuti all'attenuazione dei segnali. Possono anche consentire la connessione di segmenti diversi di cavo (anche con supporto fisico diverso, per esempio coassiale BNC o twisted-pair RJ45), purche' il protocollo di accesso sia lo stesso. 2) HUB: e' un dispositivo che realizza il nodo centrale di collegamento di una rete con topologia di connessione a stella. Se sono attivi (alimentati) fungono anche da ripetitori, se sono intelligenti possono fornire servizi (istradamento, packet switching, ecc.). Nella rete locale del laboratorio di esercitazione si puo' vedere un esempio di uso di un Hub attivo. 3) BRIDGE (ponte): e' un ripetitore intelligente, usato nella segmentazione delle reti per connettere i diversi segmenti tra loro. Permette di ridurre la congestione sul segmento dov'e' connesso, perche' riceve le trasmissioni dei pacchetti di dati dagli altri segmenti, ne interpreta l'indirizzo di destinazione (con un'apposita tabella di 'bridging') e, se non sono destinati al suo segmento, li ritrasmette, evitando che lo attraversino (cioe' permette di 'saltare' il segmento in cui si trova, funzionando da 'ponte' per il traffico dei pacchetti di dati). I bridge consentono di connettere segmenti con diversi supporti fisici (per esempio coassiale BNC o twisted-pair RJ45). Esistono anche i bridge traduttori che consentono di connettere reti con schemi di accesso diversi al supporto fisico (per esempio Token ring e Ethernet). 4) ROUTER (istradatore): e' un dispositivo di connessione superiore al bridge perche' e' in grado di effettuare scelte intelligenti sul percorso e sul filtraggio dei pacchetti in transito, al fine di ottenere il massimo throughput (velocita' di flusso) dei dati. L'istradamento viene determinato usando l'indirizzo della rete anziche' del computer destinatario (cioe' il router lavora ad un livello superiore dell'architettura di rete rispetto al bridge), usando una tabella d'istradamento. Questa tabella puo' essere statica (variata solo dall'amministratore) o dinamica, nel qual caso un apposito software verifica la disponibilita' ed efficienza dei percorsi possibili e mantiene costantemente ottimizzata la tabella d'istradamento (comunicandola anche agli altri router). Il router puo' essere un computer dedicato o un normale computer della rete, dotato di una tabella d'istradamento e collegato mediante piu' schede di rete ai diversi segmenti della rete, tra i quali deve svolgere la funzione di istradatore. Dato che il router lavora ad un livello superiore dell'architettura di rete rispetto al bridge, puo' connettere reti eterogenee funzionanti con protocolli diversi, purche' istradabili. Sono istradabili quei protocolli che possono indirizzare pacchetti dati a segmenti di rete diversi da quello a cui appartengono, come TCP/IP o IPX/SPX (non e' invece istradabile NETBEUI, che puo' indirizzare solo i computer della sua LAN). 5) BROUTER (Bridge+Router): e' un dispositivo capace di funzionare da bridge, se il pacchetto ricevuto ha un protocollo non istradabile, o da router se invece il pacchetto ricevuto ha un protocollo istradabile. Ovviamente per svolgere questi compiti ha bisogno di entrambe le tabelle, di bridging e d'istradamento. 6) GATEWAY: e' un dispositivo che svolge il lavoro di traduzione necessario per consentire la connessione di segmenti di rete eterogenei o di computer diversi nella stessa LAN (per esempio PC-IBM, Macintosh e Mainframe). Svolge anche la funzione di traduzione dei protocolli, per cui puo' ricevere un messaggio IPX/SPX e trasmetterlo in TCP/IP al destinatario che opera con questo diverso protocollo. Essendo le funzionalita' svolte dal software, questi dispositivi possono trovare realizzazione pratica in computer dedicati (in genere meno costosi di un PC) o in un normale computer della rete, dotato di quanto occorre per eseguire la funzionalita' voluta in multitasking. ⌡ 13.4.2 - INTER-RETI ---------Nel seguito ci interesseremo dell'interconnessione di reti diverse con il protocollo TCP/IP, per realizzare un servizio universale (Internet, la rete delle reti), sia pure attraverso l'uso di reti eterogenee. I dispositivi fisici che useremo per ottenere cio' sono dei particolari computer (router), dedicati a raccordare fisicamente le diverse reti, convertendo i protocolli ed instradando il traffico verso altri router, fino alla rete di destinazione finale. I componenti software, residenti su ogni computer dell'inter-rete, realizzano l'universalita' del servizio (attraverso i protocolli TCP/IP), dando all'utente l'impressione di operare su un'unica rete omogenea. In generale quindi una inter-rete e' un insieme di reti diverse collegate tra loro tramite dei router, al fine di realizzare una rete virtuale unica ed omogenea. I computer delle differenti reti,indipendentemente dalla loro dimensione, quando sono collegati all'inter-rete, sono detti 'host'. Essi comunicano tra loro tramite i software di protocollo, residenti su tutti gli host e router, che assolvono il compito di nascondere i dettagli fisici delle sottoreti e di simulare l'esistenza di un unica rete omogenea (inter-rete), trasportando i pacchetti di dati attraverso i router fino all'host di destinazione. L'inter-rete e' quindi un'astrazione resa possibile dal software dei protocolli. ⌡ 13.4.3 - PROTOCOLLI TCP/IP ----------------La prima famiglia dei protocolli sviluppati per inter-reti (TCP/IP) e' ancora la piu' usata. Essa e' strutturata in 5 livelli. SOFTWARE DI PROTOCOLLO: Livelli dell'interfacciamento ===================================================== ┌─────────────────────────────────────────┐ │ I/F con le applicazioni (HTTP, FTP,...) │ <--- Livello 5 APPLICAZIONE ├─────────────────────────────────────────┤ │ Trasporto (TCP) │ <--- Livello 4 TRASPORTO ├─────────────────────────────────────────┤ │ Indirizzamento Inter-rete (IP) │ <--- Livello 3 INTER-RETE ├─────────────────────────────────────────┤ │ Interfaccia di rete (Software di rete) │ <--- Livello 2 ├─────────────────────────────────────────┤ │ Interfaccia con l'hardware (Driver) │ <--- Livello 1 └─────────────────────────────────────────┘ RETE FISICO Il livello 1 (Fisico) si occupa dei dispositivi fisici della rete (driver). Il livello 2 (Rete) si occupa di organizzare e trasmettere i dati in pacchetti su una singola rete. Nel livello 3 (Inter-rete) ci sono i protocolli che si occupano del formato dei pacchetti per l'inoltro dal mittente al destinatario nell'inter-rete, ovvero attraverso i router da rete a rete. Il livello 4 (Trasporto) si occupa dell'affidabilita' della comunicazione nell'inter-rete (TCP). Nel livello 5 (Applicazione) ci sono i protocolli specifici delle applicazioni, quelli cioe' che specificano come una certa applicazione usa l'inter-rete (HTTP, FTP,...). ⌡ 13.4.4 - INDIRIZZAMENTO IP (INTERNET PROTOCOL) ------------------------------------Per poter simulare un sistema inter-rete omogeneo e' necessario avere un unico schema di indirizzamento nella comunicazione, in cui ad ogni stazione (punto di collegamento per host o router) corrisponde un unico indirizzo, indipendentemente dagli indirizzamenti usati sulle singole reti, che possono anche essere incompatibili. Nei protocolli TCP/IP viene usato lo schema di indirizzamento IP (Internet Protocol), in cui un indirizzo e' formato da un numero di 32 bit. Un indirizzo IP non identifica uno specifico computer ma uno specifico collegamento tra computer e sottorete (stazione o port). Se un computer ha piu' collegamenti deve avere un indirizzo IP per ogni collegamento: e' il caso dei router, che collegano sottoreti diverse, o degli host multi-homed, in cui si attiva piu' di un collegamento per migliorare l'affidabilita' o le prestazioni. Ogni pacchetto di dati inviato sull'inter-rete contiene i due indirizzi IP: quelli della stazione mittente e di quella destinataria. L'indirizzo IP, per comodita' degli utenti, viene rappresentato in forma decimale puntata (per esempio: 191.9.25.3), in cui i 4 byte (4x8=32 bit) sono scritti come interi in base 10, separati da un punto. Per maggiore comodita' e' stata anche introdotta la possibilita' di usare dei nomi simbolici, che vengono tradotti automaticamente nell'indirizzo binario IP da un apposito software. Ciascun indirizzo IP e' composto da due parti: il prefisso ed il suffisso. Il prefisso identifica la rete fisica dell'host, mentre il suffisso e' l'identificativo unico dell'host in quella rete (pero' host di sottoreti diverse possono avere lo stesso suffisso). Le lunghezze in bit di prefisso e suffisso determinano il numero massimo di reti ed il numero massimo di stazioni (host e router) all'interno di una sottorete. Per ridurre la rigidita' conseguente ad un'attribuzione unica del numero di bit ai due campi, si e' deciso di definire cinque classi di indirizzi (in base ai bit 0..3). Nelle prime tre (classi primarie A, B e C) sono attribuite lunghezze diverse per prefisso e suffisso, permettendo cosi' di ottenere la maggiore elasticita' voluta per il massimo numero di reti ed il massimo numero di stazioni all'interno di una sottorete: bit Classe A Classe B Classe C 0 1 .......7 8 16 24 31 Primo byte ┌─┬──────────┬─────────┬──────────┬─────────┐ │0│ Prefisso │ Suffisso │ 0...127 └─┴──────────┴─────────┴──────────┴─────────┘ ┌─┬─┬────────┬─────────┬─────────┬──────────┐ │1│0│ Prefisso │ Suffisso │ 128...191 └─┴─┴────────┴─────────┴─────────┴──────────┘ ┌─┬─┬─┬──────┬─────────┬─────────┬──────────┐ │1│1│0│ Prefisso │ Suffisso │ 192...223 └─┴─┴─┴──────┴─────────┴─────────┴──────────┘ Classe A B C Bits prefisso 7 14 21 N.max sottoreti 128 16384 2097152 Bits suffisso 24 16 8 N.max staz./rete 16777216 65536 256 Altre due classi sono state riservate per gli indirizzi multicast (nella trasmissione broadcast l'indirizzo del ricevente, di classe D, viene condiviso tra piu' host che ricevono cosi' contemporaneamente uno stesso pacchetto) e per usi futuri: bit Classe D Classe E 0 1 .......7 8 16 24 31 Primo byte ┌─┬─┬─┬─┬────┬─────────┬─────────┬──────────┐ │1│1│1│0│ Indirizzo multicast │ 224...239 └─┴─┴─┴─┴────┴─────────┴─────────┴──────────┘ ┌─┬─┬─┬─┬────┬─────────┬─────────┬──────────┐ │1│1│1│1│ Riservato per usi futuri │ 240...255 └─┴─┴─┴─┴────┴─────────┴─────────┴──────────┘ Nell'inter-rete quindi ogni sottorete ha un unico identificativo (network number, prefisso) ed ogni host della sottorete ha un unico identificativo diverso all'interno della sottorete (suffisso). Nell'inter-rete questi indirizzi svolgono lo stesso ruolo che gli indirizzi fisici svolgono nelle singole reti: una stazione mittente, che deve inviare un pacchetto di dati, mette l'indirizzo IP della stazione destinataria nel giusto campo del pacchetto e lo passa al software di protocollo che provvede ad inoltrarlo. Il software di protocollo istallato nella rete di destinazione provvedera' a farlo arrivare al giusto indirizzo 'fisico' della particolare rete destinataria (diverso dall'indirizzo di protocollo). Per poter collegare una rete ad Internet occorre ottenere l'attribuzione di un numero di rete dalla societa' di telecomunicazione (Internet Service Provider) che assicura la connettivita' su rete pubblica. Per garantire l'univocita' dei prefissi, queste societa' si coordinano con un'organizzazione centrale (IANA = Internet Assigned Number Authority). Nelle inter-reti private invece l'attribuzione dei prefissi spetta al proprietario. Gli indirizzi seguenti sono definiti come 'riservati' e non possono quindi essere attribuiti ad alcuna stazione: Prefisso Suffisso Tipo indirizzo Funzione --------------------------------------------------------------------Tutti 0 Tutti 0 'Questo host' Usato all'avvio Rete X Tutti 0 Rete Identifica la rete X Rete X Tutti 1 Broadcast orientato Broadcast a rete X Tutti 1 Tutti 1 Broadcast ristretto Broadcast su rete locale 127 Qualunque Loopback Per test applicazioni Il prefisso 127 e' molto utile nelle esercitazioni perche' permette di simulare il collegamento in rete, cioe' di provare i programmi applicativi senza doverli istallare su due host diversi (basta usare per esempio l'indirizzo di loopback 127.0.0.1). Esempio di attribuzione di indirizzi IP (alle sottoreti ed ai router) in un'inter-rete con tre sottoreti diverse, collegate attraverso due router: ╔══════════════════════╗ ║ Ethernet 135.108.0.0 ║ Sottorete di classe B ╚══════════╦═══════════╝ │ │ ┌────────┐ 135.108.99.5 ──> └─┤ Router ├─┐ <── 213.240.129.2 └────────┘ │ │ ╔═════════════╩════════════╗ ║ Token Ring 213.240.129.0 ║ Sottorete di classe C ╚═════════════╦════════════╝ │ │ ┌────────┐ 213.240.129.12 ──> └─┤ Router ├─┐ <── 75.0.0.15 └────────┘ │ │ ╔═══════╩══════╗ ║ WAN 75.0.0.0 ║ Sottorete di classe A ╚══════════════╝ E' possibile aumentare il numero dei router, rendendo la topologia di interconnessione ridondante, per migliorare l'affidabilita' o le prestazioni. Il software di protocollo contenuto nei router decide il successivo percorso che un pacchetto (datagramma) deve compiere, in base ad una tabella d'istradamento in esso memorizzata. Nel datagramma compare solo l'indirizzo IP della destinazione finale (non quello del successivo percorso). I router determinano quindi il percorso lungo l'inter-rete (istradamento). Inoltre, per propagare il datagramma attraverso le differenti sottoreti fisiche, i router provvedono ad inserire il datagramma in un frame ammesso alla trasmissione nei dispositivi di ogni sottorete attraversata (incapsulamento). Ognuna di queste sottoreti ha un limite alla dimensione massima del frame (MTU, Maximum Transmission Unit). Se un datagramma supera questa dimensione massima MTU, stabilita dalle specifiche delle sottoreti, il router che lo deve istradare lo divide in frammenti piu' piccoli (frammentazione), che vengono trasmessi indipendentemente lungo l'inter-rete, fino alla destinazione finale, dove vengono ricomposti per ricostruire il datagramma originario. La versione attuale del protocollo IP (versione 4) funziona molto bene, ma si prevede che risultera' presto insufficiente lo spazio d'indirizzamento a 32 bit. Per questo la IETF ha progettato la versione 6 (IPv6) con indirizzi a 128 bit. ⌡ 13.4.5 - INDIRIZZAMENTO SU INTERNET -------------------------Il protocollo di comunicazione usato in Internet e' TCP/IP (Transmission Control Protocol /Internet Prococol). Il primo protocollo (TCP) organizza i dati in pacchetti di lunghezza fissa e controlla che siano trasmessi bene (packet switching). Ogni pacchetto ha un suo indirizzo di origine e destinazione e viene inviato con istradamento in rete sul percorso piu' conveniente (PPP, Point to Point Protocol, e' l'implementazione su linea telefonica (seriale) del protocollo TCP/IP). L'indirizzo IP (di un computer) viene assegnato dall'IR (Internet Registry) ad ogni rete che chiede di connettersi ad Internet ed e' un numero di 32 bit, usualmente indicato in 4 bytes con notazione 'dotted decimal'. Esempio: C025410AH = 192.37.65.10 (in esadecimale C0.25.41.0A) ( 192=Nazione; 37=Ditta; 65=Sede; 10=Computer ) E' possibile far corrispondere ad ogni indirizzo IP un nome simbolico (piu' pratico per l'utente), composto da una sequenza di segmenti alfanumerici separati da punti: Hostname.Subdomain.ToplevelDomain Per ottenere un nome di dominio occorre registrarsi presso l'ente Internet preposto, che attribuisce un suffisso di dominio univoco. Se una ditta ha registrato il suo nome di dominio come 'ditta.it' i suoi host possono avere nomi del tipo: computer1.ditta.it dove: computer1=Nome host; ditta.it=nome del dominio Se la stessa ditta ha sedi a Roma e Milano, puo' introdurre un ulteriore livello nella gerarchia per individuare la sede, usando nomi del tipo: computer.sede.ditta.it e se ogni sede ha piu' uffici, un altro livello gerarchico puo' servire a specificarli: computer.ufficio.sede.ditta.it L'ordine gerarchico risulta cosi' inverso a quello degli indirizzi numerici IP. Il sistema dei nomi di dominio (DNS) lascia completa liberta' all'utente di scegliere numero e significato dei segmenti alfanumerici nel nome di dominio, salvo il segmento piu' significativo, che deve rispettare le assegnazioni seguenti: com Societa' commerciale edu Istituzione accademica gov Ente statale mil Ente militare net Gestore servizi di rete org Organizzazione diversa int Organizzazione internazionale arpa Dominio temporaneo ARPA sigla nazione Le stringhe usabili (domini e sottodomini) sono illimitate, senza distinzione tra maiuscole e minuscole. Questa notazione simbolica si chiama FQDN (Full Qualified Domain Name). La traduzione in indirizzo IP e' fatta da un'applicazione di data-base distribuito (residente su piu' computer della rete) chiamata DNS (Domain Name Server): un'applicazione richiede la traduzione al suo server DNS, che risponde con l'indirizzo IP. Se il server non e' in grado di eseguire direttamente la traduzione diventa a sua volta client di un altro server DNS, e cosi' via fino a quando la traduzione non e' eseguita (data-base distribuito). I piccoli utenti non hanno bisogno di istallare il software DNS perche' possono avvalersi di quello sul server dell'Internet Service Provider, che fornisce loro la connettivita' in rete. I nomi NETBIOS (Microsoft) dei computer in rete nel nostro laboratorio (es. USER1) sono lunghi fino a 15 caratteri. I servizi di traduzione degli indirizzi dei computer NETBIOS-->IP, necessari nelle reti che usano il protocollo TCP/IP (Transmission Control Protocol / Internet Protocol) sono residenti su server WINS o DNS, che ricevono la richiesta di traduzione e restituiscono l'indirizzo IP al client. L'indirizzo IP del server di traduzione degli indirizzi puo' essere impostato dall'operatore del client o assegnato automaticamente dal server Web (la scelta si fa nella finestra delle Impostazioni del protocollo TCP/IP): WINS (Window-Internet Name Service)=Servizio di traduz.del Nome NETBIOS del computer nell'indirizzo IP Esempio: Computer Client -----> USER1 -------> Server WINS <--- 121.19.2.1 <--DNS (Domain Name Service) = Servizio di traduzione del Nome NETBIOS del computer nell'indirizzo IP Esempio: Computer Client -----> USER1.AZIENDA.COM -------> Server DNS <------- 121.19.2.1 <--------Il servizio di traduzione degli indirizzi dei computer, anziche' essere centralizzato su un server in rete, puo' essere distribuito sui clients gestendo localmente i files LMHOST e HOST ⌡ 13.4.6 - PROTOCOLLO IP: DATAGRAMMI ED ISTRADAMENTO ----------------------------------------Il principale servizio di TCP/IP e' il trasporto e la consegna dei pacchetti di dati, senza connessione diretta tra le stazioni trasmittente e ricevente. Questi pacchetti (datagrammi IP) sono un formato standard (virtuale), che viene tradotto dal software di protocollo TCP/IP presente in tutte le sottoreti nei formati (reali), riconosciuti dai vari dispositivi delle reti fisiche (non omogenee), che formano l'inter-rete. Un datagramma IP e' composto dal blocco dei dati, preceduto da una intestazione contenente gli indirizzi IP di mittente e destinatario, oltre ad altre informazioni come lunghezza, tipo di dati, checksum, ecc. La lunghezza del datagramma e' stabilita dall'applicazione e puo' arrivare fino a 64K bytes. Per disturbi nella trasmissione attraverso le reti fisiche e' possibile che alcuni datagrammi IP vengano persi, alterati, duplicati o consegnati in ritardo (quindi in ordine diverso da quello di spedizione). Il software del protocollo IP non si occupa della soluzione di questi problemi, ma lascia questo compito alle applicazioni che usano IP per comunicare. Per la soluzione di questi problemi si rendono disponibili, ad un livello piu' alto della famiglia TCP/IP, i servizi piu' affidabili, orientati alla connessione, come il protocollo di trasporto TCP e ad un livello ancora superiore (perche' usa TCP) il protocollo HTTP per trasporto di ipertesti. La famiglia TCP/IP comprende anche un protocollo per il controllo e la notifica degli errori verificatisi nella trasmissione, chiamato ICMP (Internet Control Message Protocol). Questi due protocolli convivono sempre insieme nel senso che IP usa ICMP per notificare il verificarsi di anomalie e ICMP usa IP per trasmettere le notifiche degli errori. Il servizio di IP senza connessione e' un esempio di trasmissione in commutazione di pacchetto (packet switching): con questa tecnica un messaggio lungo puo' essere scomposto in piu' pacchetti (datagrammi IP), ognuno dei quali viene trasmesso indipendentemente da un router ad un altro, lungo l'inter-rete, usando le informazioni contenute nell'intestazione (indirizzo del destinatario), finche' l'ultimo router non lo consegna alla stazione di destinazione. Ciascun router dispone di un software, che usa una tabella d'istradamento con la quale, quando riceve un datagramma, puo' ricavare dall'indirizzo IP di destinazione, a quale prossimo indirizzo inoltrare il datagramma (quello di un altro router o della stazione destinataria, se si trova nella sottorete direttamente accessibile). La tabella d'istradamento viene caricata nel router all'avvio ed aggiormata ad ogni variazione dei collegamenti nella rete. Poiche' il router trasmette il datagramma ad altri router, per eseguire questo istradamento non gli occorre l'indirizzo completo della stazione destinataria, ma solo il prefisso ovvero l'indirizzo della sottorete in cui si trova la stazione di destinazione (cio' poiche' ogni router corrisponde all'interfaccia di accesso ad una sottorete). Questo consente di contenere la dimensione della tabella d'istradamento, che dovra' contenere tutte le coppie: Indirizzo della sottorete destinataria ---> Prossimo indirizzo a cui (e non di tutti gli host indirizzabili) trasmettere il datagramma Il software del router puo' ricavare l'indirizzo della sottorete di destinazione dall'indirizzo IP della stazione destinataria, presente nel datagramma, grazie alla maschera dell'indirizzo (address mask), contenuta nella tabella d'istradamento. Questa maschera specifica quali bit dell'indirizzo IP compongono il prefisso della sottorete (quelli corrispondenti ai bit 1 della maschera). Per esempio il router A dell'inter-rete seguente: ╔══════════════════════╗ ║ Ethernet 131.108.0.0 ║ Sottorete di classe B ╚══════════╦═══════════╝ │ │ ┌────────┐ 131.108.99.5 ──> └─┤Router A├─┐ <── 223.240.129.2 └────────┘ │ │ ╔═════════════╩════════════╗ ║ Token Ring 223.240.129.0 ║ Sottorete di classe C ╚═════════════╦════════════╝ │ │ ┌────────┐ 223.240.129.17 ──> └─┤Router B├─┐ <── 78.0.0.17 └────────┘ │ │ ╔═══════╩══════╗ ║ WAN 78.0.0.0 ║ Sottorete di classe A ╚═══════╦══════╝ │ │ ┌────────┐ 78.0.0.18 ──> └─┤Router C├─┐ <── 95.0.0.30 └────────┘ │ │ ╔═══════╩══════╗ Sottorete di classe A ║ WAN 95.0.0.0 ║ ╚══════════════╝ potrebbe avere una tabella di istradamento come quella sotto riportata (eventuali destinazioni non elencate avrebbero un'istradamento d'ufficio prestabilito): ESEMPIO DI TABELLA D'ISTRADAMENTO PER IL ROUTER A ------------------------------------------------------Sottorete dest. Mask bits Indirizzo d'invio ------------------------------------------------------78. 0. 0. 0 255. 0. 0. 0 223.240.129. 17 95. 0. 0. 0 255. 0. 0. 0 78. 0. 0. 18 131.108. 0. 0 255.255. 0. 0 Consegna all'host 223.240.129. 0 255.255.255. 0 Consegna all'host ------------------------------------------------------in base alla quale i datagrammi contenenti gli indirizzi di destinazione indicati nella prima colonna della tabella seguente, per esempio, vengono inoltrati agli indirizzi riportati nell'ultima colonna: DATAGRAMMA IP | TABELLA D'ISTRADAMENTO NEL ROUTER A ----------------+----------------------------------------------------Indirizzo IP | Sottorete dest. Mask bits Indirizzo d'invio ----------------+----------------------------------------------------131.108. 25.105 | 131.108. 0. 0 255.255.0.0 Consegna all'host 223.240.129.12 | 223.240.129. 0 255.255.255.0 Consegna all'host 78.112.225.65 | 78. 0. 0. 0 255.0.0.0 223.240.129. 17 95.224.131.16 | 95. 0. 0. 0 255.0.0.0 78. 0. 0. 18 ----------------+----------------------------------------------------PROCEDURA D'ISTRADAMENTO ESEGUITA DAL SOFTWARE DEL ROUTER: All'arrivo di un datagramma il software del router deve estrarre l'indirizzo IP di destinazione e poi per ogni riga della tabella d'istradamento deve: 1) applicare la maschera all'indirizzo (AND); 2) confrontare l'indirizzo della sottorete di destinazione cosi' ricavato con quello presente nella riga della tabella d'istradamento; 3) se uguale inoltrare il datagramma all'indirizzo riportato sulla stessa riga e terminare; 4) se diverso passare alla riga successiva della tabella d'istradamento, ripartendo dal punto 1; 5) se si arriva al termine della tabella senza trovare la sottorete di destinazione nella tabella, inoltrare il datagramma all'indirizzo del router prestabilito d'ufficio. Anche i router B e C dell'inter-rete considerata nell'esempio precedente avranno tabelle analoghe a quella del router A, che permettono al software dei router di determinare i percorsi per tutti i possibili indirizzi IP presenti nell'inter-rete. ⌡ 13.4.7 - PROTOCOLLO DI CONTROLLO DELLA TRASMISSIONE (TCP) -----------------------------------------------E' il piu' importante protocollo di trasporto della famiglia TCP/IP a disposizione delle applicazioni. Con TCP/IP due applicazioni su computer diversi A e B possono instaurare una connessione, scambiarsi messaggi in modo full-duplex ed affidabile e chiudere la connessione al termine (connessione punto-punto). TCP usa IP per il trasporto dei dati, incapsulando i suoi messaggi nei datagrammi IP. Quando IP ha recapitato un datagramma, ne passa il contenuto a TCP senza interpretarlo. Il software TCP dev'essere quindi nei computer tra i quali si instaura la connessione, ma non nei router, come mostrato nello schema seguente (in cui la parte tratteggiata indica il sistema di comunicazione IP usato da TCP): COMPUTER A COMPUTER B ┌──────────────┐ ┌──────────────┐ │ Applicazione │ │ Applicazione │ ├──────────────┤ ├──────────────┤ │ TCP │ Sistema di comunicazione IP │ TCP │ +--├═─═─═─═─═─═─═─┤---------------------------------├═─═─═─═─═─═─═─┤--+ | │ IP │ ROUTER │ IP │ | | ├──────────────┤ ┌──────────────┐ ├──────────────┤ | | │ I/F di rete │ │ IP │ │ I/F di rete │ | | └──────┬───────┘ ├──────────────┤ └───────┬──────┘ | | │ │ I/F di rete │ │ | | │ └───┬──────┬───┘ │ | | │ ╔════════╗ │ │ ╔════════╗ │ | | └─────╣ RETE 1 ╠──────┘ └──────╣ RETE 2 ╠─────┘ | | ╚════════╝ ╚════════╝ | +----------------------------------------------------------------------+ Esempio di connessione TCP con ritrasmissione di un messaggio (handshake): COMPUTER A ══════════ Trasmette messaggio 1 Riceve acknowledge 1 ────────────────> <──────────────── COMPUTER B ══════════ Riceve messaggio 1 Trasmette acknowledge 1 Trasmette messaggio 2 Riceve acknowledge 2 ────────────────> <──────────────── Riceve messaggio 2 Trasmette acknowledge 2 Trasmette messaggio 3 . . . . . . . . . Scade tempo di attesa ──>...Pacchetto perso... Ritrasmette messaggio 3 ────────────────> Riceve acknowledge 3 <──────────────── ecc. Riceve messaggio 3 Trasmette acknowledge 3 Il protocollo TCP e' orientato alla connessione, ovvero le applicazioni devono esplicitamente stabilire una connessione prima di comunicare, richiedendone l'apertura prima e la chiusura poi. Per alcune applicazioni e' piu' conveniente risparmiare il tempo di apertura della connessione anche a scapito dell'affidabilita' nel recapito del messaggio. Per questi casi il protocollo TCP mette a disposizione il protocollo UDP (User Datagram Protocol) per comunicazioni senza connessione (il datagramma viene spedito senza alcun controllo sul buon esito della ricezione). ⌡ 13.5 - ARCHITETTURA CLIENT-SERVER -------------------------L'inserimento nel computer dei componenti di rete fornisce la possibilita' di accesso alle risorse di altri computer anche distanti. Questo apre la strada allo sviluppo delle tecnologie di comunicazione e di elaborazione sia in rete locale che geografica, che vengono indicate oggi con il termine High-Tech. Distinguiamo due scenari applicativi: 1) Reti in cui e' prevalente la comunicazione e la distribuzione delle informazioni (World Wide Web, Intranet aziendali, ecc.). 2) Reti in cui e' prevalente l'elaborazione dei dati (Reti locali, Intranet/Extranet, ecc.); Nel primo caso vengono situate su files e directories di un computer server varie informazioni (testi, immagini, suoni, ecc.), che devono essere rese disponibili secondo le regole stabilite ai vari computer clientcollegati alla rete. Quest'idea, nata al CERN nel '90 per distribuire documenti tra i gruppi di ricerca, ha dato origine al World Wide Web ed al successo di Internet. La presentazione di informazioni multimediali con uso di ipertesti (contenenti 'link' ad altri elementi informativi rappresentabili) ha portato a progettare in modo particolare i due programmi client e server che devono gestire questo modo di distribuire ai client le informazioni residenti sui server. Sono cosi' nati i programmi Web-Server, come Apache o IIS (Internet Information Server), ed i browser, come Netscape Navigator o Internet Explorer, capaci di interrogare con opportuni protocolli di comunicazione il server, interpretando il testo ricevuto in linguaggio HTML (HypetText Markup Language). L'uso sia dei Browser che del Web server Apache, descritto in un'apposito file nel dischetto delle dispense, fa parte del programma delle esercitazioni pratiche. Nel secondo caso si puo' realizzare un sistema di elaborazione multi-utente facendo elaborare i vari computer 'client' sugli stessi dati residenti sui dischi di un computer 'server' (controllando correttamente la concorrenza con semafori messi anch'essi in files condivisi sul server). In alcuni casi pero' la quantita' di dati da trasferire dal server al client per eseguire un compito (per esempio una ricerca) puo' essere troppo grande per la capacita' di trasmissione della rete. In questi casi puo' essere piu' conveniente lanciare dal client il comando di eseguire il compito al server, che potra' eseguire l'elaborazione piu' efficientemente, potendo accedere direttamente ai dati residenti sui suoi dischi. Il server invia poi al client solo i risultati dell'elaborazione, evitando cosi' i problemi di trasmissione di molti dati sulla rete. Possiamo cosi' distinguere tre possibilita' per la cosiddetta architettura software delle applicazioni in rete: 1) Centralizzata: L'applicazione e' tutta sul server ed i client lo interrogano (Database, programmazione ad oggetti con istanze in memorie diverse) 2) Condivisione di file: L'applicazione e' sui client e usa files condivisi su uno o piu' server 3) Client/Server: L'applicazione e' distribuita su client e server (caso ibrido) In quest'ultimo modo di funzionamento, architettura Client-Server, il computer Server non solo gestisce le richieste di accesso alle sue risorse (dischi, stampanti, modem, ecc.) da parte dei computer Client, ma esegue anche dei comandi, come per esempio quelli relativi alle interrogazioni di un database, come nel caso dei Server SQL (=Structured Query Language, un linguaggio standard per database relazionali). Il funzionamento di Internet e' basato su: - Protocollo di comunicazione TCP/IP - Metodo di indirizzamento degli host - Architettura Client/Server La concorrenza (multitasking) e' essenziale, poiche' il server puo' attivare un nuovo thread (o processo o task) di controllo per ciascuna richiesta da servire, cosicche' piu' client possano essere serviti contemporaneamente. Nell'architettura client/server i client accludono a ciascuna richiesta inviata un numero identificatore (=porta) del servizio richiesto; il software del server ricevente puo' allora stabilire a quale thread inoltrare la richiesta. ⌡ 13.5.1 - COMUNICAZIONE IN RETE TRA APPLICAZIONI (INTERFACCIA SOCKET) ----------------------------------------------------------Client e server comunicano tramite il protocollo di trasporto, che puo' essere orientato alla connessione o privo di connessione (UDP). Nel primo caso le applicazioni devono instaurare una connessione prima di poter comunicare. Nel secondo caso invece l'applicazione non deve accertare, prima dello invio di un messaggio, la disponibilita' del destinatario a riceverlo, ma puo' trasmettere verso qualunque destinazione in qualunque momento. Per fare questo le applicazioni devono interagire con il protocollo di trasporto (TCP). Questo puo' avvenire utilizzando l'interfaccia API (Application Program Interface), che consiste in un insieme di procedure che le applicazioni possono richiamare. Molti costruttori hanno adottato l'interfaccia API 'socket', che si e' cosi' imposta come standard di mercato. Una procedura API in genere corrisponde ad un'operazione fondamentale, come per esempio l'apertura di una connessione o la trasmissione di un messaggio. Trattandosi di operazioni standard, le peculiarita' dell'aggancio con le applicazioni scritte su una particolare piattaforma sono curate dal sistema operativo. Nella versione Unix BSD l'interfaccia socket fa parte del sistema operativo (cosi' e' stata creata a Berkeley). In altri sistemi operativi le socket sono aggiunte come libreria di procedure (socket library). L'uso delle socket per la comunicazione attraverso il protocollo di trasporto (I/O in rete) segue le stesse modalita' di accesso ai file sequenziali: open - read - write - close La chiamata alla procedura 'open' (=creazione di una socket) restituisce indietro un numero intero descrittore della socket, che verra' usato come argomento nelle successive chiamate alle procedure read, write e close (senza bisogno di specificare ogni volta l'indirizzo del destinatario). Rispetto all'I/O su file sequenziale, alla 'open' bisogna passare piu' parametri: il protocollo di trasporto, l'indirizzo del destinatario, il tipo dell'applicazione (client o server). Esempi: 1) La procedura 'socket' crea una socket e restituisce il descrittore: descrittore = socket (fam_prot, tipo, protocollo) 2) La procedura 'close' ('closesocket' in Windows) chiude la socket: close (socket) (socket=descrittore) 3) La procedura che associa un numero di porta del protocollo alla socket: bind (socket, ind_locale, lungh_ind) 4) La procedura che definisce una socket come passiva (di un server): listen (socket, lungh_coda) 5) La procedura che accetta la prossima richiesta di connessione (server): nuova_sock = accept (socket, ind_client, lungh_ind_client) 6) La procedura che permette ad un client di connetersi ad un server: connect (socket, ind_server, lungh_ind_server) E cosi' via con le procedure per trasmettere e ricevere: send, sendto, sendmsg, recv, recvfrom, recvmsg, ecc. Il modo in cui si realizza la comunicazione tra due applicazioni, usando l'interfaccia socket, e' descritto nell'esempio seguente (tra parentesi sono indicate le procedure da usare per creare un servizio, in cui un server invia un messaggio ad un client che richiede una connessione): Applicazione sul server ----------------------Server crea una socket (socket) Le associa un N. di porta (bind) Rende la porta passiva (listen) Accetta una connessione (accept) Invia il messaggio (send) Chiude la connessione (close) Applicazione sul client ----------------------Client crea una socket (socket) Si collega al server Riceve dati in arrivo Chiude la connessione (connect) (recv) (close) Un esempio di programmazione pratica in C di questo esempio e' riportata nel Cap.25 del libro di D.Comer. Altri esempi di programmazione in Java si trovano nel tutorial sul sito della SUN: http://java.sun.com/docs/books/tutorial/networking/sockets/readingWriting.htm l Nelle esercitazioni di laboratorio verra' mostrato l'uso dei Socket nella realizzazione pratica di un server Web in Java. La comunicazione nella programmazione in architettura Client-Server puo' essere gestita anche e piu' facilmente attraverso i servlets, che sono programmi Java, analoghi alle applet, che vengono eseguiti all'interno del Web server (abilitato a Java). Si puo' dire, per analogia, che i servlet stanno al Web Server, come le applet stanno al browser. Grazie alla programmazione Java essi offrono un modo alternativo, piu' facile ed efficiente degli script CGI per estendere le funzionalita' del server (ad esempio per generare documenti dinamici, per gestire data-base aiendali, ecc.), in modo indipendente dalla piattaforma. Documentazione ed esempi si trovano nel tutorial sul sito della SUN: http://java.sun.com/docs/books/tutorial/networking/servlets/index.html Un Servlet Tutorial con esempi si puo' trovare sul sito della Unify: www.NewAtlanta.com/ServletExec AS/Documentation/servlet_tutorial.html Riportiamo qui un esempio di semplice server HTTP realizzato in Java, usato nell'esempio di commercio elettronico nelle esercitazioni (il codice Java sara' comprensibile allora, dopo lo studio del linguaggio). Esso e' in grado di ricevere la richiesta inoltrata da un browser (con un ServerSocket nel main che attiva il thread run), prelevare dal proprio hard-disk la pagina HTML richiesta (con il thread run) ed inviarla al browser (con il metodo invioPagina). Spiegazioni piu' dettagliate saranno possibili durante l'esercitazione pratica. /* ======= Esempio di server HTTP multithread con uso di Socket ========= * * Questo esempio, tratto dal programma di ricerca RUN condotto dal gruppo * di Calcolatori Elettronici, mostra come si realizza in Java un server * HTTP. Esso e' stato descritto in un apposito seminario, tenuto dal * dott.F.Baldasseroni nel nostro Laboratorio ed e' dedicato alla * programmazione in rete di applicazioni in architettura client-server. * Questo server puo' essere interrogato da un browser (client) e risponde * nel giusto formato (MIME), come farebbe un Web server Apache, IIS, ecc. * In questo esempio restituisce il file richiesto in HTTP dal browser. */ import java.net.*; import java.io.*; import java.util.*; public class httpServer extends Thread { static int Porta=4444; static boolean debug=false; static String nomeHost, errorPage; String filRequested; Socket theConnection; // -------------- Metodo main --------------public static void main (String[] args) { if (args.length == 0) { debug=false; } else { debug=true; System.out.println ("Esecuzione in modalita' debug"); } errorPage = "<html><body><h1>Documento non trovato!</h1><p><a href='index.html'>torna alla pagina iniziale</a><p></body></html>"; ServerSocket ss; // Dichiara ss come variabile ServerSocket try { ss = new ServerSocket(Porta); // <--- Attiva il ServerSocket ss, in ascolto sulla Porta=4444 System.out.println ("Server RUN in ascolto sulla porta :" + ss.getLocalPort()); // Consente gestione di piu' Client sulla stessa Porta (multithread) while (true) { httpServer ts = new httpServer (ss.accept()); // Accetta una nuova connessione ts sul Socket ss ts.start(); // Crea il thread per rispondere al Client } } catch (IOException e) { System.out.println("error 1: " + e); } // Gestione errori } // fine main // -------- Metodo httpServer, con segnatura (Socket s) --------// Definisce il Socket theConnection = Connessione stabilita sul Socket s public httpServer (Socket s) { theConnection = s; } /* ---------- Metodo run -----------Thread lanciato dal serverSocket per gestire le singole connessioni http (simile a routine di servizio d'interruzione, ma ad un livello superiore) */ public void run() { try { PrintWriter os = new PrintWriter (theConnection.getOutputStream(), true); System.out.println ("connessione attivata con " + theConnection); BufferedReader is = new BufferedReader (new InputStreamReader (theConnection.getInputStream())); System.out.println ("dati ricevuti dal client:"); String request = is.readLine(); System.out.println (request); if (request.indexOf("HTTP/")!=-1) { if (request.indexOf("GET /")!=-1) { int st, en; st = en = -1; st = request.indexOf("GET /") + 5; if (request.indexOf("html")!=-1) { en=request.indexOf("html") + 4; } else if (request.indexOf("htm")!=-1) { en=request.indexOf("htm") + 3; } else { // Spazio disponibile per gestire richieste differenti da .html e .htm, per es. l'invio di un'immagine o di un applet } if (debug) { System.out.println ( "start target: " + st + " - " + "end target: "+ en ); } try { if (debug) { System.out.println ( "page:*"+ request.substring(st,en) + "*" ); } filRequested = request.substring(st,en); } catch (StringIndexOutOfBoundsException e) { System.out.println ("errore parsing richiesta: " + e.toString()); errorResp (os); } } // end if while (true) { String thisLine = is.readLine(); System.out.println (thisLine); if (thisLine.trim().equals("")) break; } File flIn = new File (filRequested); System.out.println ("invio intestazioni MIME al client.."); // INIZIO INVIO INTESTAZIONE MIME + dati if (!flIn.exists()) { long lenTxt = errorPage.length(); Date now = new Date(); os.print ("HTTP/1.0 404 File not found\r\n"); os.print ("Date: " + now + "\r\n"); os.print ("Server: RUNServer 1.0\r\n"); os.print ("Content-length: " + lenTxt + "\r\n"); os.print ("Content-type: text/html\r\n\r\n"); errorResp (os); } else { long lenTxt = flIn.length(); Date now = new Date(); os.print ("HTTP/1.0 200 Ok\r\n"); os.print ("Date: " + now + "\r\n"); os.print ("Server: RUNServer 1.0\r\n"); os.print ("Content-length: " + lenTxt + "\r\n"); os.print ("Content-type: text/html\r\n\r\n"); invioPagina (os,filRequested, debug); } // TERMINE INVIO INTESTAZIONE MIME + dati } //end if } // end try catch (IOException e) { System.out.println("error 2: " + e); } } // end run // ------------ Metodo invioPagina -----------private void invioPagina (PrintWriter os, String pageRequested, boolean deb) throws IOException { FileInputStream fis = new FileInputStream (pageRequested); DataInputStream dis = new DataInputStream (fis); byte[] fisImage = new byte[dis.available()]; dis.readFully (fisImage); dis.close(); String pagina = new String (fisImage); if (pagina.indexOf ("Ora attuale")!=-1) { String pagina1 = pagina.substring (0,pagina.indexOf("Ora attuale")+ "Ora attuale".length()+1 ); String pagina2 = pagina.substring (pagina.indexOf("Ora attuale")+ "Ora attuale".length()+ 1, pagina.length() ); pagina = pagina1 + " " + new Date() + pagina2; } if (deb) { System.out.println (pagina); } os.println (pagina); dis.close (); theConnection.close (); } // end invioPagina // -------- Metodo errorResp --------private void errorResp (PrintWriter os) throws IOException { os.println (errorPage); theConnection.close(); } } // end class httpServer ⌡ 13.5.2 IL PROTOCOLLO HTTP -----------------HTTP (HyperText Transfer Protocol) e' un protocollo, usabile a livello applicazione, per gestire sul server le risorse ipertestuali. Con esso client e server si scambiano messaggi contenenti anche 'meta-informazioni' in un formato simile al MIME (Multipurpose Internet Mail Extensions), usato per la posta elettronica multimediale. Normalmente la comunicazione HTTP usa una connessione TCP/IP (su default port=80, se non diversamente specificato nell'URL). Esempio di URL su porta 80: "http://www.microsoft.com:80/index.html" La comunicazione tra client e server avviene in modalita' handshake: - Connessione (su TCP/IP port 80 o altro non riservato, specificato in URL) - Richiesta (messaggio inviato dal client) - Risposta (inviata dal server al client) - Close (chiusura della connessione fatta dal client o dal server) La richiesta contiene l'identificatore dell'oggetto richiesto, il comando (metodo) da applicare all'oggetto ed altre informazioni nel formato seguente (vedi www.w3.org/Protocols/HTTP/Request.html, CR+LF = Carriage Return + Line Feed = Terminatore di riga): Request = Simple o Full Simple = GET <URI> CR+LF Full = Metodo URI ProtocolVersion CR+LF [*<HTRQ Header>] [<CR+LF><Messaggio>] Metodo = Vers.Protoc.= URI = <Header> = <Messaggio> = <Comando> HTTP/1.0 (stabilisce il formato seguente) Identificatore (URL o URN) <FieldName> : <Value> <CR+LF> Oggetto, conforme al formato MIME (Multipurpose Internet Mail Extensions) Con l'identificatore URI indichiamo l'indirizzo URL (Uniform Resource Locator, che e' una stringa contenente il tipo di connessione necessario, l'indirizzo dell'host, il numero di porta opzionale ed il percorso del file richiesto) o il nome URN (Uniform Resource Name), per i server che risolvono gli URN Alcuni dei metodi possibili sono: GET : HEAD : PUT : POST : DELETE ecc. : : Scarica dal server i dati indirizzati dall'identificatore URI. Se questo indirizza un processo o uno script eseguibile viene scaricato l'output del processo e non il codice del processo o script. Come GET ma scarica solo l'Header senza il Messaggio Scrive il messaggio (oggetto) nell'URL specificato (che dev'essere gia' esistente) Idem, creando l'URL (come impostato dal client o assegnato dal server) Cancella sia l'oggetto che l'URL inviato ........... COMMERCIO ELETTRONICO --------------------Come esempio di programmazione distribuita su Internet in protocollo HTTP, viene presentata durante le lezioni un'applicazione di commercio elettronico che funziona nel modo seguente (gli indirizzi sono quelli del test delle esercitazioni): 1) Un cliente scarica dal sito di E.commerce la pagina index.html, http://localhost:4063/index.html nella quale con un form sceglie tra le opzioni acquistabili quella voluta e preme il button submit per lanciare l'ordine al servlet 'buyer' indicato nel campo 'action' del form 2) Il server riceve l'ordine (HTTP), avvia il servlet 'buyer' che verifica la correttezza dell'ordine, lo inserisce nel database (file acquisti.dat) e trasmette la conferma d'ordine come pagina HTML al client 3) Il magazziniere, con il browser di un computer client, indirizza il servlet apposito ('ordini') per evadere gli ordini http://localhost:4063/servlets/ordini che elimina l'ordine evaso dal database degli ordini da evadere e invia un'e.mail al cliente Tutti i codici risiedono nel sito che e' sul dischetto delle esercitazioni nella directory <ecom>. Si noti che, nel caso di un vero commercio elettronico in rete, tutta l'applicazione sarebbe istallata solo sul server. Sui client si potrebbero anche eseguire elaborazioni distribuite con le applet Java, scaricate di volta in volta dalla rete e quindi senza alcuna istallazione sui computer dei clienti (rete Extranet). L'esempio mostrato e' di commercio elettronico perche' stimola la curiosita' dello studente, essendo una delle tecnologie alla base della "new economy". Il tipo di elaborazione mostrata nell'esempio in realta' e' piu' generale di quanto non possa sembrare a prima vista, poiche' permette ad un utente (client) di lanciare richieste di elaborazione su un database allo stesso computer (server), dove il database risiede. Nell'ambiente dei fisici e' questa un'esigenza molto ricorrente, che puo' essere gestita con metodologie simili a quella vista ora con riferimento al commercio elettronico. Proteggendo adeguatamente gli accessi al server con certificati e firme digitali, magari crittografando anche i messaggi, e' possibile realizzare reti Intranet, in cui eseguire applicazioni di elaborazione distribuita e/o parallela a disposizione dei ricercatori dell'esperimento di fisica. Java permette cosi' lo sviluppo di elaborazioni distribuite tra server (con i servlet) e client (con le applet), dove applet e servlet possono comunicare tra loro anche usando l'interfaccia Socket. ⌡ 13.5.3 - POSTA ELETTRONICA ----------------Una delle applicazioni client-server piu' usate quella che realizza il servizio di posta elettronica, il cui uso e' ormai ben noto: - il mittente, con il suo software 'client' di posta elettronica (E-mail), contatta un computer con il software server di posta elettronica, richiedendo una connessione TCP; - appena la connessione e' instaurata le due applicazioni usano il protocollo SMTP (Simple Mail Transfer Protocol), con il quale il client si qualifica, indica il destinatario e invia il messaggio. Il messaggio puo' anche contenere informazioni di natura diversa dal testo ASCII, se il mittente include nell'intestazione una riga che indica l'uso dello standard MIME (Multipurpose Internet Mail Extensions) e nel corpo del messaggio le informazioni aggiuntive che permettono ai destinatari di interpretare il contenuto. Gli indirizzi di posta elettronica hanno il formato mailbox@computer dove: - 'computer' permette al software del computer mittente di identificare il computer destinatario; - 'mailbox' permette al software del server destinatario di individuare la casella postale, a cui corrisponde uno spazio sul disco. Un indirizzo E-mail, oltre che ad una casella postale, puo' anche corrispondere ad un programma. In tal caso il messaggio viene recapitato al programma indirizzato (per esempio un'interrogazione di un database), il quale lo elabora e rispedisce al mittente la risposta, sempre via E-mail. Per permettere l'accesso remoto alle caselle postali del server, questo deve avere costantemente attiva una speciale applicazione (server POP per accesso remoto), che risponde alle chiamate telefoniche dei computer client e, una volta accertatane l'identita' (richiedendo user_name e password), consente loro di instaurare la connessione. Per accedere alla casella postale viene usato in questo caso il POP (Post Office Protocol), che e' un protocollo della famiglia TCP/IP. ⌡ 13.5.4 - TRASFERIMENTO DI FILES (FTP) ---------------------------La famiglia TCP/IP mette a disposizione due protocolli per eseguire il trasferimento di files tra due computer: uno piu' completo (FTP, File Transfer Protocol) e l'altro invece ridotto all'essenziale (TFTP, Trivial File Transfer Protocol). Il servizio viene svolto da due applicazioni che funzionano secondo il modello client-server. Con il protocollo TFTP la comunicazione avviene attraverso connessioni UDP, mentre con FTP si usano connessioni TCP. L'utente che vuole eseguire un'applicazione FTP client, deve instaurare una connessione di controllo con l'FTP server, autenticando la propria identita' con login e password (e' anche possibile creare un account 'anonymous', quando si vuole consentire l'accesso ad utenti non registrati). La connessione di controllo rimane attiva finche' non viene eseguito esplicitamente il comando di chiusura. Attraverso la connessione di controllo si inviano i comandi, mentre per il trasferimento dei dati si usa una seconda connessione dedicata, con numero di porta diverso, che viene chiusa automaticamente al termine del trasferimento. La comunicazione FTP lungo la connessione puo' essere automatica (batch), come nel caso delle applicazioni di posta elettronica (interfaccia MIME per il trasferimento di informazioni multimediali) o interattiva. In questo caso un apposito interprete di comandi permette all'utente di eseguire, sia sul computer client che sul server, comandi standard del file system come: visualizzare files e cartelle sul client o sul server, trasferire files in formato testo o binario, ecc. I comandi dell'utente vengono prima interpretati dal client FTP. Se sono comandi locali vengono eseguiti senza comunicare con il server, altrimenti il client compone la richiesta di servizio al server e gliela invia secondo il protocollo FTP. Il server riceve, riconosce ed esegue il comando, rispondendo con lo stesso protocollo. Il servizio TFTP (FTP banale) funziona invece solo in modalita' batch, consente il solo trasferimento di file tramite UDP (quindi non instaura una connessione TCP) e non prevede ne' login ne' password (i files trasferibili risultano di dominio pubblico). ⌡ 13.5.5 - FILE SYSTEM DI RETE (NFS) ------------------------Oltre ai servizi di trasferimento dei files tra computer, la famiglia dei protocolli TCP/IP mette a disposizione anche NFS (Network File System, sviluppato da Sun e diventato ormai uno standard de-facto). Si tratta di una ulteriore funzionalita' di accesso diretto ai files di un computer remoto, che permette alle applicazioni su un client NFS di leggere, modificare o copiare porzioni di file, permettendo anche la loro condivisione in rete. Questa funzionalita' NFS, che consente l'accesso diretto ai file di rete attraverso TCP/IP, viene integrata nel file system del sistema operativo del computer (non e' un'applicazione come FTP). In questo modo con NFS un'applicazione dell'utente puo' manipolare direttamente anche files non locali: per esempio, per modificare una piccola porzione di un grande file remoto, il client NFS intercetta il comando al disco (chiamata di sistema), lo interpreta e se riconosce che il file indirizzato e' remoto (non locale) invia dati e comando di scrittura al server NFS, dove e' residente il file; questo esegue la scrittura ed invia indietro al client la risposta di conferma della corretta esecuzione del comando. In questo modo NFS funge da tramite per eseguire il comando sul computer remoto, in modo trasparente alla applicazione. Si evita cosi' il trasferimento del grande file: in rete vengono trasmessi solo i pochi dati da scrivere. La condivisione dei files in rete e' anche resa possibile, richiedendo l'accesso esclusivo ad un file. Gli altri client possono cosi' accedere al file solo quando l'accesso esclusivo viene rilasciato. L'integrazione di NFS nel sistema operativo del computer consente alle applicazioni di lavorare in modo trasparente sui files remoti. Per ottenere cio' nella configurazione di NFS si crea sul computer client una particolare cartella, che viene associata al computer remoto dove sono i files condivisi. Questa cartella riproduce un'immagine esatta del file system del computer server NFS. Quando un'applicazione accede ad un file su questa cartella speciale, il sistema client-server NFS si fa da tramite per eseguire sul computer remoto l'operazione richiesta, aggiornando anche la cartella locale. ⌡ 13.6 INTERNET ED IL WORLD WIDE WEB ----------------------------L'origine delle reti geografiche risale agli anni '60 quando il ministero della Difesa USA creo' l'ARPA (Advanced Research Projects Agency). Progressivamente s'inserirono universita' e organizzazioni di ricerca civili creando la rete Arpanet sulla quale fu presto definito il protocollo TCP/IP per permettere la connettivita' di macchine molto diverse tra loro. Negli anni '60 e '70 furono sviluppate sia reti LAN (Local Area Network) che WAN (Wide Area Network), ma con tecnologie di interconnessione tra loro incompatibili, per cui lo scambio di informazioni poteva avvenire solo all'interno di una stessa rete. Internet e' stata sviluppata per consentire l'interconnessione tra reti diverse e lo scambio di informazioni tra utenti, indipendentemente dai computer o dalle reti utilizzate. Questa connettivita' e' stata realizzata aggiungendo nella rete speciali computer chiamati 'router' con il compito di connettere LAN e WAN di tipo diverso. Ad essi si e' aggiunto anche un protocollo comune che uniformasse il metodo di trasmissione dei dati, il protocollo TCP/IP. Cosi' e' nata la rete delle reti: Internet. La nascita di Internet puo' essere collocata nel 1983, quando da Arpanet si distacco' la sezione militare, creando la sua rete Milnet. Ma e' dopo il 1990, con il progetto WWW del CERN (World Wide Web, il metodo ipertestuale e intuitivo per accedere alle risorse su Internet), che nasce Internet com'e' oggi, con i browser HTML per la navigazione su Web, il primo dei quali (Mosaic) nasce nel 1993. Con esso inizia la crescita esponenziale di Internet, mostrata in figura: N 30M 25M 20M 15M 10M 5M ^ │ ├ │ │ ├ │ │ ├ │ │ ├ │ │ ├ │ │ ├ │ │ . . Crescita esponenziale del numero di calcolatori collegati a Internet X . . . . . X . . X . . X X . X . X . X . X 0M └──X───X───X───X───X───X───X───┴───┴───┴───┴───┴───┴───┴───┴───┴── ─> 1984 1986 1988 1990 1992 1994 1996 1998 Anno Sia per l'elevato numero di computer collegati, sia perche' il percorso di una connessione con un sito puo' essere variata dai computer istradatori (router) non e' possibile avere su Internet una visione della rete come quella che si ottiene nella rete locale con 'Risorse di rete'. Per questo sono stati predisposti programmi, usati dagli amministratori delle reti, che inviano messaggi sulla rete e ne esaminano le risposte ricevute. Uno dei piu' semplici e' ping, che si usa eseguendo (per esempio): ping www.sun.com Altri programmi (per esempio 'traceroute'), inviando ripetutamente verso il sito specificato messaggi (datagrammi IP) con un tempo di vita via via crescente, ricevono in risposta messaggi di errore (per time-out, in protocollo ICMP) dai vari router fino al sito indirizzato e possono cosi' tracciare il percorso della connessione (uno di questi e' scaricabile dal sito http://shareware.cnet.com. Il World Wide Web costituisce un enorme deposito di informazioni a cui gli utenti possono accedere tramite dei programmi interattivi chiamati browser (sfogliatore di pagine). Questi sono applicazioni molto complesse (percio' non ne esamineremo i dettagli), che ricevono dall'utente una stringa URL (Uniform Resource Locator) nel formato: protocollo://computer:porta/file (il parametro :porta e' opzionale ed in genere viene omesso). Da questa stringa di caratteri il browser puo' ricavare: il nome del documento richiesto dall'utente (file), il nome del server dove risiede ed il protocollo da usare. I protocolli piu' usati, al livello di applicazione, sono HTTP (HyperText Transport Protocol) ed FTP (File Transfer Protocol); entrambi si avvalgono dei protocolli di trasporto TCP/IP. Il browser puo' quindi richiedere al server HTTP di ricevere il documento richiesto dall'utente e di visualizzarlo su video in modo corretto. Per ottenere un'indipendenza dalla piattaforma (computer, scheda video, ecc.) dell'utente (tutti possono accedere) si e' introdotta una codifica dei testo a marcatori HTML (HyperText Markup Language): in questo modo si danno al browser delle indicazioni di come impaginare il documento, lasciando al browser l'implementazione nella particolare piattaforma di queste indicazioni. Vedremo in seguito come questo avviene. Internet, la rete delle reti, costituisce cosi' una rete fisica mondiale di computer connessi a vari livelli, indipendente dalle diverse piattaforme hardware, dai sistemi operativi e dalla localizzazione geografica. I vari computer della rete sono in grado di riconoscersi e di scambiarsi dati, grazie all'uso degli stessi protocolli di comunicazione. Ogni computer client si collega direttamente con un computer server di un gestore della connettivita' Internet (Societa' di servizi, chiamate 'provider'). Questi server gestiscono i servizi di rete, come FTP, HTTP, Gopher, WAIS (per accesso a database su Internet) e quelli sulla sicurezza: nelle sottoreti aziendali c'e' anche un 'Proxi' software che mette in atto un servizio di protezione dell'accesso dall'esterno alle risorse della sottorete aziendale, chiamato firewall. Il server a cui l'utente e' direttamente collegato (quello del provider) e' dedicato al riconoscimento delle chiamate nel giusto protocollo. Pero' nella maggior parte dei casi la richiesta che gli arriva dal browser del client e' diretta ad un altro server della rete mondiale Internet, dove il documento richiesto dall'utente risiede. Il server deve allora istradare la richiesta sulla inter-rete (Internet) per farla arrivare a destinazione. Questo server in genere e' connesso nella rete locale del provider ad un altro computer (access server), che attraverso un router (istradatore), con apposita interfaccia (per esempio X25, PAD, ecc.) si collega agli altri server dei nodi di comunicazione della rete mondiale Internet (vedi la descrizione delle inter-reti e del protocollo TCP/IP). Ulteriori dettagli del funzionamento sistemistico di Internet, come pure della descrizione delle sue funzionalita', esula dagli scopi di questo corso. I tipi di collegamento possibili ad Internet sono: 1) Collegamento permanente: con un 'router' ed una linea telefonica dedicata si puo' collegare la propria rete LAN ad un host di un 'Internet access provider' (il router tradurra' i protocolli LAN/WAN e istradera' il traffico tra le due reti). 2) Collegamento diretto su linea commutata: e' quello solitamente usato da casa con protocollo PPP (che include la correzione degli errori a livello di frame, evitando le ritrasmissioni). 3) Collegamento ad una rete privata via Internet (rete privata virtuale VPN, Virtual Private Network), possibile solo se il client dispone di un nome o indirizzo IP. 4) Collegamento in sola posta elettronica (come utenti di un servizio BBS=Bulletin Board System) 5) Collegamento in emulazione di terminale: il software fa comportare il computer client come un terminale di un computer server, permettendo cosi' all'utente di lavorare sul computer server in maniera 'remota'. 6) Server privato di accesso remoto, che accetta connessioni da client remoto via modem o Internet. Essendo questa materia particolarmente ricca di sigle e nozioni, si ritiene utile descrivere alcune sigle e nozioni di interesse per gli studenti in Appendice A. !----------------------------------------------------------------------CAPITOLO 14 - Programmazione ad oggetti e linguaggio Java ═══════════ Riassunto: In questo capitolo vengono spiegate le motivazioni principali che hanno portato all'introduzione della programmazione ad oggetti e viene introdotto il linguaggio Java, utile per scrivere sia applet che vere e proprie applicazioni. ⌡ 14.1 - INTRODUZIONE ============ Iniziamo questo capitolo risulteranno utili anche per il quale e' previsto sviluppo Java (JDK, Java con una serie di informazioni preliminari, che per organizzare il lavoro delle esercitazioni, lo scaricamento dalla rete dell'ambiente di Development Kit). ⌡ 14.1.1 - PREMESSA -------Nel corso di Calcolatori Elettronici il linguaggio Java e' usato come esempio di un linguaggio moderno, orientato agli oggetti, che pur assomigliando al C++, e' relativamente piu' semplice da apprendere. Come linguaggio orientato agli oggetti, Java supporta il multitasking, cioe' la capacita' di elaborare simultaneamente piu' task, chiamati 'thread' (quindi si parla di multi-threading): per capirne l'utilita' * basta pensare alle animazioni nelle pagine Web (prodotte con Java applet). Il linguaggio di programmazione Java e' stato sviluppato da Sun (vedi il sito Internet di Sun: www.java.sun.com), l'azienda che nel 1991 per prima ha iniziato a progettare il precursore di Java, un piccolo linguaggio per microcomputer, da utilizzare in piccoli dispositivi come gli elettrodomestici. Per la progettazione di questo linguaggio gli autori trassero spunto dal linguaggio UCSD Pascal, che Niklaus Wirth aveva lanciato commercialmente nel mondo dei primi PC (Apple II) alla fine degli anni '70 e che produceva un codice intermedio che poteva essere eseguito da qualsiasi computer che fosse dotato dell'interprete per quel codice. L'inizio della vera costruzione del linguaggio Java pero' risale all'autunno 1995, quando Netscape decise di abilitare all'uso di Java il suo browser Navigator 2.0. Subito dopo, nel gennaio 1996, Sun rilascio' la prima versione di Java (ancora poco utilizzabile pero' per scopi professionali). Bisogna attendere il 1998 per l'annuncio della versione Java 2 ed il 1999 * per la sua disponibilita' come kit JDK 1.2 (attualmente e' scaricabile dal sito di Sun la versione JDK 1.2.2). Gli esempi riportati in queste dispense sono prevalentemente ispirati dalla guida ufficiale di Sun Microsystems (ci si e' attenuti quanto piu' possibile al testo per consentire al lettore di potervi fare riferimento con facilita' per gli approfondimenti voluti): Cay S.Horstman, Gary Cornell - Fondamenti di JAVA 2 - Mc Graw-Hill Un differente approccio all'apprendimento del linguaggio di programmazione Java si trova nel testo: Cadenhead - Java programming in 24 hours - Ed. Sams Net Entrambi i testi sono dotati di CD-ROM contenente l'ambiente di sviluppo Java. Scopo delle dispense e' di fornire solo una selezione degli argomenti di maggiore interesse per il corso, tenendo presente che il nostro non e' un corso di tecnica della programmazione e che lo spazio disponibile per lo studio della programmazione Java e' molto limitato. Per questo motivo lasceremo alla buona volonta' dello studente gli approfondimenti del linguaggio Java, quali gli argomenti relativi ai 'thread' per il controllo degli eventi AWT, la programmazione grafica, i componenti Swing per l'interfaccia utente, ecc. ⌡ 14.1.2 - PREPARAZIONE DELL'AMBIENTE PER LE ESERCITAZIONI ----------------------------------------------Gli esempi piu' significativi riportati in queste dispense sono tutti nel dischetto delle esercitazioni fornito dal docente e saranno usati nelle ore di laboratorio. Gli studenti sono incoraggiati a scaricare direttamente dal sito di Sun gli strumenti di sviluppo della programmazione Java piu' recenti con la relativa documentazione (messi a disposizione gratuitamente per tutta la durata del corso): Java Development Kit (JDK), ecc. Il software Java 2 SDK e' scaricabile dal sito: http://developer.java.sun.com/developer/earlyAccess/j2sdk13/ I requisiti minimi del computer con Windows95/98/NT sono: Pentium 166MHz, 32MB RAM e 65MB di spazio libero su disco Si tenga presente che, data la rapida evoluzione di questo campo, la rete (vedi www.java.sun.com) dev'essere considerata la sorgente primaria per * l'acquisizione delle informazioni su Java, poiche' la carta stampata ha tempi di pubblicazione troppo lunghi, cosicche' al livello di progresso tecnologico attuale risulta utile solo per un apprendimento istituzionale dei concetti di base (vedi i testi consigliati). La documentazione Java (Tutorial) e' scaricabile dal sito: http://www.java.sun.com/docs/books/tutorial/information/download.html La documentazione API (Application Programming Interface) e' consultabile invece sul sito /jdk/docs/api/index.html Altri siti d'interesse si trovano elencati nel file README presente nella directory jdk1.2.2 ATTENZIONE: Se si scarica la versione beta 1.4 bisogna sostituire dovunque, in queste dispense: \jdk1.2.2\... ----> \jdk1.4\... * CONSIGLIO: Si operi contemporaneamente su due finestre DOS di Windows. Gli esempi riportati in queste dispense possono cosi' essere eseguiti nella prima finestra DOS, mentre il testo delle dispense che li descrive puo' essere visualizzato nella seconda finestra sullo stesso monitor. Gli strumenti del JDK (Java Development Kit), scaricati dal sito della Sun, operano in una finestra DOS, cioe' utilizzando il file system del sistema operativo Windows, tramite i comandi dell'interprete COMMAND.COM (senza interfaccia grafica). Dopo aver scaricato la versione JDK ed averla decompressa ed istallata, semplicemente con un doppio click sull'icona (con tutte le applicazioni chiuse; se vi serve WinZip potete scaricarlo in versione shareware dal sito www.pkware.com o dal CR-ROM del libro), si avra' la directory C:\jdk1.2.2 all'interno della quale, nella subdirectory 'bin', si troveranno il compilatore 'javac.exe' e l'interprete del bytecode 'java.exe': c:\jdk1.2.2\ | +----\bin\ Programmi eseguibili jdk (javac, java, ecc.) | | | +---\ CognomeStudente\ Programmi dello studente | +---\demo\ | +---\include\ | +---\jre\ | +---\lib\ Per non mescolare i file di esercizi, su cui uno studente lavora, con quelli del jdk si raccomanda di creare all'interno di bin una subdirectory con il proprio cognome (CognomeStudente): basta cliccare sul pulsante destro e scegliere Nuovo e Cartella (New e Folder in inglese), battendoci poi il proprio cognome. Si lavorera' poi sempre dentro questa propria directory (un altro studente che lavorera' su quel computer operera' cosi' solo all'interno di un'altra directory: quella con il suo cognome). Dopo aver aperto una finestra DOS (Start-->Programmi-->Prompt di MS-DOS) i comandi da eseguire per cominciare a lavorare sono: CD \jdk1.2.2\bin MD ROSSI (Crea la directory ROSSI che conterra' solo il lavoro di Rossi) CD ROSSI Le volte successive lo studente Rossi all'inizio del lavoro, potra' mettersi nella sua sub-directory con il comando CD \jdk1.2.2\bin\ROSSI. Egli dovra' anche rendere accessibili ai programmi del jdk la biblioteca dei programmi (*.exe) e delle classi (*.class) con le seguenti istruzioni batch: PATH=c:\;c:\windows;c:\windows\command;c:\jdk1.2.2\bin set CLASSPATH=c:\jdk1.2.2;c:\jdk1.2.2\InPackage;c:\jdk1.2.2\bin\ROSSI c: cd \jdk1.2.2\bin\ROSSI Queste possono essere salvate, per comodita', in un proprio file batch SETPATH.BAT, da mettere nella directory c:\jdk1.2.2\bin\ROSSI, per poter essere piu' facilmente rieseguite con il comando batch SETPATH. I files batch .BAT presenti nel dischetto delle esercitazioni si riferiscono al jdk1.3 e vanno usati solo come esempi di riferimento. N.B. IN NESSUN CASO SI DEVE ALTERARE IL FILE 'AUTOEXEC.BAT' DEL COMPUTER ! ******************************************************************* A questo punto lo studente Rossi e' pronto per operare in Java nella sua finestra DOS, provando il programma PrimoEsempio con i passi seguenti: 1) Scrivere il programma sorgente Java con un editor (per esempio partendo dall'esempio PrimoEsempio sul dischetto). Se il programma e' una classe pubblica ed ha il nome PrimoEsempio (cioe' public class PrimoEsempio{...} ecc.) va salvato con lo stesso nome ed estensione .java, cioe': PrimoEsempio.java 2) Compilare questo file sorgente, eseguendo javac.exe, con il comando: javac PrimoEsempio.java Se non ci sono errori, la compilazione del file sorgente produce il file PrimoEsempio.class (bytecode), altrimenti vengono segnalati gli errori e si torna al punto 1 per la correzione battendo: edit PrimoEsempio.java (riferirsi al n. di riga in basso) 3) Se non ci sono errori, si puo' eseguire il programma compilato, avviando l'interprete java del bytecode con il comando: java PrimoEsempio (.class si omette) Il kit JDK e' molto sensibile alla struttura delle directories e quindi alle impostazioni delle path (vedi libro a pag.693). Per controllarle si puo' battere PATH e Invio. Per ripristinare le impostazioni originarie riavviare il sistema. Elenco dei programmi Java forniti sul dischetto delle esercitazioni: SOURCE FILE (.java) ------------------PrimoEsempio SecondoEsempio DESCRIZIONE ----------Semplice programma di benvenuto Esempio di programma con output di testo e numeri TerzoEsempio Root NewRoot Test0 QuoteLotto Riordina TestTry SwapTest SwapTest2 VirusLook EmployeeTest Parla FileTest FileCopy InKeyb ProvaBug H6809h Esempio con array di stringhe Semplice programma per stampa radice quadrata Semplice programma con if Semplice programma per provare istruzioni Esempio con input da tastiera e chiamata di metodo Esempio con passaggio di array come parametri Esempio di programma con gestione degli errori Esempio con scambio parametri che non funziona Esempio con scambio parametri che funziona Esempio tipico di programmazione ad oggetti Esempio tipico di programmazione ad oggetti Esempio di programma ad oggetti con ereditarieta' Esempio di programma con scrittura su disco Esempio di programma con I/O su disco Esempio di Package (InPackage) per input da tastiera Programma con 2 errori da rimuovere Programma Monitor scritto in Java (con oggetto Byte) ESEMPI DI APPLET: PrimoApplet.java PrimoApplet.html SecondoApplet.java SecondoApplet.html Esempio di Applet Esempio di Pagina HTML per provare Applet Esempio di Applet (con passaggio di parametri) Es. di pag. HTML per test Applet (con pass.parametri) ESEMPI GUI: FrameTest.java CloseTest.java IconTest PulsTest.java TestEvent.java Esempio di apertura di un frame Esempio di apertura di un frame Esempio di apertura di un frame Esempio di apertura di un frame Es. di apertura di un frame con COMUNICAZIONE IN RETE: httpServer.java Esempio di realizzazione di un server HTTP chiudibile con immagine con 3 pulsanti eventi (3 pulsanti) Inoltre, se si dispone del CDROM del testo (Horstmann), i sorgenti di tutti gli esempi riportati nel libro sono anche disponibili nella directory del package \jdk1.2.2\corejava (con le indicazioni di volume e capitolo del libro), per cui non occorre perdere tempo per digitare gli altri sorgenti degli esempi presenti sul libro. Siccome per comodita' in Java si usano spesso nomi lunghi (che renderebbero scomoda la digitazione dei comandi DOS), si ricordi che e' possibile lanciare velocemente editing, compilazione ed esecuzione di un programma come QuoteLotto.java nella finestra DOS ricorrendo ad uno dei due modi di lavoro seguenti: a) Eseguire l'utility DOSKEY, che memorizza la cronologia dei comandi battuti su tastiera e li ripresenta battendo 'freccia su' e 'freccia giu' (battendo i primi caratteri del comando e poi F8 questo viene trovato e completato); per inserire DOSKEY stabilmente potete mettere DOSKEY /INSERT nell'AUTOEXEC.BAT del vostro computer di casa (non in quello del laboratorio). b) Creare i 3 files batch seguenti di 1 sola riga nella directory di lavoro (esempio: \jdk1.2.2\bin\ROSSI): FILE ---e.bat c.bat r.bat RIGA (esempio) -------------edit QuoteLotto.java javac QuoteLotto.java java QuoteLotto COMANDO DOS (ABBREVIATO) -----------------------e (edit) c (compila) r (run) Si possono inoltre usare contemporaneamente due diverse finestre DOS, una per editare le correzioni e l'altra per lanciare facilmente la compilazione con il tasto di richiamo del precedente comando di compilazione. Se il package InPackage (libreria delle classi pubbliche per input da tastiera), oppure Corejava importato da molti file delle esercitazioni del libro, non sono raggiungibili dalla directory corrente, si deve eseguire l'istruzione: set CLASSPATH=c:\jdk1.2.2\bin\InPackage (oppure Corejava) (che e' inserita nel file batch SETPATH.BAT sul dischetto degli esercizi). N.B. Volendo, l'editor EDIT della finestra DOS puo' anche caricare piu' di un programma in memoria (fino a 9) ed operare passando dall'uno all'altro con i tasti Alt-1, Alt-2, ecc. Un metodo di lavoro alternativo viene fornito dall'editor TextPad, fornito shareware con il jdk1.2.2 da Sun. Con esso si puo' fare editing e poi avviare compilazione e run (del JDK), con i tasti Ctrl-1 e Ctrl-2. ⌡ 14.1.3 - PRESENTAZIONE DI JAVA --------------------Java e' un linguaggio moderno, orientato agli oggetti, che pur assomigliando * al C++, e' relativamente piu' semplice da apprendere. E' inoltre un linguaggio molto portabile, in grado di operare su ogni piattaforma, come serve sul Web (l'ingresso di Java su Internet risale al 1994, anno in cui SUN sviluppo' il primo browser scritto interamente in Java, chiamato HotJava). La programmazione ad oggetti si presenta come una metodologia di fondamentale importanza, soprattutto nei casi di lavoro di piu' persone * in collaborazione su progetti di notevole entita'. I linguaggi ad oggetti, come Java, spingono a rispettare la disciplina di programmazione necessaria in questi casi, facilitando cosi' il lavoro, riducendo il 'debugging' necessario e migliorando la manutenibilita' dei sistemi. Il processo di creazione, compilazione ed esecuzione di programmi Java avviene nel modo seguente. Il codice sorgente Java viene: - scritto con un normale editor; - compilato con un programma compilatore (javac), specifico della macchina su cui si lavora (es. Pentium, Power PC, SPARC). La compilazione fornisce un codice binario in un formato standard, che puo' essere riconosciuto ed eseguito da un interprete Java, in modo indipendente dalla piattaforma, in quanto la specificita' della piattaforma (processore, sistema operativo, ecc.) risulta contenuta esclusivamente nel programma interprete JVM (Java Virtual Machine). A differenza di quanto accade con gli altri linguaggi, come C++ e Pascal, il codice risultante dalla compilazione Java non e' quindi formato dalle istruzioni binarie della macchina su cui si lavora, ma da uno speciale codice standard, chiamato Bytecode, attraverso il quale * si ottiene l'indipendenza dalla piattaforma, che e' cosa importantissima per l'uso dei programmi sul Web); - eseguito da un interprete Java (JVM), specifico della macchina ospite, che puo' anche far parte del browser di accesso ad Internet; I programmi Java si dividono in due categorie: 1) APPLICAZIONI: sono programmi autonomi sviluppati in Java, eseguibili sulla macchina dell'utente (sotto interprete Java o anche in forma eseguibile, dopo la compilazione in istruzioni macchina); 2) APPLET: sono programmi (Java applets) scaricati, all'interno di pagine Web, da un server remoto dove risiedono, ed eseguiti sulla macchina locale all'interno del browser (che deve contenere l'interprete Java), quando l'utente accede alle pagine Web che contengono applets Java. In questo modo si puo' programmare sul Web, rendendo interattive le sue pagine (vedi la classe URL di Java, per accedere al Web: sul testo Horstmann ⌡10.4, pag.549). ⌡ 14.1.4 - UN PRIMO ESEMPIO ---------------- Riferiamoci come esempio alla semplice applicazione seguente: /* * Questo e' un esempio di * commento su piu' righe */ public class PrimoEsempio { public static void main (String[] args) { System.out.println("Benvenuto nell'ambiente di programmazione Java"); } } // Fine del programma Possiamo fare le seguenti osservazioni iniziali: * 1) Java fa distinzione tra Maiuscole e minuscole (l'ambiente DOS no); 2) Usa // per commento su 1 riga; /* ... */ per blocco di righe di commento (non annidabile con altri /* ... */ interni al blocco); si puo' anche usare /** ... */ per la generazione automatica di documentazione con l'utility javadoc; 3) { } definiscono un blocco del programma (come la coppia BEGIN...END in Pascal); per batterle, se sulla tastiera manca il tasto, fare Alt-123 e Alt-125 sul tastierini numerico; 4) Il programma viene chiamato 'class'; i sottoprogrammi si chiamano 'metodi'; 5) La parola chiave 'public' (modificatore di accesso) determina quali altre parti del programma possono avere accesso a questa classe; 6) Il nome della classe (PrimoEsempio) non dev'essere una parola riservata e per convenzione (comunemente usata) comincia con una lettera maiuscola seguita da lettere o numeri (lunghezza qualsiasi), mentre i nomi dei metodi iniziano con lettere minuscole; 7) Il nome del file su disco (sorgente) dev'essere uguale al nome della classe con estensione .java (nell'esempio dev'essere PrimoEsempio.java), se public; 8) La riga { System.out.println("Benvenuto..."); } costituisce la chiamata al metodo 'println' dell'oggetto 'System.out' con argomento (=parametro) "Benvenuto...". Questo blocco, costituente il corpo del programma PrimoEsempio, e' fatto di una sola istruzione (terminata dal punto e virgola). Esiste anche il metodo 'print' dell'oggetto 'System.out' che non avrebbe aggiunto il ritorno a capo e va usato per produrre piu' output sulla stessa riga (sono gli analoghi delle procedure 'writeln' e 'write' gia' incontrate in Pascal). La sintassi usata in questo caso di chiamata al metodo 'println' dell'oggetto System.out e' un esempio di quella valida in generale per la chiamata ai metodi (=sottoprogrammi) degli oggetti. Cioe' per chiamare il metodo di un oggetto si dovra' sempre scrivere: Oggetto.metodo(parametri) 9) La compilazione del file sorgente PrimoEsempio.java si puo' avviare, come gia' detto, in una finestra DOS con il comando: javac PrimoEsempio.java Se non ci sono errori, la compilazione del file sorgente produce il file PrimoEsempio.class (bytecode); 10) L'esecuzione del programma compilato si otterra' invece avviando l'interprete java con il comando: java PrimoEsempio (.class si omette) 11) All'avvio dell'applicazione PrimoEsempio il controllo viene passato al metodo main, che acquisisce eventuali parametri via String[] args (cioe' args[0] , args[1] , ecc.: vedi l'esempio NewRoot piu' oltre); 12) Se invece di un'applicazione si programma un'applet, questa sul WEB e' prima downloaded (come bytecode) insieme alla pagina HTML che la contiene e poi eseguita sulla macchina client dall'interprete contenuto nel browser; per la messa a punto della programmazione dell'applet l'ambiente di sviluppo (JDK) mette a disposizione un apposito programma (chiamato 'appletviewer'), che sostituisce l'interprete Java del browser sulla macchina client, permettendo un collaudo rapido, con l'uso di un apposito file HTML, in cui l'applet viene inserita (evita cioe' di dover istallare la pagina in rete, sul server Web, per poter provare il programma dell'applet); vedremo Root e RootApplet come esempi di cio'. 13) La parola chiave 'void' si mette nei metodi che non hanno paramentri in uscita, ma che possono variare attributi di classe o di oggetto (=COMMON). Senza 'void' equivalgono alle FUNCTION del Pascal; con 'void' sono equivalenti alle PROCEDURE del Pascal. ⌡ 14.1.5 - Come si lavora in JAVA: ----------------------I programmi Java sono orientati agli oggetti ( OOP = Object Oriented Programming). Questo significa (come spiegheremo meglio poi) che in JAVA si programmano le classi, che contengono oltre ai dati (attributi dell'oggetto), anche i metodi (sottoprogrammi che definiscono le funzionalita' dell'oggetto). Per esempio posso scrivere una classe Cane, che definisce gli attributi ed i metodi applicabili agli oggetti 'cane'. In un metodo (costruttore), con le istruzioni 'new', si istanziano gli oggetti, ovvero si alloca loro memoria per poterci lavorare (da quel momento in poi): cioe' con la classe Cane istanzio gli oggetti con cui il mio programma dovra' lavorare (per esempio Cocker e Boxer). Quindi in Java prima si creano gli oggetti (=istanze della classe), usando * le istruzioni 'new'; ogni oggetto istanziato viene identificato con la sua variabile oggetto (=nome identificatore dell'oggetto). Poi gli si attribuisce lo stato iniziale e quindi si lavora con gli oggetti (come fossero nuovi tipi di variabili), referenziandoli con il nome della variabile oggetto ed eseguendo chiamate ai metodi delle classi. OGGETTO = Nuovo tipo di variabile (definito con la class) che comprende oltre ai dati (=attributi dell'oggetto) anche le sue funzionalita' (=metodi dell'oggetto) CLASSE = Programmi che rappresentano la definizione formale di un oggetto (main = inizio del 'main program', e' quello lanciato al RUN) |--> ATTRIBUTI (dati, parametri in/out) OGGETTO-->| (CLASS=Master copy dell'oggetto) |--> METODI (Funzionalita', comportamento, istruzioni) Esempio: public class Cane <---- class usabile per creare oggetti 'Cane' { String name; <---- crea string variable 'name'=ATTRIBUTO public void parla() <---- parla()=METODO (=comportamento dell'oggetto) { System.out.println("Bau! Bau!"); } <--- fine sottoprogramma parla (=Metodo di Cane) } <--- fine programma Cane (=Master copy degli oggetti 'Cane') Uso: CREAZIONE DELL'OGGETTO 'Cocker': Cane Cocker= new Cane(); CREAZIONE DELL'OGGETTO 'Boxer' Cane Boxer = new Cane(); ASSEGNAZIONE DEGLI ATTRIBUTI A 'Cocker': Cocker.name="Chicca"; USO DELLA VARIABILE 'name' dell'oggetto 'Cocker':... Cocker.name ... RICHIAMO DEL METODO 'parla' dell'oggetto 'Cocker': Cocker.parla(); Esempio: l'istruzione Cocker.parla(); produce in output "Bau! Bau!") <-Oggetto-><-metodo-> Attenzione: il termine 'oggetto' viene di solito usato indifferentemente per indicare sia la classe (oggetto Cane), che le istanze della classe (oggetti Cocker, Boxer): questo non deve generare confusione. Nell'esempio considerato Cane e' l'analogo del 'type', mentre Cocker e Boxer sono le variabili-oggetto usabili per referenziare le due istanze dell'oggetto Cane, definito dalla classe sopra esemplificata (master copy dell'oggetto, cioe' matrice con cui creare le varie istanze di Cane, che sono quelle con cui lavora l'applicazione). Queste istruzioni vengono usate nella classe Parla.java (descritta anche piu' oltre come esempio di ereditarieta'), presente sul dischetto delle esercitazioni. La riportiamo anche qui' come esempio di programmazione con due oggetti Cane e Uomo, dei quali prima si creano le istanze Studente e Cocker (con 'new'), poi si elaborano (nel main), referenziandole con le rispettive variabili-oggetto Studente e Cocker (esempio: Cocker.dorme ). La procedura seguita (nel main) e' quindi: 1) Si creano le due istanze di Cane e Uomo e si definiscono le relative variabili-oggetto Studente e Cocker; 2) si elaborano le due istanze definendo il valore dell'attributo 'name' * (comune a entrambi gli oggetti) ed usando i metodi 'parla' (diverso nei due oggetti) e 'dorme' (comune a entrambi gli oggetti). /* ==== Esempio di definizione di oggetti (con ereditarieta') ==== * La routine con il metodo 'dorme' e' comune a Cane e Uomo * (e' cosi' scritta una sola volta) */ class Parla { // --- Classe (madre) degli oggetti Dormiente (con metodo 'dorme') --public static class Dormiente { String name; public static void dorme() // <--- metodo dorme (comune a Cane e Uomo) { System.out.println ("zzzzzzzzzzzzzz"); } } // Fine della classe Dormiente // --- Classe (figlia) degli oggetti 'Cane' con metodo 'parla'--public static class Cane extends Dormiente { public static void parla() // <--- Metodo parla di Cane { System.out.println("Bau! Bau!"); } } // --- Classe (figlia) degli oggetti 'Uomo' con metodo 'parla'--public static class Uomo extends Dormiente { public void parla() // <--- Metodo parla di Uomo { System.out.println("Nel mezzo del cammin di nostra vita...."); } } // --- Programma principale --public static void main (String arguments[]) { // --- Definiz.oggetti Cocker e Studente --Cane Cocker = new Cane(); Uomo Studente=new Uomo(); // --- Assegnazioni attributo name --Cocker.name = "Chicca"; Studente.name = "Mario"; // --- Inizio esecuzione --System.out.println ("Ora parla " + Studente.name + ":"); Studente.parla(); System.out.println ("Ora parla " + Cocker.name + ":"); Cocker.parla(); System.out.println ("Ora dormono entrambi:"); Cocker.dorme(); Studente.dorme(); } } // Fine della classe Parla L'output prodotto dall'esecuzione del main e' "Ora parla Mario:" "Nel mezzo del cammin...." "Ora parla Chicca:" "Bau! Bau!" "Ora dormono entrambi:" "zzzzzzzzzzzzzz" "zzzzzzzzzzzzzz" ⌡ 14.2. ELEMENTI DEL LINGUAGGIO DI PROGRAMMAZIONE JAVA ============================================== ⌡ 14.2.1 - TIPI DI VARIABILI ----------------Le variabili devono sempre essere dichiarate prima dell'uso. * In Java le variabili vanno dichiarate con il loro tipo (come in Pascal e C++). Le dichiarazioni possono comparire in qualsiasi punto del blocco di un metodo, ma una sola volta. Ogni variabile dichiarata entro {...} non puo' essere usata fuori del blocco definito da {...} (variabile locale). Esistono pero' le variabili di classe (vedi poi) che svolgono il ruolo di variabili globali della classe (se sono public anziche' private, sono vere variabili globali, accessibili a tutti i metodi dell'applicazione). I blocchi possono essere annidati uno dentro l'altro (programmazione strutturata), ma non si puo' ridichiarare la stessa variabile gia' dichiarata in un blocco annidato. Le variabili dichiarate in un metodo non possono essere usate in altri metodi (variabili locali), a meno che non siano dichiarate come 'object variable' o come 'class variable', cioe' subito dopo la dichiarazione di classe all'inizio del programma (public static). Le variabili possono essere dichiarate: 'private' 'private protected' 'public' 'static' Esempi: se possono essere modificate solo all'interno della class; se possono essere modificate solo nella class o in ogni sottoclasse figlia derivata dalla class (magari da altri programmatori, che possono cosi' usare la class, anche all'insaputa del suo autore: quindi occorre fare attenzione quando si usa il modificatore 'protected'); se possono essere modificate da altri programmi che usano l'oggetto Virus; se sono variabili di classe, cioe' comuni a tutti gli oggetti creati dalla classe. // Master copy degli oggetti 'Virus': public class Virus { integer maxFileSize=30000; public integer newSeconds=86; public String author = "Manzoni"; private protected int Minute = 30; private int Hour = 12; // Definizione oggetto 'influenza' (nuova istanza): Virus influenza = new Virus(); // Assegnazione valore alla variabile newSeconds: influenza.newSeconds = 92; Metodi e variabili sono richiamabili con un '.' dopo l'oggetto. Esempi: influenza.newSeconds = 92; (variabile) Cocker.parla(); (metodo) VARIABILI OGGETTO: maxFileSize, newSeconds, author e Minute sono 'object variables', cioe' ce n'e' una distinta copia per ogni oggetto creato dalla classe Virus. VARIABILI DI CLASSE (sono globali per la classe, come un COMMON): Le 'class variables', definite con lo statement 'static', invece sono comuni a tutti gli oggetti creati dalla classe Virus (cioe' come se fosse un COMMON /Virus/...): Esempio: static int virusCount = 0; Le variabili sono gli attributi della classe o dell'oggetto. Per indicare i numeri esadecimali si usa il prefisso 0x (esempio: 0xCAFE). Sono disponibili 8 tipi standard (=essenziali): 6 numerici e 2 non numerici. * Numeri interi (signed): (non ha tipi unsigned) byte short int long Numeri in virgola mobile: float double Caratteri: ('H' e' un carattere, "H" e' una stringa di 1 solo carattere) Variabili logiche: 1 2 4 8 4 8 byte byte byte byte byte byte (da -128 a +127) ecc. (esempio: 845623400123L) (esempio: 3.45F) (esempio: 3.45 o 3.45D) Normalmente Java usa la doppia precisione (che segue la specifica IEEE 754). char 2 byte (Unicode: vedi sito www.unicode.org; codifica di 65536 caratteri internazionali di cui circa 35000 usati; i primi 256 sono gli stessi della codifica ASCII/ANSI) boolean (ha 2 valori: true e false) Esempi di dichiarazioni di variabili: byte b; int i,j,unaVariabileIntera; long x,y; //doppia precisione (usata normalmente) char ch; // Solo dichiarazione char ch='A'; // Dichiarazione + inizializzazione Tutti i tipi standard hanno classi corrispondenti (oggetti wrapper): Integer, Long, Float, Double, Byte, Character, Void e Boolean (per esempio esiste la classe Integer, che corrisponde al tipo essenziale int; il loro uso non ci interessa per ora; ulteriori dettagli si trovano sul libro a pag.179). Il nome di una variabile e' un simbolo introdotto dal programmatore a suo piacere (generalmente e' un nome mnemonico che aiuta a ricordare il significato della costante o variabile) ma nel rispetto della sintassi definita dal diagramma seguente (impone che inizi con una lettera): ┌─────────┐ ┌<─────│ Lettera │<─────┐ │ └─────────┘ │ │ ┌─────────┐ │ Nome ───>┼─────>│ Lettera │──────┼────> │ └─────────┘ │ │ ┌─────────┐ │ └<─────│ Cifra │<─────┘ └─────────┘ I caratteri possono essere in numero qualsiasi e sono tutti significativi, * compresa la differenza tra lettere maiuscole e minuscole. Non si possono ovviamente usare i nomi riservati (elencati nell'appendice del libro). ⌡ 14.2.2 - ASSEGNAZIONI -----------Tutte le variabili dichiarate vanno inizializzate con un'assegnazione del valore iniziale che puo' essere fatta con un'apposita istruzione di assegnazione di valore: int i; // Dichiarazione i=10; // Assegnazione (equivale al := del Pascal) Java consente di assemblarle in un'unica istruzione di inizializzazione: int i=10; // Inizializzazione ⌡ 14.2.3 - CONVERSIONI DI TIPO ------------------Si possono fare operazioni con tipi numerici diversi; saranno eseguite dopo una conversione automatica al tipo di precisione maggiore. Le conversioni per attribuzione forzata di tipo (chiamate cast) possono dar luogo a perdita di precisione (per troncatura o arrotondamento). La sintassi dei cast prevede l'uso del tipo di destinazione tra parentesi seguito dal nome della variabile da convertire. Esempio: double x=9.96; int n=(int)x; La variabile n ha il valore 9 perche' (int) tronca i decimali. Se si vuole invece l'arrotondamento si deve usare il metodo Math.round: Esempio: double x=9.96; int n=(int)Math.round(x); (int) serve perche' Math.round restituisce un risultato di tipo long. ⌡ 14.2.4 - COSTANTI -------Le costanti devono avere dichiarazioni con nomi fatti di tutte lettere maiuscole, precedute dalla parola chiave 'final'. Esempio: final double PI=3.14159265; // Pi greco Di solito le costanti sono usate da piu' metodi all'interno di una classe. In questi casi si chiamano 'costanti di classe' e vanno dichiarate con l'aggiunta della parola chiave 'static'. Se poi si vuole che anche altri metodi Java esterni alla classe possano usare la costante, si aggiunge la parola chiave 'public' (specificatore di accesso). Esempio: public class UsaCostante ------> { public static final double G=9.81; // Costante di gravita' public static void main (String[] args) { System.out.println(G+" m/sec^2"); } } Riepilogando, in questo esempio, sulla riga indicata dalla freccia, le parole chiave hanno il significato seguente: public: static: final : double: altri metodi Java esterni alla classe possono usare la costante la costante puo' essere usata da piu' metodi all'interno della classe il valore non puo' essere cambiato (costante) dichiarazione del tipo ⌡ 14.2.5 - OPERATORI --------I quattro operatori aritmetici sono +-*/ La divisione tra interi si fa con lo stesso operatore / mentre per ottenere il resto si usa % (anziche' 'mod' del Pascal). Esempio: 15/4 fornisce 3; 15 % 4 fornisce 3. Gli operatori aritmetici si possono usare nelle dichiarazioni: int n=5; int a=2*n; // a inizializzato a 10 L'assegnazione x+=4; equivale alla x=x+4; Cio' vale anche per gli altri operatori (in tal caso vanno sempre posizionati prima del segno =). Esempi: *= oppure %= Altre funzioni matematiche, come l'elevamento a potenza, la radice quadrata, il valore assoluto, ecc. sono disponibili come metodi della classe Math in * Java.lang Esempi: double z = Math.pow(x,y); // per z=x^y double x= Math.sqrt(number); int n=(int)Math.round(x); ecc. Sono anche disponibili gli operatori di incremento (++) e di decremento (--), sia nella forma di suffisso che di prefisso (priorita' all'incremento). La loro definizione si capisce subito dagli esempi seguenti: int n=12; int m=7; n++; // Ora n e' 13 int n=7; int a=2*++m; // Ora e' a=16, m=8 int b=2*n++; // Ora e' b=14, n=8 Gli operatori di incremento (++) e decremento (--) sono applicabili alle variabili ma non ai numeri (4++ non e' ammesso). ⌡ 14.2.5.1 - OPERATORI LOGICI ---------------Producono un risultato logico true o false. Oltre ai soliti <,<=,>,>= ci sono gli operatori: == (uguaglianza) != (disuguaglianza) && (AND) || (OR) ! (NOT) Esempio di uso: if (args.length() == 2) { ecc. } ⌡ 14.2.5.2 - OPERATORI SUI BIT ----------------Producono un risultato intero operando singolarmente su tutti i bit (in modo booleano). Sono: & (AND) | (OR) ~ (NOT) <----- (se manca il tasto fare Alt-126) ^ (XOR) << (Shift Left) >> (Shift Right, estendendo il segno) >>> (Shift Right, riempiendo con zeri a sinistra) <<< (non esiste) Esempi: int n=9; int Bit2alla3=(n & 8)/8; // Risultato=1 (quarto bit da destra) int n=1<<3; // Risultato=8 int Bit2alla3=(n & (1<<3))>>3; // Risultato=1 (quarto bit da destra) ⌡ 14.2.5.3 - GERARCHIA DEGLI OPERATORI ------------------------L'ordine di esecuzione delle operazioni, se non diversamente imposto con le parentesi tonde, segue nell'associativita' l'ordine di precedenza indicato nella tabella seguente: Associativita': da SINISTRA a DESTRA (salvo le 2 righe indicate) Priorita'┌──────────────────────────────────── Massima │ [],(chiamata metodo) │ !,~,++,--,+,-,(cast),new <---- Associativita' invertita │ *,/,% ( DA DESTRA A SINISTRA ) │ +,- │ │ │ │ │ │ │ │ │ Minima <<,>>,>>> <,<=,>,>=,instanceof ==,!= & ^ | && || ?: │ =,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>=,>>>= <--- Assoc. invertita (DA DESTRA A SINISTRA) ⌡ 14.2.6 - STRINGHE DI CARATTERI --------------------In Java non esiste il tipo stringa, ma una classe String predefinita nella * libreria standard, per cui ogni stringa racchiusa tra virgolette e' una istanza della classe String. Esempi: String v=""; // stringa vuota String saluto="Ciao"; // stringa piena Le stringhe sono composte da sequenze di caratteri Unicode. * Operatori sulle stringhe: ------------------------1) Operatore di concatenamento (+): agisce oltre che con stringhe, anche con numeri (che vengono trasformati in stringhe prima del concatenamento). Questa proprieta' e' molto comoda nell'output di stringhe e numeri. Esempio: System.out.println("Ci sono " + virusCount + " virus."); 2) Sottostringhe: si ricavano con il metodo substring(n1,n2) della classe String, dove n1,n2 sono i caratteri da prelevare: da n1 fino a n2-1 (n2 e' escluso, cosi' la lunghezza della stringa risultante e' n2-n1). Esempio: String saluto="Ciao"; String s=saluto.substring(0,3); // Crea s="Cia" Si ricordi che il numero d'ordine dei caratteri parte da 0 e non da 1 (da sinistra a destra). 3) Lunghezza di una stringa: e' fornita dal metodo length() della classe String. Esempio: String saluto="Ciao"; int n=saluto.length(); // n=4 4) Carattere in una stringa: si puo' prelevare, oltre che con il metodo substring(n,n+1), anche con il metodo charAt(n) della classe String. E' anche possibile trasferire tutti i caratteri di una stringa in un array di caratteri, piu' comodo nella programmazione dei loop. Esempio: String frase = "bla bla"; char lettere = frase.toCharArray(); I vari caratteri nella stringa 'frase' sono cosi' referenziabili come lettere [n] con n = 0...lettere.length() 5) Modifica di una stringa: si ottiene modificando la variabile di stringa (es. saluto) facendola puntare ad una nuova stringa costruita con i metodi del caso. Esempio: String saluto="Ciao"; saluto = saluto.substring(0,3)+"k!"; // Ottiene saluto="Ciak!" <------ nuova stringa -----> 6) Gestione delle stringhe in Java: La creazione della nuova stringa invece della modifica di quella gia' esistente richiede un'elaborazione piu' lunga, ma questo svantaggio e' compensato dalla possibilita' di condivisione delle stringhe (presenti una sola volta in memoria) tra vari oggetti, resa possibile proprio dalla immutabilita' degli oggetti della classe String, prevista dalle specifiche. La creazione di sempre nuove stringhe come conseguenza delle varie modifiche lascia un certo numero di stringhe inutilizzate (perche' non piu' puntate da alcuna variabile di stringa) in memoria, per cui si avrebbe un impegno di memoria crescente da parte del programma. A cio' pensa la parte runtime di Java che provvede automaticamente al recupero della memoria non piu' utilizzata con un processo di 'garbage collection'. Per i casi in cui la manipolazione delle stringhe e' pesante ed i vantaggi della condivisione minimi (per esempio l'assemblaggio in una stringa di singoli caratteri provenienti da tastiera o da un file), Java mette a disposizione un'apposita classe StringBuffer, che permette la manipolazione diretta delle stringhe. 7) Comparazione di stringhe: il confronto tra stringhe (sia variabili * che costanti) si fa con i metodi 'equals' e 'equalsIgnoreCase'. Esempi: s.equals(t) "Ciao".equals(command) (restituiscono true o false) "Ciao".equalsIgnoreCase("ciao") Non si deve usare == per il confronto di stringhe. Per esempio: if (saluto=="Ciao")... confronta solo se le stringhe sono memorizzate nella stessa posizione di memoria (se lo sono, sono uguali, ma potrebbero essere uguali anche se memorizzate in posizioni diverse!). Il bug risultante dall'uso di == nel confronto tra stringhe sarebbe dei peggiori: intermittente e che sembra verificarsi casualmente! La classe String contiene oltre 50 metodi. Quelli ritenuti piu' utili sono elencati nelle specifiche API (Application Programming Interface) della classe java.lang.String (a pag.58 del testo). L'elenco completo si puo' trovare nella documentazione in linea. ⌡ 14.2.7 - LETTURA DELL'INPUT DA TASTIERA -----------------------------L'input in Java si fa agevolmente con programmi grafici che raccolgono i dati attraverso una finestra di dialogo appositamente predisposta. Cosi' non e' per l'utente che vuole scrivere semplici programmi senza l'uso dell'interfaccia grafica (come abbiamo fatto con il Pascal), per imparare il linguaggio. In tal caso la lettura dell'input da tastiera risulta infatti piuttosto difficile, a meno che non si predispongano delle specifiche routines, scritte per agevolare questo compito. Per risolvere questo problema della didattica di Java ci scriviamo percio', una volta per tutte, un nostro package (InPackage) contenente la classe InKeyb, che mette a disposizione dell'utente i seguenti 3 metodi (per leggere interi, double e stringhe): leggiInt, leggiDouble e leggiString con cui potremo leggere una stringa da tastiera nel modo seguente: String riga; riga = InKeyb.leggiString("Stringa="); In conclusione, usando la classe InKeyb, inserita nel package InPackage (vedi la direttiva 'import'), l'input da tastiera di un numero floating point in doppia precisione si puo' eseguire e provare con il semplice programma seguente: import InPackage.*; public class InKeybTest; // Legge un numero da tastiera { public static void main(String[] args) { double x=InKeyb.leggiDouble ("Numero ="); System.out.println(x); } } ⌡ 14.2.8 - DISPLAY DELL'OUTPUT SU VIDEO ---------------------------- * L'output prodotto dal metodo System.out.print(x) ha tutte le cifre significative della variabile x e questo non permette un'ottimale presentazione dei dati (per esempio incolonnati in tabelle). Per questo e' opportuno formattare l'output in modo appropriato. La classe NumberFormat del package java.text provvede i tre metodi formattatori standard sotto elencati per numeri, valute e percentuali, che restituiscono un oggetto di tipo NumberFormat (nf nell'esempio seguente), a cui puo' essere applicato il metodo 'format' per ottenere il numero nel giusto formato (vedi esempio). I tre metodi formattatori sono: NumberFormat.getNumberInstance() NumberFormat.getCurrencyInstance() NumberFormat.getPercentInstance() Esempi: per i numeri per la valuta per le percentuali double x=10000/3 System.out.print(x); // Stampa 3333.3333333333335 double x=10000/3 NumberFormat nf=NumberFormat.getNumberInstance(); String fx=nf.format(x); // In ambiente USA fx="3,333.33" System.out.print(fx); // Stampa 3,333.33 Il numero minimo e massimo del numero di cifre intere e frazionarie si puo' impostare con i seguenti metodi della classe NumberFormat: setMinumumIntegerDigits() setMaximumIntegerDigits() setMinumumFractionDigits() setMaximumFractionDigits() Esempio: double x=10000/3 NumberFormat nf=NumberFormat.getNumberInstance(); setMinumumIntegerDigits(6) setMaximumFractionDigits(4) String fx=nf.format(x); // In ambiente USA fx="3,333.33" System.out.print(fx); // Stampa 003,333.3333 E' anche possibile formattare definendo l'oggetto DecimalFormat che produce il formato stabilito con l'apposita stringa in argomento. Esempio: DecimalFormat df=new DecimalFormat("0.######") System.out.print(df.format(x)); // Stampa 3333.333333 I caratteri usabili per la formattazione in DecimalFormat sono riportati nelle tabelle 3.5 e 3.6 del testo. ⌡ 14.2.9 - ISTRUZIONI PER IL CONTROLLO DI FLUSSO ------------------------------------1) ISTRUZIONE IF: if (condizione) istruzione; -------------if (condizione) { blocco }; if (condizione) {blocco1} else {blocco2}; 2) OPERATORE ? : condizione? x:y; // Vale x se condiz.=true, y se false ------------3) LOOP INDETERMINATI: while (condizione) { blocco }; // Test condiz. prima ------------------- do {blocco} while (condizione); // Test condiz. dopo 4) LOOP DETERMINATI: for (istruzione1; espressione1; espressione2) {blocco}; ----------------N.B.: istruzione1 inizializza la variabile di loop espressione1 e' la condizione di continuazione loop (se true) espressione2 contiene la variazione della variabile di loop Questa istruzione 'for' equivale all'istruzione 'while' seguente: { istruzione1; while (espressione1) { blocco; espressione2; } } 5) ISTRUZIONE SWITCH: ------------------ switch (choice) { case1: ..... break; case2: ..... break; ...... ecc. ....... caseN: ..... break; default: ... break; Si confronta il valore di choice con i valori case1..N ed esegue il blocco in cui trova il true altrimenti il default (se c'e'). <--- Opzionale } Si possono usare come argomenti dello switch() solo variabili di tipo char o intere di tipo byte/short/int (escluso long). Non si possono usare intervalli di valori. 6) BREAK CON LABEL: In genere il salto incondizionato degrada la qualita' del ---------------- programma (programmazione non strutturata) e aumenta la probabilita' di errori. Tuttavia prevedere la possibilita' di uscire con un salto fuori da un loop puo' essere in certi casi vantaggioso. Per questo Java include l'istruzione break (senza label), che termina un loop (while,for), quando eseguita. Esempio: while (age<=100) { costo=(costo+paga)*(1+interesse); if costo>max) break; age++; } Nel caso di loop multipli annidati puo' convenire uscire non dal singolo loop, ma dall'intero insieme dei loop annidati: questo si puo' fare con il break con label (equivale ad un 'goto label', che pero' non c'e' in Java); la label (alfanumerica) etichetta l'istruzione di loop (piu' esterno) dal quale si deve uscire. Esempio: read_data: while (...) <-- Label=read_data { ... for (...) { ... break read_data; // Esce dal while } } 7) GESTIONE ERRORI: Con la parola chiave 'try' si definisce un blocco ---------------- di istruzioni che puo' generare una 'exception' al verificarsi di una condizione di 'errore'. Se una 'exception' viene generata, un apposito blocco di istruzioni identificato dalla parola chiave 'catch' gestisce l'errore. Con la parola chiave 'throws' inserita nella dichiarazione di un metodo si pubblicizza quali 'exception' non sono gestite nel metodo ma passate al livello piu' alto (chiamante). La parola chiave 'throw' permette all'utente di lanciare una 'exception' o qualunque classe che implementa l'interfaccia 'throwable'. Esempi di semplici programmi: ----------------------------// Esempio di uso di Radice Quadrata class Root { public static void main (String arguments[]) { int number = 100; System.out.println("La radice quadrata di " + number + " e' " + Math.sqrt(number)); } } /* Esempio di "if" e di passaggio di argomenti sulla riga di comando "java" (dopo compilato con javac NewRoot, eseguire battendo: java NewRoot 100) Mostra l'uso del parametro 100, passato al main via String[] arguments */ class NewRoot { public static void main (String[] arguments) { int number = 0; if (arguments.length > 0) number = Integer.parseInt( arguments[0] ); System.out.println ("La radice quadrata di " + number + " e' " + Math.sqrt(number)); } } // Esempio di gestione errori (=Exception) con try e catch import java.net.*; // Contiene la classe ServerSocket import java.io.*; public class TestTry extends Thread { static int Porta = 4444; Socket theConnection; public static void main(String[] args) { ServerSocket ss; // Dichiara variabile oggetto ss try { ss = new ServerSocket(Porta); // Crea oggetto ServerSocket System.out.println ("In ascolto su porta :" + ss.getLocalPort()); } catch (IOException e) { System.out.println ("Errore sulla porta " + Porta + ": " + e); System.exit (-1); } } } ⌡ 14.2.10 - METODI DELLE CLASSI (=sottoprogrammi) ------------------------------------Si tratta delle subroutines (procedure e funzioni) presenti in ogni linguaggio che qui si chiamano metodi (perche' purtroppo gli autori del linguaggio "considerano motivo di orgoglio introdurre una nuova terminologia per queste unita'" di programmazione). La definizione di un metodo deve avvenire all'interno di una classe. L'intestazione del metodo contiene le parole chiave per il controllo dell'accesso (esempio: public static) con il tipo (esempio: int) di variabile prodotta (=function) o void se non c'e' (=procedure) ed il nome del metodo. Nel primo caso (=function) il valore ritornato r viene indicato con l'istruzione finale 'return r;'. Esempio di applicazione in cui il metodo QuoteLotto() e' chiamato dal metodo main (calcola la probabilita' di fare 6 (=numbers) all'enalotto con 90 (=topNumber) numeri, cioe' calcola quote=combinazioni possibili (=90*89*88*87*86*85/6*5*4*3*2*1)): import InPackage.*; public class QuoteLotto { // Questo e' il metodo (calcola n. di combinazioni) public static long QuoteLotto (int high, int number) { long r=1; int i; for (i=1; i<=number; i++) { r=r*high/i; high--;} return r; // r=valore restituito (come long QuoteLotto) } // Questo e' il metodo main public static void main(String[] args) { int numbers=InKeyb.leggiInt ("Quanti numeri giochi = "); int topNumber=InKeyb.leggiInt ("Tot.numeri estraibili = "); long quote=QuoteLotto (topNumber,numbers);// chiama metodo System.out.println("La probabilita' e' 1 su "+quote); } } Osservazioni utili: - La sequenza sintattica prevede di scrivere prima i vari metodi ed infine il programma principale main come segue: public class NomeClasse { public static int nomeMetodo() // int e' un esempio { ... } public static void main(String[] args) {.....} } // Terminatore della classe - L'intestazione del metodo: public static long QuoteLotto (int high, int number) inizia con le parole chiave 'public static', poi il tipo di valore restituito 'long', il nome del metodo con tipi e nomi dei parametri passati. Due metodi possono anche avere lo stesso nome se hanno un diverso numero di parametri in ingresso. - PARAMETRI PASSATI al metodo: sono sempre di tipo valore e non variabile; quindi i metodi Java passano solo valori ma non possono modificare le variabili, passate come parametri, nel programma chiamante, perche' gli sono passate come copie e lui puo' modificare solo queste copie e non gli originali). Diverso e' il caso per la variabile restituita (nell'esempio e' QuoteLotto) che si rende disponibile per il programma chiamante. - Il file deve avere lo stesso nome QuoteLotto.java (perche' public). - Questo e' l'equivalente delle function del Pascal. Se non restituisce valori si mette 'void' (invece di 'long'): in questo caso equivale alla procedure del Pascal; - Se mancano i parametri si deve comunque mettere le parentesi vuote (); - Le variabili dichiarate all'interno del metodo (come 'int i') sono locali nell'interno del blocco in cui vengono dichiarate e la relativa memoria viene rilasciata quando si esce dal metodo; - Esce dal metodo quando incontra 'return r'. Se non c'e' un valore restituito r (procedure) return manca e c'e' void come tipo nell'intestazione del metodo (al posto di 'long'). ⌡ 14.2.11 - METODI DI CLASSE ---------------Rendono disponibili la funzionalita' a tutta una classe anziche' ad un solo oggetto. Hanno 'static' nella dichiarazione. Esempio: static void showVirusCount() { System.out.println("Ci sono " + virusCount + " virus."); } Richiamabile con: NomeClasse.metodoStatico(parametri); Non compare un'istanza dell'oggetto, ma il nome della classe. Quindi i metodi di classe non possono accedere ai campi della classe che non siano statici. Esempio: virus.showVirusCount(); ⌡ 14.2.12 - VARIABILI DI CLASSE ------------------E' possibile, anche se non consigliabile, definire delle variabili di classe, cioe' accessibili a tutti i metodi della classe. La sintassi e' come per le costanti di classe, cioe' vanno dichiarate come private static subito dopo la dichiarazione di classe e prima di ogni dichiarazione di metodo. Esempio: public class Employee { private static doublePensione = 7.62; public static void main(String[] args) {.....} } Sostituendo private con public si ottiene una vera variabile globale, accessibile a tutti i metodi dell'applicazione. ⌡ 14.2.13 - RICORSIONE ---------Java supporta sia la ricorsione diretta che indiretta, senza particolarita' sintattiche. Esempio di ricorsione diretta: public static long QuoteLotto (int quote, int number) { if (number<=0) return 0; else if (number==1) return quote; else return quote*QuoteLotto (quote-1,number-1)/number } Il numero number decrementa ad ogni chiamata ricorsiva, garantendo cosi' che il loop non sia infinito. ⌡ 14.2.14 - ARRAY ----In Java gli array sono oggetti. Quindi il nome nella dichiarazione dell'array e' una variabile oggetto che punta all'area di memoria dove sono i dati. Si possono fare assegnazioni tra array dello stesso tipo: in tal caso pero', se identifico due array, l'identificazione e' fatta sulle variabili-oggetto e non sui dati, per cui dopo l'identificazione entrambe le variabili-oggetto vengono a riferirsi alla stessa matrice di dati in memoria e non a due matrici di dati distinte. Cio' significa che qualsiasi modifica apportata all'una variabile si riflette anche sull'altra perche' entrambe puntano alla stessa area dati in memoria. Esempio di dichiarazione di un array a[] di interi [0..100]: int[] a = new int[100]; oppure int a[] = new int[100]; Quando gli elementi sono pochi, si puo' dichiarare e inizializzare l'array con una sola istruzione: int[] a = {2,3,5,7,11,13}; // Senza 'new' Il numero di elementi di un array puo' essere ricavato con nomeArray.length() Esempio: for (int i=0; i<a.length(); i++) System.out.println(a[i]); Array multidimensionali: Esempio di dichiarazione: -----------------------" di inizializzazione: double[][] balance; balance=new double[5][6]; Come per ogni oggetto, solo dopo l'inizializzazione e' possibile accedere agli elementi balance[i][j] dell'array. Java tratta gli array multidimensionali come array di array, cioe' come un array monodimensionale. Questo puo' facilitare l'inizializzazione ed anche la creazione di array frastagliati (vedasi libro per dettagli). ⌡ 14.2.15 - Passaggio di array come parametri --------------------------------Gli array, pur essendo oggetti, sono passati come riferimenti (nascosti), con l'indicazione del tipo di variabile. Esempio: public class Riordina // Dichiarazione di classe { // ==== Metodo ordina, con param.passato=Array a di tipo int[] ==== public static void ordina (int[] a) // Es. di array a passato come param. { int n = a.length(); int dj = n / 2; while (dj >= 1) { for (int i=dj; i<n; i++) { int temp = a[i]; int j = i; while (j>=dj && temp<a[j-dj]) { a[j] = a[j-dj]; j -= dj; } a[j] = temp; } dj /= 2; } } // ==== Metodo display (Param.passato=Array a di tipo int[] ==== public static void display (int[] a) { for (int i = 0; i < a.length(); i++) System.out.print(a[i] + " "); System.out.println(); } // ==== Metodo main ==== public static void main (String[] args) { int[] a = new int[10]; // make an array of ten integers int i; for (i = 0; i < a.length(); i++) a[i]=(int)(Math.random()*100); // fill array (numeri a caso) // Esempi di chiamata a metodi che non restituiscono valori (void), ma che // modificano l'array a[] nel main. display(a); ordina (a); display(a); } } ⌡ 14.2.16 - Esempio di I/O su disco ----------------------Al contrario delle applet, cui si impedisce normalmente di scrivere sul disco del computer locale per ragioni di sicurezza, alle applicazioni e' pressoche' indispensabile scrivere sul disco. Java tratta l'I/O dei files su disco in maniera standard, come l'I/O attraverso una connessione di rete, relegando tutte le specificita' dell'interfacciamento con il particolare file system ad un'apposita classe 'File'. Anche se i dati transitano come bytes, spesso conviene rappresentarli come strutture di livello piu' elevato, ad esempio come sequenze di caratteri od oggetti. In Java gli oggetti dai quali e' possibile leggere o scrivere si chiamano 'stream' di input o di output e vengono implementati a partire dalle seguenti classi astratte: - InputStream e OutputStream, per le sequenze di bytes; - Reader e Writer, per le sequenze di caratteri Unicode (2 bytes). Queste classi astratte contengono i comportamenti comuni di tutte le sottoclassi figlie, che vengono introdotte per gestire le operazioni di I/O in Java. Siccome Java arriva a definire oltre 60 tipi di stream diversi (per poter funzionare su ogni 'piattaforma'), la loro trattazione va oltre i limiti di questo corso (per un approfondimento ci si puo' riferire al cap.12 del testo Horstmann). Per questo motivo ci limiteremo qui' a riportare solo due esempi di lettura e scrittura di file stream, costituiti da sequenze di bytes su disco. Essendo questi i casi piu' semplici, ci permetteranno di comprendere con poca fatica gli elementi essenziali dell'I/O sui dischi in Java. I file stream usati (FileInputStream, FileOutputStream, FileReader e FileWriter) leggono e scrivono un file sul file system nativo del computer. Mentre le classi di Stream si occupano dei contenuti dei files, la classe 'File' si occupa dell'interfacciamento di Java con il file system locale (fornendo cosi' l'indipendenza dalla piattaforma per tutto il resto del codice Java) e consente la gestione completa dei files su disco, tramite metodi che permettono di eseguire anche i vari comandi, quali delete, rename, ecc. (vedi le API di java.io.File). Il seguente programma (FileTest.java) usa la classe FileWriter per scrivere dei dati sul file FileTest.txt: // ==== Esempio di scrittura su disco ==== import java.io.*; public class FileTest { public static void main (String[] args) throws IOException { /* Creo l'oggetto FileWriter (specifiche del file system locale), assegnandogli l'handle outFile */ FileWriter outFile = new FileWriter ("FileTest.txt"); /* Usando i dati dell'oggetto outFile, creo l'oggetto PrintWriter (flusso Java), referenziato con la variabile oggetto 'out' */ PrintWriter out= new PrintWriter (outFile); // Esegue scrittura su disco String testo="I re di Roma sono "; int giorni=7; out.print (testo); // Analoga a System.out.print() out.println (giorni); // Idem ... // Chiusura dell'oggetto PrintWriter (recupera memoria) out.close(); } } Questo codice crea un oggetto outFile, che rappresenta il file FileTest.txt sul file system locale del computer (File e' una utility class contenuta in java.io). Il programma apre un FileWriter su FileTest.txt. Poi scrive i dati sul writer 'out' (istanza di PrintWriter), chiudendo il writer alla fine. Il codice usato per creare il file writer (equivalente ad una 'open') e' il seguente: FileWriter outFile=new FileWriter("FileTest.txt");//Link a file system locale PrintWriter out = new PrintWriter (outFile);// Gestione standard flusso Java compattabili anche in un'unica istruzione: PrintWriter out = new PrintWriter (new FileWriter ("FileTest.txt")); Con PrintWriter si possono usare gli stessi metodi print e println, usati con System.out, per stampare numeri, boolean, caratteri, stringhe e oggetti. Le classi FileReader e FileWriter leggono e scrivono caratteri di 16-bit. Quello che segue un programma analogo al precedente, ma per un computer con file system nativo che lavora invece con caratteri di 8-bit. Esso usa le classi FileInputStream and FileOutputStream invece di FileReader e FileWriter (anche questo programma e' sul dischetto delle esercitazioni) per copiare il contenuto del file FileCopy.java nel nuovo file FileCopy.txt: // ==== Esempio di I/O su Disco (Copia un text file, byte per byte) ==== import java.io.*; public class FileCopy { public static void main (String[] args) throws IOException { /* Prima definisco due oggetti File (specifici del file system locale), assegnando loro gli handles inputFile e outFile */ File inputFile = new File ("FileCopy.java"); File outputFile= new File ("FileCopy.txt"); /* Poi, usando i dati degli oggetti inputFile e outputFile, creo gli oggetti FileInputStream e FileOutputStream (flussi Java), che saranno referenziati con le variabili oggetto in e out FileInputStream in = new FileInputStream (inputFile); FileOutputStream out= new FileOutputStream (outputFile); */ /* Ora leggo su 'in', carattere per carattere come interi (ASC), e riscrivo su out (fino all'end-of-file) */ int c; while ((c=in.read()) != -1) { // System.out.println(c); // Istruzione di test (per vedere cosa fa) out.write(c); } in.close(); // Chiusura dei flussi FileInputStream e FileOutputStream out.close(); } } Il programma apre un FileInputStream sul file FileCopy.java del file system locale ed un FileOutputStream sul file FileCopy.txt. Poi legge caratteri dal reader (in) e li scrive sul writer (out), chiudendo reader e writer alla fine. Il codice usato per creare il file reader (equivalente ad una 'open') e' in questo caso: File inputFile = new File ("FileCopy.java"); // Link a file system locale FileInputStream in=new FileInputStream(inputFile);// Gestione standard flusso La classe File (una utility class contenuta in java.io) contiene le funzionalita' necessarie per connettersi al file system del computer locale, mentre le classi di Stream si occupano dei contenuti del file (gestione standard Java dei flussi). Questo codice crea un oggetto File, che rappresenta il file FileCopy.java, presente sul file system locale del computer. !----------------------------------------------------------------------⌡ 14.3 - PROGRAMMAZIONE 'ORIENTATA AD OGGETTI' (OOP) =========================================== ⌡ 14.3.1 - Introduzione -----------JAVA e' un linguaggio totalmente ad oggetti, per cui conviene capire subito cosa cio' significa. Il processo che ha portato allo sviluppo nell'ingegneria del software della programmazione orientata ad oggetti e' simile a quello avvenuto nel campo dell'hardware: un costruttore di PC originariamente era costretto a progettare e costruire i vari componenti (meccanica, alimentatore, scheda madre, ecc.), poi si e' accorto che era piu' conveniente trasformare ove possibile il lavoro di costruttore in quello di assemblatore, lasciando ad altre societa', specializzate nella costruzione dei singoli componenti, il lavoro della loro costruzione (per esempio l'alimentatore risulta costruito meglio ed a costi piu' bassi da un costruttore specializzato di Singapore, la scheda madre da un costruttore di Taiwan, ecc.). In generale si puo' osservare che l'esigenza di progredire ha portato alla necessita' della modularizzazione, nel campo dell'hardware ma anche in quello del software, per poter fornire sistemi composti da componenti sempre piu' complessi a costi sempre piu' bassi. Nel campo del software la progressiva crescita della dimensione dei programmi (applicazioni), dagli anni settanta in poi, ha portato alla ricerca della modularizzazione e dell'indipendenza dei moduli, sia per facilitare la progettazione ed il debugging, che per rendere piu' agevole la suddivisione del lavoro ed il coordinamento tra piu' persone impegnate nello stesso progetto. Il lavoro di gruppo e la documentazione del progetto sono facilitati da metodologie che lo rappresentano con diagrammi, come quelli prodotti con UML, Universal Modeling Language (www.rational.com/uml/index.html), per esempio con il tool Together/J, che mostra la corrispondenza tra elementi del diagramma del progetto ed il codice Java). Queste metodologie si appoggiano sulle tre relazioni essenziali tra le classi di un progetto: Uso, Contenimento e Ereditarieta' (spiegate piu' avanti). La Programmazione Orientata ad Oggetti (OOP) ha sostituito le tecniche di programmazione strutturata, che consistevano nell'individuazione delle procedure (algoritmi) necessarie per elaborare i dati globali e loro successiva scrittura in istruzioni elementari. Nella programmazione strutturata procedurale la struttura dei dati veniva scelta alla fine, in modo da agevolare la programmazione delle procedure. La OOP ha invertito questo ordine, dando la precedenza alla struttura dei dati e lasciando in secondo ordine gli algoritmi che agiscono sui dati. La scrittura di programmi strutturati, suddivisi in moduli che comunicano tra loro tramite chiamate di procedure e non mediante condivisione di dati, ha costituito la base per la nascita della programmazione orientata ad oggetti. Questa non risolve pero' soltanto il problema della modularizzazione di grandi applicazioni (presentando, per esempio, invece di 2000 procedure per l'esecuzione di 2000 funzioni, solo 100 classi con una media di 20 metodi per classe), ma consente anche in modo naturale di impiegare i metodi (procedure) contenuti in una classe (modulo) su piu' blocchi di dati diversi (dati oggetto) contemporaneamente: infatti una volta definita una classe si puo' lavorare con un numero qualsiasi di istanze di quella classe (invece nella programmazione procedurale normalmente un modulo ha una sola istanza). Cio' significa che la OOP risolve automaticamente in maniera nativa i problemi di utilizzo di una sola copia del codice macchina (con i problemi di rientranza risolti a monte da compilatore e interprete VM), della salvaguardia (in aree di memoria diverse per ogni istanza) delle variabili locali e dei parametri passati delle varie istanze. Cio' e' proprio quello che serve per poter operare in multitasking. Quindi Java, essendo un linguaggio OOP, supporta l'elaborazione simultanea di piu' task (chiamati 'thread'), i quali possono lavorare su diverse istanze dello stesso oggetto, caricate in posizioni diverse della memoria. Inoltre nel lavoro di gruppo non si deve permettere, a titolo di esempio, che altri programmatori, all'insaputa dell'autore, possano accedere ad un modulo di programma (classe), derivandone altri che accedono ai campi dei dati, perche' cosi' si avrebbe come conseguenza, che se l'autore apporta dei cambiamenti alla sua classe puo' sconvolgere la funzionalita' delle applicazioni degli altri programmatori che, a sua insaputa, stanno usando la sua classe. Queste problematiche portano a concepire l'incapsulamento dei dati ed il controllo dell'accesso, che sono caratteristiche fondamentali della programmazione ad oggetti e quindi di Java. Alla fine del paragrafo 14.3.5 elencheremo gli accorgimenti che in Java si possono adottare per raggiungere questi obiettivi. ⌡ 14.3.2 - Modo di lavorare in JAVA -----------------------In JAVA si programmano le classi, che contengono i metodi (sottoprogrammi). Con questi si definiscono gli oggetti con cui si lavora (metodi costruttori), cioe' si creano gli oggetti (istanze della classe) tramite le istruzioni 'new', ognuno identificato dalla sua variabile oggetto (=nome). Poi gli si attribuisce lo stato iniziale e quindi si lavora con gli oggetti (referenziati con il nome della variabile oggetto), mediante chiamate ai metodi delle classi. Queste chiamate differiscono dalle chiamate ai sottoprogrammi della programmazione procedurale (generalizzazione): per esempio un oggetto non dovrebbe mai manipolare direttamente i dati interni di un altro oggetto. Queste elaborazioni possono avvenire invece tramite 'chiamate ai metodi' dell'altro oggetto, ovvero GLI OGGETTI CLIENT DEVONO INVIARE MESSAGGI AGLI OGGETTI SERVER che si incaricano di manipolare i dati internamente alla loro classe. In questo modo si tutela la possibilita' di riutilizzo (rientranza), si riduce la dipendenza dai dati e si facilita il debugging. Le librerie standard di Java mettono a disposizione migliaia di classi per i piu' vari scopi: interfacce grafiche per l'utente, programmazione in rete, ecc. Il lavoro del programmatore e' quello di creare classi personali per creare ed elaborare oggetti necessari all'applicazione, usando le classi disponibili nelle librerie standard utili per l'applicazione. Esempio: se ho una classe AudioClip, posso applicare i metodi di AudioClip agli oggetti del tipo AudioClip, identificati dalle rispettive variabili oggetto che posso definire cosi': AudioClip miao; // Definizione della variabile oggetto miao di tipo AudioClip (ma non dell'oggetto: finche' non viene creato l'oggetto associato, non si puo' applicare nessun metodo a miao, cioe' l'istruzione miao.play(); darebbe errore run-time). L'oggetto associato si crea con l'operatore 'new' cosi': miao=new AudioClip(); // Crea un nuovo oggetto (istanza della classe AudioClip), collegato alla variabile oggetto 'miao' cioe' alloca la memoria per i campi dell'oggetto. Queste due possono anche essere integrate in una sola istruzione (che crea l'oggetto miao) come segue: AudioClip miao = new AudioClip(); Altri esempi di uso (gia' incontrati): CREAZIONE DELL'OGGETTO 'Cocker' della classe 'Cane': ASSEGNAZIONE DEGLI ATTRIBUTI all'oggetto 'Cocker' : RICHIAMO DEL METODO 'parla' dell'oggetto 'Cocker' : RICHIAMO DELLA VARIABILE 'nome' dell'ogg. 'Cocker' : (richiamabile se non e' private). Cane Cocker=new Cane(); Cocker.name="Chicca"; Cocker.parla(); ... Cocker.name ... ⌡ 14.3.3 - Uso delle classi esistenti -------------------------Importanti sono i metodi che una classe mette a disposizione per eseguire i vari compiti sui suoi oggetti. Essi risultano nella documentazione API (Application Programming Interface) della classe. Sul libro e' riportato l'esempio di uso della classe API corejava.Day, disponibile in un package didattico del libro, chiamato corejava (che deve risultare accessibile con la CLASSPATH impostata nel vostro computer). La classe portata come esempio, Calendar.java, dopo compilazione con javac Calendar.java se eseguita con java Calendar 12 1999 visualizza il calendario di dicembre 1999. Per i metodi usati ci si e' riferiti alle note API della classe corejava.Day elencate a pag.113 del testo (copyright riservato del libro). import corejava.*; // Contiene la biblioteca con Day.class public class Calendario { public static void main(String[] args) { int m; int y; if (args.length() == 2) { m = Integer.parseInt(args[0]); // m,y passati all'avvio con java y = Integer.parseInt(args[1]); } else { Day oggi = new Day(); // Ricava m,y dalla data di oggi m = oggi.getMonth(); y = oggi.getYear(); } // ..........ecc......... System.out.println(m + " " + y); } } ⌡ 14.3.4 - Uso delle classi personali -------------------------Consideriamo come esempio tipico di programmazione ad oggetti quello della classe Employee sotto riportata. La classe deve contenere: - la dichiarazione dei campi contenuti all'interno delle varie istanze della classe Employee (nell'esempio: String nome e double salario); - un metodo (detto 'costruttore'), con lo stesso nome (Employee) della classe, usato per creare ed inizializzare gli oggetti della classe (assegnando alle variabili dei vari campi i valori iniziali voluti). Il metodo costruttore dev'essere sempre chiamato con la parola chiave 'new' e non restituisce valori (nell'esempio: il metodo Employee); - i metodi pubblici che mette a disposizione delle altre classi per l'accesso e modifica dei campi delle varie istanze create con i propri costruttori. Questi sono in genere gli unici modi di accesso a quegli oggetti (incapsulati; nell'esempio i metodi display e incremSalario), poiche' gli altri metodi dell'utente, esterni alla classe, non possono mai modificare quegli oggetti in quanto scambiano i parametri solo di tipo valore e non per riferimento (vedi piu' sotto). Se il modificatore dell'accesso e' private l'accesso e' ristretto alla classe mentre se non e' specificato l'accesso e' consentito a tutti i metodi dello stesso package (insieme di classi raggruppate con l'intestazione 'package nomePackage;' all'inizio del file). La classe mette a disposizione gli entry point dei vari metodi ma non ha un punto di inizio dell'esecuzione del programma. Per questo aggiungiamo la classe EmployeeTest per provare il programma. // ==== Programma per il test della classe Employee ==== import java.util.*; public class EmployeeTest { public static void main (String[] args) { Employee[] staff = new Employee[3]; staff[0] = new Employee ("Tizio", 45000); staff[1] = new Employee ("Caio", 65000); staff[2] = new Employee ("Sempronio" , 58000); int i; for (i=0; i<3; i++) staff[i].incremSalario(5); //Modif.delegata al metodo for (i=0; i<3; i++) staff[i].display(); } } class Employee // Inizio della classe Employee //Tutti metodi pubblici per renderli accessibili da altre classi { public Employee (String n, double s) //Metodo costruttore di oggetti { nome = n; salario = s; } public void display() { System.out.println (nome + " " + salario); } public void incremSalario (double percento) { salario = 1 + percento / 100; } // Tutti campi delle 2 istanze sono 'private' per impedire l'accesso esterno: // campi manipolabili solo dai metodi della classe Employee (incapsulamento), // cosi' in caso di errore si limita il debugging all'interno della classe. private String nome; private double salario; } ⌡ 14.3.5 - Passaggio di oggetti come parametri ----------------------------------Anche le variabili oggetto, pur essendo riferimenti come i nomi degli array, non sono passate per riferimento, ma sono usate per creare una copia dell'oggetto originale, su cui far operare le istruzioni del metodo. Quindi in Java anche gli oggetti, come i tipi standard (numeri, ecc.), sono sempre passati come valori e non come riferimenti: cioe' il metodo lavora con una copia del dato passato e non con l'indirizzo del dato originale. Come conseguenza, i metodi non possono mai ritornare variati i valori dei parametri scambiati. Per esempio se in un main si chiama il seguente metodo swap(c,d), non si ottiene lo scambio dei due oggetti c e d: static void swap (String a, String b) // Non funziona! { String temp = b; b = a; a = temp; } L'unica eccezione e' quella degli array, gia' discussa in precedenza (vedi gli esempi Riordina.java e SwapTest.java). Questa e' una situazione che puo' apparire scomoda perche' riduce la liberta' del programmatore, ma il modo di lavorare che si impone cosi' va nella direzione di produrre codici di programma, in cui gli errori sono meno frequenti e piu' localizzati. Basta in definitiva attenersi alle seguenti regole canoniche della programmazione ad oggetti: 1) Usare sempre dati 'privati': questo garantisce l'incapsulamento e confina i problemi di programmazione all'interno della classe (facilita' di debug e quindi possibilita' di gestire anche programmi molto grandi). 2) Conseguentemente, prevedere nella classe tutti i metodi di accesso e di modifica dei campi dell'oggetto necessari agli utenti esterni alla classe (essi saranno l'unico modo di accesso dall'esterno). 3) Quando si deve accedere o modificare oggetti di un'altra classe farlo solo con chiamate ai metodi di quella classe. 4) Anche se la maggior parte dei metodi che si incontrano e' di tipo 'public', e' opportuno realizzarne il piu' possibile di tipo 'private' (di uso esclusivo locale all'interno della classe), magari suddividendo i metodi grandi in piu' piccoli, alcuni dei quali possono risultare 'private'. Un metodo 'public' puo' sempre essere usato da altre classi e quindi non puo' essere eliminato o modificato con la stessa tranquillita' di un metodo 'private'. ⌡ 14.3.6 - Progettazione nella programmazione a oggetti -------------------------------------------Per risolvere un problema applicativo con la programmazione a oggetti occorre individuare le classi ed i relativi metodi da programmare. In genere nell'analisi di un problema i sostantivi corrispondono alle classi ed i verbi ai metodi. Consideriamo per esempio un sistema di gestione degli ordini. In esso le classi corrispondono ai sostantivi ARTICOLO, ORDINE, ORDINE.URGENTE, CONTO. Le varie istanze di queste classi (create con 'new') sono i dati su cui operano i metodi. Esempi di metodi della classe ORDINE (corrispondenti ai verbi) sono: - 'aggiungi', 'togli' ARTICOLO in un ORDINE - 'scarica' un ORDINE in CONTO ⌡ 14.3.7 - Relazioni tra le classi ----------------------- Uso: una classe A usa una classe B se ne chiama i metodi (tramite scambio di messaggi) o se ne manipola gli oggetti (cioe' crea, riceve o restituisce oggetti della classe B). Per esempio la classe ORDINE usa la classe CONTO in quanto gli oggetti di ORDINE (di un cliente) devono accedere agli oggetti del CONTO (di quel cliente) per controllare il credito. La classe ARTICOLO invece non usa la classe CONTO perche' i suoi oggetti per andare in un ORDINE non hanno bisogno di manipolare gli oggetti del CONTO del cliente. - Contenimento: e' un caso speciale di uso. Esempio: un oggetto ORDINE contiene oggetti ARTICOLO - Ereditarieta': corrisponde a specializzazione. La classe ORDINE.URGENTE eredita i metodi della classe ORDINE (per esempio: aggiunta di articoli in ORDINE, scaricamento di un ORDINE sul CONTO di un cliente, ecc.), ma ha anche ulteriori suoi metodi speciali (per gestire le priorita', per aggiungere i supplementi di costo, ecc.). Queste relazioni sono alla base della progettazione orientata agli oggetti, che si avvale di diagrammi in cui le classi corrispondono ai riquadri e le relazioni alle linee (diverse per le tre relazioni). Esempio di diagramma delle classi (notazione UML = Universal Modeling Language): ┌────────────────┐ CLASSI ----> │ ORDINE.URGENTE │ └───────╦────────┘ RELAZIONI ---> Ereditarieta'║ ┌───╩────┐ Uso ┌─────────┐ │ ORDINE ├--─-─-─-─>│ CONTO │ └───┬────┘ └─────────┘ Contenimento │ ┌────┴─────┐ │ ARTICOLO │ └──────────┘ ⌡ 14.4 - EREDITARIETA' ============= ⌡ 14.4.1 - Introduzione -----------Gli oggetti corrispondono a blocchi di dati relativi ad entita' soggette alla elaborazione come: eventi sperimentali, prenotazioni, impiegati, ecc. Ognuna di queste entita' ha bisogno di piu' dati per essere completamente descritta e in Java questi dati vengono definiti nel metodo costruttore, che alloca loro la memoria, ed elaborati con i metodi della classe appositamente predisposti. Talvolta a queste entita' esistenti se ne vengono ad aggiungere altre simili ma piu' specializzate, nel senso che ai campi gia' esistenti se ne aggiungono altri per descriverle. Per esempio i dirigenti di un'azienda sono anch'essi impiegati e quindi il metodo costruttore di una classe (Manager) che li rappresenta conterra' gli stessi campi dell'impiegato (Employee), piu' qualche altro (per esempio la funzione direttiva). In questi casi, in cui l'intersezione insiemistica dei campi degli oggetti delle due classi non e' vuota, e' possibile creare la nuova classe Manager, utilizzando quanto gia' programmato per la classe Employee con la tecnica dell'ereditarieta' nel modo seguente: class Manager extends Employee // <--- parola chiave 'extends' { public Manager (String n,double s) { super (n,s); // super = chiama costruttore Employee headFunction=""; // campo aggiuntivo (inizializzaz.) } public String getHeadFunction() { return headFunction; } public void setHeadFunction(String name) { headFunction=name; } private String headFunction; } La classe preesistente Employee si chiama superclasse. Tutti i metodi della superclasse sono disponibili alla classe figlia, per cui in questa si programmano solo le differenze e le aggiunte. Tutte le funzionalita' comuni alle due classi saranno quindi programmate nella superclasse. Discendenza delle classi: essendo le classi rientranti possono essere richiamate nella definizione di nuove classi in 'estensione' delle precedenti (inheritance hierarchy) con lo statement 'extends' nella dichiarazione di classe. Esempio: /* ==== Esempio di definiz.di oggetti con ereditarieta' ==== * ...Uomo extends Dormiente... significa che dalla * superclasse Dormiente (madre) deriviamo la sottoclasse Uomo * (figlia) ---> Ogni Uomo e' un Dormiente * N.B. I metodi potrebbero anche essere compilati separatamente * La routine con metodo 'dorme' e' comune a Cane e Uomo * (scritta una sola volta) */ class Parla { // --- Classe (madre) degli oggetti Dormiente --public static class Dormiente { String name; public static void dorme() // <--- metodo dorme (comune a Cane e Uomo) { System.out.println ("zzzzzzzzzzzzzz"); } } // --- Classe (figlia) degli oggetti 'Cane' con metodo 'parla'--public static class Cane extends Dormiente { public static void parla() // <--- Metodo parla di Cane { System.out.println("Bau! Bau!"); } } // --- Classe (figlia) degli oggetti 'Uomo' con metodo 'parla'--public static class Uomo extends Dormiente { public void parla() // <--- Metodo parla di Uomo { System.out.println("Nel mezzo del cammin di nostra vita...."); } } // --- Programma principale --public static void main (String arguments[]) { // --- Definiz.oggetti Cocker e Studente --Cane Cocker = new Cane(); Uomo Studente=new Uomo(); // --- Assegnazioni attributo name --Cocker.name = "Chicca"; Studente.name = "Mario"; // --- Inizio esecuzione --System.out.println ("Ora parla " + Studente.name + ":"); Studente.parla(); System.out.println ("Ora parla " + Cocker.name + ":"); Cocker.parla(); System.out.println ("Ora dormono entrambi:"); Cocker.dorme(); Studente.dorme(); } } // Fine della classe Output prodotto dall'esecuzione del main: "Ora parla Mario:" "Nel mezzo del cammin...." "Ora parla Chicca:" "Bau! Bau!" "Ora dormono entrambi:" "zzzzzzzzzzzzzz" "zzzzzzzzzzzzzz" ⌡ 14.4.2 - Gerarchie dell'ereditarieta' ---------------------------La tecnica dell'ereditarieta' si puo' estendere a piu' livelli gerarchici. Per esempio la classe Manager, figlia di Employee, puo' a sua volta essere padre di una classe ancora piu' specializzata (quella dei Dirigenti) ed altre classi specializzate di impiegati possono essere quelle degli operai e delle segretarie (che non avrebbero nulla in comune con la classe Manager). Questa situazione e' rappresentata nel diagramma seguente: EREDITARIETA': ┌───────────────────────────┐ SUPERCLASSE ----> │ Employee │ └─╦───────────╦───────────╦─┘ ║ ║ ║ ┌───╩───┐ ┌────╩─────┐ ┌──╩────┐ CLASSI FIGLIE ---> │Manager│ │Segretaria│ │Operaio│ └───┬───┘ └──────────┘ └───────┘ ┌────┴────┐ │Dirigente│ └─────────┘ Per un uso appropriato dell'ereditarieta' bisognerebbe individuare tutte le operazioni ed i campi comuni a piu' classi e spostarli piu' in alto lungo l'albero, evitando cosi' duplicazioni di definizioni e di metodi, che sono molto pericolose quando si riferiscono alle stesse entita', perche' anche correzioni e modifiche future vanno applicate in piu' punti del programma (anziche' in un solo punto: nella superclasse). Si puo' anche impedire ad una classe di diventare superclasse, mettendo il modificatore 'final' nella definizione di classe (puo' convenire per velocizzare l'elaborazione eliminando la ricerca lungo la gerarchia dei metodi chiamati: binding dinamico). Esempio: final class Employee { ..... } Gli oggetti delle classi figlie (Manager) hanno in genere piu' campi (NomeSegretaria) degli oggetti della superclasse (Employee), per cui puo' essere necessario convertire il tipo di un oggetto. Questo si puo' fare con un processo chiamato 'cast' che e' analogo a quello visto per la conversione dei tipi di variabili standard. Esempio di cast: Manager boss = (Manager) staff[0]; dove staff[0] e' un'istanza di Employee. Ovviamente il cast e' permesso solo dentro lo stesso albero di gerarchia di ereditarieta'. E' anche disponibile un operatore 'instanceof' per verificare la classe di un oggetto prima di applicare un cast, al fine di evitare 'eccezioni' runtime. Esempio: if (staff[0] instanceof Manager) { boss = (Manager) staff[0]; ............. } Funzionalita' utili a tutte le classi sono definite in una superclasse cosmica implicita, radice di tutte le classi, chiamata Object. Per la descrizione dei metodi messi a disposizione (boolean equals(), Object clone(), String toString(), ecc.) vedere le note API (sul testo o in rete). ⌡ 14.4.3 - Controllo dell'accesso ---------------------Le parole chiave (modificatori di accesso) che controllano l'accesso a campi e metodi delle classi sono tre e funzionano nel modo seguente (in ordine di restrizione decrescente): 1) 'private' : accesso 2) senza modificatore: 3)'protected': accesso 4) 'public' : accesso consentito solo entro la stessa classe; accesso consentito al package. consentito al package e a tutte le sottoclassi figlie; consentito a tutti; Nella programmazione ad oggetti si devono privilegiare le scelte che favoriscono l'incapsulamento (cioe' accesso 'private'), per i motivi gia' detti in precedenza. Si puo' rinunciare all'incapsulamento per ottenere maggiore flessibilita' e funzionalita', ma si dev'essere coscienti che si acquisiscono dei rischi. Anche l'accesso protected viola l'incapsulamento perche' puo' permettere, per esempio, ai metodi di Manager (figlio) di accedere ad un oggetto di Employee (padre), se questo e' definito come protected anziche' private. Per capire l'importanza di usare bene il controllo dell'accesso, supponiamo che Employee abbia i suoi campi definiti 'protected'; allora altri programmatori possono, all'insaputa dell'autore della classe Employee, accedere alla classe derivandone altre classi che accedono ai suoi campi protetti. Questo compromette l'incapsulamento e porta come conseguenza, a titolo di esempio, che se l'autore di Employee apporta dei cambiamenti alla sua classe puo' sconvolgere la funzionalita' delle applicazioni degli altri programmatori che, a sua insaputa, stanno usando la sua classe, poiche' l'accesso protected glielo consente. ⌡ 14.4.4 - Polimorfismo -----------Nella programmazione ad oggetti l'elaborazione procede inviando richieste (messaggi) di esecuzione di metodi agli oggetti con determinati parametri. L'oggetto, sottoclasse della sua gerarchia di ereditarieta', verifica se possiede il metodo con quel nome e con l'esatto elenco di parametri passati (che costituiscono la 'segnatura' o firma del metodo). In caso affermativo lo esegue, altrimenti PASSA LA RICHIESTA ALLA CLASSE PADRE. In questo modo la richiesta risale l'albero gerarchico verso le classi antenate, finche' non trova una corrispondenza (se non la trova si genera un errore in fase run-time). Possono coesistere, in classi diverse della gerarchia dell'ereditarieta', anche metodi con lo stesso nome e addirittura con gli stessi parametri: in tal caso il meccanismo descritto rende visibile solo il primo metodo con la giusta firma, che si incontra risalendo la gerarchia (gli altri nelle classi genitrici risultano nascosti). Questo meccanismo, con cui un oggetto risulta in grado di decidere quale metodo applicare, si chiama polimorfismo. La funzionalita' risultante e' che oggetti, ovvero sottoclassi diverse, nella catena dell'ereditarieta' possono rispondere in maniera diversa allo stesso messaggio (che e' la richiesta di esecuzione di un metodo). Il meccanismo tradizionale di chiamata di un metodo (binding statico o early binding) permette di definire completamente l'operazione in fase di compilazione del programma. Con il polimorfismo invece il compilatore genera il codice con cui poter determinare dinamicamente, in fase run-time, quale metodo eseguire, utilizzando le informazioni disponibili sull'oggetto (binding dinamico o late binding). In conclusione possiamo dire che ereditarieta' e polimorfismo determinano il modo di comportarsi degli oggetti: essi acquisiscono la capacita' di sapere come svolgere il loro lavoro, potendo anche evolvere dinamicamente il loro comportamento. ⌡ 14.5 - INTERFACCIA GRAFICA (GUI) ========================= ⌡ 14.5.1 - Apertura di una finestra -----------------------L'uso delle GUI (Graphical User Interface) e' reso molto semplice negli ambienti di sviluppo di applicazioni come Visual Basic, Visual C++ , Delphi, Forte', ecc., che mettono a disposizione del programmatore i necessari strumenti grafici di layout e gli editor di risorse; con essi si impostano le caratteristiche grafiche ed il sistema genera automaticamente il codice di programmazione GUI corrispondente. Senza usare tali sistemi di sviluppo e per comprendere la programmazione grafica GUI, diamo ora alcune informazioni di base al riguardo. Gia' nella versione 1.1, Java conteneva una libreria di classi AWT (Abstract Window Toolkit) per la programmazione dell'interfaccia grafica (GUI). Dal 1996 Netscape e Sun hanno collaborato per la creazione di una nuova libreria di interfacce utente GUI denominata Swing, che e' stata inclusa nel JFC (Java Foundation Classes, una libreria piu' ampia contenente, per esempio, anche le API di accessibilita' e le API per il drag-and-drop). Le classi Swing per l'interfaccia grafica GUI sono contenute nel package javax.swing. Mostriamone l'uso con l'esempio seguente, in cui definiamo una prima classe EsempioFrame che si comporta come la JFrame di Swing, eccetto per il titolo e le dimensioni (definite con setTitle e setSize). La seconda classe FrameTest, con il metodo main, crea l'oggetto frame (cioe' una finestra top-level, non contenuta in altre) con una chiamata a new e lo mostra su video con il metodo show nella posizione di default, che e' in alto a sinistra (si potrebbe anche posizionare altrove sullo schermo la finestra frame con il metodo setLocation). // ---- Crea un frame e lo mostra su video ---import javax.swing.*; class EsempioFrame extends JFrame // Definisce EsempioFrame con titolo e ampiezza { public EsempioFrame () { setTitle("EsempioFrame"); setSize(300,200); } } // Crea oggetto frame e lo mostra (in alto a sinistra) public class FrameTest { public static void main(String[]args) { JFrame frame=new EsempioFrame(); frame.show(); } } Invece di usare due classi distinte (con due files .class generati), potremmo fare tutto nella stessa classe EsempioFrame con due metodi EsempioFrame e main nel modo seguente: public class EsempioFrame extends JFrame { public EsempioFrame () { setTitle("EsempioFrame"); setSize(300,200); } public static void main (String[]args) { JFrame frame=new EsempioFrame(); frame.show(); } } Un problema che si presenta con i frame e' che la visualizzazione della finestra attiva un thread di interfaccia-utente, che rimane attivo anche dopo il termine del metodo main. Per chiudere il thread (cioe' per riottenere il prompt dopo l'esecuzione di 'java EsempioFrame' nella shell DOS di esecuzione del JDK) sono necessarie numerose istruzioni, che passano l'ordine della terminazione del thread del frame al programma chiamante 'java' per poterne eseguire la chiusura. Esse sono raccolte nell'apposito metodo, che riportiamo nell'esempio seguente (CloseTest.java), rimandando al testo per ulteriori dettagli: import java.awt.event.*; import javax.swing.*; // --- Definisce CloseFrame con titolo, ampiezza e posizione --class CloseFrame extends JFrame { public CloseFrame () { setTitle("CloseFrame"); setSize(300,200); // L'istruzione seguente termina il thread per la chiusura del frame addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } } // --- Crea oggetto frame e lo mostra in alto a sinistra --public class CloseTest { public static void main(String[]args) { JFrame frame=new CloseFrame(); frame.show(); } } ⌡ 14.5.2 - Metodi per gestire i frame -------------------------Nella classe JFrame sono contenuti vari metodi, che consentono di operare con i frame, cioe' con le finestre top-level, non contenute in altre. Ne elenchiamo i principali: - Metodo 'dispose' : chiude la finestra e ne recupera le risorse impegnate; - Metodo 'setIconImage': imposta immagine da usare quando la finestra e' ridotta ad icona; - Metodo 'setTitle' : modifica il testo nella barra del titolo; - Metodo 'setResizable': imposta valore boolean per ridimensionamento frame; Altri metodi sono indicati nelle note API java.awt.Component sul testo (la classe Component, nella catena di ereditarieta' della classe JFrame, e' l'avvio di tutti gli oggetti GUI). In Java un frame e' un contenitore di componenti grafici (GUI), mentre i disegni vengono normalmente eseguiti su un componente (realizzato con la classe JPanel) chiamato 'pannello del contenuto', che viene inserito nel frame. La struttura interna di un JFrame e' la seguente (gli altri pannelli servono per usi particolari come: barra dei menu', ambientazione, ecc.): ╔═════════════════════════════════════════════╗ ║ █ TITOLO █ █ █ ║ ╠═════════════════════════════════════════════╣ ║ JFrame ║ ┌─────────────────────────────────────────────┐ ║ │ JRoot │ ║ ┌─────────────────────────────────────────────┐ │ ║ │ JLayeredPane │ │ ║ ┌─────────────────────────────────────────────┐ │ │ ║ │ Pannello del contenuto (+barra dei menu') │ │ │ ║ ┌─────────────────────────────────────────────┐ │ │ │ ║ │ Pannello 'di vetro' │ │ │ │ ║ │ │ │ │ │ ║ │ │ │ │ │ ║ │ │ │ │ │═══╝ │ │ │ │ │ │ │ │ │───┘ │ │ │ │ │ │ │───┘ │ │ │ │ │───┘ │ │ └─────────────────────────────────────────────┘ Si puo' aggiungere un pannello in un frame con le istruzioni seguenti: import javax.swing.* ..................... Container ContentPane = frame.getContentPane (); JPanel pannello = new JPanel(); ContentPane.add (pannello); Per disegnare nel pannello occorre : 1) Definire una classe che estende JPanel; 2) Usare il metodo paintComponent per disegnare l'oggetto grafico. In questa sede non possiamo addentrarci ulteriormente nell'argomento, per cui invitiamo il lettore a riferirsi al testo per ulteriori informazioni. ⌡ 14.5.3 - Metodi per disegnare i testi ---------------------------I testi sono considerati disegni particolari e quindi, come oggetti di tipo Graphics, possono essere disegnati con il metodo: gr.drawString(text,xCoord,yCoord) Un esempio e' il programma TestMioFrame.java, che scrive un testo nel pannello nel modo seguente: // --- Esempio di classe che scrive (=disegna) un testo --import java.awt.*; import java.awt.event.*; import javax.swing.*; class MioFrame extends JFrame // Definisce classe MioFrame { public MioFrame () { setTitle ("MioFrame"); setSize (300,200); addWindowListener (new WindowAdapter() { public void windowClosing (WindowEvent e) {System.exit(0);} } ); // Istruzione per chiusura frame Container contentPane=getContentPane(); contentPane.add (new MioPanel()); // Crea+Insert MioPanel in MioFrame } } class MioPanel extends JPanel // Definisce classe MioPanel { public void paintComponent (Graphics gr) { super.paintComponent (gr); // Prepara sfondo, ecc. gr.drawString ("Stringa (disegnata)",75,100); // Disegna stringa } } public class TestMioFrame public static void main (String[]args) { JFrame frame=new MioFrame(); // Crea MioFrame con inserito MioPanel frame.show(); // Display MioFrame } } L'aggiornamento del display e' fatto automaticamente dalle finestre, sotto controllo del gestore di eventi Java, per cui se la schermata dev'essere ridisegnata e' opportuno usare il metodo repaint anziche' paintComponent, per non interferire con il processo di aggiornamento automatico. Per l'approfondimento relativo alla gestione dei font e dei colori si rimanda al testo. ⌡ 14.5.4 - Metodi per disegnare immagini ----------------------------Per inserire un'immagine memorizzata sul disco locale o su Internet si deve prima leggerla in formato GIF o JPEG, in un oggetto Toolkit usando il metodo getDefaultToolkit della classe Toolkit. Esempio di lettura di un'immagine dall'hard-disk locale: String name="gifIcon.gif"; Image image=Toolkit.getDefaultToolkit().getImage(name); Esempio di lettura di un'immagine da Internet: URL u=new URL("http://www.sito.com/gifIcon.gif"); Image image=Toolkit.getDefaultToolkit().getImage(u); La variabile oggetto image punta all'oggetto, che incapsula l'immagine del file gifIcon.gif e che puo' essere visualizzato con il metodo drawImage della classe Graphics: public void paintComponent (Graphics g) { ...... gr.drawImage (image,x,y,null); } Nel display di immagini si presentano problemi particolari (per esempio dovuti al ritardo dello scaricamento delle immagini dal Web), su cui non vogliamo soffermarci e per i quali rimandiamo al testo per i dettagli. Sul dischetto delle esercitazioni e' riportato l'esempio (IconTest.java) seguente: /** * Crea un frame richiudibile con immagine (icona) e lo mostra su video */ import java.awt.*; import java.awt.event.*; import javax.swing.*; // ---- Definisce IconFrame con titolo, ampiezza e posizione ---class IconFrame extends JFrame { public IconFrame () { setTitle ("IconFrame"); setSize (300,200); setLocation (100,50); // Posiziona il frame (0,0=alto a sinistra) Toolkit tk=Toolkit.getDefaultToolkit(); // Crea l'oggetto Toolkit Image img=tk.getImage ("gifIcon.gif"); // Vi incapsula l'immagine setIconImage (img); // Definisce l'immagine img come icona del frame addWindowListener (new WindowAdapter() { public void windowClosing (WindowEvent e) {System.exit(0);} }); // Termina il thread (chiude il frame) a fine main } } // ---- Crea l'oggetto frame con icona e lo mostra (in alto a sinistra) ---class IconTest { public static void main (String[]args) { JFrame frame=new IconFrame(); frame.show(); } } ⌡ 14.6 - EVENTI (I/O handling) ===================== Gli eventi generalmente sono acquisiti sotto interruzione dal sistema operativo (con le dovute priorita'). Il sistema operativo li presenta quindi alla parte di AWT (Abstract Window Toolkit), con cui comunica, che li trasforma in eventi AWT e li deposita in un'apposita coda di eventi gestita dal Java run-time. Un'altra parte di AWT trasmette poi gli eventi ai 'listener', eseguendo nell'ordine le seguenti operazioni: 1) Preleva un evento dalla coda; 2) Individua l'oggetto listener di tale evento; 3) Esegue il metodo del listener relativo a quell'evento. Il sistema run-time di Java 2 VM quindi acquisisce gli eventi off-line, gestendoli con i metodi predisposti dal programmatore, secondo le modalita' che specificheremo in questo paragrafo. Le interruzioni sono gestite dal nucleo del sistema operativo del computer locale e le relative informazioni (che chiameremo eventi o pseudo-interrupt) sono passate a Java 2 come messaggi. Per far funzionare questo sistema occorre prima attivare nel programma Java le possibili sorgenti degli eventi con il metodo seguente (in cui 'this' indica che la gestione dell'evento con 'actionPerformed' e' nello stesso programma): addActionListener (this); // Attiva pseudo-interrupt e poi quando gli eventi sono ricevuti da Java, vanno gestiti con il metodo appropriato, predisposto dal programmatore (pseudo-interrupt handler), che viene indicato con l'istruzione seguente: actionPerformed (ActionEvent evt) Anche in questo caso descriviamo le modalita' di programmazione realizzando un primo esempio di programma funzionante. Per questo prima introduciamo gli elementi di programmazione necessari e poi costruiamo gradualmente il programma di gestione di una finestra con tre pulsanti funzionanti. ⌡ 14.6.1-DICHIARAZIONI NECESSARIE ALLA CLASSE PER RICEVERE E GESTIRE EVENTI -----------------------------------------------------------------Occorre per prima cosa rendere accessibile la library java.awt.event con l'istruzione seguente: import java.awt.event.*; Poi occorre 'linkare' l'interfaccia appropriata (es. ActionListener per il pulsante del mouse o l'Invio della tastiera) con lo statement 'implements' nella dichiarazione della classe. Esempio: public class Graph extends java.applet.Applet implements ActionListener { Le interfacce disponibili, cioe' l'equivalente delle routines di servizio delle interruzioni, comprendenti la chiamata ad un corrispondente metodo predisposto dall'utente (per esempio ad 'ActionListener' corrisponde 'actionPerformed()'), per personalizzare l'azione di risposta all'azione, sono le seguenti: 1) 2) 3) 4) ActionListener per il pulsante del mouse o l'Invio della tastiera; MouseListener per il mouse; ItemListener per la scelta tra una lista o tra check-boxes; ..... ecc. Si possono rendere disponibili piu' interfacce, scrivendone i nomi dopo la parola chiave 'implements' separati da virgola, come nell'esempio seguente: public class Graph extends java.applet.Applet implements ActionListener, ItemListener { ⌡ 14.6.2 - ATTIVAZIONE ED ATTESA DI UN EVENTO ---------------------------------L'attivazione dell'attesa di un evento equivale all'abilitazione di un interrupt: per attivare la ricezione del pulsante del mouse (o del tasto 'Invio' della tastiera), cioe' per permettergli di generare un evento, dopo aver caricato ActionListener occorre per prima cosa creare su video il pulsante con l'oggetto 'Button', contenuto in ActionListener (JButton nella libreria Swing di interfacce utente GUI). Questo si fa richiamando il metodo add, come nell'esempio riportato in ⌡14.5.1, dove il pulsante viene anche inserito in un frame (crea ed attiva il pulsante 'redButton' con dentro la scritta "Red"): redButton = new JButton ("Red"); add (redButton); In Java 2 con i componenti GUI di Swing l'inserimento in un frame (finestra top level) di un pulsante si fa come nell'esempio delle esercitazioni, che qui riportiamo (PulsTest.java): // ===== Esempio che crea un frame con 1 pulsante e lo mostra su video ===== import java.awt.*; // Rende disponibile la library Abstract Window Toolkit import java.awt.event.*; // " " java.awt.event import javax.swing.*; // " i componenti d'interfaccia Swing // ---- Definisce il pulsante con titolo ---class PulsInPanel extends JPanel { public PulsInPanel() { redButton=new JButton ("Red"); add (redButton); } private JButton redButton; } // --- Definisce PulsFrame (=contenitore) con titolo,ampiezza e posizione --class PulsFrame extends JFrame { public PulsFrame () { setTitle ("Pulsante in Frame"); setSize (300,200); setLocation (100,50); // Posiziona il frame (0,0=alto a sinistra) Container contentPane = getContentPane (); contentPane.add (new PulsInPanel()); } } // ---- Crea l'oggetto frame con il pulsante e lo mostra ---class PulsTest { public static void main (String[]args) { JFrame frame = new PulsFrame (); frame.show (); } } ⌡ 14.6.3 - GESTIONE DEGLI EVENTI --------------------Si fa con il metodo 'actionPerformed', che svolge le funzioni di un 'user interrupt handler'. Ogni sorgente di eventi (attivati), relativa ad una periferica, ha un corrispondente metodo che dev'essere predisposto dall'utente per intraprendere le azioni conseguenti al verificarsi dell'evento. Per esempio, in aggiunta all'attivazione del redButton di cui sopra, bisogna inserire (nello stesso programma, perche' nell'esempio c'e' 'this') il metodo seguente: void public actionPerformed (ActionEvent evt) { ..... // Mettere qui' le istruzioni per l'user 'interrupt' handling } L'oggetto ActionEvent evt identifica la sorgente dell'interruzione con il metodo evt.getSource(): Object source = evt.getSource(); Utilizzando source il programmatore puo' far intraprendere al programma le azioni dovute con il metodo actionPerformed, come mostrato nell'esempio seguente TestEvent.java): // ---- Esempio che gestisce l'evento 'Pressione di un pulsante' ---import java.awt.*; import java.awt.event.*; import javax.swing.*; // --- Definisce i 3 pulsanti con titolo e ne attiva gli eventi --class PulsInPanel extends JPanel implements ActionListener // *** { public PulsInPanel() { yellowButton=new JButton ("Yellow"); redButton=new JButton ("Red"); blueButton=new JButton ("Blue"); add (yellowButton); add (redButton); add (blueButton); yellowButton.addActionListener (this); // Attiva pseudo-interrupt redButton.addActionListener (this); blueButton.addActionListener (this); } // ---- Metodo di gestione degli eventi (pseudo-interrupt handler) ---// (Azione: al click su un pulsante ridisegna il frame con il nuovo colore) public void actionPerformed (ActionEvent evt) { Object source = evt.getSource(); Color color = getBackground(); if (source==yellowButton) color=Color.yellow; else if (source==redButton) color=Color.red; else if (source==blueButton) color=Color.blue; setBackground (color); repaint(); } private JButton yellowButton; private JButton redButton; private JButton blueButton; } // Termine del metodo costruttore // --- Definisce PulsFrame (=contenitore) con titolo, ampiezza e posiz.--class PulsFrame extends JFrame { public PulsFrame () { setTitle ("Pulsanti in Frame"); setSize (300,200); setLocation (100,50); // Posiziona il frame (0,0=alto a sinistra) // L'istruzione seguente termina il thread alla chiusura del frame addWindowListener (new WindowAdapter() { public void windowClosing(WindowEvent e) {System.exit(0);} }); Container contentPane = getContentPane(); contentPane.add (new PulsInPanel()); } } // ---- Crea l'oggetto frame con 3 pulsanti e lo mostra sul video ---class TestEvent { public static void main(String[]args) { JFrame frame = new PulsFrame(); frame.show(); } } Molte altre informazioni sono necessarie al programmatore di interfacce GUI, poiche' molteplici sono gli eventi possibili. Si dovra' anche imparare, per esempio, a gestire gli eventi della tastiera o ad accedere alla coda degli eventi AWT. Per questo rimandiamo chi volesse approfondire questa conoscenza al libro di testo di Java 2, cap.8. Nella pratica comunque il compito e' facilitato dagli appositi sistemi integrati per sviluppo di applicazioni Java (come Symantec Visual Cafe' o Borland Jbuilder o IBM VisualAge o Sybase PowerJ). Con essi si puo' produrre facilmente i codici di interfaccia grafica necessari alle proprie applicazioni, evitando cosi' di entrare in dettagli di programmazione lunghi e tediosi (che e' comunque sempre opportuno conoscere). !--------------------------------------------------⌡ 14.7 - APPLET JAVA =========== ⌡ 14.7.1 - Premessa -------Il primo browser capace di eseguire i bytecode delle applet con una macchina virtuale Java e' stato HotJava, presentato nel 1995 dalla Sun: esso era stato scritto completamente in Java per dimostrare le potenzialita' del linguaggio. Questa capacita' di eseguire applet inserite nelle pagine Web non ha comportato pero' una grande diffusione di HotJava, perche' pochi utenti erano disposti a cambiare browser solo per acquisire questa nuova funzionalita'. La popolarita' delle applet si e' estesa quando, nel 1996, anche Netscape ha incluso una macchina virtuale Java nella nuova release suo browser Netscape 2.0, spingendo cosi' anche Internet Explorer a fare la stessa cosa. Il linguaggio Java da allora si e' sviluppato molto rapidamente dalla versione 1.0, gestita da questi browser nel 1996. Meno rapidamente sono stati aggiornati i browser, per cui oggi solo le loro versioni piu' recenti sono in grado di gestire buona parte di Java 1.1, ma non ancora Java 2. In aggiunta a questa problematica nell'uso del linguaggio Java nelle applet, c'e' da considerare che vi sono fastidiose limitazioni ed incompatibilita' tra linguaggio e browser. Cio' perche' Microsoft probabilmente non fara' gestire al suo browser quelle parti di Java che risultano concorrenziali con la propria tecnologia. Inoltre Netscape che potrebbe farlo e' ormai praticamente uscita dal mercato delle macchine virtuali (consentira' pero' l'uso di altre macchine virtuali Java nel proprio Navigator). Per combattere questa situazione, che potrebbe compromettere l'uso delle applet Java, Sun ha rilasciato un pacchetto, chiamato Java Plug-In, che innestandosi nei due browser Navigator ed Explorer, consente loro di eseguire applet Java con tutte le funzionalita' aggiornate, utilizzando un ambiente run-time esterno fornito da Sun. La macchina virtuale risultante potra' cosi' essere mantenuta aggiornata da Sun, per permettere di eseguire anche le nuove funzionalita' del linguaggio, comprese nelle versioni Java 2 e successive. Questa soluzione risulta pero' un po' limitativa per le applicazioni in rete globale perche', se il visitatore di una pagina Web non ha sul proprio computer il Java Plug-In gia' installato, gli si deve richiedere di installarlo per vedere l'applet, cliccando sull'apposito pulsante che l'amministratore del sito potra' aver predisposto per consentire il download del Plug-In. Questa procedura, oggi largamente usata, produce una diffusione del Plug-In, anche se e' un pacchetto voluminoso, poiche' basta caricarlo una sola volta. L'alternativa all'uso del Plug-In sarebbe quella di programmare applet con le sole funzioni di Java 1.0; in tal caso pero' l'applet risulterebbe piu' semplice ed allora presumibilmente sarebbe anche possibile farne a meno, realizzando le sue funzionalita' con altri strumenti come JavaScript per le funzioni logiche dei programmi, i form per l'immissione dei dati ed i file GIF per le animazioni. Questa situazione sembra limitare un poco l'utilizzo in rete delle applet Java per le applicazioni piu' complesse, anche per il maggior onere temporale dello scaricamento da rete di codici estesi, lasciando invece intatte tutte le potenzialita' del linguaggio Java per le applicazioni da eseguire sulle macchine locali, al fine di usufruire dei suoi vantaggi quali l'indipendenza dalla piattaforma e l'accesso agevolato ai database ed alla rete. L'istallazione sul Web pero' offre altri vantaggi quali la disponibilita' per l'utente di piu' applicazioni che non sul disco locale, la maggiore facilita' di manutenzione ed aggiornamento delle applicazioni (basta aggiornare l'unica copia sul Web anziche' le copie locali sui computer di tutti gli utenti). Per questo oggi un fertile campo per le applicazioni Java di successo e' quello delle reti Intranet aziendali. In questi casi l'istallazione 'una tantum' dei Plug-In sulle macchine della rete Intranet non pone problemi e la maggiore larghezza di banda consente di trasferire efficientemente anche codici estesi. Il quadro risultante per l'uso delle applet e' cosi' il seguente: 1) Nelle reti Intranet, con i Plug-In istallati su tutti i computer client, si utilizzano applet, con le funzioni Java piu' avanzate, per avere meno problemi di portabilita', una migliore manutenibilita' del software ed una maggiore flessibilita' nella programmazione. 2) Sulla rete globale Internet, volendo evitare al visitatore l'istallazione del Plug-In, si possono usare (invece delle applet): - lo scripting per le convalide; - i file GIF per le animazioni; - i form e l'elaborazione su server (script CGI, servlet Java o linguaggi di scripting per server) per l'immissione dei dati. Nei limiti del nostro corso, ci limiteremo ora a mostrare con alcuni esempi come si programma e come funziona un'applet Java, lasciando poi alla buona volonta' dello studente gli eventuali approfondimenti, reperibili sul testo (Horstmann). ⌡ 14.7.2 - Esempi di applet ---------------Le applet sono programmi Java di particolare interesse poiche' sono programmi che vengono scaricati attraverso la rete con le pagine HTML ed il loro codice (bytecode) viene eseguito dall'interprete Java, chiamato Virtual Machine (VM), contenuto nei browser, come Netscape Navigator o MS Internet Explorer o HotJava della Sun (scritto in Java). Per provarli, se si vuole evitare di caricarli con un browser WEB, viene fornito con i sistemi di sviluppo dei programmi un apposito visualizzatore di pagine HTML chiamato 'appletviewer': con esso faremo le nostre prove. La diversita' dell'ambiente in cui le applet lavorano (la pagina HTML) comporta che esse hanno una struttura diversa da quella delle applicazioni Java viste finora. Per prima cosa le applet non hanno un blocco main(). Hanno invece vari altri blocchi in funzione di quello che devono fare. Due blocchi standard sono: 1) init() per inizializzare quanto occorre; 2) paint() per impostare la grafica necessaria. Vediamoli all'opera su un esempio gia' visto in precedenza, trasformato ora in applet (tutti i files sono presenti sul dischetto delle esercitazioni): /* ---- Primo esempio di Applet ---Dopo la compilazione con: javac PrimoApplet.java puo' essere eseguito con: appletviewer PrimoApplet.html */ public class PrimoApplet extends java.applet.Applet { int numero; // --- Blocco di inizializzazione --public void init () { numero = 225; } // --- Display di testo+dato dentro l'applet (posiz.=25,50) --public void paint (java.awt.Graphics gr) { gr.drawString ("La radice quadrata di " + numero + " e' " + Math.sqrt(numero),25,50); } } Questa applet dev'essere inserita in una pagina HTML che possiamo battere, nella sua forma piu' semplice, con un comune editor e poi salvare con il nome PrimoApplet.html: <applet code="PrimoApplet.class" height=100 width=300> </applet> In questo file abbiamo inserito il nome dell'applet e le dimensioni della finestra di display dell'applet. L'esecuzione, con l'ausilio dell'appletviewer, si ottiene battendo: appletviewer PrimoApplet.html ⌡ 14.7.3 - Passaggio di parametri ad un'applet ----------------------------------Si possono passare parametri ad un'applet da una pagina HTML con il tag <param> modificando cosi' il file PrimoApplet.html: <applet code="SecondoApplet.class" height=100 width=300> <param name="NUMERO" value=196> </applet> Le modifiche richieste nel programma SecondoApplet.java, per acquisire il numero, sono quelle indicate nel blocco init(): /* --- Secondo esempio di Applet con lettura di parametro su pagina HTML --Dopo la compilazione con: javac SecondoApplet.java puo' essere eseguito con: appletviewer SecondoApplet.html */ public class SecondoApplet extends java.applet.Applet { int numero; // --- Blocco di inizializzazione --public void init () { String parameter = getParameter("NUMERO"); // <----- Righe cambiate if (parameter!=null) numero=Integer.parseInt(parameter); // " " } // --- Display di testo+dato dentro l'applet (posiz.=25,50) --public void paint (java.awt.Graphics gr) { gr.drawString ("La radice quadrata di " + numero + " e' " + Math.sqrt(numero),25,50); } } Per vedere il risultato bisogna poi, anche in questo caso: 1) Compilare con javac SecondoApplet.java 2) Eseguire con appletviewer SecondoApplet.html Si possono passare quanti parametri occorrono con il tag <param>, purche' ognuno abbia un attributo 'name' diverso. Questo apre la possibilita' al server Web di confezionare appositamente delle pagine HTML con parametri da passare all'applet in funzione dei risultati delle elaborazioni da lui eseguite. Un'utile sorgente di informazioni su come funzionano le applet ed i tag HTML sono le pagine WEB scaricate dalla rete. Basta visionarne il codice HTML: - con Navigator cliccare su: View --> Document --> Source - con Explorer cliccare su: View --> Source ⌡ 14.8 - PROBLEMI DI SICUREZZA E PROGRAMMAZIONE IN RETE ============================================== ⌡ 14.8.1 - Sicurezza con le applet ----------------------Una volta scaricata una pagina Web il browser (se abilitato) esegue tutte le applet che vi incontra. L'utente non ha possibilita' di confermare o interrompere l'esecuzione. Quindi e' necessario considerare i problemi di sicurezza, dato che navigando in rete possiamo scaricare da un sito remoto pagine contenenti applet di provenienza non garantita, che potrebbero anche minacciare l'integrita' del nostro sistema locale. Per questo motivo nel browser sono state inserite delle apposite funzioni per la gestione della sicurezza delle applet: quando un'applet tenta di violare le regole di accesso stabilite, viene prodotta un'eccezione di tipo SecurityException che ne blocca l'attivita'. Vediamo allora, nel rispetto di queste regole, cosa possono fare e non fare le applet. - Le 1) 2) 3) 4) applet possono: visualizzare immagini; riprodurre suoni; ricevere pressione di tasti e click del mouse; trasmettere all'host l'input dell'utente (per esempio per fare un ordine di commercio elettronico). - Le applet non possono: 1) alterare il sistema dell'utente; 2) prelevare informazioni contenute nel sistema dell'utente (come il nome dell'utente, il suo indirizzo di posta elettronica, ecc); 3) avviare un programma sul sistema dell'utente; 4) comunicare con un host diverso da quello da cui sono state prelevate (server di origine). 5) leggere o scrivere sul file system del computer client; Questi controlli sono possibili perche' il bytecode Java e' eseguito sotto interprete, che puo' verificare l'istruzione prima di eseguirla, impedendo cosi' l'esecuzione di applet 'ostili', che possono violare le regole della sicurezza (vedi libro a pag.528). Queste restrizioni risultano pero' anche fortemente limitative delle possibilita' di programmazione in rete offerte dal linguaggio Java. Per risolvere questi problemi e' stata introdotta la tecnica delle firme digitali, che trattiamo per completezza e perche' la sua conoscenza puo' risultare utile anche nel mondo del lavoro. ⌡ 14.8.2 - Certificati e firme digitali ---------------------------Si tratta di una tecnica che garantisce sia l'autenticita' di un messaggio ricevuto su Internet (cioe' che non e' stato intercettato e modificato da un eventuale hacker), che l'identita' del mittente. Per garantire l'autenticita' di un messaggio gli si aggiunge una stringa, chiamata 'message digest', risultante dall'applicazione di un algoritmo al messaggio (come per i check-sum). Per esempio JDK1.2 usa due algoritmi: SHA-1 che produce una stringa di 20 bytes e MD5 che ne produce una di 16. Questo pero' non e' sufficiente poiche', essendo gli algoritmi di dominio pubblico, un hacker potrebbe intercettare il messaggio, modificarlo, calcolare la nuova stringa e reinstradarlo al destinatario, che non potrebbe accorgersi della manipolazione. Per risolvere il problema si puo' criptare il 'message digest' con una chiave segreta. Se questa fosse unica pero' andrebbe usata sia per criptare che per decriptare e quindi l'utente di una banca non avrebbe la garanzia che questa chiave, nota anche agli impiegati della banca, non possa essere divulgata, vanificando cosi' la sicurezza ricercata. Si e' cosi' introdotto un sistema di crittografia del 'message digest' basato su una coppia di chiavi: una 'chiave privata' a disposizione del mittente, con cui cripta il message digest, e l'altra 'pubblica' con cui il destinatario puo' decriptare il message digest, con tutta sicurezza poiche', se un eventuale hacker tentasse la ricodifica del message digest con una sua coppia di chiavi, il destinatario ricostruirebbe con la sua chiave pubblica un message digest diverso da quello inviato dall'hacker. La firma digitale (digital ID) consiste in questo metodo di criptaggio dei message digest (e non del messaggio che puo' anche essere spedito in chiaro). Si ottiene cosi' la garanzia che il messaggio non e' stato indebitamente manipolato lungo la rete. La segretezza del messaggio e' un altro problema che viene affrontato con altri diversi metodi specifici di crittografia. Per garantire l'autenticazione del messaggio occorre pero' risolvere anche un altro problema: come si puo' garantire che la chiave pubblica sia autentica e che non sia stata inviata da qualcuno che vuole spacciarsi per qualcun'altro? Per questo e' stato introdotto il 'certificato di autenticita'. Esso permette di risalire dalla chiave pubblica, consegnata al destinatario insieme al certificato, alle generalita' del mittente contenute nel certificato stesso. Quest'associazione, tra chiave pubblica e generalita' del mittente, e' garantita dalla firma digitale di un'autorita' di certificazione (CA), la cui autenticita' puo' essere verificata con la chiave pubblica della stessa CA, presente su un suo certificato che accompagna sempre quello del mittente. Questo autocertificato della CA viene trasmesso proprio perche' funge da contenitore della chiave pubblica della CA. I certificati vengono rilasciati a pagamento dalle CA (per esempio la Verisign fa pagare $20/anno). Per imparare a programmare invece si possono usare gli autocertificati ottenibili con gli strumenti di Java, Netscape e Authenticode di MS (che non hanno valore legale). ⌡ 14.8.3 - Thrusted applets ---------------Le applet sono limitate nelle loro possibilita' dal Security Manager di Java (si dice che le limita nella sandbox), come spiegato in precedenza. E' pero' possibile concedere loro di eseguire anche le operazioni potenzialmente pericolose per la macchina client, rendendole 'thrusted applet', ovvero passando l'iter dell'autenticazione con la tecnica del certificato di autenticita' e della firma digitale. In altre parole l'utente, avendo la garanzia dell'identita' del mittente di un'applet, concede di ridurre le protezioni nel proprio browser al fine di consentire all'applet di scrivere sull'hard disk locale, di avviare l'esecuzione di programmi, ecc. per svolgere un piu' sofisticato lavoro di programmazione in rete. La procedura per creare un 'thrusted applet' e mandarlo in esecuzione sulla macchina client non e' affatto semplice e richiede l'uso di vari strumenti. Noi li esamineremo in dettaglio, non solo per imparare a gestire applicazioni di commercio elettronico o di banking in rete (per qualche studente sicuramente risultera' utile), ma anche perche' questo e' il metodo per controllare la concessione di privilegi di accesso alle risorse locali alle applet Java sulle macchine client, e questo e' un passo fondamentale che ci permettera' di progettare la programmazione distribuita in rete di applicazioni scientifiche. Il produttore dell'applet deve: - produrre un certificato e creare una coppia di chiavi (digital ID); - porre la coppia di chiavi in un database detto 'keystore' - inserire l'applet in un archivio .jar e firmarlo - esportare il certificato con la chiave pubblica (cioe' con KeyTool.exe genera un file .cer che va inviato al destinatario) L'utente destinatario deve: - inserire nel proprio keystore il certificato - concedere i privilegi di accesso richiesti sulla propria macchina usando il PolicyTool Il modo piu' veloce per imparare ad usare i thrusted applets e' di farne funzionare uno. Per questo mostreremo un esempio riportato nei tutorial di Java al sito: http://java.sun.com/docs/books/tutorial/security/1.2/toolsign/index.html ⌡ 14.8.4 - Sicurezza sotto Security Manager di Java (JDK1.2) ------------------------------------------------Seguendo l'esempio della Sun eseguiremo tutti i passi necessari per la creazione, distribuzione ed esecuzione di un thrusted applet, usando gli strumenti del JDK1.2 (keytool, jarsigner, PolicyTool e jar). Programmi e dati del test li caricheremo in un'apposita directory C:\Test. A) Nell'esempio il produttore dell'applet e' Tizio. Per prima cosa si dovra' generare una coppia di chiavi che mettera' in un database keystore, che chiamera' tiziostore. Nel sistema il certificato e le chiavi saranno referenziati con uno pseudonimo (detto alias in Java e nickname in Netscape). Per Tizio sara' usato l'alias 'signFiles' per le chiavi che sono in tiziostore. Poi Tizio dovra' produrre il proprio certificato di autenticita' che sara' chiamato Tizio.cer Infine dovra' inserire il thrusted applet in un contenitore .jar e apporgli la firma. B) L'utente destinatario nell'esempio si chiama Caio. Egli dovra' importare il certificato di Tizio in un proprio keystore, che chiamera' caiostore, e lo referenziera' con l'alias 'tizio'. Dovra' poi usare il proprio PolicyTool per stabilire quali privilegi concedere al thrusted applet di Tizio (puo' accorgersi di quali sono necessari all'applet facendolo eseguire e segnando tutti i messaggi di SecurityException forniti dal Security Manager di Java ogni volta che si tenta l'accesso ad una risorsa protetta). Potra' poi eseguire l'applet sotto tutela del Security Manager di Java. Per questo occorre un'indicazione esplicita all'interprete: ... -Djava.security.manager ... (nelle applicazioni) ... -J-Djava.security.policy ...(negli applet) Se eseguiamo il test su un solo computer (stand-alone), useremo la stessa directory C:\Test per caricare sia i files di Tizio che quelli di Caio. Nel nostro esempio vogliamo eseguire un applet chiamato Writ.class, che sara' contenuto nel file AppletWri.jar e che legge il file Test.dat sull'hard disk locale per poi riscriverlo con lettere maiuscole: queste elaborazioni sarebbero normalmente proibite dal Security Manager, ma se eseguiamo le operazioni di firma digitale dell'applet che ora descriviamo, l'applet, divenuto thrusted, potra' eseguirle senza ostacoli. I codici della pagina HTML e dell'applet sono contenuti tra i files delle esercitazioni con i nomi Writ.html e Writ.java: <html> <head><title>Prova thrusted applet</title></head> <body> <Applet Code="Writ.class" archive="AppletWri.jar" width=400 height=210> </Applet> </body></html> // Esempio di Applet che legge Test.dat e scrive CapTest.dat (con maiuscole) import java.io.*; import java.awt.*; public class Writ extends java.applet.Applet { public void paint (Graphics g) { String NPath = getCodeBase().toString(); int LName = NPath.length(); NPath = "C:"+NPath.substring (8,LName); File sorgente = new File(NPath,"Test.dat"); boolean esistefile = sorgente.exists(); Long LFile = new Long (sorgente.length()); if (esistefile==true) { g.drawString ("lunghezza del file "+NPath+"Test.dat: " +LFile.toString()+"byte.",5,50); try { File temp = new File (NPath,"CapTest.dat"); // --- Crea flusso input --FileReader fr = new FileReader (sorgente); BufferedReader in = new BufferedReader (fr); // --- Crea flusso output --FileWriter fw = new FileWriter (temp); BufferedWriter out = new BufferedWriter (fw); boolean eof = false; int inChar = 0; // --- Loop lettura+modifica+scrittura --do { inChar=in.read(); if (inChar!=-1) { char outchar=Character.toUpperCase ((char)inChar); out.write (outchar); } else eof=true; } while (!eof); in.close(); out.close(); } catch(IOException e) {g.drawString("Errore-"+e.toString(),5,50);} catch(SecurityException se) {g.drawString("Errore-" +se.toString(),5,50);} } else g.drawString("Il file "+NPath+"Test.dat non esiste.",5,50); } } Le operazioni necessarie per firmare questo applet sotto Security Manager di Java JDK1.2 sono, nell'ordine: OPERAZIONI DEL PRODUTTORE DEL SOFTWARE (Tizio) ============================================== Generazione della coppia di chiavi ---------------------------------Cominciamo con la generazione di una coppia di chiavi con lo strumento keytool: c:\Test\keytool -genkey -alias signFiles -keypass kpi135 -keystore tiziostore -storepass ab987c Sono richieste due chiavi (che occorre appuntarsi perche' saranno richieste anche nel seguito). Esse sono: keystore password = ab987c (protegge l'intero keystore) keyentry password = kpi135 (protegge la chiave privata) Rispondere quindi alle domande poste dal keytool sulle proprie generalita'. Questo comando genera il keystore 'tiziostore' in C:\Test ed un autocertificato valido per 90 giorni. Le chiavi, contenute in tiziostore, saranno referenziate con l'alias signFiles. Generazione dell'autocertificato -------------------------------Si fa con la funzione export di keytool: c:\Test\keytool -export -keystore C:\Test\tiziostore -alias signFiles -file Tizio.cer Il tool chiede la password ab987c e genera il certificati Tizio.cer nel database tiziostore relativo all'alias signFiles. Inserimento dell'applet nel file contenitore .jar ------------------------------------------------Si fa con lo strumento jar.exe, avendo prima compilato Writ.java in C:\Test. Se si e' gia' posizionati con cd \Test basta digitare: jar cf AppletWri.jar Writ.class Questo comando crea il file AppletWri.jar, che potremo firmare con: c:\Test\jarsigner -keystore C:\Test\tiziostore -storepass ab987c -keypass kpi135 AppletWri.jar signFiles OPERAZIONI DELL'UTENTE DEL SOFTWARE (Caio) ========================================== Verifica dei privilegi richiesti -------------------------------Si esegue l'applet con il comando: C:\Test\Appletviewer Writ.html e si prende nota dei privilegi richiesti. Importazione del certificato ---------------------------Per importare il certificato Tizio.cer fornito dal produttore nel proprio keystore, chiamato 'caiostore', basta digitare: c:\Test\keytool -import -alias tizio -file Tizio.cer -keystore caiostore Quando chiede la chiave si puo' battere ab987c Questo comando crea il keystore caiostore e vi inserisce il certificato Tizio.cer, referenziandolo con l'alias 'tizio'. Concessione dei privilegi (agli applet firmati tizio) ------------------------Si avvia il Policy Tool con C:\Test\Policytool Si inserisce l'URL di keystore con: Edit-->Change KeyStore e poi New KeyStore URL = file:/C:/Test/caiostore e OK Poi nella finestra Policy Tool cliccare su Add Policy Entry che permette di associare all'alias tizio i privilegi richiesti. Nella finestra Permission inserire: FilePermission: Target Name : Actions : java.io.FilePermission << ALL FILES >> read,write,delete,execute e OK Salvare quindi il policy file cosi' creato cliccando nella finestra del Policy Tool su 'File' e 'Save As' e battendo come nome del file: C:\Test\caiopolicy Questo completa la procedura. Se tutto e' andato bene in C:\Test devono esserci i seguenti files: 1) 2) 3) 4) 5) AppletWri.jar Writ.html caiostore caiopolicy Test.dat (questo e' un qualunque file di testo da leggere con l'applet) ESECUZIONE DEL TEST DEL THRUSTED APPLET ======================================= Si fa con il comando: C:\Test\Appletviewer -J-Djava.security.policy = caiopolicy Writ.html L'esecuzione e' corretta se scrive: "Lunghezza del file C:\Test\Test.dat: nnn byte" e in C:\Test scrive il file CapTest.dat ⌡ 14.8.5 - Sicurezza sotto Security Manager di Netscape ed IE -------------------------------------------------Purtroppo allo stato attuale di queste tecnologie le procedure da seguire sono diverse per le varie Java VM e relative versioni. ⌡ 14.9 - PROMEMORIA FINALE ================= ⌡ 14.9.1 - RIEPILOGO SINTETICO ------------------Riportiamo gli elementi concettuali e le definizioni introdotte: |--> ATTRIBUTI (dati, parametri in/out) OGGETTO-->| (CLASS=Master copy dell'oggetto) |--> METODI (Comportamento, istruzioni) METODI: sono i task distinti compresi nella classe (sezioni della class) Esempio: println() Metodi e variabili sono richiamabili con un '.' dopo l'oggetto Esempio: Cocker.parla(); EVENTI: con essi si gestisce l'I/O acquisito sotto 'interruzione'. --------- . ----------CLASSI (Programmi): -----------------class = inizio di un programma o sottoprogramma main = 'main program' (quello lanciato al RUN) public class Cane <---- class usabile per creare oggetti 'Cane' { String name; <---- usa string variable 'name'=ATTRIBUTO public void parla() <---- metodo (comportamento) dell'oggetto { System.out.println ("Bau! Bau!"); } <--- fine sottoprogramma parla (=Metodo di Cane) } <--- fine programma Cane (=Master copy degli oggetti 'Cane') Uso: CREAZIONE DELL'OGGETTO 'Cocker': ASSEGNAZIONE DEGLI ATTRIBUTI A 'Cocker': RICHIAMO DEL METODO 'parla' dell'oggetto 'Cocker': RICHIAMO DELLA VARIABILE 'n' dell'oggetto 'virus': Cane Cocker = new Cane(); Cocker.name="Chicca"; Cocker.parla(); virus.n = 92; Esempio: l'istruzione Cocker.parla(); produce in output "Bau! Bau!") OGGETTI: -------Se nella classe Virus c'e' il metodo Virus (stesso nome), questo viene eseguito alla prima definizione dell'oggetto. Esempio: // Programma principale class VirusStart { public static void main (String arguments[]) { Virus influenza = new Virus(); Virus raffreddore = new Virus(); Virus aids = new Virus(); System.out.println("Ci sono " + Virus.getVirusCount() + " virus."); } } // class Virus { static int virusCount = 0; <-- def.class variable=COMMON: non eseguibile public Virus() <--- metodo Virus (stesso nome: e' eseguito { virusCount++; alla dichiarazione dell'oggetto) } static int getVirusCount() { return virusCount; } } Eseguendo java VirusLook viene stampato: "Ci sono 3 virus." Il simbolo ++ e' usato per l'incremento: es. influenza.virusCount++; METODI (=sottoprogrammi): ------------------------Sono sottoprogrammi che hanno uno o piu' parametri in ingresso e uno (=FUNCTION) o nessun parametro in uscita se c'e' void (=PROCEDURE con passaggio di parametri di classe/oggetto, come area COMMON). N.B. due metodi possono anche avere lo stesso nome se hanno un diverso numero di parametri in ingresso. Esempio di metodo inseribile nella classe virus per infettare filename (ritorna con success=true se lo ha infettato): boolean public infectFile (String filename) { boolean success = false; // Qui' c'e' il codice infettante (se infetta mette success=true) ................. return success; <--- questa e' la variabile passata (del tipo } indicato nell'intestazione: FUNCTION) Il metodo si richiama come le FUNCTION. Esempio se ho l'oggetto malaria della classe virus, posso scrivere: if (malaria.infectFile(currentFile)) System.out.println(currentFile + " e' stato infettato"); else System.out.println(currentFile + " non e' stato infettato"); Un metodo getHour con 'public' si puo' usare per far accedere altri programmi ad una variabile privata Hour: int public getHour() { return Hour; } <--- Hour = valore ritornato Altro esempio di metodo con attributo newValue in input (con 'public' e' richiamabile anche fuori della classe): void public setHour(int newValue) <--- newValue = valore input { if (newValue<24) newHour = newValue; } <--- senza return, perche' non ritorna valori (void): e' una PROCEDURE che modifica newHour=variabile di classe (=COMMON) METODI DI CLASSE: ----------------Rendono disponibili le funzionalita' a tutta una classe anziche' ad un solo oggetto. Hanno 'static' nella dichiarazione. Esempio: static void showVirusCount() { System.out.println("Ci sono " + virusCount + " virus."); } Richiamabile con: virus.showVirusCount(); !-----------------------------------------------------------CAPITOLO 15 - Programmazione sul Web: Web Server, HTML, CGI, Java applet ═══════════ Riassunto: In questo capitolo viene presentato il Web server ed una breve introduzione al linguaggio HTML, alle pagine Web dinamiche (con script CGI), ed attive (con tecnologia Java). ⌡ 15.1 - CENNI AL LINGUAGGIO HTML -----------------------HTML (HyperText Markup Language) e', come dice il nome, un linguaggio ipertestuale basato su marcatori (Markup), appositamente creato dai ricercatori del CERN per creare pagine Web; lo scopo iniziale era quello di scambiarsi rapidamente on-line documenti su supporto elettronico anziche' cartaceo. Nel 1990 con il progetto WWW (World Wide Web il metodo ipertestuale e intuitivo per accedere alle risorse sulla rete), al CERN nasce Internet com'? oggi con i browser HTML per la navigazione su Web, il primo dei quali (Mosaic) nasce nel 1993. Oggi le regole HTML sono stabilite dal consorzio W3 (vedi http://www.w3.org). La tecnica di inserire con un text editor dei marcatori in un documento e' stata usata fin dai tempi dei primi word processor. Inserendo nel testo sequenze di caratteri che non potevano comparire nel documento originario si davano direttive al programma di impaginazione e stampa per pilotarne il comportamento: per esempio si usava una riga che inizia con un punto esclamativo seguita dai caratteri dell'apposita direttiva per ottenere la stampa con caratteri corsivi da quel punto in poi. Con i marcatori (tag) HTML si definiscono gli elementi logici del documento, come titoli, paragrafi, elenchi, ecc. Il programma di impaginazione e stampa contenuto nel browser Web (Netscape, Explorer, ecc.) provvedera' ad assegnare una formattazione standard che rendera' l'output gradevole. Con la versione HTML 3.2 sono poi state introdotte anche delle ulteriori funzioni di formattazione che permettono di sviluppare la creativita' degli autori delle pagine Web. Per esempio per scrivere una frase in rosso basta inserire il tag <font color="red"> nel modo seguente: <font color="red">Nel mezzo del cammin di nostra vita</font> Il tag </font> ne chiude l'effetto. Non tutti i tag necessitano del tag di chiusura. I tag sono 'case insensitive' cioe' possono essere scritti indifferentemente con lettere minuscole o maiuscole. Queste caratteristiche rendono HTML facile da usare perche' non presuppone ne' conoscenze tecniche, ne' capacita' di programmazione. Grazie a cio' molte persone hanno potuto avvicinarsi al Web Publishing senza particolari ostacoli ed oggi su Internet esistono milioni di creatori di pagine Web. HTML quindi non e' un vero linguaggio di programmazione ma un sistema di marcatura di sorgenti composti da ipertesti, cioe' testi, senza alcun elemento grafico, video o sonoro (anche se questi poi possono risultare presenti nell'output prodotto dal browser) contenenti collegamenti ipertestuali. Un ipertesto e' un documento composto di argomenti contenenti riferimenti ad altri argomenti (collegamenti ipertestuali) inseriti al fine di consentire la lettura saltando da un argomento all'altro (navigazione). Un esempio di utilizzo di ipertesti ? la Guida di Windows e Hypercard di Macintosh. Un collegamento ipertestuale e' un puntatore contenuto in una pagina WEB che punta ad un altro file sul WEB, che in genere e' un'altra pagina WEB, ma potrebbe anche essere un file multimediale o addirittura un programma. La presenza di un collegamento ipertestuale in un testo e' resa visibile dal browser con un colore diverso, mentre risulta invisibile su un'immagine (pero' il cursore cambia aspetto quando ci si passa sopra). Cliccando su un collegamento ipertestuale il browser preleva il file dall'indirizzo specificato e lo visualizza se e' una pagina HTML oppure lo apre (con il programma associato: per esempio con un riproduttore di suoni se e' un file WAV). Il linguaggio ipertestuale HTML (HyperText Markup Language) e' quindi il linguaggio che definisce le direttive per l'impaginazione e lo sviluppo di documenti interattivi. Il browser WEB esegue la formattazione della pagina in base a queste direttive (tag) contenute nel codice HTML, adeguando la larghezza delle righe alla larghezza dello schermo o della finestra. Cosi' browser diversi potranno mostrare visualizzazioni diverse per la stessa pagina WEB. Tutti gli elementi della pagina (informazioni e link) sono cosi' al loro posto ma l'aspetto sul video potr? essere diverso. Le specifiche sono curate dal consorzio W3C (vedi http://www.w3c.org), cui partecipano Adobe, HP, IBM, MS, Netscape, Novell, SoftQuad e Sun. HTML permette la creazione di pagine WEB interattive, nelle quali possiamo incorporare veri programmi con JAVA, GCI, ActiveX e VRML (Virtual Reality Modeling Language, per la visualizzazione di scenari a tre dimensioni all'interno delle pagine WEB). La codifica delle direttive (tag) necessarie al browser per l'impaginazione, nel codice HTML non sono un compito semplice, ma i nuovi editor HTML, progettati secondo la tecnica WYSIWYS ("What You See Is What You Get") sollevano l'utente dall'obbligo di conoscere i codici di formattazione del linguaggio ipertestuale, lasciandolo concentrare pi? facilmente sul lavoro creativo. HTML permette di produrre documenti elettronici che hanno il pregio di poter essere sfogliati con un computer, passando rapidamente da un argomento ad un altro ('navigazione') tramite i collegamenti (link) presenti nelle pagine, senza dover scorrere sequenzialmente il testo. Le pagine Web cosi' prodotte possono essere messe a disposizione di tutti gli utenti di Internet o limitata alla rete locale o Intranet aziendale. Uno dei metodi migliori di comprendere come funzionano i marcatori HTML e' quello di vederli all'opera su pagine Web fatte da altri. In questo modo, oltre ad acquisire idee nuove, si puo' anche importare parti di codice HTML nei propri files, dopo averli visualizzate, selezionate e copiate (con i comandi messi a disposizione dal browser). Gli strumenti software necessari per produrre documenti sorgente HTML sono: 1) Editor di testo (come NotePad), meglio se editor HTML (come Front Page o Dreamweaver della Macromedia); 2) Browser (come Explorer o Netscape Navigator); 3) Programma FTP (File Transfer Protocol), per inviare i files sul server Web (con Password e UserId, che impediranno ad altri di modificare i files); 4) Software grafico, se si vogliono inserire immagini nel testo (molte immagini preconfezionate si possono reperire come 'clip art' su siti come www.yahoo.com). La pubblicazione della pagina Web prodotta puo' essere fatta sull'hard disk della macchina locale per uso esclusivo dell'autore, o sulla rete locale rendendola accessibile ai soli utenti abilitati della rete locale, o sulla rete Intranet aziendale, compatibile Internet ed estesa geograficamente ma non accessibile, agli utenti generici di Internet o su un Web server pubblico di Internet, accessibile a tutti. Gli amministratori delle reti, contattabili via E.mail, devono fornire le informazioni necessarie per la pubblicazione sul Web. L'uso del formato standard HTML favorisce lo scambio di documenti, rendendoli indipendenti dalla piattaforma (CPU, sistema operativo, formato documenti di output, ecc.). ⌡ 15.1.1 - GENERALITA' SUI MARCATORI (=TAG) HTML ------------------------------------I TAG si dividono in due categorie: 1) TAG che definiscono gli elementi del documento, come titoli, elenchi, paragrafi, ecc.; 2) TAG che inseriscono oggetti come immagini, suoni, applet e sequenze animate. Un caso a parte sono i TAG che racchiudono i commenti: <!- ..commento.. -> Molti TAG possono avere un attributo ed un secondo TAG di chiusura composto dallo stesso TAG letterale, preceduto da una barra '/'. Esempio: <TAG>testo del documento</TAG> o con attributo: <TAG BGCOLOR="#RRGGBB">testo del documento</TAG> I TAG sono 'case insensitive' cioe' possono essere scritti indifferentemente con lettere minuscole o maiuscole. Siccome il browser eseguira' una formattazione automatica (pilotata dai TAG) verra' ignorato ogni tentativo di formattazione compiuto dall'autore inserendo righe vuote (con il tasto Invio) o spazi aggiuntivi. Cioe' per esempio: <TAG>testo del documento</TAG> produce lo stesso effetto di: <TAG> testo del documento </TAG> Questo tipo di formattazione del sorgente puo' comunque essere fatta per migliorarne la leggibilita'. I TAG possono essere annidati: per esempio, si puo' ottenere la stampa in grassetto e corsivo annidando i rispettivi TAG cosi': <B><I>testo del documento</I><B> (si noti l'ordine dei TAG di chiusura, che rispetta l'annidamento dei TAG). MARCATORI DI STRUTTURA ---------------------I TAG di struttura che e' opportuno inserire SEMPRE sono i seguenti: 1) <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//IT"> Identifica il formato del documento e la versione HTML usata. Non ha TAG di chiusura. In sua assenza il browser non interpreta i TAG seguenti come direttive di formattazione e li stampa cosi' come li incontra. 2) <HTML>...</HTML> Identifica la codifica HTML 3) <HEAD>...</HEAD> Identifica l'intestazione del documento 4) <TITLE>...</TITLE> Identifica il titolo del documento 5) <BODY>...</BODY> Identifica il corpo del documento All'interno del TAG <BODY> si possono facoltativamente inserire i seguenti attributi per modificare i colori: BGCOLOR="#RRGGBB" Modifica i colori Red,Green,Blue (RGB) definiti con 2 caratteri esadecimali (256 colori) BACKGROUND="URL" Mette immagine come sfondo TEXT="#RRGGBB" Modifica i colori del testo nel corpo della pagina Web. Esempio (pagina Web con sfondo): <BODY BGCOLOR="#3399CC" TEXT="#FFFFFF"> Esempio di documento con i TAG di struttura (da usare come modello standard di base per tutte le pagine HTML da creare): <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//IT"> <HTML> <HEAD> <TITLE>Titolo del documento</TITLE> </HEAD> <BODY> Testo del documento </BODY> </HTML> Esistono altri TAG di struttura che pero' non trattiamo in queste dispense perche' si riferiscono a casi particolari. MARCATORI PRINCIPALI -------------------I TAG di struttura che seguono sono quelli presenti pressoche' in tutti i documenti, per formattare i titoli, i paragrafi, gli elenchi e per fare risaltare parti del testo. Basta inserire la coppia di TAG per ottenere l'effetto voluto. Marcatura di <H1>...</H1> <H2>...</H2> ....... <H6>...</H6> un titolo: Titolo di primo livello (caratteri di dimensione massima) Titolo di secondo livello (caratteri di dimensione minore) seguono altri livelli da 3 a 5 Titolo di sesto livello (caratteri di dimensione minima) Marcatura di un paragrafo: <P>...</P> Indica un paragrafo ( </P> e' facoltativo ) <BLOCKQUOTE>...</BLOCKQUOTE> Indica un paragrafo da scrivere rientrato Marcatori di evidenziazione: <EM>...</EM> Evidenzia il testo racchiuso (generalmente in corsivo) <STRONG>...</STRONG> Evidenzia al massimo il testo racchiuso <B>...</B> Evidenzia il testo in grassetto <I>...</I> Evidenzia il testo in corsivo Marcatura di un elenco: <OL>...</OL> Indica un elenco numerato sequenzialmente <UL>...</UL> Indica un elenco non numerato (puntato) <LI> e' il TAG singolo (senza chiusura </LI>) che va messo all'inizio di ogni voce dell'elenco (per numerare o puntare). MARCATORI IPERTESTUALI PER COLLEGAMENTI (LINK) ---------------------------------------------Permettono di realizzare collegamenti ipertestuali sia interni ad uno stesso documento, sia esterni verso file esterni sullo stesso server Web che su altri computer collegati in rete. Questi collegamenti si identificano con il TAG di ancoraggio: <A>...</A> Definisce l'ancoraggio al quale si associa un riferimento con gli attributi seguenti: NAME="..." Definisce l'etichetta di destinazione del collegamento (corrisponde al punto in cui e' inserito il TAG nel documento) HREF="..." HyperText Reference: indica l'indirizzo collegato (interno o esterno al documento) a cui saltare con un click. Esempi: <A HREF="#ancora">Testo</A> per Rif.in stessa pag, messo con <A NAME='ancora'> <A HREF="Pag2.htm">Testo</A> per Rif.in stesso sito (Pag2.htm in stesso sito) <A HREF="http://www.globus.org">Testo</A> per Rif. esterni (sul Web) <A HREF="mailto:[email protected]">Testo</A> per invio automatico E.mail Per comprenderne il funzionamento invitiamo il lettore a sperimentare gli esempi seguenti visualizzandoli con un browser. Immettete prima il seguente testo con un editor (come NotePad) e salvatelo poi con il nome c:\documenti\cani.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//IT"> | <HTML> | <HEAD><TITLE>Cani</TITLE> | </HEAD> | <BODY> | File cani.html <H2>Abitudini dei cani</H2> | .... Testo del paragrafo ....... | </BODY> | </HTML> | Avviate il vostro browser e caricate l'indirizzo c:\documenti\cani.html per vedere il risultato. Ora introducete nel file un'etichetta label1 nel modo seguente, salvandolo con il nome c:\documenti\test1.html: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//IT"> <HTML> <HEAD><TITLE>Cani</TITLE> </HEAD> <BODY> <H2><A NAME="label1">Abitudini</A> dei cani</H2> .... Testo del paragrafo ....... </BODY> </HTML> | | | | | File test1.html | | | | Si osservi come questo ancoraggio non venga visualizzato dal browser. Utilizziamo ora questo ancoraggio, inserendo un collegamento ad esso nel testo dello stesso file salvandolo come test2.html, come segue: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//IT"> <HTML> <HEAD><TITLE>Cani</TITLE> </HEAD> <BODY> <H2><A NAME="label1">Abitudini</A> dei cani</H2> .... Testo del paragrafo ....... I cani hanno le <A HREF="#label1">abitudini</A>. </BODY> </HTML> | | | | | | File test2.html | | | | Questo collegamento, realizzato con il TAG <A HREF="#label1">, alla etichetta "label1", creata in altro punto dello stesso documento con il TAG <A NAME="label1"> viene mostrato dal browser evidenziando la parola tra i due TAG <A HREF="#label1">...</A>, cioe' "abitudini" e facendo cambiare il puntatore quando passa sopra questa parola (aspetto tipico dei collegamenti). Se il collegamento a #label1 viene invece fatto da una pagina esterna a questo documento, che si trova come file test1.html sul server Web cani.animali.org, si dovra' inserire nel testo l'indirizzo URL nel modo seguente: ...... testo prec. ...... Studiamo le <A HREF="http://cani.animali.org/test1.html#label1">abitudini</A> dei cani. ...... segue altro testo ..... Cliccando sulla parola evidenziata (cioe' abitudini, che e' compresa tra i due TAG) il browser preleva dalla rete il file indirizzato (test1.html sul server cani.animali.org con il protocollo http) e lo presenta su video alla posizione dell'etichetta "#label1". Lo scaricamento di files da un sito si fa allo stesso modo con l'URL del sito ed il protocollo ftp anziche' http (si ricordi che gli URL fanno differenza tra minuscole e maiuscole). MARCATORI INSERIMENTO DI IMMAGINI --------------------------------Il seguente TAG permette di inserire in un documento HTML files grafici in formato GIF (disegni) o JPG (fotografie) e non in altri formati: <IMG SRC="..."> Inserisce nel punto in cui e' il TAG l'immagine il cui URL, assoluto o relativo, e' inserito al posto dei puntini (non richiede TAG di chiusura). Esempio: <IMG SRC="immagine.gif"> = Image Search Sono previsti inoltre i seguenti attributi: ALIGN="..." Allinea immagine nel documento ALT="..." Indica un testo alternativo da visualizzare se l'immagine non viene visualizzata (per esempio perche' l'utente ha disabilitato la visualizzazione di immagini per velocizzare il browser). Esempio: <IMG SRC="image.gif" ALT="Commento">=Immagine+Commento BORDER=n Definisce il numero n di pixel della cornice di contorno della immagine, cioe' il suo spessore. HEIGHT=n Definisce l'altezza della cornice di contorno dell'immagine (n=numero di pixel) WIDTH=n Definisce la larghezza della cornice di contorno dell'immagine (n=numero di pixel) Esempio: <IMG SRC="image.gif" WIDTH=150 HEIGHT=180> =Immagine Altri parametri (HSPACE, VSPACE, ALIGN) e marcatori per governare allineamento e colori o per generare effetti speciali, immagini cliccabili, ecc. sono disponibili, ma non sono inseriti in queste dispense e nel nostro corso. Tra marcatori ed attributi esistono oltre 300 definizioni da imparare. Fortunatamente pero' questo lavoro e' reso largamente non necessario dai vari editor HTML, che introducono automaticamente nel codice HTML la codifica delle direttive (TAG) necessarie al browser per l'impaginazione, In particolare i nuovi editor HTML, progettati secondo la tecnica WYSIWYS ("What You See Is What You Get") sollevano completamente l'utente dallo onere di conoscere i codici di formattazione del linguaggio ipertestuale, lasciandolo concentrare pi? facilmente sul lavoro creativo. Libro (tascabile): Ray & Ray - HTML for dummies - Ed. Apogeo Sito con guida interattiva: www.html.it ⌡ 15.2 - IL SERVER HTTP APACHE --------------------A differenza del browser, il server Web e' un'applicazione abbastanza semplice, che deve rimanere costantemente in ascolto delle richieste che arrivano su una porta, per esaudirle. Esso costituisce il lato server dell'applicazione tipica di Internet: la navigazione in rete avviene infatti con un browser (client) che interroga un Web server, ottenendone informazioni e servizi per l'utente. Descriveremo qui' il Web server Apache, scaricabile gratuitamente dalla rete, perche' lo useremo nelle esercitazioni pratiche e perche' e' meno dipendente dalla piattaforma Microsoft-Windows. Quando nel nostro browser impostiamo un URL (Uniform Resource Locator), succede a grandi linee questo: 1) per prima cosa il nostro computer contatta il server HTTP, individuato dall'URL da noi indicato; 2) il server HTTP cerca il nome del file che abbiamo richiesto nel nostro URL e, una volta trovato, ci manda indietro il file; 3) come ultima cosa il nostro browser, una volta ricevuto il file, lo esamina, lo elabora e ce lo mostra a video, nove volte su dieci come una normale pagina Web (formato HTML). E' anche possibile pero' configurare il server HTTP in modo che, ogni volta che viene richiesto un file in una determinata cartella, non lo mandi direttamente al client ma lo esegua (script CGI) come un vero e proprio programma e solo l'output di tale programma venga inviato al computer client nel giusto formato perche' lo visualizzi (pagina Web dinamica). Un esempio puo' essere il counter: il computer di chi entra nel sito dove risiede il counter non fa alcuna elaborazione tranne visualizzare il file grafico .gif (se si tratta di un counter grafico), risultato dell'elaborazione fatta dal server: intercettare il visitatore, preparare un'immagine con il numero di accessi ed inviarla al client perche' la visualizzi. Questa funzionalita' viene chiamata Common Gateway Interface e questi programmi sono chiamati script CGI. CGI significa "Common Gateway Interface" ed e' il metodo con cui un server interagisce con database, documenti ed altri programmi, inviando o ricevendo dati; questi dati saranno poi inviati sul web in formato direttamente visualizzabile dal browser. Il linguaggio principale per scrivere i CGI e' il Perl, anche se e' possibile utilizzare altri linguaggi (soprattutto C). Per fare queste cose occorre programmare un server Web: per esercitarsi con queste tecniche di comunicazione non conviene lavorare su un vero Web server in rete. E' meglio installare e configurare un server Web su un PC disponibile, senza bisogno di reti e modem sempre collegati. Avviandone il funzionamento in multitasking, il browser potra' cosi' comunicare con un Web server Apache sullo stesso PC, simulando il collegamento in rete (funzionamento loopback di TCP/IP, con indirizzo 127.0.0.1). Per provare ad usare gli script CGI dovremo acquisire un minimo di conoscenza del linguaggio Perl (anch'esso scaricabile gratuitamente dalla rete) ed avere il suo interprete sul nostro PC. Non tratteremo questi argomenti nelle lezioni, ma faremo esempi del loro uso nelle esercitazioni. Per capire meglio l'interazione tra browser e server Web e' opportuno avere entrambi sul proprio PC e farli comunicare in multitasking, simulando il collegamento in rete. Per installare e configurare il Web server Apache sul proprio PC stand-alone (per poter lavorare senza collegamento in rete), dotato di sistema operativo Windows 98, basta scaricare il giusto download dal sito www.apache.org Con un doppio click si procedera' poi alla decompressione del file ed alla istallazione standard di Apache, che verra' eseguita in modo automatico. Per la successiva configurazione di Apache, il file principale da modificare e': c:\Programmi\Apache Group\Apache\conf\httpd.conf Vediamo com'e' strutturato tale file: intanto, ogni parametro risulta ampiamente commentato sul file stesso, per rendere chiaro il significato di quel parametro. I parametri di configurazione piu' significativi da impostare sono: 1) ServerType: 2) Port 80: Va lasciato impostato su standalone Il valore di default e' 80, non conviene cambiarlo 3) HostnameLookups: Fa il Log dei nomi dei client (on) o solamente il loro numero IP (off); lasciatelo pure su off 4) ServerAdmin: E' inutile su un server locale ma se impostate il vostro indirizzo e-mail, ad ogni errore Apache comunichera' con voi come amministratore. 5) ServerRoot: La directory dove Apache conserva i log, gli errori ed i file di configurazione; di solito e': c:\Programmi\Apache Group\Apache 6) LoadModule: Indica ad Apache quali moduli caricare; potete iniziare senza variarlo (nessun modulo caricato) ma si possono poi caricare anche tutti (l'avvio di Apache sara' piu' lento, ma solo di qualche secondo). Indispensabili per provare gli script saranno tutti i moduli per i CGI, per il Perl e i mime. 7) ErrorLog: File dove Apache scrive gli errori: logs/error.log puo' andare bene 8) ServerName: Il nome del vostro server; se non l'avete mai impostato, sara' "localhost.localdomain" ma, se volete cambiare il nome al vostro host potete lanciare (da root) hostname e impostare il nome dell'host. 9) DocumentRoot: La directory nella quale mettere i file html per la pagina locale, solitamente c:\Programmi\Apache Group\Apache\htdocs Se in tale directory mettete un vostro index.html, indicando 'localhost' come indirizzo del browser, vedrete proprio questa pagina e da questa partiranno le altre pagine. 10) DirectoryIndex: Il nome della pagina che verra' visualizzata come indice, solitamente index.html 11) ScriptAlias: Se lo impostiamo ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ i nostri CGI saranno in /usr/lib/cgi-bin/, e saranno chiamati tramite http://localhost/cgi-bin/nome_cgi.cgi 12) AddHandler cgi-script: Per gestire script CGI va impostato a .cgi A questo punto il server locale dovrebbe essere configurato e funzionante. Se qualcosa non corrispondesse a queste istruzioni, potete riferirvi alla documentazione, inclusa nell'archivio che avete scaricato nella cartella /usr/doc/apache. Per provare Apache avviare il browser ed accedere all'URL (index.html): http://NomeHost Il nome del vostro computer in rete e' visibile con Risorse computer-->Pannello controllo-->Rete-->Identificazione Nei dischetti delle esercitazioni sono presenti i files da trasferire nella root-directory del sito c:\Programmi\Apache Group\Apache\htdocs Con essi si potra' provare l'effetto dei marcatori HTML e di alcuni script CGI. Per avviare ed arrestare Apache si possono usare i due comandi seguenti (a cui, per comodita' si possono far corrispondere due diverse icone): "c:\Programmi\Apache Group\Apache\Apache.exe" -d "c:\Programmi\Apache Group\Apache" -s "c:\Programmi\Apache Group\Apache\Apache.exe" -d "c:\Programmi\Apache Group\Apache" -k shutdown ⌡ 15.3 - PAGINE WEB DINAMICHE - CGI -------------------------Esistono tre tipi di pagine Web: 1) STATICHE: sono quelle composte dal solo testo HTML; sono visualizzate sempre nella stessa maniera. 2) DINAMICHE: sono pagine che possono variare ad ogni consultazione (come quelle contenenti un contatore di accessi), in quanto vengono composte in HTML dal Web server di volta in volta, prima di essere inviate al client richiedente. Pero' una volta inviate non variano fino ad una nuova consultazione sul server (aggiornamento). 3) ATTIVE: il contenuto della pagina viene reso variabile non da una elaborazione del server, ma del client. Questo infatti riceve dal Web server anche un programma che viene eseguito dal computer client, mentre l'utente osserva il documento su video. Esempi sono le animazioni o la visualizzazione di dati che devono essere continuamente aggiornati come quelli di borsa. Potendo i computer degli utenti essere diversi, e' indispensabile che la tecnologia usata per scrivere questi programmi sia indipendente dalla piattaforma. A differenza delle pagine statiche, che chiunque e' in grado di scrivere con un buon editor HTML, la scrittura di pagine dinamiche richiede una certa capacita' di programmazione, che diventa ancora maggiore per le pagine attive. In quest'ultimo caso inoltre s'incontrano anche le problematiche della sicurezza, dato che si importano programmi che vengono automaticamente eseguiti sul computer client (si deve essere certi dell'affidabilita' del mittente). Nel server Web dev'essere istallata un'applicazione per ogni documento dinamico e le sue impostazioni devono consentirgli di trovare (nella giusta cartella) l'applicazione relativa al documento richiesto. La tecnologia piu' diffusa per la composizione di questi documenti dinamici e' lo standard CGI (Common Gateway Interface) e le applicazioni si chiamano programmi CGI. Lo standard CGI stabilisce le modalita' con cui vengono avviati i programmi CGI e l'interpretazione del loro output. I programmi CGI possono essere scritti nel linguaggio piu' conveniente per programmare l'applicazione: per esempio se si devono fare molti calcoli si puo' usare Fortran, C o C++ mentre se si deve solo comporre testi si puo' usare il Perl o la shell del sistema operativo del server ovvero l'interprete dei comandi (script CGI: contiene una sequenza di comandi come quella che l'utente batterebbe manualmente al prompt della finestra DOS, nel caso di Windows 98). ⌡ 15.3.1 - PASSAGGIO DI PARAMETRI AI PROGRAMMI CGI: FORM --------------------------------------------I programmi CGI possono ricevere parametri in input, forniti dal server o anche dal browser mediante l'aggiunta di un "?" seguito da un suffisso alla stringa URL inviata al server. Per esempio si passa il parametro 5 cosi': URL = http://www.sito.it/cgi/appl?5 (il prefisso identifica il documento dinamico, mentre tutto quanto segue il "?" nella stringa URL viene passato come variabile locale chiamata QUERY_STRING al programma CGI). Altre variabili locali vengono passate dal server al programma CGI come, per esempio: SERVER_NAME = Nome di dominio del computer server GATEWAY_INTERFACE = Versione del software CGI usato dal server SCRIPT_NAME = Percorso dopo il nome del server nell'URL REMOTE_ADDR = Indirizzo IP del computer client I programmi CGI possono memorizzare informazioni tra due esecuzioni successive in due modi: - sul disco del server se la memorizzazione dev'essere permanente; - nella stringa URL del documento, inserita come riferimento invisibile all'utente nella pagina dinamica inviata al browser, se devono essere memorizzate per una sola sessione (gli ritornera' indietro nell'URL se l'utente clicca sul riferimento, per aggiornare la pagina). Esempio: <A HREF="http://www.sito.it/cgi/appl?5"> Clicca qui' per aggiornare la pagina.</A> (il parametro 5 ritorna al programma CGI se l'utente clicca sul riferimento mostrato dal browser). Questa tecnica puo' essere usata nei programmi CGI per acquisire i dati immessi dall'utente in un modulo appositamente predisposto (form) nella pagina dinamica. Per esempio, se l'utente immette il suo nome e l'eta' nel form, il browser inserisce questi dati nell'URL: http://www.sito.it/cgi/appl?NOME=Mario,ANNI=40 e cosi' possono arrivare al programma CGI che li utilizza. ⌡ 15.4 - PAGINE WEB ATTIVE: TECNOLOGIA JAVA ---------------------------------Per ottenere un aggiornamento continuo della pagina mostrata dal browser si sono dapprima usate pagine dinamiche aggiornate di continuo dal server, eseguendo continuamente il relativo programma CGI (tecnica server PUSH). Questa soluzione e' insoddisfacente per il sovraccarico che produce sul server. La soluzione piu' soddisfacente e' quella di delegare al browser l'aggiornamento della pagina, mediante l'esecuzione di un apposito programma (applet) inviato dal server ed inserito nel codice HTML per mezzo di appositi Tag, come nell'esempio seguente: ......... <applet code="EsempioApplet.class" height=100 width=300> </applet> ......... dove EsempioApplet.class e' il file contenente il bytecode dell'applet. Per ottenere l'indipendenza dalla piattaforma il linguaggio Java non viene compilato nel codice di un particolare processore, ma in un codice intermedio universale (bytecode), che viene eseguito da un interprete (JVM, Java Virtual Machine) contenuto nei browser (scritti per tutte le specifiche piattaforme). Per usare le applet e' necessario conoscere il linguaggio di programmazione Java (vedi prossimo capitolo). Piccole applet, in forma sorgente, possono essere usate anche nel linguaggio di script chiamato JavaScript. Le istruzioni dello script (=copione) vengono eseguite dal browser, quando sono incontrate nella pagina html. Con esse si puo' per esempio filtrare i dati immessi dall'utente o riprodurre suoni da file audio. ⌡ 15.5 - ESEMPI DI JAVASCRIPT E SCRIPT CGI --------------------------------Un esempio di uso di piu' JavaScript (eseguiti dal browser nella macchina CLIENT) e di uno script CGI (eseguito dal Web server nella macchina SERVER, quando richiesto dal client), si trova sul dischetto delle esercitazioni nel file index.html. Questo file, che ora descriviamo, si trova nella directory htdocs e costituisce un piccolo campionario dell'uso degli script: --------------------- Inizio del file index.html ------------------------<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> <!- Identifica il tipo di documento -> <html> <!- Tag di inizio codice HTML (case insensitive) -> <!- Questi sono commenti -> <head> <!- Tag di inizio intestazione -> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <!Attributi del documento -> <title>Pagina di prova dell'installazione di un sito Web con Apache</title> </head> <!- Tag di fine intestazione -> <!- Tag di inizio parte principale (corpo) del documento -> <body text="#000000" bgcolor="#FFFFFF" link="#0000FF" vlink="#000080" alink="#FF0000"> <center><h1> <! Height 1 (=max) dei caratteri (titolo, allineato al centro) > Apache funziona! </h1></center> <BR> <!- Tag di Ritorno a capo -> Cominciamo ora a vedere all'opera: un CGI (eseguito da Perl sul Server) e due JavaScript (eseguiti dal browser sul Client). <p> <!- Tag di nuovo Paragrafo -> Esempio N.1 (Link a CGI): Il CGI, contenuto nel file Esempio2.cgi, viene eseguito solo se si clicca sul link seguente: <a href="http://127.0.0.1:80/cgi-bin/Esempio2.cgi"> Prova script CGI </a> <p align=left> <!- Esempio di paragrafo allineato a sinistra -> Esempio N.2 (un JavaScript esterno): E' stato scritto in index.html il richiamo al JavaScript contenuto nel file prova.js (scritto con Blocco Note), che produce un avviso con il comando seguente: alert('Avviso prodotto dal primo JavaScript (esterno)'); <SCRIPT SRC="prova.js"></SCRIPT> <p align=right> <!- Esempio di paragrafo allineato a destra -> Esempio N.3 (due JavaScript interni): E' stato scritto direttamente in index.html il codice HTML di due JavaScript che producono gli avvisi N.1 e N.2 <SCRIPT Language="Javascript"> x=1; alert('Avviso N.'+x+' prodotto dal secondo JavaScript (interno)'); </SCRIPT> <SCRIPT Language="Javascript"> x++; alert('Avviso N.'+x+' prodotto dal secondo JavaScript (interno)'); </SCRIPT> <p> <!- Esempio N.4: JavaScript eseguito al verificarsi di un evento (onmouseover) -> <a href="#" onmouseover="alert('OK, ci sei passato')">Esempio JavaScript N.4 (Evento): viene eseguito se passi qui sopra</a> <p> <!- Esempio N.5: JavaScript eseguito al posto di un link -> <a href="javascript:alert('JavaScript eseguito al Click (come un link)')">Esempio JavaScript N.5 (Link): viene eseguito solo se clicchi qui</a> <p> <HR> <!- Linea orizzontale -> <BIG> <!- Inizia testo grande -> <b>Arrivederci!</b> <!- Scritta in grassetto -> </BIG> </body> <!- Tag di fine parte principale (corpo) del documento -> </html> <!- Tag di fine codice HTML -> --------------------- Fine del file index.html ------------------------Scaricando questa pagina con un browser, sul vostro computer (dopo aver avviato il Web server Apache) con http://localhost/index.html si ottengono tre messaggi prodotti dai tre Javascript (in esempio N.2 ed esempio N.3) e viene visualizzata poi la pagina index.html. Il primo messaggio (in esempio N.2) e' prodotto con il file prova.js richiamato nella riga seguente: <SCRIPT SRC="prova.js"> </SCRIPT> Questo file (prova.js) contiene uno script (esterno), composto dalla sola riga seguente (che produce il messaggio di avviso): alert('Avviso prodotto dal primo JavaScript (esterno)'); Gli altri due script in esempio N.3 (interni alla pagina html) producono due messaggi, con incremento di una variabile x: <SCRIPT Language="Javascript"> x=1; alert('Avviso N.'+x+' prodotto dal secondo JavaScript (interno)'); </SCRIPT> ......... <SCRIPT Language="Javascript"> x++; alert('Avviso N.'+x+' prodotto dal secondo JavaScript (interno)'); </SCRIPT> La pagina html contiene anche il seguente link allo script CGI contenuto nel file Esempio2.cgi (in esempio N.1), situato nella directory cgi-bin del server: <a href="http://127.0.0.1:80/cgi-bin/Esempio2.cgi"> Prova script CGI </a> Quando nel browser si clicca sul link, il Web server Apache viene comandato di eseguire lo script CGI, che invoca l'interprete Perl e prepara una nuova pagina html, eseguendo le istruzioni di print nello script. Questa nuova pagina viene poi inviata al browser, che la visualizza. Lo script CGI, contenuto nel file Esempio2.cgi, e' il seguente: #!/Perl/bin/perl print "Content-type:text/html\n\n"; print "<html><head><title>Prima pagina di prova</title></head>\n"; print"<body>\n"; print"Esempio di CGI: questa pagina e' stata confezionata da Esempio2.cgi, interpretato da Perl sul Web server, ed inviata al client che la sta ora visualizzando con il browser."; print "<h2>Ciao a tutti!</h2>\n"; print "Se volete, potete tornare alla pagina precedente con Back"; print"</body></html>\n"; Questo script CGI e' quindi eseguito sul server, a differenza dei due JavaScript precedenti, che venivano eseguiti sul client. Nell'esempio N.4 c'e' un JavaScript, che viene pero' eseguito solo al verificarsi di un evento (onmouseover=passaggio del mouse sopra alla scritta). Nell'esempio N.5 e' contenuto un altro JavaScript: in questo caso viene eseguito solo se si clicca sulla sua scritta (come fosse un link). !----------------------------------------------------------------------CAPITOLO 16 - Architetture avanzate ═══════════ Riassunto: In questo capitolo viene data una classificazione dei sistemi di elaborazione parallela e ne vengono discussi gli elementi alla base del loro sviluppo.. (.... scrittura dispense in corso: vedi Fotocopie ....) Classificazione SISD, SIMD, MISD, MIMD Complessita' collegamenti, crossbar switch, ecc. !----------------------------------------------------------------------CAPITOLO 17 - Acquisizione di dati sperimentali ═══════════ Riassunto: Scopo essenziale di questo capitolo e' quello di introdurre le problematiche connesse al controllo automatico degli apparati sperimentali ed all'acquisizione dei dati (on-line). Dopo l'introduzione degli standard NIM, CAMAC, VME e FASTBUS si discutono i sistemi si elaborazione parallela multiprocessore. ⌡ 17.1 - ACQUISIZIONE DEI DATI SPERIMENTALI ================================== La fisica si occupa di scoprire e studiare le leggi fisiche della Natura ovvero i rapporti quantitativi che intercorrono tra le grandezze osservabili, cioe' suscettibili di misurazione e quindi quantificabili con numeri: per questo, come la matematica ed i calcoli numerici hanno un ruolo fondamentale in fisica, cosi' anche i calcolatori elettronici sono fondamentali per i fisici come strumenti di calcolo (uso OFF-LINE). ║ Inoltre i calcolatori elettronici sono anche l'elemento centrale di analisi e controllo in linea (uso ON-LINE) di tutti i moderni apparati sperimentali e vanno quindi visti come parte essenziale dello strumento con cui il fisico misura le grandezze fisiche osservabili, che sono oggetto di studio della fisica sperimentale. Cosi' per i fisici lo studio dei calcolatori elettronici e dei loro campi e modalita' di impiego deve essere associato allo studio degli altri elementi che concorrono a formare l'apparato sperimentale, con cui indaghiamo il mondo fisico, che sono: 1) i trasduttori delle informazioni fisiche (per esempio i rivelatori di particelle); 2) gli strumenti che si usano per misurare le grandezze fisiche osservate dai trasduttori; 3) le metodologie di acquisizione ed analisi dei dati forniti dagli strumenti, in cui e' fondamentale l'impiego dei calcolatori elettronici. I calcolatori elettronici sono quindi usati, oltre che per la raccolta e l'analisi dei dati sperimentali, anche per l'automazione dell'esperimento. Le problematiche relative al loro uso sono comuni a tutte le applicazioni di questo tipo. Per capirle piu' completamente consideriamo il caso degli apparati sperimentali piu' complessi, quelli usati nella fisica delle particelle elementari. In questi esperimenti capita che il flusso di dati fornito dall'apparato sia troppo elevato per essere raccolto integralmente dal sistema di computers. Cio' accade perche' nei dati sono presenti molti eventi 'di fondo', cioe' non interessanti per la fisica in studio o addirittura errati ('rumore'). E' necessario allora aggiungere all'apparato sperimentale un sistema di filtraggio hardware, chiamato Trigger, che blocchi questi eventi spuri prima che arrivino al sistema di acquisizione computerizzato, lasciando passare solo gli eventi d'interesse, che devono essere in quantita' minore o uguale della massima gestibile dal sistema di computers. Mentre in un'oscilloscopio il trigger e' la condizione che fa partire lo 'sweep' orizzontale (la manopola regola la soglia in ampiezza che costituisce la condizione), qui' il trigger e' la condizione che fa partire l'acquisizione dei dati. ⌡ 17.1.1 - UN SEMPLICE ESEMPIO DI ACQUISIZIONE DI DATI SPERIMENTALI ======================================================== Siccome agli studenti non risulta immediatamente comprensibile cosa significhi un sistema di trigger in un apparato sperimentale complesso, presentiamo un semplice esempio che aiuta a capire meglio questo argomento. Il sistema di trigger che presentiamo si riferisce ad un ipotetico esperimento, in cui si vogliono separare i mesoni μ dai mesoni ⌠, misurandone la velocita' ß ed il momento p. Progettiamo l'apparato sperimentale indicato in figura (dove sono usati deliberatamente i termini inglesi internazionalmente usati dai fisici) in base ai seguenti criteri: 1) Per determinare la velocita' ß misuriamo il tempo di volo tra i contatori a scintillazione S1 ed S2 con uno strumento TAC (Time to Amplitude Converter), che misura l'intervallo di tempo tra gli impulsi prodotti da S1 ed S2 (gli impulsi elettrici sono prodotti da un fotomoltiplicatore che raccoglie la luce prodotta dagli atomi dello scintillatore nel decadimento dai livelli eccitati dal passaggio del mesone carico). 2) Per determinare l'angolo di scattering ed il momento p del mesone misuriamo il raggio di curvatura del percorso del mesone nel campo magnetico, a partire dai tre punti di attraversamento delle camere a fili C1, C2 e C3 (le camere a fili sono rivelatori che permettono questa localizzazione; il campo magnetico, grazie alla forza di Lorentz, produce una curvatura del percorso che e' funzione del momento p e quindi ci permette di misurarlo). 3) Per separare i mesoni μ dai ⌠ aggiungiamo un blocco di ferro di spessore superiore al range dei mesoni ⌠ (che quindi non riescono ad attraversarlo), ma inferiore al range dei mesoni μ (che passeranno poiche', non interagendo nuclearmente con l'assorbitore, sono piu' penetranti). Lo scintillatore S3 quindi dara' un segnale nel caso di un mesone μ, mentre non dara' alcun segnale nel caso di un mesone ⌠. (angolo di scattering=teta) ╔══════════════╗ ┌┐ | / ║ ____________╔═╗....││..............| / ║ Beam μ ⌠ ╚═╝ ││ ⌠ | / C2 ║<── Campo magnetico (fascio) Target └┘ C1| / ║ (bersaglio) S1 | / ║ (curva la traiettoria) ║ / ║ S1,S2,S3 = Scintillatori ╚═╗ C3 ║ ╚═ ----:--- ═╝ C1,C2,C3 = Camere a fili : :⌠ CAMPO MAGNETICO: misura p,teta S2 ┌─────:─────┐ (Coincidenza) TEMPO DI VOLO : misura ß └─────:─────┘ ANTICOINCIDENZA: separa μ da ⌠ ╔════════:════════╗ ║ Assorbitore Fe ║<── Arresta i ⌠ ╚═════════════════╝ Non arresta i μ ┌───────────┐ S3 └───────────┘ (Anticoincidenza) La logica di trigger in questo esperimento quindi sara': __ LOGICA DEL TRIGGER: Evento ⌠ = S1.S2.S3 Evento μ = S1.S2.S3 Schema di montaggio dei moduli standard NIM (risoluzione 0.1 nsec): S1 ┌──────────┐ ────>│Ritardo D1│────┐ ╔═══╗ ( Moduli NIM ) └──────────┘ └─>║AND║────┐ S1 ┌──────────┐ ┌─>╚═══╝ │ ╔═══╗ ────>│Ritardo D2│────┘ └─>║AND║────> SEGNALE DI TRIGGER ⌠ └──────────┘ ╔═══╗ ┌─>╚═══╝ (anticoincidenza S3) ───────────────────────>║NOT║────┘ S3 ╚═══╝ I ritardi D1 e D2 sono inseriti per mettere in tempo i segnali all'ingresso delle coincidenze AND all'istante T (vedi schema seguente). Temporizzazione dei segnali: |<-------------- D1 --------------->| S1 ───────────┐ ┌──────────────────────────────────────── └───┘ |<--D2-->| S2 ──────────────────────────────────────┐ ┌───────────── └───┘ | S3 ───────────────────────────────────────────────┐ ┌──── └---┘ T ⌡ 17.1.2 - SISTEMA DI ACQUISIZIONE DEI DATI SPERIMENTALI ============================================= Questo filtraggio degli eventi (Trigger) viene realizzato in un primo stadio con semplici sistemi elettronici in logica combinatoria fatta con moduli standard NIM, particolarmente veloci, come quello mostrato in figura. La semplicita' delle funzioni sintetizzate non consente pero' di essere selettivi al massimo, per cui e' possibile che con questi sistemi di trigger non si riesca ad abbassare a sufficienza il flusso (rate, numero di eventi al secondo) dei dati verso il sistema dei computers che devono acquisirli. Si usa allora, sul flusso di dati gia' ridotto dal sistema di trigger primario, un sistema hardware di trigger secondario che, realizzando funzioni logiche piu' complesse (anche se in modo un poco piu' lento), assicura un ulteriore fattore di riduzione del flusso dei dati, portandolo ad un livello sopportabile dal sistema di computers. I computers montati in linea (ON-LINE) con l'apparato di acquisizione dei dati possono a questo punto assicurare un ulteriore fattore di riduzione dei dati mediante algoritmi software, lasciando registrare su supporto magnetico (per le successive elaborazioni piu' sofisticate al centro di calcolo) solo gli eventi interessanti, depurati da gran parte degli eventi di fondo che erano presenti in origine. Lo schema di funzionamento dell'intero sistema di acquisizione-dati di un esperimento di fisica delle alte energie (cioe' del supermicroscopio con cui i fisici scrutano il mondo subnucleare) puo' cosi' essere rappresentato con lo schema seguente: ┌───────────────┐ │ Eventi Fisici │ └───────┬───────┘ ┌─────┴──────┐ │ Rivelatori │ └──┬──┬──┬───┘ ┌──────┴──┴──┴────────┐ Reset │ Strumenti di misura ├<──────────┐ └───┬─────┬─────┬───┬─┘ │ │ │ ┌───┴───┴────────┐ No │ │ │ │Trigger primario├───>┤ F │ │ └───────┬────────┘ │ I │ │ │OK │ L │ ┌──┴─────────┴─────┐ No │ T │ │Trigger secondario├──────>┤ R │ └────────┬─────────┘ │ A │ │OK │ G ╔═══╩═══════════╩═════╗ │ G I blocchi ║ Acquisizione Evento ║ │ I ╚══════════╦══════════╝ │ O a tratto doppio ║ │ ╔══════╩══════╗ No │ rappresentano ║ Preanalisi ╠──────────────>┘ ╚══════╦══════╝ il software ║OK ╔══════════════╩═══════════════╗ ( ON-LINE ) ║ Analisi completa + ║ ║ Monitoraggio esperimento ║ ╚══╦═══════════╦════════════╦══╝ │ ┌────────┴─────────┐ │ │ │Supporti magnetici│ │ │ └────────┬─────────┘ │ │ ╔════════╩═════════╗ │ Software (OFF-LINE) ---> │ ║Centro di calcolo ║<─┘ │ ╚════════╦═════════╝ │ ┌────────┴─────────┐ └─>│ U O M O │ └──────────────────┘ Altri compiti del sistema di computer on-line sono: 1) selezionare e comprimere i dati sperimentali prima di memorizzarli sul supporto magnetico; 2) eseguire in tempo reale tutti i controlli sulla correttezza di funzionamento dell'apparato (monitoraggio); 3) fornire ai fisici di turno tutti gli 'output' necessari a valutare l'andamento dell'esperimento; 4) di assicurare la connettivita' del sistema verso l'esterno. Le strategie di gestione software sono molteplici e dipemdono dalle peculiarita' dell'esperimento. ⌡ 17.1.3 - UN ESEMPIO DI SISTEMA DI ACQUISIZIONE DATI ========================================== Per aiutare lo studente a comprendere meglio le problematiche della gestione software di un sistema di acquisizione dati on-line, riportiamo un semplice esempio di organizzazione possibile dei compiti descritti nel paragrafo precedente e' riportato nello schema seguente: PRIORITA': ┌─────────────────>┐ │ ┌───────┴────────┐ Interrupt da livelli inferiori Alta │ │ ACQUISIZIONE │ (Legge e accoda nuovo evento) (foreground) │ └───────┬────────┘ │ │(fine acquisizione) │ │ │ interrupt┌───────┴────────┐ Analizza tutti eventi in coda Intermedia │<─────────┤ ANALISI │ (filtraggio software) (foreground) │ └───────┬────────┘ │ │(coda vuota) │ │ │ interrupt┌───────┴────────┐ Spende il tempo restante in Bassa └<─────────┤ BACKGROUND │ compiti di monitoraggio, ecc. (background) └────────────────┘ Questo schema e' realizzabile organizzando le priorita' delle interruzioni su vari livelli, in cui il trigger degli eventi fisici occupa uno dei livelli piu' alti. Il suo funzionamento puo' essere descritto cosi': - L'interrupt di un evento fisico forza il computer ad andare in ACQUISIZIONE, leggendo i dati dell'evento dagli strumenti ed inserendoli nella coda degli eventi da analizzare. - Terminata l'acquisizione, il controllo viene passato ai programmi di ANALISI che elaborano tutti gli eventi in coda (eventualmente scartandoli), interrompendosi all'arrivo di un nuovo evento (le interruzioni di livello piu' basso restano disabilitate: per questo i livelli di ACQUISIZIONE e ANALISI costituiscono la piattaforma di lavoro piu' prioritaria 'Foreground' rispetto al 'Background'). - Terminata l'analisi (coda vuota) il controllo passa ai programmi di BACKGROUND, dove il computer spende tutto il tempo 'libero', con tutte le interruzioni abilitate. Come esempio riportiamo l'impostazione delle priorita' delle varie interruzioni in un sistema di acquisizione on-line, effettivamente realizzato in un esperimento di fisica: Priorita' int. Sorgente interrupt Commento ----------------------------------------------------------------------1 Output su nastro magn. Output disabled blocca acquisiz. 2 Counters, R.T.Clock Immediate reset if counter Overflow 3 Trigger eventi fisici Questo avvia l'acquisizione+analisi 4 Refreshing display Esempio di attivita' meno prioritaria 5 Console input Idem (ma input prioritario su output) 6 Console output Ecc. Al livello Background un'impostazione possibile della supervisione dei compiti da eseguire puo' essere rappresentato dallo schema seguente (i compiti eseguiti, o task, vanno programmati dall'utente secondo le esigenze del suo specifico esperimento): ┌───────────────────>┐ (Loop continuo) │ │ │ ╔════════════════╩═════════════╗ No │ ║ Ricevuto comando da utente ? ║───────>┐ │ ╚════╦═══════╦═══════════╦═════╝ │ │ │ │ │Si │ │ ┌──┴──┐ ┌──┴──┐ ┌──┴──┐ ╔═══════╩════════╗ Si │ │Task1│ │Task2│.....│TaskN│ ║ Controllo HV ? ║────────>┐ │ └──┬──┘ └──┬──┘ └──┬──┘ ╚═══════╦════════╝ ┌───────┴────────┐ ├<───────┴───────┴───────────┘ No│ │Esegue controllo│ │ Task singoli │ └───────┬────────┘ │ (eseguiti una tantum) │<─────────────────┘ │ ╔═══════╩════════╗ Si │ ║Refresh display?║────────>┐ │ ╚═══════╦════════╝ ┌───────┴────────┐ │ No│ │ Esegue refresh │ │ │ └───────┬────────┘ │ LIVELLO BACKGROUND │<─────────────────┘ │ ================== | │ | (Task ripetitivi) │ | │ ╔═══════╩════════╗ Si │ ║Garbage collect?║────────>┐ │ ╚═══════╦════════╝ ┌──────┴──────┐ │ No│ │ Esegue G.C. │ │ │ └──────┬──────┘ └<──────────────────────────────────────────┘<─────────────────┘ Il computer spende tutto il suo tempo libero nel loop in cui esegue ripetitivamente una serie di compiti (i task ripetitivi), ognuno controllato da un apposito flag di attivazione impostabile dall'utente (se il flag e' off il task non viene eseguito). Tra i task ripetitivi c'e' l'esame del buffer di input dei comandi. Se un comando viene ricevuto, il corrispondente task viene eseguito una sola volta (sono i task singoli). Il numero di task inseribili nelle due categorie risulta praticamente illimitato. ⌡ 17.2 - GLI STANDARD DI STRUMENTAZIONE ELETTRONICA ========================================== Negli anni '50 i vari laboratori nazionali si costruivano autonomamente l'elettronica necessaria per fare la selezione dei dati finora descritta, accumulando cosi' negli anni un patrimonio di know-how e di strumentazione costruita. Negli anni '60 pero' ci si accorse che in occasione di collaborazioni internazionali si avevano dei seri problemi ad usare contemporaneamente nello stesso esperimento strumenti costruiti da laboratori diversi con standard (meccanici, elettrici, funzionali, ecc.) diversi. Si comincio' cosi' a studiare uno standard unico che potesse essere accettato da tutti e che risolvesse i problemi di acquisizione-dati previsti per gli anni futuri, compreso quello di consentire l'interfacciamento al computer di un grande numero di strumenti. Il risultato di questi studi fu, dopo lo standard NIM per l'elettronica combinatoria veloce (discriminatori, TAC, AND, OR, NOT, ecc. da usare nel sistema di trigger), lo standard CAMAC per la strumentazione di acquisizione dei dati (convertitori ADC/TDC, scale di conteggio, pattern units, ecc.). Lo standard CAMAC , introdotto dal comitato europeo ESONE nel 1969, si proponeva quindi di risolvere i seguenti problemi: 1) Standardizzazione dell'interfacciamento della strumentazione; 2) Espansione delle capacita' di interfacciamento del computer. Il documento ufficiale di riferimento, contenente documentazione e specifiche e': CAMAC: Sistema modulare di strumentazione per l'elaborazione dei dati ( Documento C.E.E. - EUR 4100 - 1970 ) Successivamente, negli anni '80, per superare le prestazioni del CAMAC, sono stati introdotti gli standard VME (TTL) e FASTBUS (praticamente in tecnologia ECL), capaci di ospitare anche piu' CPU e memorie per l'elaborazione distribuita. Per capire meglio come vengono usati questi standard, osserviamo lo schema seguente, in cui risultano evidenziati i blocchi che li utilizzano: ┌───────────────┐ │ Eventi Fisici │ └───────┬───────┘ ┌─────┴──────┐ │ Rivelatori │ └──┬──┬──┬───┘ │ │ │ ╔══════╩══╩══╩════════╗ Reset 5 ns ║ Standard NIM ║<─────────┐ T I blocchi ╚═══╦═════╦════╦════╦═╝ │ R │ ┌──┴────┴────┴─────┐ No │ I a tratto doppio │ │Hardware Processor├─────>┤ G │ └────────┬─────────┘ │ G indicano │ │OK │ E ╔═══╩═══════════╩═════╗ No │ R gli standard 50 ns ║Standard FASTBUS/VME ║─────────>┘ ╚═══════════╦═════════╝ NIM,FASTBUS,VME,CAMAC │ ╔══════════╩═════════╗ 1000 ns ║ Standard CAMAC/VME ║ ╚══════════╦═════════╝ │ ┌──────────────┴───────────────┐ ( ON-LINE ) │ HOST COMPUTER │ └──┬───────────┬────────────┬──┘ │ ┌────────┴─────────┐ │ │ │Supporti magnetici│ │ │ └────────┬─────────┘ │ │ ┌────────┴─────────┐ │ ( OFF-LINE ) │ │Centro di calcolo │<─┘ │ └────────┬─────────┘ │ ┌────────┴─────────┐ └─>│ U O M O │ └──────────────────┘ ⌡ 17.2.1 - LO STANDARD CAMAC ================= Il CAMAC e' uno standard di strumentazione nato al CERN, che stabilisce regole uniformi per la costruzione e l'interconnessione degli strumenti di uso comune nella fisica sperimentale. Esso consente ad un host computer di accedere agli strumenti contenuti in un 'crate' (contenitore meccanico degli strumenti, altrimenti detto 'cassa CAMAC'), compiendo su di essi operazioni di I/O. Un crate CAMAC puo' contenere 23 moduli (=cassettini) di strumentazione, indirizzabili dall'host computer con un numero di stazione N (=1..23). Oognuno di questi moduli puo' contenere fino a 16 strumenti, indirizzabili con un sub-address A (=0..15). L'interfacciamento e' realizzato in TTL, attraverso un modulo master a doppio slot, chiamato controller, e da un'elettronica digitale slave in ogni modulo, in comunicazione sincrona con il master con ciclo di 1μs. Con questa comunicazione master-slave si realizza il multiplexaggio dell'I/O bus del computer sui 23x16 strumenti, consentendo il collegamento individuale di ogni strumento al computer. Il prezzo che si paga per ottenere questo ampliamento delle capacita' di I/O del computer e' che l'operazione di I/O CAMAC avviene in due passi: 1) Impostazione di indirizzo e comando nei registri del controller; 2) Esecuzione dell'operazione di I/O (sul registro D del controller). Lo schema di collegamento e' riportato nello schema seguente: ╔═════════════════╗ ║ HOST COMPUTER ║ ╚════════╦════════╝ I/O port║ ║ <------------------ Crate CAMAC -----------------> ║ ╔═══╗N,A,F┌───┬────────────┐ Computer║ ║ ║════>┤ C │ Controller │ DATAWAY (ciclo=1μs) ╠══╣I/F║ ├───┤ ├═════╦═════ ═ ═ ═ ═ ══════╗ I/O bus ║ ║ ║<═══>┤ D │ CAMAC │ ┌─┴──┐ ┌──┴─┐ ║ ╚═══╝ R,W └───┴────────────┘ │ S1 │ │ S1 │ ║ │ .. │ ............ │ .. │ | Fino a 16 strumenti --->│ .. │ │ .. │ | (ogni modulo) │ S16│ │ S16│ | └────┘ └────┘ | C,D=Registri del controller N=1 N=23 | R,W=Linee Read/Write <--- Fino a 23 moduli ---> N,A,F=Linee di comando (ogni crate) I/F e' il circuito di interfacciamento del modulo controller (standard CAMAC) con l'I/O bus (proprietario) dello specifico computer. In questo schema si deve usare un circuito I/F per ogni crate; in realta' si puo' costruire I/F nello stesso controller, rendendolo cosi' specifico per quel tipo di computer. Lo standard CAMAC prevede anche l'uso di un solo circuito I/F, che interfaccia l'I/O bus del computer con uno speciale bus standard CAMAC, simile al dataway e chiamato branch highway, che permette il collegamento di un numero massimo di 8 crates su un solo I/O port del computer. In tal caso lo schema di collegamento e' il seguente: ╔═════════════════╗ ║ HOST COMPUTER ║ ╚════════╦════════╝ I/O port║ ║ Computer║ I/O bus ║ ╔═╩═╗ ║I/F║ <------------------ Crate CAMAC ------------------> ╚═╦═╝ ┌──────────────────┐ ║N,A,F│ Controller CAMAC │ DATAWAY (ciclo=1μs) ╠═════┤ ┌───┬───┐ ├═════╦═════ ═ ═ ═ ═ ══════╗ CAMAC ║ R,W │ │ C │ D │ │ ┌─┴──┐ ┌──┴─┐ BRANCH ║ └─────┴───┴───┴────┘ │ S1 │ │ S1 │ HIGHWAY║ │ .. │ ............ │ .. │ ║ Fino a 16 strumenti --->│ .. │ │ .. │ ║ (ogni modulo) │ S16│ │ S16│ ║ └────┘ └────┘ | N=1 N=23 | | | | <---- Fino a 23 moduli ---> ..... ECC. (FINO A 8 CRATES SULLO STESSO BRANCH HIGHWAY) Per la comunicazione sul dataway i progettisti hanno riservato: N (Numero del modulo): una linea individuale di indirizzamento per ogni modulo, per un totale di 24 linee individuali (dirette dal controller ai moduli); L (Look-at-me): una linea individuale di richiesta di attenzione per ogni modulo, per un totale di 24 linee individuali (dirette dai moduli al controller); A (sub-Address): 4 linee omnibus per l'indirizzamento dei 16 strumenti all'interno di un modulo; F (Funzione): 5 linee omnibus per la codifica della funzione da eseguire sullo strumento indirizzato R,W (Read/Write): 24+24 linee omnibus per lo scambio di dati tra modulo e controller. La struttura del dataway e' indicata nello schema seguente: Linee Omnibus F,A,R,W ╔══════ ═ ═ ═ ═ ═══════╦════════╦════════╗ ║ ┌─────────<──────────║────────║────────║────┐ ║ │ .................║........║........║....│ ║ │ Linee individuali: ║ ┌──<───║────<───║──┐:│ ║ │ N=da CS a moduli ║ │ N ║ ┌──<───║─┐│:│ ┌┴─┴┐ L=da moduli a CS ┌┴─┴┐ ┌┴─┴┐ N ┌┴─┴┴┴┴┐ │S1 │ │S1 │ │S1 │ │ C S │ │.. │ .............. │.. │ │.. │ │ O T │ │.. │ │.. │ │.. │ │ N A │ │.. │ │.. │ │.. │ │ T T │ │.. │ │.. │ │.. │ │ R I │ │.. │ │.. │ │.. │ │ O O │ │S16│ │S16│ │S16│ │ L N │ └───┘ └───┘ └───┘ └──────┘ N=1 N=23 N=24 CS <---- Fino a 23 moduli ----> <-- Controller--> Le linee omnibus collegano i pin corrispondenti su TUTTE le stazioni (N=1..24). Le linee individuali collegano la stazione di controllo alle stazioni (slot) dei singoli moduli: - 24 linee N (select), dirette dal controller verso i moduli, usate per selezionare il modulo a cui e' diretto il comando; - 24 linee L (Look-at-me), dirette dal modulo al controller, usate per chiedere attenzione (interrupt) al computer. Il registro C del controller deve contenere un comando composto da N,A,F per 14 bit complessivi, impacchettati nel modo stabilito dal costruttore del controller. Il registro D e' dedicato alla memorizzazione temporanea del dato da trasferire sul dataway. Se l'I/O port a cui e' connessa l'interfaccia CAMAC (I/F) e' sugli indirizzi C100H per il dato e C101H per il controllo, lo schema di programmazione di input ed output su CAMAC e' il seguente: Operazione di input ------------------LDA# NAF STA C101H LDA C100H ........ Operazione di output -------------------LDA# DATO STA C100H LDA# NAF STA C101H ........ Il ciclo CAMAC di trasferimento del dato sul Dataway viene avviato dal controller al termine dell'istruzione STA C101H (invio del comando al controller). E' un ciclo sincrono con due segnali di strobe S1 e S2, che permettono, per esempio, di leggere (S1) ed azzerare (S2) uno strumento dello stesso ciclo CAMAC. Il ciclo dura 1μs, secondo lo schema temporale che segue. DATAWAY TIMING: <---------- Ciclo CAMAC (1μs) ----------> | | | | | | Commando e Busy ──────┐ | | | | ┌──── (N,A,F,B) └───────────────────────────────────────┘ | | | | | | Dati (R,W) ────────┐ | | | | ┌──── └───────────────────────────────────────┘ | | | | | | Strobe S1 ────────────────────────────┐ ┌──────────────────── | | └───────┘ | | | | | | | | Strobe S2 ────────────────────────────────────────┐ | ┌──────── | | | | └───────┘ | | 200ns | 200ns | 200ns | 200ns | 200ns | Dopo il ciclo CAMAC di 1μs, il dato e' stato recapitato: 1) nel primo caso (input) sul registro D del controller e puo' quindi essere prelevato subito dopo con l'istruzione LDA C100H; 2) nel secondo caso (output) nello strumento indirizzato (modulo N, sub-address A. Per quanto riguarda le richieste di interruzione da parte degli strumenti CAMAC, tenendo presente che tutte quelle provenienti da un crate afferiscono ad un unico I/O port del computer, i progettisti hanno riprodotto all'interno del CAMAC uno schema di priorita' analogo a quello con cui vengono gestite le richieste d'interruzione delle periferiche all'interno del computer: anche in quel caso infatti devono essere tutte ricondotte (con un OR gerarchico) ad un unico input nell'unita' centrale CPU. Lo schema e' quindi: <---------- Moduli CAMAC ---------> ┌─────────┐ ┌─────────┐ │ │ ┌─ F2│ │ │ ┌─ F2│ │ ╔╩═╩╗ │ │ ╔╩═╩╗ │ CONTROLLER CAMAC │ ║AND║ │-- - - - - - │ ║AND║ │ ┌─────────────────────┐ │ ╚═╦═╝ │ │ ╚═╦═╝ │ │ F1 │ └───│─────┘ └───│─────┘ │ ╔════╗ │ ╔════╗ │ │ └─────────────>║ ║ └──>║ ║ │ │ ..............│..║ OR ╠─────>║ AND╠────┐ └─────────────────────────────────────>║ ║ ║ ║ │ │ ╚════╝ ╚════╝ │ │ └─────────────────────┘ │ <----------------------- Crate CAMAC -------------------------> │ | | ┌──────────────┐ | │ F ╔════╗ │ │ PERIFERICA │ └──>║ ║ │ │ DEL │ ────>║ AND╠───────┤ COMPUTER │ Done ║ ║ │ │ │ ╚════╝ │ │ └──────────────┘ │ │ - - - - - - - │ │ │ COMPUTER ┌─────────│───┐ │ F0──┐ │ │ │ ╔╩═╩╗ │ │ ║AND║ │ │ ╚═╦═╝ │ └────────│────┘ │ Interrupt ********* I flip-flop F0,F1,F2, impostabili con normali comandi CAMAC, costituiscono i bit di mascheramento, che agiscono a vari livelli gerarchici: 1) F0 blocca tutte le interruzioni; 2) F1 blocca tutte le interruzioni di un crate CAMAC; 3) F2 blocca le interruzioni di un modulo; appositi moduli (LAM grader consentono di pilotare anche le interruzioni dei singoli strumenti). ⌡ 17.2.2 - LO STANDARD VME =============== Il CAMAC con il suo ciclo di 1μs risulto' presto inadeguato a fronteggiare il rapido sviluppo delle esigenze della fisica sperimentale condotta ai grandi acceleratori di particelle elementari. Il primo accorgimento piu' semplicemente realizzabile con l'esistente (CAMAC) e' stato quello di sfruttare il parallelismo intrinseco nelle procedure di acquisizione dati impegnando piu' processori in parallelo, ognuno collegato a diversi crate CAMAC o anche inserendo microprocessori all'interno dei controller CAMAC. Quando anche questi sistemi risultarono insufficienti per trattare in linea l'accresciuto flusso di dati dall'apparato sperimentale, divento' evidente la necessita' di progettare dei sistemi standard di prestazioni superiori a quelle del CAMAC. Le idee che furono alla base della progettazione dei nuovi standard (VME e Fastbus) sono: 1) Consentire a piu' di un master di operare sull'interconnessione, mentre nel dataway CAMAC c'e' un solo master: il controller (questa caratteristica viene indicata con il termine inglese Multimastership). 2) Comunicazione asincrona, per permettere l'esecuzione di transazioni alla massima velocita' possibile: con la sincronizzazione handshake tra i moduli master e slave il ciclo dura meno se i moduli sono veloci, mentre dura di piu' se i moduli sono piu' lenti (perche' si attendono a vicenda). Si e' cosi' arrivati all'introduzione dei due nuovi standard VME (in TTL) e Fastbus, ancora piu' veloce (perche' puo' lavorare in ECL). (.... scrittura dispense in corso ....) !----------------------------------------------------------------------APPENDICE A - SIGLE E DEFINIZIONI UTILI ═══════════ Elenchiamo sinteticamente il significato di alcune sigle usate (glossari piu' completi sono reperibili in rete, per esempio sul sito della Sun): FNC (Federal Networking Council, sito www.fnc.gov): Sistema globale di comunicazione basato sul protocollo TCP/IP (acronimo di Transfer Control Protocol/Internet Protocol) e successivi sviluppi futuri, che fornisce: - indirizzamento logico univoco; - comunicazione; - accesso a servizi di alto livello basati sulla comunicazione. WWW (World Wide Web): metodo ipertestuale e intuitivo per accedere alle risorse su Internet, unendo al testo anche elementi grafici, audio, video ed altre funzionalita' multimediali, comprese applicazioni programmabili. Il tutto quindi suscettibile di un'interfacciamento 'amichevole' (tramite grafica e mouse). Quindi il Web e' grafico, inter-piattaforma, distribuito, dinamico, multimediale, interattivo (web = ragnatela) Server WEB: Computer gestore di servizi Internet memorizza le pagine WEB e le invia al browser del Client quando questo le richiede. Non e' pero' solo un file server di rete, poiche' e' anche in grado, su richiesta del browser, di eseguire dei programmi (script CGI). Script: E' un testo, composto da una sequenza di comandi di scripting, che viene eseguito da un interprete (scripting engine, come Perl, JScript e VBScript)), producendo normalmente un output HTML da inviare al browser come risultato dell'elaborazione (documento dinamico). Possono essere facilmente prelevati da una pagina HTML ed inseriti in un'altra con la tecnica 'copia&incolla'. Script CGI (Common Gateway Interface): Sono programmi eseguibili dal server WEB su richiesta del browser del computer client. In genere il risultato dell'elaborazione viene inviato al client sotto forma di pagina HTML. Un uso tipico e' quello di acquisire i dati di registrazione di un cliente che sottoscrive un modulo per accedere ad un servizio. L'elaborazione CGI comprendera' l'inserimento dei dati del cliente nel data-base sul server, la predisposizione di una pagina di risposta ed il suo invio al computer client. Tutti i programmi CGI di uso piu' frequente sono disponibili come componenti predefiniti di tutti i piu' diffusi programmi editor HTML Ipertesto: Documento composto di argomenti contenenti riferimenti ad altri argomenti (collegamenti ipertestuali) al fine di consentirne la lettura saltando da un argomento all'altro (navigazione). Un esempio di utilizzo di ipertesti e' la Guida di Windows e Hypercard di Macintosh. Collegamento ipertestuale: e' un puntatore contenuto in una pagina WEB che punta ad un altro file sul WEB che in genere e' un'altra pagina WEB ma potrebbe anche essere un file multimediale o addirittura un programma. La presenza di un collegamento ipertestuale e' visibile in un testo con un colore diverso, mentre e' invisibile su un'immagine (pero' il cursore cambia aspetto quando ci si passa sopra). Cliccando su un collegamento ipertestuale il browser preleva il file dall'indirizzo specificato e lo visualizza se e' una pagina HTML oppure lo apre (con il programma associato: per esempio con un riproduttore di suoni se e' un file WAV). Linguaggio ipertestuale HTML (HyperText Markup Language): e' il linguaggio per la definizione delle direttive per l'impaginazione e lo sviluppo di documenti interattivi. Il browser WEB esegue la formattazione della pagina in base a queste direttive (tag) contenute nel codice HTML, adeguando la larghezza delle righe alla larghezza dello schermo o della finestra. Quindi browser diversi mostreranno visualizzazioni diverse per la stessa pagina WEB. Tutti gli elementi della pagina (informazioni e link) sono cosi' al loro posto ma l'aspetto sul video potra' essere diverso. Specifiche curate dal consorzio W3C (vedi http://www.w3.org). HTML permette la creazione di pagine WEB interattive, nelle quali possiamo incorporare veri programmi con JAVA, CGI, ActiveX e VRML (Virtual Reality Modeling Language, per la visualizzazione di scenari a tre dimensioni all'interno delle pagine WEB). La codifica delle direttive (tag) necessarie al browser per l'impaginazione, nel codice HTML non sono un compito semplice, ma i nuovi editor HTML, progettati secondo la tecnica WYSIWYS ("What You See Is What You Get") sollevano l'utente dall'obbligo di conoscere i codici di formattazione del linguaggio ipertestuale, lasciandolo concentrare piu' facilmente sul lavoro creativo. Rete Internet: I computer collegati in Internet comunicano tra loro con il protocollo TCP o UDP, secondo lo schema seguente: Livello Applicazione: HTTP, FTP, telnet, ... Livello Trasporto : TCP, UPD, ... Livello Rete : IP, ... Livello Device : Device driver, ... L'utente programmera' in Java, per esempio, al livello Applicazione usando le classi del package Java.net e potra' scegliere TCP o UDP tra i protocolli di trasporto, in base alle esigenze dell'applicazione. HTTP (HyperText Transfer Protocol): protocollo a livello applicazione per gestire sul server le risorse ipertestuali. Normalmente la comunicazione HTTP usa una connessione TCP/IP (default port=80); le specifiche pero' richiedono solo che il protocollo di trasporto sia affidabile. La comunicazione e' handshake: client e server si scambiano messaggi contenenti anche 'meta-informazioni' in un formato simile al MIME (usato per la posta elettronica multimediale). TCP/IP: Il protocollo TCP/IP (Transfer Control Protocol/Internet Protocol) e' alla base del software usato su Internet per la trasmissione dei dati. E' costituito da 2 componenti: i protocolli IP e TCP. IP : scompone i dati e li inserisce in pacchetti software in modo che possano essere piu' facilmente trasferiti attraverso le reti. I computer 'router' lungo il percorso istradano i pacchetti utilizzando gli indirizzi dei computer sorgente e destinazione inseriti nei pacchetti, fino a farli pervenire al computer di destinazione che, quando li riceve, li riassembla. TCP (Transfer Control Protocol) e' un protocollo basato sulla connessione punto-punto dei due computer e garantisce il flusso di dati, come e' richiesto da protocolli di applicazione, come HTTP o FTP o Telnet. Il software del protocollo TCP assicura la correttezza del riassemblaggio, chiedendo la ritrasmissione dei pacchetti persi o danneggiati. Quindi il protocollo TCP/IP permette la commutazione di pacchetto (suddivisione dei dati in vari pacchetti e trasmissione commutata dei pacchetti in modo da sfruttare al massimo la capacita' della linea di trasmissione. E' strutturato su 4 livelli: - livello fisico (della rete); - indirizzamento e trasmissione; - controllo (con eventuale ritrasmissione) e gestione degli invii; - livello applicazioni (genera i dati come richiesti per l'utente). UPD: Il protocollo UPD (User Datagram Protocol) e' invece un altro protocollo di trasferimento di dati che non instaura una connessione tra i due computers: uno trasmette pacchetti di dati indipendenti dell'applicazione (datagrams) e non fa alcun controllo sulla loro corretta ricezione: non viene cosi' garantita ne' la ricezione ne' l'ordine di arrivo dei pacchetti, ma soltanto il loro veloce invio. UPD e' piu' veloce perche' elimina l'overhead temporale richiesto per instaurare la connessione tra i due computer. Un esempio del suo uso e' il 'ping', cioe' il test della qualita' di una connessione: un diverso protocollo che assicurasse una connessione affidabile renderebbe impossibile rilevare la qualita' della linea. Indirizzo IP: ciascun computer collegato ad Internet deve avere un indirizzo IP univoco di 32 bit, che gli viene assegnato dal provider del servizio di connettivita' Internet o dall'amministratore della rete intranet, che a loro volta l'hanno ottenuto dall'organismo internazionale a cio' preposto. Se si usa un account PPP/SLIP l'indirizzo IP viene assegnato di volta in volta dal provider al momento del collegamento. Gli indirizzi IP dei computer sorgente e destinatario vengono inseriti in ogni pacchetto e sono utilizzati dai computer router per istradarli in maniera ottimale fino a farli giungere al computer di destinazione. L'indirizzo IP e' formato da quattro numeri (=0..255) di tre cifre a cui corrisponde, in base al DNS (Domain Name System) il piu' mnemonico e significativo indirizzo simbolico che usa l'utente nella navigazione, secondo la struttura: 'protocollo.nomedominio.suffisso' Esempio: l'indirizzo 199.170.0.150, corrispondente ad un server dell'FBI, si trasforma in DNS nell'indirizzo www.fbi.gov (sito ufficiale dell'FBI), molto piu' comodo da usare. L'ultimo campo si chiama suffisso ed individua un dominio. Oltre ai domini nazionali (es. IT=Italia, FR=Francia, ecc.) sono attivi i seguenti altri: EDU = Organizzazioni di ricerca e Universita'; COM = Organizzazioni commerciali; GOV = Enti governativi; MIL = Enti militari; NET = Gestori di rete; ORG = Altre organizzazioni. Altri suffissi che saranno attivati sono: FIRM, STORE, WEB, ARTS, REC, INFO, NOM. Localizzatore URL (Uniform Resource Locator): e' lo standard di indirizzamento delle risorse secondo il modello: "protocollo.nomedominio.suffisso" Il protocollo usato da internet e' HTTP. Esempio: "http://www.microsoft.com/index.html" I browser Web sono in grado di usare anche altri protocolli nell'indirizzo URL. Esempi: 1) per usare un protocollo FTP per scaricare files da un server FTP l'indirizzo sara': "ftp://nome_del_sito/percorso/nome_del_file" Esempio: "ftp://ftp.netscape5.com/navigator/3.0/mac/README.TXT" (se manca nome_file il server Web mostrera' l'elenco dei files nella directory); 2) con il protocollo FILE si accede ad un file locale del computer. Esempio: "file:///C|/WWW/INDEX.HTM" Si noti che si usa | al posto dei due punti (usati nell'indicazione del numero della la porta, 80 pe default) e che si usano sempre le barre / (slash anziche' backslash). 3) per accedere ad un server Gopher da un browser Web l'indirizzo URL sara' del tipo: "gopher://nome_server_gopher/. 4) si puo' anche usare un protocollo di posta elettronica. Esempio: "mailto:[email protected]" Si puo' cosi' inviare un messaggio all'indirizzo indicato. Il localizzatore URL fornisce la localizzazione di un file sul WEB e identifica il servizio Internet, come WWW o FTP, che gestira' il file. Nel caso di un protocollo http (significa che il file si trova su un server WEB) il formato e' il seguente: http://www.microsoft.com/prodotti/prodotto.htm#informazioni Protocollo<--path di rete--> path <--NomeFile--> Segnalibro Gli URL possono anche contenere l'indicazione del numero di porta, che in pratica determina quale dei diversi processi attivi sull'host dovra' gestire la richiesta, cioe' i protocolli di trasporto TCP o UPD usano i numeri di porta per 'mappare' i dati entranti nel server ad uno dei processi attivi sul server. Esempio: "http://www.microsoft.com:80/index.html" (se mancante, http assume il numero di porta 80 come default) Il numero di porta e' di 16 bit, quindi va da 0...65535, ma i numeri 0...1023 sono riservati ai servizi del sistema. Esempi di altri URL con protocolli diversi sono: ftp://ftp.microsoft.com/italy/file.doc (su un server FTP) mailto:[email protected] (il browser apre il modulo di E.mail) file://computer.host/file.doc (il file.doc sta su computer.host) Se l'URL e' completo si dice assoluto, altrimenti e' relativo (alla pagina corrente). Cioe' se computer o directory non sono indicati vengono usati quelli del file corrente, mentre se il file non e' indicato viene assunto come predefinito index.htm Se il segnalibro esiste il browser visualizza la pagina a partire dalla posizione del segnalibro. Intranet: e' una rete basata sulla tecnologia Internet, ma sviluppata per uso privato; realizza un Web interno, cioe' riservato ai membri di una stessa organizzazione. Pur essendo basata sulla tecnologia Internet, che permette la connettivita' di utenti che possono essere migliaia e sparsi anche in localita' molto distanti, una rete intranet mantiene le caratteristiche di una rete privata, poiche' viene realizzata all'interno di un 'firewall', cioe' di un meccanismo software capace di bloccare eventuali tentativi di accesso non autorizzati dall'esterno, garantendo allo stesso tempo l'accesso all'intera rete intranet da parte degli utenti ad essa connessi. All'interno della rete intranet l'amministratore puo' concedere particolari diritti agli utenti connessi, controllando con certificati e firme digitali le sicurezze dei browser (vedasi capitolo relativo). I vantaggi delle reti intranet derivano dalla possibilita' di usare tanti strumenti disponibili commercialmente con i quali si puo' facilmente: - Distribuire documenti in formato elettronico - Gestire i documenti ed i dati per il lavoro dell'organizzazione - Fornire accesso al data-base comune - Distribuire ed aggiornare programmi - Distribuire elaborazioni e input/output - Realizzare forum di discussione o collegamenti diretti audio/ video. Extranet: e' una rete Intranet estesa ad alcuni membri esterni alla organizzazione, ma interessati ad accedere all'intranet aziendale (come clienti e fornitori, partner d'affari, ecc.). L'accesso viene controllato dal server aziendale tramite username, password e firme digitali. Servizi messi a disposizione da Internet: - Posta Elettronica (E-Mail), con messaggi comprendenti audio, video e immagini - World Wide Web (enorme serbatoio di informazioni, organizzato come un insieme di siti tra cui e' possibile 'navigare' (to browse) con i collegamenti ipertestuali; con un Bookmark si puo' memorizzare un utile indirizzo Web per poi tornarci facilmente); - Bullettin Board (newsgroup e forum) con cui si realizzano gruppi di discussione scrivendo messaggi su un 'server news' che possono essere letti da un client dotato del software 'newsreader'. - comunicazione diretta (es. canali IRC=Internet Relay Chat e videoconferenze con connessione diretta video/voce, senza costi di collegamenti interurbani). - FTP (File Transfer Protocol) usato per il trasferimento di file. - Gopher per l'esplorazione di Internet con una serie di menu' - i servizi avanzati. Gli strumenti 'intelligenti' sono: - i browser, come Netscape Navigator o Microsoft Internet Explorer (IE 4.0), prelevano dal server Web indicato nell'indirizzo internet (con il protocollo indicato) le informazioni e le formattano per visualizzarle sul proprio sistema, in base alle sue caratteristiche ed alle funzionalita' attivate; - i motori di ricerca, come Altavista (www.altavista.digital.com) o HotBot (www.hotbot.com), che gestiscono indici ricercando i link su un dato tema; - le mailing list; - i newsgroup (che sono bacheche pubbliche di comunicazione via E-mail); - la Usenet (un insieme di newsgroup, basato su computer "news server" che si sincronizzano continuamente in modo da tenere tutte le news aggiornate; - downloading o trasferimento di files tra due computer con il protocollo FTP (File Transfer Protocol). Attivita' specifiche per Internet: Editoria, Pubblicita', Commercio elettronico, Applicazioni avanzate (Java, Realta' virtuale,...), Telelavoro, ecc. ALTRE INFORMAZIONI UTILI ======================== Elenco di sigle e parole usate (consultare il glossario per le altre): -----------------------------WYSIWYS = tipo di visualizzazione usato da editor HTML come Front Page (WYSIWYS="What You See Is What You Get") Provider = Societa' che fornisce accesso alla rete tramite uno o piu' POP (Point Of Presence), cioe' numeri telefonici. POP,POP3 = Post Office Protocol (l'ultima versione e' la terza POP3, piu' usata) IMAP = Internet Message Access Protocol (concorrente di POP3, piu' evoluto ma meno diffuso) SNMP = Simply Network Management Protocol ( e' usato dal programma 'Agente SNMP' caricato nei computer client che vogliono monitorare e/o amministrare l'attivita' di rete) Altre sigle: MSDN = logo for MicroSoft Developers Network JDK = Java Development Kit IDE = Integrated Development Environment RAD = Rapid Application Development API = Application Programming Interfaces SQL = Structured Query Language ODBC = Open Database Connectivity (driver per compatib.esterna di database) GIF = Graphics Interchange Format JPEG = Join Photographics Experts Group PNG = Portable Network Graphics W3C = World Wide Web Consortium Funzioni disponibili su Internet: --------------------------------- Navigazione - E-mail - Newsgroup e Forum - Download di files - Teleconferenza/Videoconferenza - Chat - Multiutenza - Editing HTML - Pubblicazione on-line di siti - Integrazione di software esterni - Supporto di plug-in Programmi disponibili in Windows 98: ----------------------------------Internet Explorer: browser con navigazione a menu' o a barra di pulsanti; OutLook Express: per E-mail e per consultazione di newsgroup (come Eudora); FrontPage Express: come editor HTML; Microsoft Chat: per il chat on-line su Internet con immagini e suoni; NetMeeting: chat con supporto per videoconferenze; Wizard di connessione: per creare facilmente gli accessi ad Internet; NetShow Player: per presentazioni multimediali on-line. HyperTerminal: programma per collegarsi ad un computer remoto o ad un BBS (Bullettin Board Service); basta seguire le istruzioni su video per usufruire del servizio; Motori di ricerca ed esempi di siti Web: ---------------------------------------AltaVista: www.altavista.com HotBot: www.hotbot.com NewsBot: www.newsbot.com (versione di HotBot dedicata al mondo UseNet) Virgilio: www.virgilio.it (in italiano) Software: WinFiles: VolFTP: Eudora: Tucows: www.winfiles.com (serbatoio di software di tutti i tipi) www.volftp.it www.eudora.com www.tucows.com (raccolta di shareware, plug-in per browser, ecc.) Turismo: Amadeus: www.amadeus.net (network internazionale per prenotazioni, ecc.) Canale Turismo Tin: http://turismo.tin.it/ (informazioni sul turismo) WeatherLab: www.weatherlab.com (servizio di previsioni meteo) Cultura: Alice: www.alice.it Amazon: www.amazon.com (libreria virtuale con catalogo di ricerca) Rock On Line: www.rockol.it (informazioni su eventi musicali, ecc.) Mondo Media: www.tin.it/mondomedia Mailing List: www.cilea.it/maillist www.cilea.it/WWW-map/ www.paginegialle.it http://mailory.tin.it/ National Geographic: www.nationalgeographic.com Aiuto per nuovi utenti: news.announce.newusers news.newusers.questions (nome del newsgroup) Demo di applicazioni ingegneria: www.leader.it (internet/intranet) Corso minimo: www.freesoft.org/CIE/index.htm !----------------------------------------------------------------------APPENDICE B - Il simulatore H6809 ═══════════ Uno dei campi di applicazione dei computer piu' di successo e' quello delle simulazioni. Il successo deriva dal fatto che, una volta simulato in processo o un apparato, risulta molto agevole, economico e veloce verificarne il comportamento in funzione dei parametri costruttivi e quindi ottimizzarli prima della sua onerosa costruzione effettiva. I fisici sono soliti simulare al computer gli apparati sperimentali di rivelazione dei fenomeni fisici prima della loro costruzione, assumendo accettabili modelli matematici come descrizione del loro funzionamento, anche in quei casi in cui l'indagine fisica li portera' ad investigare su fenomeni fino ad allora inesplorati: esempi tipici sono i programmi 'Montecarlo', di cui viene fatto un semplice esempio nelle esercitazioni (simulazione Montecarlo dello 'scattering coulombiano'). Argomento centrale delle esercitazioni pratiche e' invece la simulazione del funzionamento di un processore Motorola 6809 (semplificato) fatta con un programma Pascal su PC-IBM. Oltre a comprendere il funzionamento di un tale simulatore, lo studente potra' usarlo per eseguire programmi in codice M6809 senza avere fisicamente disponibile questo processore. Una tale metodologia e' chiaramente di grande utilita' nei casi di progettazione di nuove macchine elettroniche, consentendone il test prima della costruzione. Il listato sorgente del simulatore e' fornito sul dischetto delle esercitazioni con due errori che ne alterano, sia pur di poco, il funzionamento. Viene richiesto allo studente di rivelare questi errori, conoscendo il corretto funzionamento del processore M6809, e quindi di rimuoverli. Il simulatore potra' poi essere usato per le esercitazioni sui programmi scritti in linguaggio a basso livello (Assembly e linguaggio macchina) per il processore H6809. !----------------------------------------------------------------------APPENDICE C - Programma delle esercitazioni ═══════════ PROGRAMMA DI LAVORO DURANTE LE ESERCITAZIONI PRATICHE: -----------------------------------------------------1) Uso dei comandi elementari del Sistema Operativo MS-DOS 2) Uso dell'Editor del TURBOPASCAL 3) Prove di uso delle istruzioni Pascal in semplici programmi ( xxxDEMO.PAS ) 4) Comprensione ed ampliamento del programma ADDING.PAS 5) Studio del programma Monitor 6) Studio del programma di simulazione H6809BUG.PAS (con BINPROG.PAS); ricerca ed eliminazione degli errori ivi contenuti 7) Studio del microcomputer H6809 simulato su IBM-PC, scrivendo ed eseguendo un semplice programma in linguaggio macchina 8) Ampliamento del simulatore H6809 introducendovi altre istruzioni come quelle di branch condizionato o di I/O o il sistema d'interruzione 9) Realizzazione in Basic di un simulatore dello scattering coulombiano con il metodo di Montecarlo 10) Programmazione concorrente del Montecarlo su una LAN in elaborazione parallela ('farm' di computer). 11) Uso dell'Editor dell'ambiente JDK1.3 (Java Developmenti Kit) 12) Prove di uso delle istruzioni Java in semplici programmi (xxxdemo.java): PrimoEsempio, SecondoEsempio, TerzoEsempio, Test0, Root, NewRoot, SwapTest, SwapTest2, Riordina, VirusLook QuoteLotto: esempio con input da tastiera e chiamata a metodo EmployeeTest, H6809h: Monitor H6809 scritto in java Root, EsempioApplet: esempio di applet Prova: programma contenente un errore da trovare 13) Studio del programma Monitor H6809g.java, con particolare attenzione all'uso dell' oggetto Byte (esempio di programmazione ad oggetti) 14) Uso del server httpServer per connessione HTTP con un browser. AVVERTENZA IMPORTANTE ===================== Le esercitazioni si svolgono usando un dischetto personale per ogni studente: su questo dischetto vi sono i files contenenti le dispense del corso, gli appunti (LEZ??.PAS) ed i programmi per le esercitazioni. Gli esercizi e le dispense su Internet, sulla programmazione ad oggetti e sul linguaggio Java sono consegnati agli studenti all'inizio del modulo B (subito dopo Pasqua). Lo studente dovr? riconsegnare questo dischetto al docente al termine delle esercitazioni e non portare dischetti propri per ragioni di sicurezza e protezione dai 'virus'. Un virus e' un programma nascosto all'interno di un altro programma usato dell'utente. Quando viene lanciato in esecuzione esso modifica gli altri programmi eseguibili presenti sul dischetto e nel computer, installandosi in maniera invisibile nella memoria e nel sistema operativo, in modo da rimanere stabilmente installato nella macchina. Questa risulta così 'infettata' ed in grado di infettare tutti i programmi eseguibili, presenti sui dischetti in essa inseriti da utenti ignari . Questo potenziale processo di contagio dei dischetti del nostro corso e' possibile sia nei computers del nostro laboratorio che in quelli del CATTID, poiche' sono centri aperti a tutti e qualcuno puo' portare dischetti infetti dall'esterno. I vostri dischetti, una volta infettati, se portati fuori dall'Universita' possono infettare il vostro computer di casa, dell'ufficio, ecc. Per questi motivi lo studente deve riconsegnare il suo dischetto al docente al termine delle esercitazioni e non portare mai dischetti propri al laboratorio o al CATTID. !----------------------------------------------------------------------Argomenti --------GUI EVENTI (SICUREZZA) Data ---9/5 " no APPLET RETI 11/5 " LAB. (Java) INTER-RETI INDIRIZZAMENTO IP PROTOCOLLO TCP 16/5 " " M 18/5 " " V INTERNET HTML CGI LAB. (seminario) Acquisiz.dati+CAMAC VME + FASTBUS LAB. (HTML+CGI) (ARCHITETT.PARALLELE) M V 14/5 L 21/5 23/5 25/5 L M V 28/5 30/5 L M