domenica 28 dicembre 2008

MINIX: Il livello più basso

Il file mpx88.s fa parte dello strato più basso del kernel MINIX. In esso sono descritte ed implementate in assembly tutte le operazioni che consentono di cambiare lo stato di un processo(processing switching) e di gestire lo scambio di messaggi (message handling).

Questo file è utilizzato ogni qual volta si fà riferimento al kernel.
Es. per l’ invio e/o ricezione di messaggi e per la gestione delle interruzioni/trap.
Nei casi di eccezioni hardware, quali TRAP o interruzioni, prima di eseguire il relativo gestore viene chiamata la procedura save() per salvare lo stato della macchina nella tabella dei processi. Successivamente si passa a lavorare sullo stack del kernel e finalmente viene eseguita la reale trap o chiamato il reale gestore di interruzioni scritto in codice C. Al ritorno della chiamata di procedura relativa ai gestori, il numero di processo o task contenuto in ‘cur_proc’ viene messo in esecuzione.
I punti d’ingresso(entry points) definiti in questo file sono:
  • minix: inizializza lo stack del kernel e chiama la procedura main(scritta in C )
  • s_call: per un processo o task che vuole inviare o ricevere un messaggio
  • tty_int: routine d’interruzioni per ogni tasto pressato e rilasciato
  • lpr_int: routine d’interruzioni per ogni linea di stampata
  • disk_int: routine d’interruzioni per il disco
  • wini_int: routine d’interruzioni per Winchester
  • clock_int: routine d’interruzioni per il clock (Hz)
  • surprise : per altri interrupt(inaspettati)
  • trp: per tutte le trap(esclusa trap su divisione)
  • divide: per le trap del tipo divide overflow
  • restart: per l’inizio dell’ esecuzione di un task o un processo
Queste entry point vengono chiamate prima di eseguire la relativa procedura in C

MINIX
L’entry point per il kernel MINIX setta il DS e l’ SS del kernel all’indirizzo 4 e lo stack pointer sp del kernel viene inizializzato al valore #K_STACK_BYTES(256); infine viene chiamata la procedura main. Tutte queste operazioni vengono chiamate ad interruzioni disabilitate.
Ricordiamo che:
  • DS : Data Segment è l’area che contiene i dati del programma
  • CS : Code Segment è l’area di memoria che contiene le istruzioni(il testo) del programma
  • SS : Stack Segment è l’area di memoria relativa allo stack del programma
  • ES: Extra Segment è l’area di memoria per le informazioni extra relative al programma
|*===========================================================================* |* MINIX * |*===========================================================================* MINIX: | this is the entry point for the MINIX kernel.
jmp M.0 | skip over the next few bytes
M.0: cli | disable interrupts

mov ax,4 | build has loaded this word with ds value
mov ds,ax | ds now contains proper value
mov ss,ax | ss now contains proper value
mov _scan_code,bx | save scan code for '=' key from bootstrap
mov sp,#_k_stack | set sp to point to the top of the
add sp,#K_STACK_BYTES | kernel stack
call _main | start the main program of MINIX


System Call
Questo entry point inserisce i parametri della funzione sys_call(function, caller, src_dest, m_ptr) nella stack del kernel prima della vera chiamata in codice C.
Al ritorno della procedura per la chiamata di sistema viene mandato un task o un processo in esecuzione(viene chiamata la procedura restart)
I parametri della sys_call vengono recuperati accendendo alla posizione della tabella dei processi relativa al processo o task in esecuzione.


|*===========================================================================* |* s_call * |*===========================================================================* _s_call: | System calls are vectored here.
call save | save the machine state

mov bp,_proc_ptr | use bp to access sys call parameters
push 2(bp) | push(pointer to user message) (was bx)
push (bp) | push(src/dest) (was ax)

push _cur_proc |push caller
push 4(bp) | push(SEND/RECEIVE/BOTH) (was cx)
call _sys_call | sys_call(function, caller, src_dest, m_ptr)
jmp _restart | jump to code to restart proc/task running


Interruzioni e trap.
l’entry point per :
  • interruzione da tastiera(tty_int) :
  • interruzione stampa di una linea(lpr_int)
  • una trap(trp)
  • una trap di divisione(divide ,es. divisione per 0)
  • altre interruzioni inaspettate(surprise)

si salva lo stato della macchina , si chiama il relativo gestore e si continua l’esecuzione


Es. routine per interruzione d'input da terminale
|*===========================================================================* |* tty_int * |*===========================================================================* _tty_int: | Interrupt routine for terminal input.
call save | save the machine state
call _keyboard | process a keyboard interrupt

jmp _restart | continue execution



I punti di ingresso alla chiamata delle routine di interruzione per :
  • il floppy disk
  • winchester disk
  • clock
hanno un comportamento analogo; per chiamare la funzione interrupt(definita in codice C,proc.c) vengono copiati i parametri nello stack del kernel.

Es. Nella routine di interruzione per il floppy disk viene costruito il messaggio di interruzione da inviare; successivamente viene inpilato nello stack insieme all’argomento FLOPPY che identifica il destinatario.
Infine viene chiamata la funzione interrupt.
In questo caso gli argomenti passati alla funzione sono FLOPPY, &intmess.
Se la routine chiamata fosse stata di clock o winchester, i parametri passati alla funzione interrupt sarebbero rispettivamente [CLOCK, &intmess] o [WINI, &intmess]

|*===========================================================================* |* disk_int * |*===========================================================================* _disk_int: | Interrupt routine for the floppy disk.
call save | save the machine state
mov _int_mess+2,*DISKINT| build message for disk task
mov ax,#_int_mess | prepare to call interrupt(FLOPPY, &intmess)
push ax | push second parameter
mov ax,*FLOPPY | prepare to push
first parameter
push ax | push first parameter
call _interrupt | this is the call

jmp _restart | continue execution




idle
l’entry point idle fa attesa attiva aspettando un interruzione
idle: | executed when there is no work
sti | enable interrupts

L3: wait | just idle while waiting for interrupt
jmp L3 | loop until interrupt



Save
Questo è il punto di ingresso utilizzato per salvare lo stato del sistema, quindi le informazioni relative al processo in esecuzione, nella tabella dei processi.
Nel dettaglio, vengono recuperati dallo stack del programma attualmente in esecuzione i valori della program status word (psw), del segmento del codice(cs) e il program counter(pc) e salvati nella tabella dei processi.
Successivamente si salvano i registri relativi al processo, che in ordine di inserimento sono : sp,es,bp,di,si,dx,cx,bx,ax.
La procedura termina con un salto all’indirizzo del chiamante(indirizzo di ritorno della save)

Nota: _proc_ptr ritorna la posizione(indirizzo) nella tabella dei processi del processo in esecuzione.

|*===========================================================================* |* save * |*===========================================================================* save: | save the machine state in the proc table
.
..
pop ds_save | stack: psw/cs/pc/ret addr
pop ret_save | stack: psw/cs/pc

mov bx_save,bx | save bx for later ; we need a free register
mov bx,_proc_ptr | start save set up; make bx point to save area
add bx,*OFF | bx points to place to store cs
pop PC-OFF(bx) | store pc in proc table
pop csreg-OFF(bx) | store cs in proc table
pop PSW-OFF(bx) | store psw
mov ssreg-OFF(bx),ss | store ss

push ds_save | start saving all the registers, sp first
push es | save es between sp and bp

mov es,bx | es now references kernel memory too

push bp | save bp
push di | save di
push si | save si

push dx | save dx
push cx | save cx
push bx_save | save original bx
push ax | all registers now saved


mov ax,ret_save | ax = address to return to
jmp (ax) | return to caller; Note: sp points to saved ax

Restart
Complementare a Save, questo entry point inizializza ed esegue un processo o un task.
Se il sistema è attivo, i valori psw, cs, sp e dei registri del processo da mandare in esecuzione, contenuti nella tabella dei processi, vengono ripristinati inserendoli nello stack.

|*===========================================================================* |* restart * |*===========================================================================* _restart: | This routine sets up and runs a proc or task.
cmp _cur_proc,#IDLE | restart user; if cur_proc = IDLE, go idle
je idle | no user is runnable, jump to idle routine
cli | disable interrupts

mov sp,_proc_ptr | return to user, fetch regs from proc table
pop ax | start restoring registers

pop bx | restore bx
pop cx | restore cx
pop dx | restore dx
pop si | restore si

pop di | restore di
mov lds_low,bx | lds_low contains bx
mov bx,sp | bx points to saved bp register
mov bp,SPLIM-ROFF(bx) | splimit = p_splimit
mov splimit,bp | ditto
mov bp,dsreg-ROFF(bx) | bp = ds

mov lds_low+2,bp | lds_low+2 contains ds
pop bp | restore bp
pop es | restore es
mov sp,SP-ROFF(bx) | restore sp

mov ss,ssreg-ROFF(bx) | restore ss using the value of ds push PSW-ROFF(bx) | push psw
push csreg-ROFF(bx) | push cs

push PC-ROFF(bx) | push pc
lds bx,lds_low | restore ds and bx in one fell swoop

iret | return to user or task



le costanti utilizzate in mpx88.s e contenute in com.h sono:
K_STACK_BYTES = 256 | dimensione kernel stack
WINI = -6 |identificativo per task che si occupa del wininchester disk
FLOPPY = -5 |identificativo per il task floppy disk
CLOCK = -3 | identificativo per il task clock
IDLE = -999 | costante che identifica sistema inattivo
DISKINT = 1 | per identicare il tipo del mess. di interruzione disk
CLOCK_TICK = 2 | per identicare il tipo del mess. di interruzione clock

martedì 16 dicembre 2008

MINIX: I processi

Il file proc.c contiene tutte le principali operazioni compiute dai processi e per la gestione dei messaggi. La comunicazione tra processi,in MINIX, è effettuata attraverso lo scambio di messaggi, utilizzando delle procedure chiamate send e receive.

Le procedure che descriverò, contenute nel file proc.c cono:
  • system call : chiamata quando un processo o task vogliono eseguire una SEND e/o RECIVE
  • interrupt : utilizzato dalla routine delle interruzioni per inviare il messaggio di interrupt ad un task
  • ready: prende un processo della lista pronti e lo mette in esecuzione
  • unready: rimuove il processo dalla lista pronti
  • sched: se un processo è da tanto tempo in esecuzione, ne viene schedulato un altro
  • pick_proc: porta un processo in esecuzione
  • send: invia un messaggio tra pocessi
  • receive: per la ricezione di messaggi tra pocessi
interrupt
Quando occorre una interruzione viene chiamata la funzione interrupt, la quale prova ad inviare un messaggio di interruzione al task con identificatore passato come argomento
PUBLIC interrupt(task, m_ptr)

int task; /* number of task to be started */

message *m_ptr; /* interrupt message to send to the task */

{/* An interrupt has occurred. Schedule the task that handles it. */

il messaggio di interruzione ha come sorgente la costante che identifica l'hardware, definita nel file com.h,il destinatario ed il puntatore al buffer del messaggio

if (mini_send(HARDWARE, task, m_ptr) != OK) {

/* The message could not be sent to the task; it was not waiting. */

Se l'invio del messaggio di interruzione fallisce, (es. non era in attesa di riceverlo), si setta nella bitmap busy_map per ricordarsi cheil task destinatario è occupato e si salva il riferimento al messaggio di interruzione nel vettore message *task_mess[NR_TASKS+1] (contiene i puntatori ai messaggi dei task occupati).

Nel caso in cui la send è stata eseguita con successo, quindi l'interruzione hardware è stata inviata correttamente, si resetta a zero il bit nella busy_map per identificare che il task è di nuovo libero.
busy_map &= ~this_bit; /* turn off the bit in case it was on */

old_map = busy_map;


Se vi è qualche altro task che ha un interruzione pendente, allora la richiesta viene soddisfatta inviandogli il messaggio di interruzione ed si aggiorna la bitmap relativa ai task occupati
if (old_map != 0) {

for (i = 2; i <= NR_TASKS; i++) {

/* Check each task looking for one with a pending interrupt. */

if ( (old_map>>i) & 1) {
/* Task 'i' has a pending interrupt. */
n = mini_send(HARDWARE, -i, task_mess[i]);
if (n == OK) busy_map &= ~(1 << i);


Infine, si controlla se la lista pronti relativa ai taks non è vuota e se c'è
una processo utente in esecuzione, allora porto il task in esecuzione

/* If a task has just been readied and a user is running, run the task. */
if (rdy_head[TASK_Q] != NIL_PROC && (cur_proc >= 0 || cur_proc == IDLE))
pick_proc();


System Call
Solo le system call posso eseguire le procedure send e/o receive, per l'invio e/o ricezione dei messaggi.
Tali chiamate sono eseguite quando viene fatta una trap al kernel con una istruzione INT.
La trap viene catturata e chiamate le procedure send e/o receive .


le System Call prendono come parametri:
  • il tipo di funzione da eseguire
  • il chiamante, identifica il processo che ha chiamato la sys_call.
  • l'identificatore del processo mittente(in caso di receive) e/o destinatario(in caso di send) del messaggio
  • il puntatore al messaggio

PUBLIC sys_call(function, caller, src_dest, m_ptr)

int function; /* SEND, RECEIVE, or BOTH */

int caller; /* who is making this call */
int src_dest; /* source to receive from or dest to send to */

message *m_ptr; /* pointer to message */

Le uniche chiamate di sistema consentite ai processi utente sono dove sono presenti sia la send che la receive, altrimenti nel registro per il codice di ritorno da system call del processo chiamante verrà assegnato un codice di errore.

if (function != BOTH && caller >= LOW_USER) {

rp->p_reg[RET_REG] = E_NO_PERM; /* users only do BOTH */
return;
}

Negli altri casi si limita ha chiamare le procedure send e/o receive a secondo dei casi
if (function & SEND)

n = mini_send(caller, src_dest, m_ptr); /* func = SEND or BOTH */

if (function & RECEIVE)
n = mini_rec(caller, src_dest, m_ptr); /* func = RECEIVE or BOTH */

Send
La procedura send è utilizzata per inviare un messaggio tra due processi.
Se il destinatario è in attesa di questa comunicazione il messaggio viene copiato nell'area di memoria della parte DATI del processo destinatario ed in seguito ne viene fatta la sveglia.

Nel caso contrario, se il destinatario non è in attesa di ricevere o è in attesa di ricevere da un altro processo, il mittente(il chiamante della send) passa in attesa ed il messaggio viene messo in coda.
In minix i processi utente posso inviare solo al File System ed al processo
gestore della memoria.


I parametri sono:
  • identificatore processo mittente(il chiamante),
  • identificatore processo destinatario
  • il puntatore al messaggio da recapitare.
PUBLIC int mini_send(caller, dest, m_ptr)
int caller; /* who is trying to send a message? */
int dest; /* to whom is message being sent? */

message *m_ptr; /* pointer to message buffer */


Dagli identificatori, passati come argomento alla procedura, si accede alla tabella dei processi e si recuperano gli indirizzi del processo chiamante e del processo destinatario.

Vengono fatti controlli di validazione per verificare se il destinatario è ancora in vita e se l'intero messaggio può essere contenuto nel segmento DATI dell'aera di memoria del
processo chiamante('caller')

if (dest_ptr->p_flags & P_SLOT_FREE)
return(E_BAD_DEST); /*dead dest */

if (vhi < vlo || vhi - caller_ptr->p_map[D].mem_vir >= len)
return(E_BAD_ADDR);


Se il destinatario era bloccato in attesa di questo messaggio viene chiamata la routine
cp_mess[src, src_clicks, src_offset, dst_clicks, dst_offset] che esegue una copia
veloce di un messaggio da una parte ad un altro qualsiasi indirizzo di memoria e svegliato il destinatario,se il processo diventa runnable(dest_ptr->p_flags == 0) viene inserito nella propria lista pronti(ready).
N.B.Dopo la sveglia il destinatario si troverà il messaggio nella sua m
emoria DATI.
/* Check to see if 'dest' is blocked waiting for this message. */

if ( (dest_ptr->p_flags & RECEIVING) &&

(dest_ptr->p_getfrom == ANY || dest_ptr->p_getfrom == caller) ) {

/* Destination is indeed waiting for this message. */

cp_mess(caller, caller_ptr->p_map[D].mem_phys, m_ptr,

dest_ptr->p_map[D].mem_phys,
dest_ptr->p_messbuf);

dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */
if (dest_ptr->p_flags == 0) ready(dest_ptr);
}

cp_mess è implementata direttamente in assembly,contenuta nel file klib88.asm, durante il trasferimento copia l'identificatore del processo mittente nella prima parole del messaggio
In tal modo il processo destinatario potra ricavare l'indirizzo del processo ac
cedendo alla tabella dei processi.
le restanti 11 parole del messaggio vengono copiate contenuto dall'area DATI del mittente al 'area DATI del destinatari.
Un messaggio è composto da 12 parole.Ricordiamo che in minix un
a parola è composta da 16-bit(2byte), di conseguenza l'intero messaggio consta di 24byte
;===========================================================================
; cp_mess
;===========================================
================================
Msize = 12 ; size of a message in 16-bit words
cp_mess:
...
mov es:[di],ax ; copy sender's process number to dest message
...

mov cx,Msize-1 ; remember, first word doesn't count

rep movsw ; iterate cx times to copy the message
...

ret ; that's all folks!
Nella situazione in cui il destinatario non è in attesa di ricevere la comunicazione viene salvato il puntatore al messaggio ed attraverso la procedura unready il caller passa in stato di attesa per invio non riuscito
/* Destination is not waiting. Block and queue caller. */

if (caller == HARDWARE) return(E_OVERRUN);

caller_ptr->p_messbuf = m_ptr;

caller_ptr->p_flags |= SENDING;

unready(caller_ptr);


Infine il puntatore al processo mittente(caller) viene inserito nella lista dei processi che hanno comunicazioni pendenti


Receive
E' la procedura complementare alla Send; utilizzata quando un processo o task vuole ricevere un messaggio.
In questo caso i parametri sono:
  • identificatore processo che vuole ricevere(il chiamante),
  • identificatore processo del mittente atteso
  • il puntatore al buffer per il messaggio
PRIVATE int mini_rec(caller, src, m_ptr)
int caller; /*process trying to get message */

int src; /* which message source is wanted (or ANY) */

message *m_ptr; /* pointer to message buffer */


Se la coda dei processi che hanno provato ad inviare un messaggio al chiamante non è vuota , (bloccati perché il destinatario non era in attesa del messaggio), si cerca un mittente con il messaggio desiderato (ANY, prende il primo messaggio presente in coda).
Tale messaggio viene acquisisce attraverso cp_mess (descritta in precedenza), che fa la copia dalla memoria fisica dell'area dati del processo sender alla memoria fisica della parte dati del processo destinatario.

Dopo la copia si sblocca il mittente, attraverso la procedura ready che inserisce il descrittore di processo mittente nella opportuna lista pronti, di conseguenza viene aggiornata la coda dei processi delle comunicazione pendenti con il chiamante, eliminando il processo mittente utilizzato per la receive.
caller_ptr = proc_addr(caller);/* pointer to caller's proc structure */

/* Check to see if a message from desired source is already available. */

sender_ptr = caller_ptr->p_callerq;

while (sender_ptr != NIL_PROC) {

sender = sender_ptr - proc - NR_TASKS;

if (src == ANY || src == sender) {

/* An acceptable message has been found. */

cp_mess(sender, sender_ptr->p_map[D].mem_phys, sender_ptr->p_messbuf,
caller_ptr->p_map[D].mem_phys, m_ptr);

sender_ptr->p_flags &= ~SENDING; /* deblock sender */

if (sender_ptr->p_flags == 0) ready(sender_ptr);


Se il messaggio richiesto non è ancora disponibile, si salva lo stato della comicazione e si blocca il chiamante.

caller_ptr->p_getfrom = src;

caller_ptr->p_messbuf = m_ptr;

caller_ptr->p_flags |= RECEIVING;

unready(caller_ptr);


Infine si verifica se ci sono segnali pendenti da parte del kernel al processo gestore memoria, quindi viene chiamata la procedura assembly inform(MM_PROC_NR) per informare il gestore

if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform(MM_PROC_NR);


pick_proc
Questa funzione è utilizzata principalmente per settare 'cur_proc' e 'proc_ptr',rispettivamente l'identificatore al processo corrente e il puntatore alla tabella dei processi
Se il sistema è inattivo 'cur_proc' e 'proc_ptr' vengono settati ad un valore speciale IDLE, per identificare lo stato di inattività.

Per impostare il processo corrente controlla se è presente un processo nelle liste pronti, in ordine di priorità, verifica : quella dei task, dei processi server e di quelli utente.
register int q; /* which queue to use */

if (rdy_head[TASK_Q] != NIL_PROC) q = TASK_Q;
else if (rdy_head[SERVER_Q] != NIL_PROC) q = SERVER_Q;

else q = USER_Q;

if (rdy_head[q] != NIL_PROC) {

/* Someone is runnable. */

cur_proc = rdy_head[q] - proc - NR_TASKS;

proc_ptr = rdy_head[q];


Se è in esecuzione il task per il controllo del ciclo di clock, 'cur_proc' = CLOCKTASK.

Viene settato anche il puntatore al processo bill_ptr che conta i cicli di clock della CPU.

ready
La ready inserisce il puntatore al processo(al descrittore di processo), passato come argomento, alla propria lista pronti ricavandola dall'identificatore del processo.
PUBLIC ready(rp)

lock(); /* disable interrupts */
r = (rp - proc) - NR_TASKS; /* task or proc number */

q = (r < 0 ? TASK_Q : r < LOW_USER ? SERVER_Q : USER_Q);

...

// aggiunge rp in rdy_head[q]

..

restore(); /* restore interrupts to previous state */

Tutte le operazioni vengono effettuate ad interruzioni disabilitate, chiamanto la procedura lock definita a livello assembler e alla fine viene chiamata la
procura assembly restore ripristinare le interruzioni allo stato precedente.
In tal modo siamo sicuri queste operazioni vengono eseguite in maniera indivisibile, senza essere interrotte da interruzioni.
Il codice assembly delle per le operazioni lock e restore sono presenti nel file klib88.asm

;===========================================================================

; lock
;===========================================================================

; Disable CPU interrupts.
lock:

pushf ; save flags on stack
cli ; disable interrupts
pop lockvar ; save flags for possible restoration later
ret ; return to caller

;===========================================================================
; restore
;===========================================================================
; Restore enable/disable bit to the value it had before last lock.
restore:
push lockvar ; push flags as they were before previous lock
popf ; restore flags
ret ; return to caller

unready
Rimuove il processo ,il cui puntatore è passato come parametro, dalla sua lista pronti.
Il codice è simile alla ready, ma in questo caso viene fatta la rimozione anzichè l'inserzione dalla lista rdy_head[q].
Un processo può essere in stato unready anche se un segnale di kill l'ha terminato
.
Anche in questa procedura su utilizza la lock e restore per le stesse motivazioni della procedura descritta in precedenza


sched
Descrive la politica di scheduling per i processi utente. Se un processo è da troppo tempo in esecuzione ed un altro è presente in lista pronti. Il processo corrente viene inserito in fondo alla lista pronti utente.
La procedura viene eseguita ad interruzioni disabilitae
PUBLIC sched()
{
lock(); /* disable interrupts */
if (rdy_head[USER_Q] == NIL_PROC) {
restore(); /* restore interrupts to previous state */

return;
}
/* One or more user processes queued. */
rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q];

rdy_tail[USER_Q] = rdy_head[USER_Q];

rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready;

rdy_tail[USER_Q]->p_nextready = NIL_PROC;

pick_proc();

restore(); /* restore interrupts to previous state */


Per capire meglio uso che i processi fanno delle primitive di comunicazion
e mi rifaccio al classio esempio di un processo 'APPL' che vuole leggere dal disco; 'DRIVER' è il processo gestore del disco con il quale altri processi attraverso opportune send e/o receive
Nel compilato del processo APPL ci sarà il riferimento alle procedure di comunicazione che il kernel minix mette a disposizione.


Es. In pseudocodice,nel compilato dei processi, avremo:

APPL:
...
send(driver,“LETTURA N parole”)

recieve(driver,risultato)
...

DRIVER:
...

recieve(appl,“LETTURA N parole ”)
send(appl,letti())

...

Il prossimo post su minix descriverò file mpx88.asm che contiene lo strato più basso del kernel Minix, sono quasi 300 righe di codice assembly utilizzare per implementare lo switching dei processi, gestione dei messaggi, la procedura save() che contiene il codice per salvare lo stato della macchina quando occorre una Trap o un' Int,etc

Daniele Licari

domenica 14 dicembre 2008

GYM Concept Car

GYM Concept Car è un progetto di Da Feng, uno studente di design della Coventry University, che quest'anno ha proposto un auto elettrica, dal designin parte ispirato dagli aerei da combattimento WW2, con all'interno attrezzature da palestra, quali: step machine, vogatore,brench press, spinte per braccia e spalle L'auto ha un unico sedile e telaio in lega di magnesio e carbonio.

Progetto curioso, quanto inutile

sabato 13 dicembre 2008

C++0x : il nuovo standard

Questo periodo è di grande attesa per i devoti al C++. Dopo circa dieci anni dalla prima ISO standard, C++ si appresta ad essere rivoluzionato.
L'intenzione, con il nuovo standard, è di riportare interesse sul linguaggio e migliorare le performance.
C++0x, il nuovo standard atteso per il 2009, avrà un nuovo insieme di features, le caratteristiche principali sono:
  • concetti: permette ai programmatori di specificare vincoli sui parametri dei template in modo da facilitare l'uso dei generici. Esempio di concetto:

concept LessThanComparable {
bool operator<(const T& x, const T& y); } template
requires LessThanComparable
const T& min(const T& x, const T& y) {
return x < y ? x : y; }

  • hash table: quattro nuovi containers non ordinati (std::unordered_map, std::unordered_multimap, std::unordered_set, std::unordered_mutiset )
  • puntatori intelligenti(smart pointer): automatizza l'uso di risorse con puntatori shared_ptr condivisi, superando le limitazioni del std::auto_ptr del C++98.
  • Deduzione dinamica del tipo: le nuove keyword auto e decltype permetteranno di ricavare il tipo di un espressione a run-time senza che viene dichiarata esplicitamente.
auto func(int x)->double {return pow(x);}
  • Lambda expressions e chiusure: lambda expressions è una funzione senza nome, definita dov'è chiamata.esempio:
//a lambda expression is used as an argument
myfunc([](int x, int y) -> int {return x+y;} )
  • Caratteristiche che semplificano i compiti ricorrenti.es. Un puntatore nullo letterale chiamato nullptr, enumerations fortemente tipato, delegating and inheriting constructors
  • alignof e alignas: due nuovi operatori,per avere il controllare su compilazione ed esecuzione nell'ambiente di programmazione, si può decidere l'allineamento in memoria dei dati
  • constexpr: nuova parola chiave che permette al programmatore di dichiarare che un determinato oggetto o espressione viene valutata e inizializzato a tempo di compilazione. Al contrario di const che si limita a constatare che l'oggetto in questione non cambi il suo stato dopo l'inizializzazione.
const int y=func(); //dynamic initialization
int buff[y]; //error, y isn't a compile-time constant
constexpr int f(int x){
return x * x; } //mandatory compile-time evaluation
int arr[f(3)];//OK, array's size is 9

  • nuovi algoritmi
  • Compatibile con i standard precedenti
[rif.]
The State of the Language: An Interview with Bjarne Stroustrup, devx.com

Daniele Licari

giovedì 4 dicembre 2008

MINIX : Task per gestire la memoria

memory.c contiene il task gestore dei drivers per i quattro file speciali :
  • /dev/null - null device (data sink)
  • /dev/mem - absolute memory
  • /dev/kmem - kernel virtual memory
  • /dev/ram - RAM disk

rispettivamente dispositivo nullo, memoria assoluta, memoria virtuale del kernel e RAM.
La funzione di mem_task e' quella di ricevere i messaggi, in base al codice operativo eseguire operazioni di lettura,scrittura,setup e ritornare l'esito al mittente. Gli unici tipi di messaggio consentiti sono: lettura,scrittura e setup.
Tutti i messaggi sono nella forma :

  • Tipo di operazione
  • Identificatore del dispositivo(per identificare i 4 driver)
  • Il processo che vuole utilizzare l'I/O
  • Lunghezza del messaggio in byte
  • Posizione da cui cominciare a leggere/scrivere
  • Puntatore al buffer del messaggio

L'Esito,nel caso di opcode di lettura/scrittura, è dato dal numero di byte trasferiti quando e' positivo o dal codice di errore quando negativo.
Nel caso in cui stiamo rispondendo ad un messaggio di setup, e' dato dal flag OK o dal codice errore di I/O .
Il messaggio di risposta e della forma:


Ogni dispositivo ha associato due variabili : ram_origin,per l'indirizzo di partenza della memoria. ram_limit,per indirizzo limite

#define NR_RAMS 4 /* number of RAM-type devices */
PRIVATE message mess; /* message buffer */
PRIVATE phys_bytes ram_origin[NR_RAMS];
/* origin of each RAM disk */
PRIVATE phys_bytes ram_limit[NR_RAMS]; /* limit of RAM disk per minor dev. */


In fase di inizializzazione viene settata la dimensione della memoria fisica e di quella virtuale.
/* Initialize this task. */
ram_origin[KMEM_DEV] = (phys_bytes) get_base() << CLICK_SHIFT;
ram_limit[KMEM_DEV] = (sizes[0] + sizes[1]) << CLICK_SHIFT;
ram_limit[MEM_DEV] = MEM_BYTES;

le operazioni eseguite dal task stanno all'interno di un ciclo infinito
while (TRUE) {
/* First wait for a request to read or write. */
receive(ANY, &mess);
if (mess.m_source < 0)
panic("mem task got message from ", mess.m_source);

caller = mess.m_source;
proc_nr = mess.PROC_NR;
/* Now carry out the work. It depends on the opcode. */
switch(mess.m_type) {
case DISK_READ: r = do_mem(&mess); break;
case DISK_WRITE: r = do_mem(&mess); break;
case DISK_IOCTL: r = do_setup(&mess); break;
default: r = EINVAL; break;
}
/* Finally, prepare and send the reply message. */
mess.m_type = TASK_REPLY;
mess.REP_PROC_NR = proc_nr;
mess.REP_STATUS = r;

send(caller, &mess);
}


Passiamo ora a descrivere la procedura do_mem chiamata in causa da richieste di lettura o scrittura.
Se il mittente ha fatto riferimento al dispositivo nullo /dev/null, con codice operativo di lettura ritorna un errore; in caso di scrittura si ha esito positivo.

Nel caso in cui il dispositivo con il quale il mittente voglia comunicare sia la memoria fisica,virtuale o ram(/dev/mem,/dev/kmem,/dev/ram) e' necessario verificare se l'indirizzo nel quale il mittente vuole accedere si trovi compreso tra l'indirizzo di origine e di fine del dispositivo, ram_origin[device]< POSITION <ram_limit[device].

In caso di esito negativo la procedura termina con errore, diversamente si ricava l'indirizzo fisico nella sezione dati del processo mittente tramite la funzione umap(trasforma indirizzi logici in fisici) contenuta nelfile system.c che discuteremo nei prossimi post.
user_phys = umap(rp, D, (vir_bytes) m_ptr->ADDRESS, (vir_bytes) count);

Successivamente, se il codice operativo è di lettura, il trasferimento avviene da memoria fisica all'area dati del processo mittente. In caso di scrittura da area dati del processo a memoria fisica

if (m_ptr->m_type == DISK_READ)
phys_copy(mem_phys, user_phys, (long) count);
else
phys_copy(user_phys, mem_phys, (long) count);
return(count);


Nel caso in cui il tipo del messaggio è IOCTL(Cotrollo), viene chiamata la funzione do_setup, utilizzata per settare origine e limite del dispositivo con identificatore contenuto nel messaggio

lunedì 1 dicembre 2008

Realizzare un grafico di funzione in Phyton.

Da poco ho iniziato la mia avventura con Phyton ed è sorprendente quanto sia flessibile,
il binding dinamico ti semplifica la vita in fase di stesura. Il problema stà nel fatto di essere troppo pignolo a run-time e a causa dell'identazione che dà la semantica al programma.
Ogni tanto spuntano fuori "indetationerror" senza una logica spiegazione.
Comunque quì allego la mia prima applicazione(applicazioncina);il codice è tutto commentato.
Risultato per la funzione y=x^2/20




Codice.
ho diviso l'applicazione in due parti.
Il main, dove vi è definata la funzione.

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import plot
# salvo la lista dei punti nel file result.txt
# apro il file in scrittura
f = open("result.txt","w")
# vettore di punti da visualizzare
plotList = [];
# voglio visualizzare la funz y=x^2/20 nel range x=[-60,60]
for x in range(-60,60):
y=(x**2)/20
f.write(str([x,y])+"\n")
plotList.append([x,y])

f.close()
print " Fine! Il risultato e' stato salvato nel file result.txt ";

#Visualizzo il plot, passo la lista di punti come arcomento
plot.view(plotList)



l'altra parte, plot.y, contiene il codice del plot.

from Tkinter import *

def view(pointList):
root = Tk()
root.title('MyPlot')
#origine delle assi
originX = 100
originY = 250

try:
#creo un canvas
canvas = Canvas(root, width=500, height=300, bg = 'white')
canvas.pack()
# aggiungo pulsante di quit
Button(root, text='Quit', command=root.quit).pack()

# creo assi
canvas.create_line(originX ,originY ,400,250, width=2)
canvas.create_line(originX ,originY ,100,50, width=2)

for i in range(11):
x = originX + (i * 30)
canvas.create_line(x,originY ,x,originY -5 , width=2)
canvas.create_text(x,originY +4, text='%d'% (10*i), anchor=N)

#salvo il punto iniziale e lo trasformo in cordinate cartesiane traslate all'origine
initp = pointList[0];
oldp = [(originX + initp[0]),(originY - initp[1])]


for p in pointList :
xx = originX + p[0]
yy = originY - p[1]
#creo le linee per unire i punti di colore blu
canvas.create_line(oldp[0],oldp[1],xx , yy, fill="blue")
oldp = [xx,yy]

except:
print 'An error has occured!'

root.mainloop()


sabato 29 novembre 2008

Kernel MINIX : I Parte

HTML clipboard
Che cos'è un sistema operativo?cosa fa?
Per capire come sia fatto analizziamo il codice sorgente del cuore(kernel) di un sistema operativo minimale Minix.
Per semplicità di trattazione si prende in considerazione una delle prime versioni del kernel creata da Andrew S. Tanenbaum ,intel 1.1.
Le prime versioni erano fortemente legate all'architettura sottostante. Successivamente si sono resi conto che potevano utilizzare istruzioni condizionali (#if,#ifndef,#endif).
Minix implementa le strutture dati e i processi atti ad offrire servizi minimali del sistema operativo quali gestione dei thread,spazi di indirizzamento o comunicazione interprocesso.E' arrivato alla versione 3.1.3.

Personalmente analizzerò ogni singolo file,data l'ampiezza dell'argomento,anche se cercherò di essere più sintetico possibile, dividendo la descrizione in più' post.
In questa prima "puntata" definisco tutte le principali strutture dati che servono per descrivere il sistema.

type.h
Il file type.h contiene la definizione dei tipi utilizzati dal kernel e sono presenti alcune macro. In dettaglio abbiamo:
- le variabili utilizzate dal file system(numero blocchi,inode,zona,link,posizione del file,tipe,id dell'utente e del gruppo)
unshort block_nr; /* block number,assenza,max */
unshort inode_nr; /* inode number,assenza,max */
typedef unshort zone_nr; /* zone number,assenza 0,max 0177777(base ottagonale) */
typedef long zone_type; /* zone size */
typedef unshort dev_nr; /* major | minor device number ,no dev*/
typedef char links; /* number of links to an inode,max */
typedef long real_time; /* real time in seconds since Jan 1, 1980 */
typedef long file_pos; /* position in, or length of, a file */
typedef short int uid; /* user id */
typedef char gid; /* group id */

- locazioni gestite dal gestore della memoria, quali: informaizoni sull' indirizzo vituale e fisico.Un Click è 256bytes
typedef unsigned vir_bytes; /* virtual addresses and lengths in bytes */
typedef unsigned vir_clicks; /* virtual addresses and lengths in clicks */
typedef long phys_bytes; /* physical addresses and lengths in bytes */
typedef unsigned phys_clicks; /* physical addresses and lengths in clicks */
typedef int signed_clicks; /* same length as phys_clicks, but signed */

- per le comunicazioni interprocesso sono definiti 6 tipi di messaggio. La struttura del messaggio


- la struttura dati per mappare indirizzi virtuali ad indirizzi fisici e quella per la copia tra locazioni di memoria
struct mem_map {
vir_clicks mem_vir; /* virtual address */
phys_clicks mem_phys; /* physical address */
vir_clicks mem_len; /* length */
};

copy_info { /* used by sys_copy(src, dst, bytes) */ };

signal.h
signal.h contiene 16 costanti per la definizione dei segnali(
kill,bus error,float point exception,alarm clock,bad argument to system call,trace trap,Termination sw, stack fault.)
#define NR_SIGS 16 /* number of signals used */

Const.h
Definisce le costanti utilizzate dal kernel i, in particolare notiamo:
1. la memoria per i processi è divisa in 3 parti
  1. Text, dove vengono memorizzate tutte le istruzioni del processo
  2. Data, quì vengono memorizzati i dati statici e le costanti
  3. Stack, pila di chiamate a subroutine
#define PRIVATE static /* PRIVATE x limits the scope of x */
#define PUBLIC /* PUBLIC is the opposite of PRIVATE */
#define HZ 60 /* clock freq (software settable on IBM-PC) */
#define BLOCK_SIZE 1024 /* # bytes in a disk block */
#define SUPER_USER (uid) 0 /* uid of superuser */
#define NR_TASKS 8 /* number of tasks in the transfer vector */
#define NR_PROCS 16 /* number of slots in proc table */
#define T 0 /* proc[i].mem_map[T] is for text */
#define D 1 /* proc[i].mem_map[D] is for data */
#define S 2 /* proc[i].mem_map[S] is for stack */

2. Il processo numero zero e' quello resposabile alla gestione della memoria.
Nella tabella dei proc, in seconda posizione(posizione 1 del vettore) troviamo il processo file system e in terza posizione quello per la gestione utenti.
Minix è un sistema multi utente.
/* Process numbers of some important processes */
#define MM_PROC_NR 0 /* process number of memory manager */
#define FS_PROC_NR 1 /* process number of file system */
#define INIT_PROC_NR 2 /* init -- the process that goes multiuser */

3. sono presenti informazioni utili al file system, in particolare per utilizzare gli inode. Sono presenti le costanti per distinguere se una operazione è di lettura o scrittura, l'ampiezza della parola, la lunghezza massima del path, i tipi di file e per i diritti su di essi.
#define LOW_USER 2 /* first user not part of operating system */
#define TO_USER 0 /* flag telling to copy from fs to user */
#define FROM_USER 1 /* flag telling to copy from user to fs */
#define READING 0 /* copy data to user */
#define WRITING 1 /* copy data from user */
#define WORD_SIZE 2 /* number of bytes per word */
#define MAX_PATH 128 /* max length of path names */
#define MAX_ISTACK_BYTES 1024 /* maximum initial stack size for EXEC */
#define ROOT_DEV (dev_nr) 256 /* major-minor device number of root dev */
#define BOOT_DEV (dev_nr) 512
/* for inode */
#define I_TYPE 0170000 /* this field gives inode type */
#define I_REGULAR 0100000 /* regular file, not dir or special */
#define I_BLOCK_SPECIAL 0060000 /* block special file */
#define I_DIRECTORY 0040000 /* file is a directory */
#define R_BIT 0000004 /* Rwx protection bit */
#define W_BIT 0000002 /* rWx protection bit */
#define X_BIT 0000001 /* rwX protection bit */
#define I_NOT_ALLOC 0000000 /* this inode is free */

com.h
In questo file sono definite le costanti e gli identificatori di funzioni utilizzati per la comunicazione tra processi.
/* Task numbers, function codes and reply codes. */
#define SEND 1 /* function code for sending messages */
#define RECEIVE 2 /* function code for receiving messages */
#define BOTH 3 /* function code for SEND + RECEIVE */
#define ANY (NR_PROCS+100) /* receive(ANY, buf) accepts from any source */
#define HARDWARE -1 /* used as source on interrupt generated msgs */
#define TTY -7 /* terminal I/O class */
#define PRINTER -8 /* printer I/O class */
#define FLOPPY -5 /* floppy disk class */
#define WINCHESTER -6 /* winchester (hard) disk class */
#define MEM -4 /* /dev/ram, /dev/(k)mem and /dev/null class */
#define CLOCK -3 /* clock class */
#define SYSTASK -2 /* internal functions */(forl,exit,copy,exec,abort,time)

error.h
Sono presenti tutti i codici di errori.

call.h
Contiene gli identificatori di tutte le system call consentite, 5 identificatori di funzioni che non sono chiamate di sistema ma vengono processate come tali.
#define NCALLS 69 /* number of system calls allowed */
#define EXIT 1
#define FORK 2
#define READ 3
#define WRITE 4
#define OPEN 5
#define CLOSE 6
#define CHROOT 61
/* The following are not system calls, but are processed like them. */
#define KSIG 64 /* kernel detected a signal */

sgtty.h
Contiene le strutture dati per le operazioni di I/0 su file(stream,tty, nastro, disco di file, ecc)
sgttyb = { char sg_ispeed; /* input speed (not used) */
char sg_ospeed; /* output speed (not used) */
char sg_erase; /* erase character */
char sg_kill; /* kill character */}
struct tchars {
char t_intrc; /* SIGINT char */
char t_quitc; /* SIGQUIT char */
char t_startc; /* start output (initially CTRL-Q) */
char t_stopc; /* stop output (initially CTRL-S) */
char t_eofc; /* EOF (initially CTRL-D) */
char t_brkc; /* input delimiter (like nl) */
};

stat.h
per le statistiche sugli inode. es. tipo del file,se è una directory, dimensione, data creazione.etc.

stdio.h
Descrive le funzioni, attraverso macro, standard I/O e definisci la struttura dati per il file.
Gli stream sono file con associato il buffer ed il puntatore che definisce il tipo.
Le funzioni di i/o più comuni prelevano o inseriscono caratteri dalla stream.
#define BUFSIZ 1024
#define NFILES 20
#define NULL 0
#define EOF (-1)
#define CMASK 0377
#define READMODE 1
#define WRITEMODE 2
#define UNBUFF 4
#define _EOF 8
#define _ERR 16

#ifndef FILE
extern struct _io_buf {
int _fd;
int _count;
int _flags;
char *_buf;
char *_ptr;
} *_io_table[NFILES];
#endif /* FILE */

#define getchar() getc(stdin)
#define putchar(c) putc(c,stdout)
#define puts(s) fputs(s,stdout)
#define fgetc(f) getc(f)
#define noperprintf(p) ((p)->_flags &= ~PERPRINTF)
#define perprintf(p) ((p)->_flags |= PERPRINTF)//If you want a stream to be flushed after each printf use:


regexp.h
Vi sono sotto routine per expressioni regolari e la strtuttra dati regexp migliora il patter matching

ctype.h
Sono definite delle macro utilizzabili per ricavare il tipo del carattere
se è alfanumerico,maiuscolo,minuscolo,compreso tra [0-9],spazio,etc.
#define isalpha(c) ((_ctype_+1)[c]&(_U|_L))
#define isupper(c) ((_ctype_+1)[c]&_U)
#define islower(c) ((_ctype_+1)[c]&_L)
#define isdigit(c) ((_ctype_+1)[c]&_N)

lib.h
Funge da unificatore dei vari header e definisce il vettore delle eccezioni
extern int begsig(); /* interrupts all vector here */

blocksize.h
Contiene solo la costante per la dimensione del blocco dati utilizzato dal fylesustem
#define BLOCK_SIZE 1024 /* file system data block size */

pwd.h
Viene definita la struct per la password
char *pw_name /* user's login name */
uid_t pw_uid /* numerical user ID */
gid_t pw_gid /* numerical group ID */
char *pw_dir /* initial working directory */
char *pw_shell /* program to use as shell */

grp.h
password per il gruppo
char gr_name /* the name of the group */
char *passwd;
gid_t gr_gid /* numerical group ID */

setjmp.h
Specifica il buffer che si occupa di salvare lo stato corrente delle CPU
#define _JBLEN 3
typedef int jmp_buf[_JBLEN];

package KERNEL
type.h
Contiene la struttura dati pc_psw, dipendente dalla macchina, che è utilizzata per salvare sullo stack, il program counter e i registri in caso di trap o interruzione per poi ripristinarli.
program status word è il registro contiene i bit che specificano il livello di privileggio con cui il processore sta eseguendo e il bit che abilità il processore a ricevere l'interruzioni
struct pc_psw {
int (*pc)(); /* storage for program counter */
phys_clicks cs; /* code segment register */
unsigned psw; /* program status word */
};

glo.h
Variabili globali utilizzati dal kernel per per il clock e il timer, processi,segnali, messaggi, il tipo della CPU, lo stack del kernel e dei task. Ogni task ha uno stack di 256 bytes ed il kernel a un solo stack di 256bytes
EXTERN real_time realtime; /* real time clock */
EXTERN int cur_proc; /* current process */
EXTERN int prev_proc; /* previous process */
EXTERN int olivetti; /* TRUE for Olivetti-style keyboard */
EXTERN int pc_at; /* PC-AT type diskette drives (360K/1.2M) */
EXTERN struct t_stack {
int stk[TASK_STACK_BYTES/sizeof(int)];
} t_stack[NR_TASKS - 1]; /* task stacks; task = -1 never really runs */

EXTERN char k_stack[K_STACK_BYTES]; /* The kernel stack. */

const.h
Costanti usati dal kernel.
  • Ogni processo usa 11 registri generali. In ordine sono : ax, bx, cx, dx, si, di, bp, es, ds, cs, ss
    • AX (Accumulatore): Utilizzato nelle istruzioni aritmetiche e di I/O
    • BX (Base Register): Può essere utilizzato come registro base per il calcolo di indirizzi
    • CX (Count Register): Utilizzato tipicamente come contatore
    • DX (Data Register): Utilizzato in operazioni di I/O, moltiplicazione, divisione con numeri a 32 bit (in coppia con AX)
    • SP (Stack Pointer): Puntatore alla cima dello stack
    • BP (Base Pointer): Utilizzato come puntatore all’interno dello stack ma può
    • essere impiegato amche come generico registro indice
    • SI (Source Index): Registro sorgente o come generico registro indice.
    • DI (Destination Index): Registro destinazione
    • CS Segmento codice.
    • DS Segmento dati.
    • ES Segmento extra.
    • SS Segmento stack.
  • il valore iniziale dello stack pointer è 0x0010 dato che 3 parole sono inserite dal kernel
#define NR_REGS 11 /* number of general regs in each proc slot */
#define INIT_SP (int*)0x0010 /* initial sp: 3 words pushed by kernel */ Stackpointer
  • i seguenti identificatori di registri sono mappati nel codice assembly. Difatti un warning avvisa di non cambiarli senza cambiare il valore corrispondente nel codice assembly.

#define ES_REG 7 /* proc[i].p_reg[ESREG] is saved es */
#define DS_REG 8 /* proc[i].p_reg[DSREG] is saved ds */
#define CS_REG 9 /* proc[i].p_reg[CSREG] is saved cs */
#define SS_REG 10 /* proc[i].p_reg[SSREG] is saved ss */

  • - E' definito il vettore delle interruzioni per gli interrupt generati da orologio,tastiera,floppy,stampante,system call,I/0 e divisione.Sono presenti maschere interruzioni, per abilitare e disabilitare alcuni bit

#define INT2_MASK 0xA1 /* setting bits in this port disables ints */
#define ENABLE 0x20 /* code used to re-enable after an interrupt */
#define IDLE -999 /* 'cur_proc' = IDLE means nobody is running */

  • Tre differenti code per lo scheduling,una per i task, una per i processi server(offre servizi di sistema a livello utente) e l'altra per i processi utente.

#define NQ 3 /* # of scheduling queues */
#define TASK_Q 0 /* ready tasks are scheduled via queue 0 */
#define SERVER_Q 1 /* ready servers are scheduled via queue 1 */
#define USER_Q 2 /* ready users are scheduled via queue 2 */

la funzione print del kernel
#define printf printk /* the kernel really uses printk, not printf */

proc.h
In questo file è dichiarato la tabella dei processi(array di processi), con i puntatori necessari a gestirla(processo corrente, processo in testa e coda),ed i puntatori alle liste pronti relative alle tre code di scheduling. Viene fatto riferimento a codice assembly. Si noti che anche i task fanno parte della tabella.

La struttura processo contiene, nel nosto caso,
- 11 registri, con relativo mapping in memoria,
- il puntatore stack pointer,
- la struttura dati per salvare il pc e i registri in caso di interruzione,
- un flag per lo stato del processo,
- il valore più piccolo consentito dello stack,
- l'id del processo(pid),
- l'ora
- puntatore alla lista processi destinatari,
- puntatore al buffer del messaggio,
- puntatore il processo che vuole comunicare con me e puntatore al prossimo processo in stato di pronto

EXTERN struct proc {

int p_reg[NR_REGS]; /* process' registers */
int *p_sp; /* stack pointer */
struct pc_psw p_pcpsw; /* pc and psw as pushed by interrupt */
int p_flags;/* P_SLOT_FREE, SENDING, RECEIVING,etc,if =0 runnable*/
struct mem_map p_map[NR_SEGS];/* memory map */
int *p_splimit; /* lowest legal stack value */
int p_pid; /* process id passed in from MM */
...
struct proc *p_callerq;/* head of list of procs wishing to send */
message *p_messbuf; /* pointer to message buffer */
int p_getfrom; /* from whom does process want to receive? */
struct proc *p_nextready; /* pointer to next ready process */
} proc[NR_TASKS+NR_PROCS];

dmp.c
Contiene informazioni utili per il debugging. Stampa tutte le informazione sul processo in esecuzione(pid,stato,etc..)
table.c
Contiene tutti i dati definiti nei vari Header, fa il merge di tutte le strutture dati

Daniele Licari