ANDI #$DFFF,SR
Invece per passare dalla modalità utente a quella sistema (cioè
cambiare il bit 13 da 0 a 1) l'unico modo è fare una chiamata di sistema
(TRAP). Tutte le eccezioni passano al modo supervisore, qualsiasi sia il valore precedente del bit S. Si noti che se il bit S era posto a 0 si ha contemporaneamente al cambio di modo anche il cambio dello stack pointer.| trap | switch | nome |
| 0 | - | read |
| 1 | - | write |
| 2 | - | open |
| 3 | 0 | close |
| 3 | 1 | unlink |
| 3 | 2 | rewind |
| 10 | - | chname |
Un processo per gestire l'ingresso e l'uscita di dati sia su file che su console deve avvalersi di opportune strutture dati chiamati flussi. Ogni processo ha a disposizione 20 flussi, i primi tre sono aperti automaticamente e si riferiscono allo standard input, output, error. Un flusso che abbia per oggetto un file deve essere aperto; solo dopo si potrà leggere o scrivere sul flusso, si potrà anche riavviare il flusso e infine chiuderlo.

int open(path, flag, mode)
/* apre un file come un flusso */
{
se non e' stato indicato nessun file esci
se il file non esiste
se O_CREAT e' vero crea il file altrimenti esci
se non e' stato chiesto nessun permesso
restituisci l'esistenza del file
se !access() /* verifica i permessi */
esci
altrimenti {
se trovi un posto libero nella tabella globale dei file {
occupa una riga della tabella inserendo un puntatore al
file, la dimensione del file, offset zero o uguale alla
dimensione, l'indice del file nella directory, il permesso
con cui e' aperto
se c'e' un posto libero nella tabella locale dei file {
riempilo con l'indice della tabella globale
restituisci l'indice della tabella locale
}
altrimenti esci
}
}
}
int read(fd, buf, nbyte)
/* legge da un flusso aperto */
{
Calcolo indice della tavola globale dei file usando fd
Se il flusso e' lo standard input chiama la getch ed esci
Se l'indice e' incorretto esci
Se il flusso e' aperto in lettura {
Leggi le caratteristiche del flusso
Leggi i dati e copiali nella stringa passata
aggiorna i parametri del flusso
restituisci il numero di byte letti
}
}
int write(fd, buf, nbyte)
/* scrive su un flusso aperto */
{
Calcolo indice della tavola globale dei file usando fd
Se il flusso e' lo standard output chiama la printf ed esci
Se l'indice e' incorretto esci
Se il flusso e' aperto in scrittura {
Leggi le caratteristiche del flusso
se siamo alla fine di un blocco
alloca il blocco di memoria successivo se libero
scrivi la stringa passata
aggiorna i parametri del flusso
restituisci il numero di byte scritti
}
}
int close(fd)
/* chiude un flusso */
{
Se il descrittore fd e' corretto {
Calcolo l'indice della tabella globale dei file usando fd
Libero la locazione nella tabella globale dei file
Libero la locazione nella tabella locale dei file
}
Altrimenti esci
}
int rewind(fd)
/* riavvia un flusso */
{
Se il descrittore fd e' associato ad un file {
Calcolo l'indice della tabella globale dei file usando fd
Poni a zero la posizione attuale
}
}
int chname(oldname, newname)
/* chiamata di sistema */
/* Cambia il nome di un file. */
{
se il file con il nome vecchio esiste
se non esiste nessun file con il nome nuovo
cambia il nome al file
}
| trap | switch | nome |
| 4 | - | exec |
| 5 | - | kill |
| 6 | - | exit |
La chiamata exec permette di creare un nuovo processo. In sostanza vengono disattivati gli interrupt, viene chiamata la funzione ParseDetect che provvede ad accodare il nuovo processo. Infine vengono riattivati gli interrupt.
La chiamata kill permette di eliminare un processo in corso d'esecuzione. Può essere chiamata in due modi: o fornendo il PID (Process ID) o il nome del processo da eliminare. Nel primo caso vengono eliminati tutti i thread che appartengono al processo specificato. Altrimenti vengono cercati tutti i thread che hanno il nome specificato e si verifica se hanno tutti lo stesso PID; in caso affermativo vengono eliminati tutti, altrimenti viene visualizzato il PID e il TID di ciascun thread e si invita ad effettuare la cancellazione mediante il PID.
void Kill (param, switch)
{
se switch indica che e' stato indicato il PID {
per tutte le priorita' dei thread
se nella coda di priorita' c'e' un thread con PID dato
DiscardTCB /* elimina il thread */
}
altrimenti se e' indicato il nome {
per tutte le priorita' dei thread
se nella coda di priorita' c'e' un thread con nome dato
aggiungi il thread alla lista dei thread trovati
se e' stato trovato almeno un thread {
se i thread trovati appartengono a piu' processi
visualizza il PID di ciascun thread
altrimenti {
per tutti i thread trovati
DiscardTCB /* elimina il thread */
}
}
altrimenti
visualizza messaggio
}
}
void DiscardTCB(p,i)
/* libera le risorse occupate da un thread */
{
libera i blocchi di Heap del processo
se il TID e' nullo
libera la memoria dati e codice
se il thread e' in foreground
resetta l'input al sistema
libera lo Stack
togli Il TCB dalla coda in cui si trova e
mettilo nella coda dei TCB liberi
}
| trap | switch | nome |
| 8 | - | alloc |
| 9 | - | free |
Ad ogni chiamata di sistema ``alloc'' si verifica innanzitutto se le
dimensioni di memoria richieste possono essere soddisfatte (cioè sono comprese 1 e
8192), in caso favorevole si cerca il primo blocco di heap puntato dal TCB corrente, si verifica se
all'interno c'è uno spazio libero abbastanza grande, altrimenti si cerca il blocco successivo; se si arriva alla fine della lista di blocchi senza soddisfare la richiesta, allora si alloca un nuovo blocco, lo si aggiunge alla coda di blocchi di Heap posseduti dal processo e si occupa lo spazio richiesto e viene restituito il puntatore alla memoria allocata.
void * alloc (size)
/* alloca una zona di memoria lunga size byte */
{
se la dimensione richiesta non rientra nei limiti esci
carica il primo blocco di Heap del processo attuale
finche' il puntatore di blocco non e' nullo
per tutte le posizioni libere dentro al blocco
se le dimensioni sono sufficienti {
occupa lo spazio che occorre
restituisci puntatore allo spazio allocato
}
se e' possibile allocare un altro blocco di Heap {
concatena il nuovo blocco alla coda
occupa lo spazio che occorre
restituisci puntatore allo spazio allocato
}
}
int GetHeapBlock ()
/* alloca un blocco dalla memoria heap */
{
per tutti i blocchi di Heap
se trovi un blocco libero {
marca il blocco come occupato
restituisci il puntatore al blocco
}
}
void free (i)
/* libera una zona di memoria puntata da i */
{
calcola il blocco di Heap che contiene la zona da liberare
cerca la zona precedente e la successiva
se la zona precedente o la successiva sono libere fondile assieme
libera la zona attuale
se il blocco e' rimasto vuoto
chiama FreeHeapBlock /* libera il blocco di Heap */
}
void FreeHeapBlock (i)
/* libera un blocco dalla memoria heap */
{
se il blocco da liberare non e' il primo della lista
cerca il blocco precedente e collegalo al successivo
libera il blocco indicato
}
void FreeUserHeap (p)
/* libera tutti i blocchi dalla memoria heap occupati da un processo */
{
carica il primo blocco di Heap del processo puntato da p
finche' esiste un blocco successivo
libera il blocco attuale e passa al successivo
}
| trap | switch | nome |
| 11 | >0 | thread_create |
| 11 | 0 | thread_self |
| 12 | 0 | thread_mutex_init |
| 12 | 1 | thread_mutex_lock |
| 12 | 2 | thread_mutex_unlock |
| 12 | 3 | thread_mutex_destroy |
| 13 | 0 | thread_exit |
| 13 | 1 | thread_cancel |
La chiamata di sistema thread_create alloca un TCB (la struttura utilizzata dallo schedulatore per memorizzare un thread), inizializza tutti i parametri del TCB e lo inserisce nella coda d'attesa.
thread_create(fun)
/* crea un nuovo thread all'interno di un processo */
{
GetTCB(); /* alloca un TCB */
azzera tutti i registri;
copia il puntatore alla memoria, il registro A6, il nome del
processo, il PID, la priorita'
calcola e setta il TID e il tempo di creazione
QueueIMP(); /* inserisci il TCB nella coda di schedulazione */
restituisci il TID
}
thread_self()
/* restituisce il TID del thread in esecuzione */
{
restituisci il TID
}
thread_exit()
/* esce da un thread */
{
per tutti i mutex disponibili {
se il mutex e' posseduto da questo thread
libera il mutex
}
DiscardTCB() /* libera il TCB utilizzato */
ritorna allo schedulatore
}
thread_cancel (TID)
/* cancella un thread */
{
per tutti i mutex disponibili {
se il mutex e' posseduto da TID
marca il mutex come bloccante la cancellazione
}
se il thread non possiede nessun mutex
kill (PID) /* elimina il thread */
}
thread_mutex_init()
/* crea un nuovo mutex (semaforo binario) */
{
per tutti i mutex disponibili {
se il mutex e' inutilizzato {
marca il mutex come utilizzato
restituisci il numero di mutex
}
}
restituisci -1
}
thread_mutex_lock (mutex)
/* blocca un mutex */
{
se mutex esiste {
down(&mutex)
copia il TID del proprietario
}
}
thread_mutex_unlock (mutex)
/* sblocca un mutex */
{
se mutex e' bloccato {
se il richiedente e' il proprietario {
se il mutex ha bloccato una cancellazione
thread_cancel(TID)
libera il mutex
}
}
}
thread_mutex_destroy()
/* distrugge un mutex */
{
se mutex esiste {
se mutex e' libero
cancella mutex
}
}
Back to Lucio's Home page.