Articolo pubblicato sul n. 110 di
MCmicrocomputer
(Edizioni
Technimedia Srl - Roma) nel settembre 1991
Multitasking:
OCCAM: canali e
messaggi
di Andrea de Prisco
Dopo la doverosa introduzione del numero scorso, questo mese
scenderemo un po' piu' nei dettagli riguardo le
comunicazioni tra processi OCCAM. Vedremo poi come risolvere
il problema della sincronicita' delle comunicazioni,
mostrando un primo esempio di programma OCCAM. Naturalmente
parallelo...
Array e matrici
OCCAM mette a disposizione, purtroppo, ben pochi tipi di
dato. Oltre ai gia' citati INT e BYTE esiste il tipo di dato
BOOL (che puo' assume solo uno dei due valori TRUE o FALSE)
e, in alcune implementazioni, anche il tipo REAL32 e REAL64
che differiscono per il numero di bit delle rispettive
rappresentazioni.
Tutti i tipi di dato (compresi i canali, come vedremo)
possono essere "vettorizzati" ossia utilizzati come vettori
o matrici. Cio' e' possibile indicando nella dichiarazione
il numero di elementi di cui e' composta la nostra sruttura
accanto al tipo degli elementi stessi. Ad esempio lo
dichiarazione:
[100] INT pippo:
definisce un array di interi di nome "pippo" i cui elementi
sono indirizzati con pippo[0], pippo[1], . . . , pippo[99].
Analogamente possiamo definire array multidimensionali
semplicemente indicando piu' dimensioni nella dichiarazione:
[50][100] BOOL pluto:
definisce una matrice 50x100 i cui elementi sono di tipo
BOOL.
Sono possibili assegnamenti tra array senza ricorrere a loop,
a condizione che gli array sorgente e destinatario siano
dello stesso tipo (dimensione dell'array e tipo degli
elementi). Se le dimensioni non coincidono (ma solo il tipo
degli elementi) e' possibile effettuare assegnamenti
parziali. Parti di array piu' grandi in array piu' piccoli
o, viceversa, array piu' piccoli in parti di array piu'
grandi. Scrivendo ad esempio:
[pippo FROM 10 FOR 50]
indichiamo di fatto i 50 elementi dell'array pippo dalla
posizione 10 fino alla posizione 59. Questa indicazione e'
valida tanto per la sorgente che la destinazione di un
assegnamento o, piu' in generale, in qualsiasi valutazione
di espressione. Facciamo un esempio. Immaginiamo di aver
dichiarato anche l'array "piolo" nel seguente modo:
[50] INT piolo:
l'assegnamento:
piolo := [pippo FROM 10 FOR 50]
e' certamente lecito. Analogamente possiamo riferirci a
sottoinsiemi sia come sorgente che come destinazione:
[piolo FROM 5 FOR 10] := [pippo FROM 50 FOR 10]
in questo caso i dieci elementi di pippo dalla posizione 50
alla posizione 59 sono copiati negli elementi di piolo dalla
posizione 5 alla 14. L'importante e' che il sottoinsieme
destinazione e il sottoinsieme sorgente abbiano pari
dimensioni (ed elementi dello stesso tipo)
Per finire (i progettisti di OCCAM si sono proprio
sbizzarriti con gli array) e' possibile definire una
abbreviazione per indicare un determinato sottoarray di un
array di partenza. Ad esempio scrivendo:
pippino IS [pippo FROM 10 FOR 50]
possiamo alternativamente accedere ai 50 elementi di pippo
attraverso il nome pippino. Cosi' pippino[0] sara' pippo[10]
e cosi' via fino a pippino[49] che sara' pippo[59]. In tal
modo pippino e' a tutti gli effetti un array di 50 elementi
di tipo INT e come tale dovra' essere trattato in ogni
istruzioni di assegnamento (sia come sorgente che come
destinazione) o di valutazione di espressione.
Canali con tipo
Come precedentemente anticipato, le comunicazioni tra
processi OCCAM avvengono tramite scambio messaggi per mezzo
di canali di comunicazione. Ricordiamo che il canale e' un
mezzo logico di comunicazione sin al momento del lancio
dell'applicazione parallela (quindi rimane tale anche
terminata la compilazione) in cui assume aspetto fisico in
due distinte forme a seconda che i processi comunicanti
siano in esecuzione sullo stesso o su differenti transputer.
Nel primo caso, infatti, il canale viene mappato in memoria
e corrisponde, in pratica, ad un buffer "a zero posizioni"
in cui il processo mittente inserisce il messaggio inviato e
contemporaneamente il destinatario lo legge. Nel caso di
processi in esecuzione su CPU diverse, il canale logico e'
mappato sul link fisico che collega i due transputer in
questione.
In ogni caso, prima di utilizzare un canale e' necessario
dichiarare il suo nome e il tipo di messaggio che dovra'
trasferire (quest'ultimo tipo, nella definizione del canale,
e' detto "protocollo"). Ad esempio:
CHAN OF INT pippo:
definisce un canale di nome "pippo" sul quale "passano" solo
ed esclusivamente interi (il protocollo e' INT).
Volendo definire canali un po' piu' complessi, e'
sufficiente indicare il tipo del messaggio come nel caso
appena visto per gli interi. Ad esempio se dobbiamo
trasferire da un processo ad un altro array di 100 byte,
scriveremo:
CHAN OF [100]BYTE NomeCanale:
Chiaramente esiste anche un meccanismo per definire canali
con protocollo array di lunghezza variabile, che dovremo
conoscere solo a tempo di esecuzione nel momento in cui ci
accingiamo ad effettuare la comunicazione da parte del
mittente e che conosceremo appena completata l'operazione da
parte del destinatario del messaggio. Scrivendo:
CHAN OF INT::[]BYTE NomeCanale:
dichiariamo che il canale e' utilizzato per trasferire array
di BYTE (potevano anche essere INT) di lunghezza variabile.
Al momento della vera e propria send indicheremo la
lunghezza. Ad esempio:
NomeCanale ! 25::pippo
spedisce l'array pippo (precedentemente dichiarato come
[25]BYTE pippo). Nello stesso programma in un altro punto
potremo utilizzare lo stesso canale per spedire un array di
lunghezza diversa, ad esempio:
NomeCanale ! 100::pluto
in questo caso pluto e' un array di 100 byte.
Conseguentemente il processo destinatario effettuera' la sua
receive indicando una variabile di tipo INT nella quale
ricevera' la lunghezza dell'array e un nome di un proprio
array lungo almeno quanto l'array da ricevere:
NomeCanale ? Lunghezza::Array
Esistono poi delle abbreviazioni per definizioni di canali
il cui tipo e' abbastanza complesso. Si tratta di definire a
parte il protocollo di comunicazione e poi inserire il nome
di questo nelle varie definizioni di canale necessarie.
PROTOCOL sequenza IS INT;INT;INT;INT:
CHAN OF sequenza lista:
definisce un protocollo di nome sequenza corrispondente a
quattro interi "sparati" l'uno dopo l'altro e subito dopo un
canale di nome lista con protocollo sequenza (appena
definito). Cosi' il processo mittente potra' ad esempio
eseguire:
lista ! 15; 35; 665; 455
e corrispondentemente il destinatario:
lista ? a; b; c; d
Naturalmente la sequenza di tipi indicata nella definizione
di protocollo puo' anche essere dismogenea: l'importante che
tanto il mittente quanto il destinatario si attengano
strettamente alla definizione data prima di utilizzare il
canale per le comunicazioni.
Protocolli variabili
Potrebbe essere utile dichiarare un canale di piu' tipi
diversi e poi utilizzare di volta in volta a tempo di
esecuzione il tipo giusto a seconda dei casi. Questo e'
possibile in OCCAM grazie cosiddetto protocollo variabile.
Ad esempio definiamo il protocollo variabile di nome
"multi":
PROTOCOL multi
CASE
a; BOOL;INT
b; BYTE;BYTE
c; BYTE;BOOL;INT
:
e poi definiamo il canale di nome "star":
CHAN OF multi star:
Il processo mittente, per effettuare la send di un messaggio
sul canale star deve indicare quale dei tre protocolli
possibili intende utilizzare per quella comunicazione e poi
regolarsi di conseguenza. Scrivendo ad esempio:
star ! a; TRUE; 250
utilizziamo il canale star impostando il protocollo "a" e,
quindi, inviando al destinatario un valore BOOL seguito da
un INT.
Il processo destinatario che utilizza il canale a protocollo
variabile puo' comportarsi in due differenti modi: se
conosce a priori quale dei tre tipi di messaggio arriveranno
indichera' semplicemente il caso "a" nella sua receive:
star ? CASE a; x; y
dove natural,ente "x" e' una variabile BOOL e "y" una
variabile "INT". Diversamente, se non conosce a priori il
tipo del messaggio in arrivo puo' indicare le varie
alternative:
star ? CASE
a; x; y
b; z; t
c; z; x; y
in questo modo se sul canale arriva un messaggio di
protocollo "a" saranno assegnate le variabili "x" e "y", se
e' di tipo "b" saranno assegnate le variabili di tipo BOOL
"z" e "t", se il protocollo e' di tipo "c" saranno assegnate
le variabili "z", "x" e "y".
Comunicazioni asincrone
In OCCAM tutte le comunicazioni inter process avvengono in
maniera sincrona: l'attimo logico in cui un processo
mittente effettua una send e' lo stesso in cui il
destinatario esegue la receive.
In altre parole se uno dei due processi arriva prima all'
appuntamento attendera' il rispettivo partner prima di
continuare per la propria strada.
Articolo pubblicato
su
www.digiTANTO.it - per ulteriori informazioni
clicca qui
|