In che modo il processore trova il codice del kernel dopo un interrupt?


13

Quando si verifica un interrupt, il processore anticipa il processo corrente e chiama il codice del kernel per gestire l'interrupt. Come fa il processore a sapere dove inserire il kernel?

Comprendo che ci sono gestori di interrupt che possono essere installati per ogni linea di interrupt. Ma poiché il processore esegue solo la "logica cablata", deve esistere un posto predefinito che punta a un gestore di interrupt stesso o ad un codice che viene eseguito prima del gestore (poiché possono esserci più gestori per una linea di interruzione, suppongo che quest'ultimo).

Risposte:


13

All'avvio, il kernel inizializzerà una tabella vettoriale di interrupt (chiamata tabella descrittore di interrupt o IDT su x86) che punta a un gestore di interrupt per ogni riga.

Prima dell'80286, l'IDT era sempre memorizzato a un indirizzo fisso; a partire dall'80286, l'IDT viene caricato usando l' LIDTistruzione.

Le tabelle vettoriali di interrupt puntano a un singolo gestore per linea di interrupt; detto ciò, un kernel potrebbe scegliere, ad esempio, di fornire un gestore di interrupt che esegue diverse altre routine di interrupt, o di fornire un singolo gestore che copra alcuni o tutti gli interrupt. Linux fa queste cose fornendo un gestore di interrupt generico che determina quale linea di interruzione è stata chiamata e trova il gestore downstream appropriato da chiamare.


1
quindi il processore utilizza la linea di interruzione come indice dell'IDT, inserisce la voce nel PC e inizia l'esecuzione? ma non esiste una funzione generica che viene eseguita prima di tutti i gestori di interrupt? per Linux sarebbe do_IRQ (). è questa la funzione a cui punta ogni voce IDT, indipendentemente dalla linea di interruzione?
Philipp Murry,

@PhilippMurry sì. Il kernel usa quindi il proprio set di gestori di interrupt (di cui può essercene più di uno per riga) per gestire effettivamente l'interrupt, prima di tornare al codice precedentemente eseguito.
Adam Maras,

ok, quindi in realtà ci sono due tipi di gestori di interrupt: quelli che il processore chiama (sempre do_IRQ ()) e quelli che il kernel chiama (quello che ho registrato tramite request_irq ()). potresti forse aggiungere questo alla tua risposta? penso che lo accetterò :) grazie mille
Philipp Murry,

1
@PhilippMurry Non sono un esperto di come Linux gestisce gli interrupt (e di come gli sviluppatori del kernel attingono a quel sistema) ma ho aggiunto alcune ulteriori informazioni in un senso più ampio su come i kernel possono avere la propria gestione ISR.
Adam Maras,

12

Sì, esiste un posto predefinito che contiene l'indirizzo del codice a cui passare: un vettore di interrupt . A seconda del processore, questa può essere una posizione specifica nella memoria fisica (8088), una posizione specifica nella memoria virtuale, un registro del processore, una posizione nella memoria indicata da un registro (ARM, 386), ...

I dettagli variano su processori diversi, ma i principali elementi comuni alla gestione di un interrupt nel processore sono:

  • La maschera si interrompe (in modo che ogni interruzione successiva dovrà attendere).
  • Impostare la modalità del processore su kernel o modalità di interruzione (se il processore ha tali modalità).
  • Salvare il valore del contatore del programma in un luogo noto (registro o memoria).
  • Possibilmente salvare il valore di altri registri o passare da un banco di registri all'altro).
  • Eseguire le istruzioni successive (al nuovo valore del PC).

1

Le altre due risposte (al momento della stesura) parlano di interruzioni e IDT. Questo è corretto, tuttavia, su una moderna CPU Intel-esque, non ci sono meno di tre modi per chiamare un kernel.

Metodo n. 1: interrompe.

Questo è spiegato sopra. Si imposta una voce nella tabella del descrittore di interrupt / vettore di interrupt, quindi si esegue un interrupt di software per accedere al kernel.

Il vantaggio principale di questo metodo è che un kernel tipico deve essere in grado di gestire comunque gli interrupt e funziona su hardware arcaico.

Metodo n. 2: chiama gate.

Un gate di chiamata è un tipo speciale di selettore di segmento. Il target della chiamata deve essere caricato nella tabella dei descrittori di segmento globale o locale (rispettivamente GDT e LDT). Se esegui quindi un'istruzione di chiamata remota utilizzando il gate di chiamata come segmento (l'offset della chiamata viene ignorato), ciò consente di chiamare un codice più privilegiato. Le porte di chiamata sono estremamente flessibili; l'architettura IA-32 ha quattro livelli di privilegi e call gates ti consente di chiamare qualsiasi livello.

Non credo che Linux abbia mai usato le porte di chiamata, ma lo ha fatto Windows 95. I servizi del kernel Win95 ( krnl386.exee kernel.dll) erano effettivamente eseguiti in modalità utente (anello 3). Il livello di privilegio più alto (anello 0) è stato utilizzato solo per i driver e un microkernel che ha eseguito solo la commutazione di processo. La chiamata ai driver è stata effettuata utilizzando le porte di chiamata. Ciò ha consentito al codice legacy a 16 bit (di cui ce n'era molto!) Di utilizzare i driver Win95 semplicemente usando una chiamata remota standard, proprio come facevano sempre.

La protezione inadeguata della tabella dei descrittori globali è stata la causa di numerosi exploit di Windows 95, che sono riusciti a installare le proprie porte di chiamata scrivendo sulla memoria.

Metodo n. 3: SYSCALL / SYSRET e SYSENTER / SYSEXIT

Queste sono due serie di istruzioni, inventate indipendentemente da AMD e Intel, ma essenzialmente fanno la stessa cosa. SYSCALL / SYSRET è arrivato per primo ed era solo AMD, SYSENTER / SYSEXIT era Intel, ma AMD lo implementa ora. Quindi descriverò SYSENTER / SYSEXIT.

A differenza delle porte di chiamata, SYSENTER può essere utilizzato solo per trasferire all'anello 0 e può trasferire solo in una posizione. Tuttavia, ha il vantaggio di avere una latenza estremamente bassa perché, diversamente da una chiamata o interruzione, non tocca lo stack.

Il percorso di trasferimento viene impostato utilizzando tre registri specifici del modello: uno per le informazioni sul segmento e uno per il puntatore dell'istruzione e il puntatore dello stack del codice del kernel. Poiché nulla viene "inserito" nello stack, il codice della modalità utente è responsabile del dire al kernel dove tornare passando il puntatore dell'istruzione return e il puntatore dello stack nei registri. Il kernel è responsabile del ripristino del puntatore dello stack e l'istruzione SYSEXIT ripristina il puntatore dell'istruzione.

Ulteriori informazioni sulle istruzioni SYSENTER e SYSEXIT.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.