AmigaOS - Lezione N.ro 5

In questa puntata cominceremo a vedere gli eseguibili Amiga. Questi sono divisi in task e processi. La differenza più importante tra i task ed i processi e` che un task, essendo ad un livello piu` basso, non puo` usare la dos.library e nemmeno chiamare funzioni che la potrebbero usare (file, librerie su disco da aprire).
Un processo invece non ha queste limitazioni.

Le funzioni e procedure che exec ci mette a disposizione sono:

1) APTR AddTask( struct Task *task, APTR initPC, APTR finalPC );
2) void RemTask( struct Task *task );
3) struct Task *FindTask( UBYTE *name );
4) BYTE SetTaskPri( struct Task *task, long priority );
5) ULONG SetSignal( unsigned long newSignals, unsigned long signalSet );
6) ULONG SetExcept( unsigned long newSignals, unsigned long signalSet );
7) ULONG Wait( unsigned long signalSet );
8) void Signal( struct Task *task, unsigned long signalSet );
9) BYTE AllocSignal( long signalNum );
10) void FreeSignal( long signalNum );
11) LONG AllocTrap( long trapNum );
12) void FreeTrap( long trapNum );

void ChildFree( APTR tid );

void ChildOrphan( APTR tid );

void ChildStatus( APTR tid );

void ChildWait( APTR tid );


Tralasciamo le varie ChildXXX che non sono documentate e che non ho la piu` pallida idea di cosa facciano (anche se ho una vaga intuizione).

Vediamo prima le piu` semplici. La funzione FindTask ha la stessa funzione delle precedenti FindXXX, ovvero ricercare un dato task nella lista di exec. Con la caratteristica in piu` che se viene chiamata con NULL come parametro, restituira` il puntatore del task chiamante.

Questo e` molto utile per utilizzare la funzione SetTaskPri sul proprio task. Come dice il nome, questa serve per impostare una nuova priorita` al task specificato, ritornando la vecchia priorita`.

Ci sono poi le funzioni 5, 7, 8, 9 e 10 che servono un po' come la Signal e la
Alarm sui sistemi unix. Infatti e` possibile richiedere al sistema di riservare un determinato segnale per poi rimanere in attesa di questo.


I segnali disponibili per task sono 32 (da 0 a 31), di cui i primi 16 per l'utente. Se viene specificato come parametro -1, la funzione ritornera` il primo segnale libero.

Una volta che il segnale e` stato allocato, ci si puo` porre in sua attesa con la funzione Wait. Spettera` poi ad un'altro task (o allo stesso sistema operativo, a seconda del tipo di segnale richiesto) risvegliare il task con una Signal.
Finito di usare il segnale, lo si potra` restituire con una FreeSignal. Infine la SetSignal serve per impostare o leggere il valore di un segnale usando una maschera.


Per esempio, per controllare e reimpostare il segnale <CTRL-C>:

if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
{
printf("Hai premuto CTRL-C!\n");
}

Bisogna pero` rendere noto, come fa infatti la documentazione di sistema, che queste funzioni sono a basso livello e che in pratica vengono usate dal sistema operativo per realizzare le porte messaggi.


Infatti, se avessimo due porte messaggi dal quale voler aspettare, dovremo utilizzare questo frammento di codice:

[...]

struct MsgPort *port1,*port2;
ULONG sigs1,sigs2,signals;
[...]
sigs1 = 1L << port1 -> mp_SigBit;
sigs2 = 1L << port2 -> mp_SigBit;
signals = Wait(sigs1 | sigs2);
if(signals & sigs1)
{
[...]
}
else if(signals & sigs2)
{
[...]
}
else
{
[...]
}

[...]


Adattandolo ovviamente alle sue esigenze. Per esempio, se volessimo ricevere messaggi da due finestre aperte (anticipo un attimo le future lezioni):

[...]

struct Window *win;
ULONG sigs1,signals;
sigs1 = (1L << win -> UserPort -> mp_SigBit);
signals = Wait(sigs1);

[...]

AllocTrap, FreeTrap e SetExcept, servono le prime per richiedere e poi poter rilasciare una TRAP della CPU per poter rimpiazzare il codice di default con del proprio.

Mentre la SetExcept serve per impostare un'eccezione. Le trappole che mette a disposizione exec sono quelle associate all'istruzione assembly TRAP. Infatti la AllocTrap puo` essere chiamata con un numero compreso tra 0 e 15 o -1 per nessuna preferenza.

Rimangono per ultime le funzioni AddTask e RemTask. Come il loro nome spiega, servono per aggiungere e rimuovere un task dalla lista di sistema.


Notare che tutte le risorse allocate, dovranno essere deallocate precedentemente o non saranno rilasciate.


La funzione RemTask, libera le liste di memoria presenti nella lista TC_MEMENTRY della struttura Task, che ora vedremo. Per creare un task, ci viene in contro la funzione CreateTask della amiga.lib.

struct Task *CreateTask(STRPTR name,LONG pri,funcEntry initPC ,ULONG stackSize);

Questa funzione, si preoccupa di allocare tutto il necessario e di chiamare internamente la AddTask per accodare il task appena creato. Inoltre inizializa il campo tc_MemEntry in modo che quello che e` stato allocato dalla CreateTask venga rimosso dalla RemTask. Vediamo una chiamata tipica di questa funzione:

extern void functionName();
char *tname = "unique name";
struct Task *task;

task = CreateTask(tname,0L,functionName,4000L);

crea un task di nome unique name, a priorita` 0 e con 4000 byte di stack facendo partire l'esecuzione dalla funzione functionName che risiede in un modulo esterno a quello chiamante che sara` stato compilato senza il controllo dello stack poiche` e` dinamico.


Vediamo dunque la struttura Task:

/* Please use Exec functions to modify task structure fields, where  available.
*/
struct Task {
struct Node tc_Node;
UBYTE tc_Flags;
UBYTE tc_State;
BYTE tc_IDNestCnt; /* intr disabled nesting*/
BYTE tc_TDNestCnt; /* task disabled nesting*/
ULONG tc_SigAlloc; /* sigs allocated */
ULONG tc_SigWait; /* sigs we are waiting for */
ULONG tc_SigRecvd; /* sigs we have received */
ULONG tc_SigExcept; /* sigs we will take excepts for */
UWORD tc_TrapAlloc; /* traps allocated */
UWORD tc_TrapAble; /* traps enabled */
APTR tc_ExceptData; /* points to except data */
APTR tc_ExceptCode; /* points to except code */
APTR tc_TrapData; /* points to trap data */
APTR tc_TrapCode; /* points to trap code */
APTR tc_SPReg; /* stack pointer */
APTR tc_SPLower; /* stack lower bound */
APTR tc_SPUpper; /* stack upper bound + 2*/
VOID (*tc_Switch)(); /* task losing CPU */
VOID (*tc_Launch)(); /* task getting CPU */
struct List tc_MemEntry; /* Allocated memory. Freed by RemTask() */
APTR tc_UserData; /* For use by the task; no restrictions! */
};

A questo punto, siccome ad inizio lezione avevamo introdotto la differenza tra task e processo, vediamo quindi le procedure e funzioni che ci mette a disposizione, questa volta la dos.library:

struct MsgPort *CreateProc( STRPTR name, long pri, BPTR segList,long stackSize);
struct Process *CreateNewProc( struct TagItem *tags );
struct Process *CreateNewProcTagList( struct TagItem *tags );
struct Process *CreateNewProcTags( unsigned long tag1type, ... );
LONG RunCommand( BPTR seg, long stack, STRPTR paramptr, long paramlen );
struct Process *FindCliProc( unsigned long num );
BPTR LoadSeg( STRPTR name );
void UnLoadSeg( BPTR seglist );
BPTR NewLoadSeg( STRPTR file, struct TagItem *tags );
BPTR NewLoadSegTagList( STRPTR file, struct TagItem *tags );
BPTR NewLoadSegTags( STRPTR file, unsigned long tag1type, ... );

Andrea Carolfi

Homepage: http://www.aspide.it/freeweb/hurricane/