Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
Altri tipi di attesa
Il meccanismo di attesa basato sulla creazione di una waitqueue è conveniente nelle situazioni in cui la funzione che scoprirà il verificarsi dell'evento conosce la relativa coda dell'evento. Esistono però altre situazioni, nelle quali l'evento atteso è scoperto da una funzione che non ha modo di conoscere la waitqueue; in questi casi viene invocata una variante della wakeup che riceve come argomento direttamente un puntatore al processo da risvegliare. Wakeup:process ( task_struct *processo ) Due esempi di questo approccio sono: - quando un processo esegue exit() e deve essere risvegliato il relativo processo padre, se ha eseguito una wait(): wakeup_process (parent_ptr) - quando un processo deve essere risvegliato ad un preciso istante di tempo. 6.1. Gestione di un timeout Un timeout definisce una scadenza temporale, ovvero definisce un particolare istante futuro. Il tempo interno del sistema è rappresentato dallavariabile jiffies
, che registra il numero di tick di clock di sistema intercorsi dall'avviamento del sistema; dipende quindi dal ciclo di clock.
Il servizio sys_nanosleep(timespec t)
definisce una scadenza posta ad un tempo t
dopo la sua invocazione, e pone il processo in stato di attesa fino a quel momento:
current->state = ATTESA schedule_timeout(timespec_to_jiffies(&t))
L'interrupt del clock aggiorna i jiffies
; noi ipotizziamo che esista una routine controlla_timer
che controlla la lista dei timeout per verificare se qualcuno di essi è scaduto.
Quando il timer scade, controlla_timer
invoca la funzione timer.function
, passandole timer.data
come parametro: wakeup_process(timer.data)
.
4L_3. GESTIONE DELLO STATO DEI PROCESSI
7. RIASSUNTO
7.1. Funzioni dello scheduler
schedule()
- if (CURR.stato==ATTESA) dequeue_task(CURR)
- esegui il context switch
check_preempt_curr()
- verifica se il task deve essere preempted (pone TIF_NEED_RESCHED a 1)
- invocata da
wake_up
che modifica l'insieme dei processi pronti
enqueue_task (): inserisce il task nella runqueue
dequeue_task (): elimina il task dalla runqueue
resched (): pone TIF_NEED_RESCHED a 1
task_tick (): scheduler periodico, invocata dall'interrupt del clock
interagisce direttamente con le altre routine del nucleo
aggiorna i vari contatori e determina se il task deve essere preempted perché è scaduto il suo quanto di tempo (invoca resched)
L_3. GESTIONE DELLO STATO DEI PROCESSI
L_4. SERVIZI DI SISTEMA
1. AVVIAMENTO, INIZIALIZZAZIONE E INTERPRETE COMANDI
Al momento dell'avviamento del SO, cioè del suo caricamento in memoria (bootstrap), il sistema deve svolgere alcune operazioni di inizializzazione; tali operazioni consistono nell'inizializzazione di alcune strutture dati e nella creazione di un processo iniziale, detto processo 1, che esegue il programma init.
In LINUX tutte le operazioni di avviamento successive alla creazione del processo 1 sono svoltedal
- programma init, cioè da un normale programma non privilegiato, utilizzando i meccanismi descritti nella programmazione di sistema di modo utente. Il processo 1, eseguendo init, crea un processo per ogni terminale sul quale potrebbe essere eseguito un login; questa operazione è eseguita leggendo un apposito file di configurazione. Quando un utente si presenta al terminale ed esegue il login, il processo che eseguiva il programma di login lancia in esecuzione il programma shell (interprete comandi), e da questo momento la situazione è quella di normale funzionamento.
- IL PROCESSO “IDLE” Talvolta si verifica la situazione in cui nessun processo utile è pronto per l’esecuzione; in tali casi viene posto in esecuzione il processo 1, che viene chiamato Idle, perché non svolge alcuna funzione utile dopo aver concluso l’avviamento del sistema. Il processo Idle assume le seguenti caratteristiche:
- i suoi diritti di esecuzione sono sempre
<p>La funzione syscall()
, sempre della libreria glibc, incapsula la chiamata della SYSCALL che invoca il SO.</p>
<p>Per effettuare il passaggio di parametri dalla funzione syscall()
alla funzione del kernel system_call()
occorre:</p>
<ul>
<li>passare il numero del servizio da invocare nel registro rax
</li>
<li>passare eventuali parametri nei registri rdi
, rsi
, rdx
, r10
, r8
, r9
</li>
</ul>
<h4>SERVIZI DI SISTEMA</h4>
<p>La funzione syscall()
invoca il kernel tramite l'istruzione SYSCALL che invoca la funzione del kernel system_call()
.</p>
<p>A sua volta, la system_call()
invoca la System Call Service Routine opportuna sys_xxx
, per eseguire il servizio xxx
in base al numero presente nel registro rax
.</p>
<p>Esempio: invocazione del servizio read()
</p>
<ol>
<li>programma → read(fd, buf, len)
//in glibc, modo U</li>
<li>read(fd, buf, len)
→ syscall(sys_read, fd, buf, len)
//in glibc, modo U</li>
<li>syscall()
: pone sys_read
nel registro rax
, pone fd
, buf
, len
nei registri rdi
, rsi
, rdx
, esegue l'istruzione SYSCALL //passaggio a modo S</li>
<li>inizia la funzione del kernel</li>
</ol>
system_call che a sua volta invoca la System CallService Routine opportuna per eseguire il servizio sys_read5. esecuzione del servizio sys_read (codice del SO)6. il servizio ritorna alla funzione system_call7. la funzione system_call esegue l'istruzione SYSRET per tornare al processo che aveva richiesto il servizio4. CREAZIONE DEI PROCESSI4.1. Funzioni di libreriaI processi normali sono creati quando si esegue una fork(), quelli leggeri quando si esegue unathread_create(), che condivide con il chiamante memoria e tabella dei file aperti.Nella libreria glibc troviamo una ulteriore funzione, clone(), che permette di creare un processo con caratteristiche di condivisione definibili analiticamente tramite una serie di flag.int clone ( int(*fn)(void*), void *child_stack, int flags, void *args, …)- int(*fn)(void*): indica che si tratta di un puntatore a una funzione che rivece un puntatorea void come argomento e restituisce un intero- void *child_stack: è l'indirizzo
della pila che verrà usata dal processo figlio- void *arg: è il puntatore ai parametri da passare alla funzione fn- flags clone_vm: indica che i due processi utilizzano lo stesso spazio di memoriao clone_files: indica che i 2 processi condividono la tabella dei file apertio clone_thread: indica che il processo viene creato per implementare un thread.oLa funzione clone crea un processo figlio che eseguirà la funzione fn(*arg), utilizzerà una pilaall'indirizzo child_stack e condividerà con il chiamante gli elementi indicati dai flags presentinella chiamata. 2L_4. SERVIZI DI SISTEMA3L_5. SCHEDULERIl comportamento del SO è fortemente caratterizzato dalle politiche adottate per decidere qualiprocessi eseguire e per quanto tempo eseguirli (politiche di scheduling).Il componente del SO che realizza le politiche di scheduling è detto Scheduler, il cuicomportamento deve garantire le seguenti condizioni:- che i processi piùimportanti vengano eseguiti prima dei processi meno importanti- che i processi di parti importanza vengano eseguiti in maniera equa, ovvero nessun processo deve attendere il proprio turno di esecuzione per un tempo molto superiore agli altri.
1. INTRODUZIONE
Dati n processi che devono essere eseguiti, la politica Round Robin consiste nell'assegnare ad ogni processo uno stesso quanto di tempo; questa politica è equa e garantisce che nessun processo rimanga bloccato per sempre.
Lo scheduler interviene per determinare, nell'ambito dei processi pronti, quale processo mandare in esecuzione; deve scegliere il processo con il maggior diritto di esecuzione.
I momenti in cui è necessaria questa scelta sono:
- quando un processo si autosospende, per scegliere il prossimo processo da eseguire
- ogni volta che un processo viene risvegliato, perché si estende l'insieme dei processi pronti; se il nuovo processo ha un diritto di esecuzione superiore rispetto a quello corrente,
sisospende il processo corrente per eseguire il nuovo processo (preemption)- ogni volta che il processo in esecuzione è gestito con politica Round Robin e il suo quanto ditempo è esaurito.
2. REQUISITI DEI PROCESSI
- Real-time: questi processi devono soddisfare dei vincoli di tempo estremamentestringenti e devono quindi essere schedulati con estrema rapidità, garantendo in ogni caso di non superare un preciso vincolo di ritardo massimo.
- Semi real-time: questi processi, pur richiedendo una relativa rapidità di risposta, non richiedono la garanzia assoluta di non superare un certo ritardo massimo.
- Normali: sono tutti gli altri processi
Processi I/O: processi che si sospendono spesso perché hanno bisogno di un I/O
Processi CPU: processi che usano molto la CPU, perché si sospendono raramente.
3. CLASSI DI SCHEDULING
Per supportare le diverse categorie di processi lo Scheduler realizza diverse politiche di scheduling; ogni politica è
realizzata da una Scheduler Class. Nel descrittore di un processo, il campo "constant struct sched_class * sched_class" contiene un puntatore alla struttura della scheduler class deputata a gestirlo. Lo scheduler è l'unico gestore della runqueue; per questo motivo le altre funzioni devono chiedere allo scheduler di eseguire operazioni sulla runqueue. Questa scelta permette di organizzare le runqueue in maniera adatta alle diverse classi di scheduling senza dover modificare il resto del sistema.SCHEDULER
Attualmente le tre classi supportate (in ordine di diritti di esecuzione) sono:
- SCHED_FIFO: First In First Out
- SCHED_RR: Round Robin
- SCHED_NORMAL
La funzione schedule() invoca la funzione pick_next_task(), che a sua volta invoca le funzioni specifiche di ogni singola classe in ordine di importanza delle classi.