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

Nessun commento: