Lezione T8 Gli scheduler CPU di Linux

Pagina creata da Dario Guidi
 
CONTINUA A LEGGERE
Lezione T8
Gli scheduler CPU di Linux
Sistemi Operativi (9 CFU), CdL Informatica, A. A. 2013/2014
Dipartimento di Scienze Fisiche, Informatiche e Matematiche
Università di Modena e Reggio Emilia
http://weblab.ing.unimo.it/people/andreolini/didattica/sistemi-operativi
                                                                           1
Quote of the day
                     (Meditate, gente, meditate...)

“The design relies on the fact
that interactive tasks, by
their nature, sleep often.”

Con Kolivas (1977-)
Anestesista, programmatore
Il pioniere del “desktop interattivo”

                                                      2
Parametri di progetto di uno scheduler
                    (Repetita juvant)

Numero e formato delle code dei processi pronti.
Algoritmo di scheduling per ciascuna coda.
Criterio usato per spostare un processo in una
coda con priorità maggiore.
Criterio usato per spostare un processo in una
coda con priorità minore.
Criterio usato per scegliere la coda in cui inserire
inizialmente un processo.
                                                   3
LINUX v1.2

             4
Coda dei processi pronti
                (Ve ne è una sola; semplice semplice)

Esiste una unica lista contenente tutti i processi
attivati sulla macchina. Il primo elemento della
lista è la task_struct di init.

    init_task
                          ...
                                                        5
Definizione delle priorità
               (Priorità statica, the UNIX way)
Ogni processo ha una priorità statica (nice
value o nice) (re)impostabile dall'utente.
La priorità varia da -20 (priorità più alta) a +19
(priorità più bassa). Il valore di default è 10.
→ Schema classico delle priorità in UNIX.
Non è prevista una modifica dinamica delle
priorità da parte del kernel.

                                                  6
Algoritmo di scheduling
                  (Round Robin; what else?)

L'algoritmo di scheduling pesca in modalità
“round robin pesata” uno dei processi in
TASK_RUNNING.
  Un processo in attesa da più tempo viene favorito
  rispetto ad un processo che attende da meno tempo.
  A parità di attesa, un processo a priorità più alta viene
  favorito rispetto ad un processo a priorità più bassa.
A tal scopo, ad ogni processo è associato un
contatore, inizializzato al valore della priorità
statica.                                        7
Aggiornamento del contatore
                  (Round Robin; what else?)

Ad ogni invocazione di schedule(), l'intera
lista è scandita ed i contatori aggiornati:
  contatore = (contatore >> 1) + priority
Si sceglie il processo per cui contatore è massimo.

                                                  8
Modifica dinamica della priorità
                   (Non pervenuta)

Non esiste alcun meccanismo per la modifica
dinamica della priorità in funzione della natura
del processo.
L'unica priorità esistente è quella statica.
La priorità iniziale del processo è:
  il valore iniziale di default (10).
  oppure un valore impostato con il comando nice.
                                                    9
Problemi 1/3
                     (Tanti, purtroppo)

L'algoritmo di scheduling è O(n) rispetto al numero di
processi (una invocazione di schedule() → una
scansione lineare della lista).
    Che succede se schedule() è invocata migliaia di
    volte al secondo in un sistema server con migliaia di
    processi attivi?

                                                       10
Problemi 2/3
                   (Tanti, purtroppo)

Non vi è alcuna differenziazione in classi; l'unico
discriminante è una priorità statica.
  Che succede se un processo muta spesso la propria
  natura? Il programmatore deve reimpostare ogni
  volta la priorità statica a mano?
  Che succede ad un terminale interattivo che va in
  competizione con uno script di make?

                                                 11
Problemi 3/3
                    (Tanti, purtroppo)

Vi è una unica lista globale.
  L'accesso alla lista va “serializzato” per impedire le
  modifiche concorrenti. → I processori si “accodano”
  per accedere alla lista. → Le prestazioni non scalano
  con il numero di processori.
Questo non è un vero problema, dal momento
che Linux v1.12 non supporta sistemi SMP.
                                                      12
LINUX v2.2

             13
Differenze rispetto allo scheduler v1.2
                (Classi di processi, goodness())

Lo scheduler del kernel v2.2 è una estensione di
quello presente nella versione v1.2.
   Differenziazione dei processi in più classi tramite un
  insieme più ampio di priorità.
  Introduzione di una funzione (goodness())
  per il calcolo della “bontà” di un processo (ovvero
  quanto è favorevole schedularlo).

                                                       14
Il nuovo schema di priorità
                  (Da [-20, 19] a [0, 139])
Si usano 140 livelli di priorità.
Livelli [0, 99]: usati da algoritmi di scheduling di
tipo soft real time (SCHED_FIFO, SCHED_RR), che
hanno sempre la precedenza sull'algoritmo
standard.
Livelli [100, 139]: corrispondono al vecchio
intervallo [-20, 19] e sono utilizzati dall'algoritmo
di scheduling di default (round robin pesato).
                                                   15
Classi di processi: SCHED_FIFO
                      (Alta priorità)
Impostabili staticamente dall'utente all'avvio del
processo.
SCHED_FIFO: FCFS senza prelazione “soft real
time”. Opera ad una priorità in [0, 99]. Un processo
non si ferma se non quando termina o richiede
I/O.
  → Occhio: pericolo di stallo della macchina in caso di
  ciclo infinito!

                                                      16
Classi di processi: SCHED_RR
                    (Alta priorità)

Impostabili staticamente dall'utente all'avvio del
processo.
SCHED_RR: RR “soft real time”. Opera ad una
priorità in [0, 99].

                                                17
Classi di processi: SCHED_OTHER
                     (Bassa priorità)
Impostabili staticamente dall'utente all'avvio del
processo.
SCHED_OTHER: algoritmo di scheduling RR
pesato (derivato da quello presente in v1.2).
Opera ad una priorità in [100, 139] ed è pertanto
sempre battuto da SCHED_FIFO o SCHED_RR.
  → Occhio: rischio di starvation in caso di esecuzione
  con un processo CPU-bound soft real time!

                                                     18
Criterio di scelta del processo
                  (Priorità + algoritmo)

Algoritmo multilivello.
Passo 1: si individua il processo con priorità più
alta nella lista dei processi.
Passo 2: si applica l'algoritmo di scheduling
associato al processo.

                                                19
La funzione goodness()
              (Stima dell'interattività di un processo)
La funzione goodness() ritorna un peso
(weight) nell'intervallo [-1000, 1000]. Il peso è
usato per scegliere il processo da schedulare.
  -1000: mai selezionare questo processo.
  1000: processo soft real time, da selezionare subito.
Algoritmo:
  Classe == SCHED_FIFO | SCHED_RR? Sì → weight=1000
  Processo gira su stesso processore? Sì → weight += bonus
  Processo    usa     le    stesse   aree   di memoria?
     Sì → weight += bonus
  weight += priorità processo                            20
Problemi 1/2
                       (Un po' meno)

L'algoritmo di scheduling è O(n) rispetto al numero di
processi (una invocazione di schedule() → una
scansione lineare della lista).
    Che succede se schedule() è invocata migliaia di
    volte al secondo in un sistema server con migliaia di
    processi attivi?

                                                       21
Problemi 2/2
                      (Un po' meno)

Vi è una unica lista globale.
  L'accesso alla lista va “serializzato” per impedire le
  modifiche concorrenti. → I processori si “accodano”
  per accedere alla lista. → Le prestazioni non scalano
  con il numero di processori.
Questo comincia ad essere un problema, dal
momento che Linux v2.2 supporta architetture
SMP.
                                                      22
LINUX v2.4

             23
Differenze rispetto allo scheduler v2.2
               (Classi di processi, goodness())

Lo scheduler del kernel v2.4 è una estensione di
quello presente nella versione v2.2.
  Uso di una coda contenente solo processi in stato di
  pronto.
  Uso di quanti di tempo variabili (time slice).
  Uso di un sistema di schedulazione a crediti.

                                                    24
Time slice
                 (Quanto di tempo variabile)

Ciascun processo può eseguire al più per un
intervallo di tempo calcolato dinamicamente
(time slice).
Il calcolo avviene mediante un sistema a crediti:
  un credito → 10ms di esecuzione.
  un processo acquisisce un credito ogni volta che si
  blocca → premio all'interattività.
  dopo l'esecuzione di una time slice si scala un credito.
  nessun credito → niente processore.
                                                        25
Recrediting
            (Quanti crediti sono riassegnati ai processi?)

Quando tutti i processi in stato TASK_RUNNING
hanno esaurito i crediti, il kernel ricalcola il
credito per tutti i processi (recrediting).
Algoritmo di aggiornamento:
  next_credit = prev_credit / 2 + priority
Si cerca di ridurre il credito a disposizione dei
processi per non farli eseguire troppo a lungo.
Se il processo è interattivo → il credito aumenta.
Se il processo non è interattivo → il credito si
riduce.                                            26
Algoritmo di scheduling
                   (Round Robin; what else?)

L'algoritmo di scheduling pesca il processo
migliore tramite la funzione goodness(), in
modo analogo al kernel v2.2.
La funzione goodness() tiene ora in conto
anche dei seguenti aspetti:
  affinità al processore.
  crediti rimanenti.
                                               27
Problemi 1/2
                    (Pochi, ma grossi)

L'algoritmo di scheduling è O(n) rispetto al numero di
processi.
    Una invocazione di schedule() → una scansione
    lineare della run queue).
    Un recrediting → una scansione lineare della run
    queue.

                                                    28
Problemi 2/2
                   (Pochi, ma grossi)

I processori sono inattivi durante il recrediting.
→ starvation dei processori (tanto più marcata
quanti più sono).

                                                29
LINUX V2.5.49 - V2.6.22

                          30
Differenze rispetto allo scheduler v2.4
              (Parecchie; è una riscrittura da zero)

Obiettivi.
Favorire i processi interattivi indipendentemente
dal carico di lavoro applicato al processore.
Scalare bene le prestazioni dello scheduler con
  il numero di processi.
  il numero di processori.

                                                       31
Coda dei processi pronti
                                    (Una per CPU!)

  La coda contiene due array di priorità di 140 elementi
  cadauno (una coda per ciascuna priorità possibile).
     Array dei processi attivi (active): non hanno esaurito il
     tempo di esecuzione loro assegnato.
     Array dei processi spirati (expired): hanno esaurito il
     tempo di esecuzione loro assegnato.
                   Active array                           Expired array
 alta   Priorità      Lista di task_struct     Priorità      Lista di task_struct
           [0]                                   [0]
           [1]                                   [1]

                                                                                    32
bassa    [140]                                  [140]
Il difetto del Weighted Round Robin
             (L'algoritmo standard in pseudocodice → O(n))

for (p = init_task; p != init_task; p++) {
  if (p->prio != MAX)     Aging (per evitare la
     p->prio++;           starvation del processo)
  if (p->prio > current->prio)               Si schedula il
                                             primo processo
     switch_to(p);                           con priorità più
}                                            alta trovato.

Il ciclo for cicla sull'intera lista dei processi e pesca quello
a priorità più alta fra gli eseguibili.
→ Algoritmo O(n) rispetto al numero di processi.
Bisogna cercare di evitare il ciclo for sull'intera lista. 33
Un algoritmo O(1) rispetto ai processi
  (Ricorda molto da vicino la tecnica del double buffering per i videogiochi)

1.Trovare in tempo O(1) la coda di priorità più elevata in active
  contenente la task_struct di un processo eseguibile.
2.Se non esiste una task_struct, tutti i processi sono spirati. Si
  scambiano gli array active ed expired (operazione fattibile
  in tempo O(1)) e si ripete 1.
3.Sia next=task_struct in cima alla coda di priorità più
  elevata (O(1)).
4.Si ricalcola la priorità di next (O(1).
5.Si effettua un cambio di contesto a next (O(1)).
6.Quando next ha usato la sua timeslice, lo si inserisce nella coda
  di priorità opportuna nell'array expired (O(1)).
7.Si invoca schedule() per schedulare un altro processo (O(1)).34
Come risolvere il passo 1.
    (Come trovare la coda con priorità più elevata senza scandirle tutte)

Si usa una bitmap di 140 bit (5 interi a 32 bit).
                   Nella coda i è presente la
     bit i=1   → task_struct di un processo
                   eseguibile.
                      Nella coda i non è presente la
      bit i=0   → task_struct di un processo
                      eseguibile.
L'istruzione assembly bsfl (Intel) individua in tempo
costante (O(1)) il primo bit non nullo in una bitmap. 35
Stima dell'interattività
             (Il kernel riconosce i processi interattivi)

Obiettivo della stima: aumentare in maniera dinamica
la priorità di un processo interattivo.
Criterio di interattività: frequenza di blocco processo.
  Frequenza elevata: processo → I/O-bound
  Frequenza bassa: processo → CPU-bound
Implementazione: campo sleep_avg inserito nella
task_struct del processo.
  Run → blocked: si sottrae a sleep_avg il tempo di
  esecuzione dell'ultima timelice.
  Blocked → run: si aggiunge a sleep_avg il tempo di
  blocco (fino ad un massimo di 10ms).            36
Calcolo dinamico della priorità
   (Il processo esegue tanto spesso quanto richiesto dalla sua natura)

La funzione effective_prio(), definita nel
file $LINUX/kernel/sched.c, calcola il boost
di priorità da assegnare ad un processo in base
alla sua interattività.
Tale funzione assegna un bonus di priorità
nell'intervallo [-5, +5].
  Processo fortemente I/O-bound → -5.
  Processo fortemente CPU-bound → +5.
                                                                         37
Calcolo dinamico della timeslice
    (Il processo esegue tanto tempo quanto richiesto dalla sua natura)

La durata della timeslice è:
  memorizzata nel campo time_slice della
  task_struct.
  calcolata dalla funzione task_timeslice() nel
  file $LINUX/kernel/sched.c.
Tale funzione scala la priorità statica (PS) di un
processo in un valore nell'intervallo [5ms, 800ms]:
  PS < 120 → time_slice = (140 – PS) * 20
  PS ≥ 120 → time_slice = (140 – PS) * 5                                 38
Alcuni valori di esempio
             (Valgono più di 1000 parole)

Priorità          PS      NICE              Time_slice
Altissima        100       -20                 800 ms
Alta             110       -10                 600 ms
Normale          120         0                 100 ms
Bassa            130        10                 50 ms
Bassissima       139        19                   5 ms

                                                         39
Una riflessione su sleep_avg
     (STOP! A che serve tutto questo? Qual è l'effetto sui processi?)

La metrica sleep_avg è molto accurata!
Un processo che si blocca spesso, ma che
esaurisce la sua timeslice continuamente, non
riceve un bonus grande.
  Effetto ricompensa per i processi interattivi.
  Effetto punizione per i processi non interattivi.
Un task che riceve un bonus di priorità lo perde
se comincia ad abusare del processore.
  Cambi di contesto non compensati da attese
  provocano la decrescita di sleep_avg e del bonus. 40
Una pecca dell'algoritmo O(1)
    (Un processo CPU-bound può rallentare un processo I/O-bound)
Scenario.
  Un processo CPU-bound ed un processo I/O-bound
  sono in competizione per il processore.
  Il processo I/O-bound termina la sua timeslice per
  primo.
  Il processo CPU-bound continua a consumare la sua
  timeslice per molto più tempo.
→ Il processo CPU-bound blocca il processo
  I/O-bound almeno fino all'esaurimento della
  sua timeslice!
                                                                   41
Una euristica risolutiva
                   (La classica “pezza”)

Se il processo è sufficientemente interattivo, al
termine della sua timeslice
  non viene spostato nell'array expired
  viene reinserito nell'array active.
Rationale: reinserendo il processo interattivo
nell'array di priorità active, esso continua ad
essere rischedulato immediatamente.
                                               42
Aggiornamento delle timeslice
                (Rimettiamo insieme tutti i pezzi)

scheduler_tick() in $LINUX/kernel/sched.c.
  Si decrementa time_slice nella task_struct del
  processo in esecuzione.
  Se time_slice == 0, la timeslice è spirata e bisogna
  decidere in che array piazzare la task_struct.
  Si invoca la macro TASK_INTERACTIVE() per capire
  se il task è sufficientemente interattivo.
  Si invoca la macro EXPIRED_STARVING() per capire se
  la runqueue ha processi in starvation.
  Se la runqueue non ha processi in starvation ed il task è
  sufficientemente interattivo, viene reinserito in active. 43
Problemi 1/2
        (I vecchi problemi sono spariti; ne sorgono di nuovi)

È complicato stimare l'interattività di un processo.
  I/O-bound → deve essere schedulato più spesso.
  CPU-bound → deve essere schedulato meno spesso e
  con timeslice più lunghe.
È complicato calcolare una giusta durata.
  Timeslice troppo piccola → elevato aggravio legato al
  cambio di contesto.
  Timeslice troppo grande → scheduler degenera in
  FIFO (bene per il batch, pessimo per l'interattivo).
                                                                44
Problemi 2/2
        (I vecchi problemi sono spariti; ne sorgono di nuovi)
La priorità è relativa mentre la timeslice è
assoluta.
  Due processi con NICE 0 ed 1: le timeslice sono di
  100ms e 95ms → 5% di differenza.
  Due processi con NICE 18 e 19: le timeslice sono di
  10ms e 5ms → 100% di differenza!
Sono state progettate diverse euristiche per
affrontare questi problemi.
Problema: le euristiche possono essere sfruttate
per provocare Denial of Service (fatta l'euristica,
trovato l'inganno!).                             45
LINUX V2.6.23-

                 46
Differenze rispetto allo scheduler v2.5
             (Parecchie; è una riscrittura da zero)

Obiettivi.
Semplificare lo scheduler, togliendo tutte le
euristiche viste in precedenza.
Rendere lo scheduler equo (fair) verso tutti i
processi.
Mantenere elevata l'interattività con gli utenti.
→ Completely Fair Scheduler (CFS)
                                                      47
Fair Scheduling
           (CFS cerca di emulare il comportamento di uno scheduler ideale)
  CFS cerca di imitare al meglio il comportamento
  di uno scheduler multitasking ideale
  (Generalized Processor Sharing, GPS) con le
  seguenti caratteristiche:
       timeslice infinitesima.
       servizio simultaneo ad n processi con capacità di
       elaborazione pari ad 1/n.
   Un                                                             Servito a capacità
processo                                                          massima della CPU.

  Tre                                                       Serviti simultaneamente
processi                                                    ad un terzo della capacità
                                                            massima della CPU. 48
Perché GPS?
                (Già, perché proprio GPS?)

GPS ha latenza di servizio minima. I processi non
possono essere serviti più celermente di quanto
non riesca a fare GPS.
Perché non implementare direttamente GPS?

                                               49
Problema
                 (GPS non è implementabile)

GPS non è implementabile perché un processore
non può servire simultaneamente più processi.
  No, usare più processori non vale! GPS è definito solo
  per un processore.
  No, non si può ridurre la timeslice ad un valore quasi
  nullo nell'algoritmo O(1). L'aggravio legato al cambio
  di contesto ammazzerebbe le prestazioni.
→ Si deve implementare una approssimazione di
GPS → Ripensamento delle strutture dati.    50
Strategia implementativa 1/3
             (Cambia parecchio rispetto all'O(1))

Si cambia l'ordinamento della coda di pronto.
  Non più FIFO con priorità.
  I processi sono ordinati in base a quanta CPU hanno
  ricevuto in meno rispetto a GPS.
→ In tal modo si può scegliere in maniera
efficiente il processo con il maggior bisogno di
CPU.
                                                    51
Strategia implementativa 2/3
               (Cambia parecchio rispetto all'O(1))

Non si usa una timeslice fissa per un processo.
  Si usa una sola timeslice, condivisa fra tutti i processi.
  → Una volta esaurita la timeslice, tutti i processi
  devono           essere         stati           schedulati.
  → Ogni processo è schedulato almeno una volta nella
  timeslice.
→Approssimazione         della                   “schedulazione
simultanea” effettuata da GPS.
                                                             52
Strategia implementativa 3/3
             (Cambia parecchio rispetto all'O(1))

Non si usa Round Robin per la scelta del processo.
  Si sceglie il processo che ha ricevuto meno CPU
  rispetto a GPS.
→ CFS si comporta come GPS “a tratti”.

                                                    53
Scheduling latency
            (La timeslice condivisa da tutti i processi)
La scheduling latency è la timeslice unica
condivisa fra tutti i processi.
  Approssimazione di una timeslice infinitesima.
  Valore di default: 20ms (→ applicazioni multimediali).
  Tunable: /proc/sys/kernel/sched_latency_ns
Ciascun processo ottiene una uguale proporzione
di timeslice.
  timeslice(task)=sched_latency_ns/nr_tasks.
  timeslice(task) non può essere inferiore a 4ms.
  Tunable per regolare la timeslice minima:
  /proc/sys/kernel/sched_min_granularity_ns.               54
Priorità e pesi
       (Il peso quantifica la priorità nelle formule dello scheduler)

La priorità statica del processo (in [0, 139]) è usata
per definire dei valori detti pesi (weight). I pesi
servono per scalare le grandezze di un processo
(fetta di timeslice associata, tempo di esecuzione
effettivo) in funzione della sua priorità.
I pesi sono calibrati sperimentalmente e
decrescono con la priorità assoluta.
  Priorità=0: peso massimo
  Priorità=139: peso minimo                                             55
Calcolo dinamico del quanto di tempo
         (Il processo esegue quanto gli serve, entro 20 ms.)

La timeslice è la scheduling latency media per
processo, pesata con la frazione relativa del peso
del processo considerato.
  timeslice(task) = [ sched_latency_ns/nr_tasks ] *
                    [ weight(task) / Σtasks weight (task) ]
Rationale: più è elevata la priorità, più è basso il
valore di priorità assoluta, più è alto il peso, più
aumenta la frazione di timeslice assegnata.
                                                               56
Il tempo virtuale
      (Scorre più o meno rapidamente a seconda della priorità)

Ciascun processo ha associato un tempo virtuale
di esecuzione (virtual run time).
  Campo vruntime della task_struct.
Durante l'esecuzione di un processo, il tempo
virtuale cresce:
  normalmente alla priorità standard (120).
  più lentamente a priorità più alte (120).
                                                                 57
Aggiornamento del tempo virtuale
       (Scorre più o meno rapidamente a seconda della priorità)

Ad ogni rischedulazione viene ricalcolato il peso
del processo uscente. Il valore vruntime è
aumentato       del     tempo      di    esecuzione
delta_exec in maniera proporzionale al
rapporto fra la priorità standard e quella attuale.
  vruntime = [vruntime+delta_exec] *
             [ weight(prio=120) / weight(prio) ]
Rationale: più elevata è la priorità assoluta, più è
alto il peso, più lentamente cresce vruntime. 58
L'algoritmo di scheduling
                     (Molto semplice)

Si sceglie il processo eseguibile il cui vruntime
è minimo.
Problema: vruntime può andare in overflow. In
tal caso, il processo attuale è eseguito per sempre
(vruntime è a 64 bit!).
Soluzione: si sceglie il processo eseguibile per
cui la differenza vruntime – min_vruntime è
minima.
  → Tale differenza non è soggetta ad overflow.   59
La “coda di pronto”
                (Non è più una coda!)

Come pescare efficientemente (O(1)) il processo
con differenza vruntime – min_vruntime
minima?
Si usa un albero rosso-nero (red-black tree) di
task_struct ordinate secondo la chiave
vruntime – min_vruntime.
                                             60
L'albero rosso-nero
                     (Ed alcune osservazioni di contorno)
Δ = vruntime - min_vruntime                       Inserimento: O(log n)
                                                  Cancellazione: O(log n)
                                                  Aggiornamento: O(log n)
                                                  Estrazione minimo: O(1)

                     Δ = 300                      vruntime e min_vruntime
                                                  sono aggiornati ad ogni
                                                  cambio di contesto oppure
              Δ = 100         Δ = 400             interruzione.
                                                  I processi tendono a spostarsi
        Δ=0             Δ = 150         Δ = 410   da sinistra verso destra man
                                                  mano che eseguono. I processi
                                                  a priorità più alta si spostano
Viene sempre selezionato                          più lentamente.
il processo che si trova in                                                     61
fondo a sinistra.
Fairness fra utenti diversi
  (Finora i processi sono stati considerati tutti appartenenti ad un utente)

Si supponga di avere 25 processi, di cui
  20 relativi ad un utente A.
  5 relativi ad un utente B.
CFS cerca di essere fair con tutti → l'utente A
ottiene una fetta di CPU molto più consistente
rispetto all'utente B.
→ Si rende necessario estendere i meccanismi di
fairness a gruppi di processi.
                                                                               62
Scheduling gerarchico
            (Si schedulano processi e gruppi di processi)

A partire dal kernel v2.6.24 è stato aggiunto il supporto
per la schedulazione gerarchica (hierarchical
scheduling).
Si introduce una nuova struttura dati:
   struct sched_entity
   (definita in $LINUX/include/linux/sched.h)
che rappresenta un gruppo di processi.
Ogni entity ha un suo vruntime e un suo peso,
calcolato in maniera analoga a quanto visto prima.
                                                            63
L'albero rosso-nero, task e entity
                 (Si schedulano prima le entity e poi i singoli processi)
  Δ = vruntime - min_vruntime
                                    Entity rappresentante
 Entity rappresentante              tutti i processi.
 il gruppo dei processi                     Entity rappresentante
 dell'utente A.         1                   il gruppo dei processi
                                            dell'utente B.     1. Si parte dalla entity
                          Δ = 300
                                                                  rappresentante      tutti
                2                                                 i processi.
                  Δ = 100       Δ = 400                        2. Si schedula la entity
                                                                  relativa ai processi del
                                                                  gruppo A (ha un vruntime
         3                                                        più basso).
           Δ=0             Δ = 150        Δ = 410              3. Si schedula il processo
                                                                  con più fame di CPU nel
Task_struct dei processi Task_struct dei processi                 gruppo A (vruntime=0).
                                                               4. Si cambia contesto a tale
appartenenti al gruppo appartenenti al gruppo                     processo.              64
dell'utente A.               dell'utente B.
Puoi anche leggere