Panda+ รจ un kernel UNIX-like sviluppato in C per il corso di Sistemi Operativi, eseguito sull'emulatore di architettura MIPS uMPS3. Segue il modello THE di Dijkstra con 6 livelli di astrazione, di cui il progetto implementa il Livello 2 (gestione code e strutture dati) e il Livello 3 (il kernel del S.O.).
Il kernel include scheduling preemptive round-robin, gestione delle eccezioni e degli interrupt, 10 syscall, semafori binari (Passeren/Verhogen), I/O su device, namespace dei processi e un meccanismo Pass Up or Die per l'escalation delle eccezioni.
Panda+ segue il modello THE di Dijkstra con 6 livelli di astrazione. Il progetto implementa i Livelli 2 e 3: le strutture dati su cui il kernel si basa, e il kernel stesso. I livelli inferiori (0-1) sono forniti dall'emulatore hardware uMPS3, mentre i livelli superiori (4-6) estenderebbero il S.O. con strutture di supporto, file system e shell interattiva.
Il Livello 2 fornisce le strutture dati fondamentali usate dal kernel: i Process Control Block (PCB) organizzati in free list, code di processi e alberi di processi con relazioni padre-figlio-fratello. I descrittori dei semafori (SEMD) sono gestiti tramite hash table per lookup O(1). I namespace forniscono isolamento tra processi raggruppando processi correlati.
typedef struct pcb_t {
struct list_head p_list; /* process queue */
struct pcb_t *p_parent; /* ptr to parent */
struct list_head p_child; /* children list */
struct list_head p_sib; /* sibling list */
state_t p_s; /* processor state */
cpu_t p_time; /* cpu time used */
int *p_semAdd; /* blocked on sem */
support_t *p_supportStruct;
nsd_t *namespaces[NS_TYPE_MAX];
pid_t p_pid;
} pcb_t;Il kernel parte da main(): configura il Pass Up Vector per la gestione delle eccezioni e del TLB-Refill, inizializza le strutture dati della Fase 1 (PCB, semafori, namespace), imposta l'Interval Timer a 100ms, alloca il primo processo in kernel mode con interrupt abilitati, e infine invoca lo scheduler.
void main() {
passupvector->tlb_refill_handler = uTLB_RefillHandler;
passupvector->exception_handler = exceptionHandler;
initPcbs(); initASH(); initNamespaces();
mkEmptyProcQ(&readyQueue);
LDIT(PSECOND); /* interval timer = 100ms */
pcb_PTR init = allocPcb();
init->p_s.status = ALLOFF | IMON | IEPON | TEBITON;
init->p_s.pc_epc = (unsigned int)test;
RAMTOP(init->p_s.reg_sp);
insertProcQ(&readyQueue, init);
scheduler();
}Lo scheduler implementa una politica round-robin preemptive con time slice. Estrae il prossimo processo dalla ready queue, arma il Processor Local Timer e carica lo stato del processo nella CPU. Se non esistono processi, il sistema si ferma. Se i processi sono tutti bloccati senza nessuno su semafori di device, e' un deadlock โ PANIC. Altrimenti, abilita gli interrupt e la CPU attende che un device sblocchi un processo.
void scheduler() {
currentProcess = removeProcQ(&readyQueue);
if (currentProcess != NULL) {
setTIMER(TIMESLICE * TIMESCALE);
LDST(¤tProcess->p_s);
}
else if (processCount == 0) HALT();
else if (softBlockedCount == 0) PANIC();
else {
setSTATUS(getSTATUS() | IECON | IMON);
WAIT();
}
}Quando si verifica un'eccezione, uMPS3 salva lo stato del processore e salta all'exception handler. Il gestore legge il registro Cause per indirizzare alla routine appropriata: gestore degli interrupt, eccezione TLB (pass-up or die), gestore delle syscall, o eccezione generica. Le syscall sono permesse solo da kernel mode โ una syscall da user mode genera un'eccezione Reserved Instruction.
void exceptionHandler() {
switch (CAUSE_GET_EXCCODE(getCAUSE())) {
case EXC_INT: /* Interrupt */
interruptHandler(); break;
case EXC_MOD: case EXC_TLBL: case EXC_TLBS:
passUpOrDieHandler(PGFAULTEXCEPT); break;
case EXC_SYS: /* Syscall */
systemcallHandler(); break;
default:
passUpOrDieHandler(GENERALEXCEPT);
}
}Il kernel espone 10 syscall: creazione/terminazione processi, Passeren (P) e Verhogen (V) su semafori binari, I/O su device, conteggio tempo CPU, attesa clock, accesso alla struttura di supporto, recupero PID ed enumerazione dei figli. I semafori coordinano l'accesso a risorse condivise โ un processo che chiama P su un semaforo a zero viene bloccato e rischedulato; V sblocca un processo in attesa o imposta il semaforo a 1.
/* Syscall dispatch (reg_a0) */
case CREATEPROCESS: createProcess(...);
case TERMPROCESS: termProcess(pid);
case PASSEREN: passeren(sem); /* P() */
case VERHOGEN: verhogen(sem); /* V() */
case DOIO: doIO(cmdAddr, cmdVal);
case GETTIME: getTime();
case CLOCKWAIT: clockWait();
case GETSUPPORTPTR: getSupportPtr();
case GETPROCESSID: getProcessId(parent);
case GETCHILDREN: getChildren(buf, size);