Articolo pubblicato sul n. 92 di MCmicrocomputer (Edizioni Technimedia Srl - Roma) nel gennaio 1990
Amighevole: Quarta e, per il momento, ultima puntata dell'ADP multitasking toolbox per Amiga. Prima di passare alla descrizione dell'ultima manciata di funzioni e' d'obbligo (mica tanto) una precisazione. Ho notato, con un po' di dispiacere, che qualche lettore che mi ha contattato ha fatto un po' di confusione tra l'mttb e ADPnetwork. Qualcuno (anzi, piu' d'uno) s'e' anche complimentato col sottoscritto per il bell'ADPnetwork Toolbox, mischiando pericolosamente le due cose. L'mttb e' praticamente un linguaggio di programmazione che permette di scrivere facilmente applicazioni multiprocesso. Ovvero invece di scrivere un unico programma "grande" che fa molte cose, si scrivono un insieme di processi che singolarmente svolgono poche funzioni, ma globalmente (e grazie al multitask di Amiga) equivalgono, ottimizzandone le prestazioni, all'unico programmone iniziale. Il software di rete dell'ADPnetwork e' stato scritto interamente utilizzando l'mttb (come linguaggio di programmazione) ma le funzioni svolte sono ben diverse. L'mttb serve per far comunicare piu' processi su una stessa macchina, ADPnetwork serve per far comunicare processi in esecuzione su macchne diverse collegate in rete. E' chiaro ? Non vi nascondo pero' che l'attuale mttb si sta gia' espandendo verso la rete, permettendo, in futuro, molte delle funzionalita' attualmente offerte "in locale" anche "in remoto", ma e' un discorso che semmai riprenderemo tra qualche mese dopo aver completato la serie di articoli su ADPnetwork. I comandi presentati questo mese riguardano la spedizione inter process di caratteri e puntatori, e la creazione e il controllo di processi figli. Come potete vedere dal listato, le due nuove coppie di send e receive fanno al loro interno comunque uso delle note SendBlock e ReceiveBlock e quindi servono solo per semplificare operazioni comunque gia' possibili. Infatti SendBlock e ReceiveBlock sono in grado di spedire e ricevere da un processo ad un altro qualsiasi cosa mantenuta in memoria. Cominciamo dalla spedizione caratteri. Il processo interessato all'operazione esegue semplicemente un:
SendChar(mode, porta, carattere)
dove "mode" e' come al solito MODE_SYNC o MODE_ASYNC per comunicazione sincrona o asincrona, "porta" e' il nome della porta sulla quale effettuare la spedizione e "carattere" e', naturalmente, il carattere da spedire sottoforma di costante o variabile. Per fare un esempio, spedizioni "valide" di caratteri possono essere:
SendChar(MODE_SYNC,"yourport",'a') SendChar(MODE_ASYNC,"destport",v[35]) SendChar(MODE_SYNC,"yourport",p)
dove naturalmente p e' una variabile di tipo char e v un array di caratteri. Nel processo partner, ovvero il processo destinatario, oltre alla definizione di porta mttb:
NewPort("nome della porta")
troviamo la chiamata:
L = ReceiveBlock(mode,porta,&var)
dove "mode" e' il modo di ricezione MODE_WAIT o MODE_NOWAIT per receive bloccante e non bloccante, "porta" e' la porta precedentemente definita (nello stesso processo!) e "&var" e' il puntatore ad una variabile di tipo carattere (o ad un elemento di un array di caratteri) nella quale riceveremo il carattere. Se la receive era non bloccante (MODE_NOWAIT) e il mittente non aveva ancora effettuato la spedizione, viene restituito il carattere nullo e ritornato uno 0 come risultato della ReceiveBlock (posto nel nostro caso nella variabile intera "L"). Discorso del tutto analogo per spedire e ricevere i puntatori: le due funzioni sono SendPointer e ReceivePointer. Anche in questo caso, alla ReceivePointer sara' necessario passare l'indirizzo della varabile dicharata come "puntatore a qualcosa". Facciamo un esempio chiarificatore. Immaginiamo che il processo mittente spedisca in modo sincrono il puntatore al suo array di char Pippo sulla porta "ics" del processo ricevente. La chiamata sara':
SendPointer(MODE_SYNC,"ics",Pippo)
essendo Pippo effettivamente il puntatore al primo elemento dell'array. Nel processo ricevente troveremo:
char *Pluto; NewPort("ics"); ReceivePointer(MODE_WAIT,"ics",&Pluto);
e al ritorno della ReceivePointer avremo in "Pluto" il puntatore all'array "Pippo" del mittente. Inutile ricordarvi che gli altrui puntatori vanno manipolati con molta cura e che non bisognerebbe mai modificare zone di memoria il cui puntatore e' stato ceduto a qualcun altro. Come dire che e' meglio lasciare perdere queste due funzioni prima di aver preso la mano coi problemi multitask. Per quanto riguarda la creazione di processi figli, il discorso si complica un po' ma non eccessivamente. Innanzitutto sono state utilizzate all'interno delle funzioni mttb, chiamate a funzioni del Workbench. Si presuppone che i processi da lanciare come figli siano ovviamente opportunamente compilati e presenti in un qualsiasi device sottoforma di file eseguibile. Partiamo con la funzione RunProcess che permette di lanciare un numero di processi compreso tra 1 e 8. Tale funzione restituisce il controllo al processo chiamante (il padre) appena sono terminati tutti i processi lanciati (i figli). I parametri da utilizzare sono semplicemente il numero di processi e l'uno dopo l'altro i nomi dei processi da lanciare. I nomi dei processi devono naturalmente coincidere coi nomi dei file eseguibili, e gli stessi sono passati al sistema come nomi dei processi in esecuzione sulla macchina (in questo caso path di indirizzamento escluso). Se ad esempio da un processo invochiamo:
RunProcess(3,"pippo","df0:pr/pluto","c:list")
creeremo tre processi di nome "pippo", "pluto" e "list" caricati rispettivamente dalla directory corrente, dalla directory "pr" del disco posto in df0 e dal device logico "c:". L'esecuzione del processo chiamante continuera' appena i tre processo lanciati saranno tutt'e tre terminati. Se invece siamo interessati a lanciare processi in maniera asincrona, ovvero a creare figli continuando pero' la nostra elaborazione, esiste la funzione StartProcess che lancia un sottoprocesso e restituisce subito il controllo al chiamante. Per fare questo dobbiamo farci carico di una piccola incombenza, ovvero tenere traccia del puntatore al segmento di memoria utilizzato per caricare il processo da eseguire. Tale puntatore, restituito appunto dalla StartProcess, sara' utilizzato sempre da noi per testare (eventualmente) lo stato del processo lanciato e/o per attendere la sua termninazione (deallocando cosi' il segmento utilizzato). Oltre a questo la StartProcess permette di lanciare processi a priorita' variabile e non a priorita' zero come fa di default la RunProcess. Proviamo a lanciare i tre di sopra in maniera asincrona:
BPTR seg1, seg2, seg3;
e' la dichiarazione dei tre puntatori BCPL che utilizzaremo. Segue:
seg1=StartProcess("pippo",0); seg2=StartProcess("df0:pr/pluto",0); seg3=StartProcess("c:list",0);
a questo punto l'elaborazione del processo chiamante continua e possiamo ad esempio chiederci se un determinato processo e' terminato oppure no. Esiste la funzione booleana CheckEndProcess alla quale passiamo il BPTR corrispondente al processo da testare:
if (ChechEndProcess(seg1)) printf("processo terminato");
stampera' la stringa indicata se il processo "pippo" (associato a seg1, ricordate?) e' terminato. Analogamente possiamo attendere la fine di un processo da noi creato, con la funzione WaitEndProcess passandogli nuovamente il BPTR iniziale. In questo caso si torna dalla funzione solo quando il processo al BPTR passato come parametro sia effettivamente terminato. Impaginato originale... Articolo pubblicato su www.digiTANTO.it - per ulteriori informazioni clicca qui |