Articolo pubblicato sul n. 114 di MCmicrocomputer (Edizioni Technimedia Srl - Roma) nel gennaio 1992

MCmicrocomputer


 

Multitasking:
OCCAM: un esempio concreto
(seconda parte)

di Andrea de Prisco

Continuando il discorso iniziato lo scorso mese, su questo numero commenteremo la rimanente parte del software di rete di ADPnetwork scritta in OCCAM. Inutile dirvi che per comprendere il funzionamento delle varie routine descritte e' necessario tenere sott'occhio controllo il listato pubblicato sull'articolo precedente nonche' la descrizione a blocchi del numero di novembre ultimo scorso. Sempre e solo, come detto, con l'unico scopo di non perdervi nei meandri dei troppi canali e processi di cui parleremo. In piu', pubblichiamo questo mese il file di "include" contenente le varie costanti e il ".PGM" che, come vedremo, alloca processi e canali del processore utilizzato.Copertina del numero di MCmicrocomputer contenente l'articolo

Poche chiacchiere

Visto che il mese scorso a furia di discorsetti, introduzioncine, parentesucce e affini siamo riusciti a malapena a commentare solo il listato del processo Dispatcher, questo mese partiamo subito con il commento, iniziando per l'appunto dal processo successivo, ReceiverAmiga.
Rappresentato nel pallogramma di figura 1 (il listato, come detto, dovete cercarlo sul numero scorso) ha in pratica funzioni di buffer per i messaggi in partenza e per quelli in transito. Per la precisione si tratta di un "multibuffer" in quanto pacchetti di natura o lunghezza diversa vengono bufferizzati al suo interno in array differenti, in modo da dimensionare quest'ultimi differentemente e, perche' no, eventualmente creare corsie preferenziali per messaggi piu' importanti.
Ad esempio, i pacchetti di Ack (generati dai processi dispatcher delle varie macchine in rete), sono sicuramente piu' urgenti di qualsiasi altro tipo di pacchetto in quanto se non arriva in tempo, il mittente del messaggio (del quale appunto il Dispatcher del destinatario ha generato il pacchetto di ack) provvedera' a rispedirlo pensando che si sia perso per strada. Con conseguente aggravio sul traffico generale
dei pacchetti, con ulteriori ritardi, Ack sempre piu' lenti e cosi'
1via come un gatto che rincorre la coda che oltre a non afferrarla mai satura ben presto tutti i buffer e tutti i link della rete.
Quindi, precedenza assoluta agli Ack, sia in transito che in partenza. Localmente, poi, ad ogni scheda, e' opportuno tenere i buffer piu' vuoti possibile (per evitare il riempimento totale e l'uccisione da parte del dispatcher dei pacchetti in arrivo): in pratica (sempre figura 1) accogliere principalmente le richieste del processo SenderLink che prende i pacchetti bufferizzati e li spedisce sulla rete.
Da qui il motivo dell'utilizzo, nel processo ReceiverAmiga (presente sul numero scorso), di un costrutto PRI ALT che in caso di piu' guardie verificate da priorita' alla prima (l'ordine e' quello dato dal listato stesso). Ogni comando con guardia del processo ReceiverAmiga il controllo (nella guardia, appunto) riguarda lo stato dei buffer stessi, vuoto, pieno, "mezzo pieno". Quest'ultimo caso e' stato aggiunto per lasciare sempre almeno mezzo buffer ai pacchetti (non Ack) in transito su quel nodo. Vediamo, allora, caso per caso le varie alternative del comando PRI ALT (l'ordine, come detto, implica priorita'). Nel primo caso troviamo la guardia:

(FrameCount > 0) OR (AckCount > 0) & GiveMe ? dummy

che letteralmente significa: "se FrameCount e' maggiore di zero (buffer pacchetti lunghi non vuoto) oppure AckCount e' maggiore di zero (buffer pacchetti di Ack non vuoto) e c'e' una richiesta da parte del processo SenderLink di un nuovo pacchetto da spedire esegui le linee di codice seguenti il successivo SEQ". Chiaramente FrameCount e' una variabile che contiene continuamente il numero di pacchetti bufferizzati nel buffer "normale" e AckCount in quello "speciale". Per moderare, poi, la prevaricazione prioritaria dei pacchetti di tipo Ack, il corpo della sequenza di comandi associata alla prima guardia si comporta in maniera "flip-flop" pescando una volta prima dal buffer degli Ack e la volta successiva prima in quello dei pacchetti normali. Va da se' che qualora uno dei due fosse vuoto comunque parte il pacchetto presente in quello pieno. Tutto cio' e' realizzato dalla variabile booleana swap che una volta vale TRUE e la volta successiva FALSE facendo eseguire alternativamente o il primo o il secondo ramo del comando IF.
Il resto del listato del processo ReceiverAmiga si commenta da se': seguono le guardie d'ingresso per il buffer degli Ack, per il buffer normale con accesso per i pacchetti in transito e in ultimo (quindi con priorita' piu' bassa e per di piu' con il buffer "dimezzato") sempre per il buffer normale, ma questa volta per i pacchetti in partenza. In quest'ultimo caso, il pacchetto viene completato di CRC calcolato sull'effettivo corpo (body) del messaggio trasportato dal pacchetto in partenza. Questo per sgravare quanto piu' possibile il processore dell'Amiga che oltre a implementare la rete deve anche continuare a funzionare come computer per l'utente.

L'altro buffer

Il processo SenderAmiga (sempre in figura 1) bufferizza i messaggi in arrivo su quel nodo e li spedisce all'Amiga. Anche in questo caso troviamo una PRI ALT apparentemente funzionante al contrario: viene data priorita' al riempimento del buffer invece che al suo svuotamento. Il motivo e' molto semplice: e' assolutamente necessario non bloccare mai il processo Dispatcher (eseguito sul transputer il quale e' MOLTO piu' veloce del 68000 di cui e' dotato l'Amiga) il quale svolge funzioni tanto per il nodo in questione quanto per tutti i nodi della rete (reinoltrando pacchetti in transito). Al punto che, l'abbiamo detto lo scorso mese, nel caso in cui il buffer di SenderAmiga fosse malauguratamente pieno il Dispatcher butta il pacchetto non bufferizzato e si rimette in attesa sul link esterno (nodo precedente nell'architettura di ADPnetwork). In pratica il processo SenderAmiga preleva comunque la richiesta di inseriemento da parte del Dispatcher rispondendogli sul canale OK con un valore TRUE se il pacchetto e' stato bufferizzato, FALSE se lo spazio non c'era. Riallacciandoci brevemente al commento dello scorso mese, nel primo caso il Dispatcher genera se necessario l'Ack del messaggio effettivamente arrivato (si presume che una volta bufferizzato, prima o poi l'Amiga se lo legga...) nel secondo non fa nulla, uccidendo cosi' di fatto il pacchetto "sfortunato".
La seconda guardia d'ingresso del processo SenderAmiga effettua l'interfacciamento col processo "minore" AmigaInterface, inviando ad esso un pacchetto bufferizzato ad ogni sua richiesta sul canale "strobe". Sempreche', naturalmente, il buffer non sia vuoto (condizione "totale > 0").

I processi "minori"

AmigaInterface e SenderLink sono due processi di interfaccia che non fanno altro che richiedere al corrispondente processo buffer (SenderAmiga per il primo e ReceiverAmiga per il secondo) un elemento bufferizzato ed effettuare rispettivamente la spedizione di quest'ultimo verso l'Amiga o verso il nodo successivo.
La loro "esistenza" e' dovuta al fatto che un processo buffer, per come e' strutturato l'OCCAM (e in generale la comunicazione inter process ad ambiente locale), e' di solito realizzato attraverso un comando alternativo (ripetitivo), con o senza priorita', sul quale e' possibile inserire solo guardie d'ingresso. In generale, il processo buffer, attende da due (o piu') canali d'ingresso comandi che possono essere di inserimento o di estrazione elemento nel/dal buffer. La coppia di processi "buffer-interfaccia" puo' semmai essere vista dal punto di vista logico come un'unica entita' buffer (box tratteggiati sempre in figura 1) in cui quello che entra dai canali d'ingresso del processo buffer automaticamente esce dal canale d'uscita del processo di interfaccia appena c'e' qualcosa nel buffer e il canale d'uscita e' libero (e' terminata, cioe', la spedizione precedente). Inutile commentarvi le tre (identiche) linee dei due processi che si autospiegano al primo colpo d'occhio.


Il processo MASTER

Tutti i processi finora descritti sono in pratica delle subroutine con tanto di passaggio di parametri. Chi e', dunque, che li fa partire come processi ? In fondo al listato pubblicato sul numero scorso c'e' un ultimo processo: Netputer. La sua funzione e' quella di si' di lanciare i processi, ma soprattutto di collegare i loro canali. Far si', ad esempio (figura 1), che il canale strobe di AmigaInterface sia lo stesso canale amigarequest di SenderAmiga sul quale il primo invia messaggi, il secondo riceve. Cio' si realizza passando ai due come parametro lo stesso canale, dichiarato dal processo Netputer nelle sue dichiarazioni. Li' il canale si chiama ancora strobe, ma nulla vietava di dichiararlo con nome Pippo e sempre con tale nome passarlo ai due processi. Che poi essi al loro interno lo chiamino rispettivamente strobe ed amigarequest e' solo una questione di nomi, che in quanto tali a tempo di esecuzione spariscono. Ne' piu' ne' meno di quanto succede coi parametri attuali e formali delle normali subroutine degli altrettanto normali (nel senso di classici) linguaggi di programmazione.
Come sempre visibile nel listato pubblicato lo scorso mese, Netputer ha bisogno sua volta di alcuni parametri esterni (come i link fisici e l'indirizzo di rete) che gli saranno passati all'interno del file di configurazione come vedremo ora.

L'eseguibile

In figura 2 e' mostrato il file di configurazione per ottenere l'eseguibile per il transputer. O, meglio, per ottenere un vero e proprio file di boot che inoltrato attraverso un link su un transputer appena resettato (e con il piedino di BootFromLink settato oppotunamente, diversamente il boot sara' effettuato da piu' classiche rom) permette a questo di partire con il codice da eseguire. Lo stesso file di configurazione permette di mappare processi su reti di transputer fornendo comunque sempre e solo un unico eseguibile da mandare al primo transputer della rete che provvedera' a prendersi la parte di codice di sua competenza e a rigirare ai rimanenti nodi il codice per gli altri transputer.
Commentiamolo brevemente. A parte i due include iniziali necessari per le costanti di I/O e per gli indirizzi dei link fisici, il tutto si svolge dichiarando alcuni canali, piazzando questi sui link fisici dei processori disponibili, e chiamando i processi o il processo da eseguire passando gli eventuali parametri. Nel caso mostrato in figura 1 abbiamo un solo transputer (T2, famiglia T200), quattro canali (uno verso il nodo successivo, uno verso il nodo precedente, uno bidirezionale da/verso l'amiga) e solo codice ("netputt2. c2h") generato dal linker dopo la compilazione. Il processo Netputer e' naturalmente contenuto in questo codice cosi' come sono in esso contenuti tutti gli altri processi lanciati in parallelo da questo.

Per concludere

In figura 3 e' mostrato l'include "ADPnet.inc" utilizzato dal software di rete. Li' dentro troverete tutte le costanti usate, la tabella delle costanti per il calcolo del CRC a 32 bit e due array per il controllo veloce della lunghezza e del tipo del pacchetto ricevuto da parte del processo Dispatcher che, tra le tente cose, deve anche riconoscere pacchetti validi da eventuale "monnezza" erroneamente in circolazione sulla rete (a causa ad esempio di "morte violenta" di un nodo). Possiamo anche per questo mese mettere il nostro "punto e basta" con la soddisfazione pero' di non aver lasciato discorsi a meta': non crediate che ci divertiamo a spezzare gli articoli ! E' solo che stringendo troppo si rischia di non riuscire a farsi comprendere, pubblicando articoli di 10-12 pagine si ha quasi la certezza di non essere affatto letti. Ci spiace solo per chi ha perso il numero precedente o perdera' questo. Pazienza !


Articolo pubblicato su www.digiTANTO.it - per ulteriori informazioni clicca qui