Articolo pubblicato sul n. 114 di
MCmicrocomputer
(Edizioni
Technimedia Srl - Roma) nel gennaio 1992
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.
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
|