|
![]() |
AmigaDev Quarta parte L'appuntamento di questo mese riguarda la comunicazione interprocesso, che può essere considerata uno dei punti di forza di AmigaOS, grazie alla sua semplicità ed efficienza. Il meccanismo offerto da Exec è basato sulle porte e sui messaggi, che qui discuteremo. I segnali I segnali sono indicatori di eventi; essi servono, come è evidente dal nome, a segnalare un evento ad un task. Sono il meccanismo a più basso livello su cui si fonda la comunicazione interprocesso. Ogni task ha a disposizione 32 diversi segnali, 16 dei quali sono usati dal sistema operativo. Ad ognuno dei 16 segnali "utente" può essere associato un significato dall'applicazione, tramite un meccanismo di allocazione. La funzione Wait(), che abbiamo incontrato nello scorso numero, non fa altro che attendere l'attivarsi di un segnale, che può essere causato da un altro task o dal sistema operativo. In sintesi, ogni segnale è un bit di una parola lunga. Per questo la funzione Wait() accetta in ingresso una maschera a 32 bit che indica per quali segnali bisogna attendere, e ritorna 32 bit che indicano quali segnali si sono attivati. Le porte Le "message port" possono essere considerate punti di raccolta dei messaggi; quando un task vuole comunicare con un altro, invia il suo messaggio ad una porta posseduta da questo. Quando un messaggio arriva ad una porta viene eseguita un'azione (specificata dalla porta stessa) che può essere una delle seguenti: attivazione di un segnale, esecuzione di una interruzione software o nessuna operazione. L'azione più comune associata all'arrivo di un messaggio è l'attivazione
di un segnale. Il campo mp_SigBit della struttura MsgPort ( Il campo mp_Flags specifica l'azione da compiere all'arrivo di un
messaggio; se vale PA_SIGNAL viene attivato il segnale mp_SigBit del
task mp_SigTask; se vale PA_SOFTINT viene eseguita una interruzione
software (mp_SigTask deve puntare, in questo caso, anziché ad una struttura
Task, ad una struttura Interrupt); PA_IGNORE indica invece che non accadrà
nulla all'arrivo dei messaggi.
Il campo mp_MsgList è una struttura List che raccoglie i messaggi
arrivati alla porta. mp_Node, invece, serve ad agganciare la porta messaggi
alla lista delle porte pubbliche e ad associarle un nome, in modo che
possa essere trovata da altri task.
È importante notare che alla porta può essere associato un solo segnale,
che può avere i due soli stati 1 e 0; dunque l'attivarsi del segnale
non indica quanti messaggi sono arrivati alla porta, ma solo che sono
arrivati dei messaggi.
Creare una porta messaggi
La funzione di Exec CreateMsgPort() (disponibile dalla versione 36
della libreria) può essere usata per allocare ed inizializzare una nuova
porta messaggi. Essa alloca automaticamente un segnale per il task chiamante
e imposta la porta affinché questo venga segnalato all'arrivo dei messaggi.
Se poi si intende rendere pubblica la porta, è necessario associarle
un nome (mp_Node.ln_Name) ed aggiungerla alla lista di sistema con la
funzione AddPort().
La funzione FindPort() permette di trovare l'indirizzo di una porta
pubblica dato il suo nome. Essa ritorna NULL se non esiste una porta
con il nome specificato. L'utilizzo di questa funzione richiede alcune
cautele particolari; i lettori interessati troveranno tutti i dettagli
nell'esempio sul CD.
Prima di distruggere una porta, è necessario "rispondere" a tutti
i messaggi che essa contiene (vedremo più avanti come); se essa era
stata resa pubblica con AddPort() bisogna quindi chiamare RemPort()
per rimuoverla dalla lista di sistema; infine, la funzione DeleteMsgPort()
si occupa di deallocare le porte allocate con CreateMsgPort().
I messaggi
La struttura Message ( Per inviare un messaggio, bisogna usare la funzione PutMsg(), specificando
il puntatore alla porta di destinazione e il puntatore al messaggio.
Se si richiede una risposta, bisogna impostare il campo mn_ReplyPort
prima di chiamare PutMsg().
Per mandare il proprio task "a dormire" in attesa di un messaggio,
è possibile usare la funzione WaitPort(), che ritorna il primo messaggio
accodato alla porta (senza tuttavia rimuoverlo da essa). È quindi possibile
usare la funzione GetMsg() per rimuovere il messaggio dalla porta e
ottenere gli altri messaggi accodati (se presenti); essa ritorna un
puntatore al messaggio o NULL se non ci sono più messaggi in attesa.
Il modo più comune per ottenere tutti i messaggi in coda è chiamare
GetMsg() in un ciclo while fino a che non viene ritornato NULL (vedere
l'esempio).
Anziché usare WaitPort(), è possibile usare la funzione Wait() passandogli
il bit di segnale della porta; con Wait() si ha il vantaggio di poter
attendere più segnali contemporaneamente.
Una volta ricevuto un messaggio, è necessario rispondere al task che
ce lo ha inviato con la funzione ReplyMsg(). In questo modo segnaliamo
ad esso che abbiamo finito di usare i dati contenuti nel messaggio e
quindi può riutilizzare quella struttura Message per un altro messaggio
o può deallocarla.
La comunicazione
Riassumendo, la comunicazione tra due task avviene nel modo seguente.
Almeno uno dei due task crea una porta messaggi (devono farlo entrambi
se si vuole ottenere una risposta ai messaggi) e la rende pubblica associandogli
un nome (a meno di avere un altro modo di comunicare l'indirizzo della
porta all'altro task). L'altro task cerca la porta per nome con FindPort()
e le invia un messaggio (PutMsg()). Se il primo task era in attesa,
viene risvegliato e prende il messaggio con GetMsg(), compie le eventuali
elaborazioni e risponde con ReplyMsg() rimandando i dati (eventualmente
modificati) al mittente. Quest'ultimo riceve la risposta (viene svegliato
se era in attesa, ecc.) e può quindi elaborarla.
Si noti che normalmente bisogna sempre richiedere una risposta, perché
il mittente dovrà deallocare la memoria allocata per il messaggio quando
il destinatario ha finito di usarla. Inoltre, è sempre opportuno rispondere
il prima possibile ai messaggi, poiché il mittente potrebbe essere in
attesa della risposta per poter deallocare la memoria e quindi uscire.
Per un esempio pratico e tutti i dettagli fate riferimento al CD allegato.
|
Copyright (C) 1999-2002, la redazione di AmigaLife.
Il logo e le copertine della rivista sono tratti dal sito Pluricom e sono Copyright (C) 1992-2001 Pluricom S.r.l.