Ogni regione nel diagramma è un segmento?
Questi sono 2 usi quasi totalmente diversi della parola "segmento"
- segmentazione x86 / registri di segmenti: i moderni sistemi operativi x86 usano un modello di memoria piatta in cui tutti i segmenti hanno la stessa base = 0 e limite = max in modalità 32-bit, lo stesso che l'hardware applica a quello in modalità 64-bit , rendendo la segmentazione un po 'vestigiale . (Ad eccezione di FS o GS, utilizzato per l'archiviazione locale thread anche in modalità 64 bit.)
- Linker / sezioni / segmenti del caricatore di programmi. ( Qual è la differenza di sezione e segmento nel formato di file ELF )
Gli usi hanno un'origine comune: se si stesse utilizzando un modello di memoria segmentata (soprattutto senza memoria virtuale di paging), si potrebbe avere i dati e gli indirizzi BSS essere relativo alla base DS segmento di stack rispetto alla base SS, e il codice relativo alla Indirizzo di base CS.
Pertanto, è possibile caricare più programmi diversi su indirizzi lineari diversi o addirittura spostarli dopo l'avvio, senza modificare gli offset a 16 o 32 bit rispetto alle basi del segmento.
Ma poi devi sapere a quale segmento è relativo un puntatore, quindi hai "puntatori lontani" e così via. (I programmi x86 a 16 bit reali spesso non avevano bisogno di accedere al loro codice come dati, quindi potevano usare un segmento di codice 64k da qualche parte, e forse un altro blocco 64k con DS = SS, con lo stack che scendeva da offset elevati e i dati a in fondo. O un minuscolo modello di codice con tutte le basi dei segmenti uguali).
In che modo la segmentazione x86 interagisce con il paging
Il mapping degli indirizzi in modalità 32/64 bit è:
- segment: offset (base del segmento implicita dal registro che tiene l'offset o sovrascritta con un prefisso di istruzione)
- Indirizzo virtuale lineare a 32 o 64 bit = base + offset. (In un modello di memoria piatta come quello utilizzato da Linux, anche puntatori / offset = indirizzi lineari. Tranne quando si accede a TLS rispetto a FS o GS.)
le tabelle delle pagine (memorizzate nella cache da TLB) mappano l'indirizzo lineare a 32 (modalità legacy), 36 (PAE legacy) o 52 bit (x86-64). ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
Questo passaggio è facoltativo: il paging deve essere abilitato durante l'avvio impostando un bit in un registro di controllo. Senza paginazione, gli indirizzi lineari sono indirizzi fisici.
Si noti che la segmentazione non consente di utilizzare più di 32 o 64 bit di spazio di indirizzi virtuali in un singolo processo (o thread) , poiché lo spazio di indirizzi piatto (lineare) in cui tutto è mappato ha solo lo stesso numero di bit degli offset stessi. (Questo non era il caso di x86 a 16 bit, in cui la segmentazione era effettivamente utile per utilizzare più di 64k di memoria con registri e offset per lo più a 16 bit.)
La CPU memorizza nella cache i descrittori di segmenti caricati dal GDT (o LDT), inclusa la base del segmento. Quando si dereferenzia un puntatore, a seconda del registro in cui si trova, il valore predefinito è DS o SS come segmento. Il valore del registro (puntatore) viene trattato come un offset dalla base del segmento.
Poiché la base del segmento è normalmente pari a zero, le CPU lo fanno in casi speciali. O da un altro punto di vista, se si fare avere una base segmento diverso da zero, i carichi hanno la latenza in più perché il caso (normale) "speciale" di bypassare aggiungere l'indirizzo di base non si applica.
Come Linux imposta i registri di segmenti x86:
La base e il limite di CS / DS / ES / SS sono tutti 0 / -1 in modalità 32 e 64 bit. Questo è chiamato modello di memoria piatta perché tutti i puntatori puntano nello stesso spazio degli indirizzi.
(Gli architetti della CPU AMD hanno sterilizzato la segmentazione applicando un modello di memoria piatta per la modalità a 64 bit perché i sistemi operativi tradizionali non lo utilizzavano, ad eccezione della protezione no-exec fornita in modo molto migliore tramite il paging con PAE o x86- 64 formato tabella di pagine.)
TLS (Thread Local Storage): FS e GS non sono fissi alla base = 0 in modalità lunga. (Erano nuovi con 386 e non usati implicitamente da alcuna istruzione, nemmeno dalle rep
istruzioni -string che usano ES). x86-64 Linux imposta l'indirizzo di base FS per ciascun thread sull'indirizzo del blocco TLS.
ad esempio mov eax, [fs: 16]
carica un valore a 32 bit da 16 byte nel blocco TLS per questo thread.
il descrittore di segmento CS sceglie la modalità in cui si trova la CPU (modalità protetta 16/32/64 bit / modalità lunga). Linux utilizza una singola voce GDT per tutti i processi dello spazio utente a 64 bit e un'altra voce GDT per tutti i processi dello spazio utente a 32 bit. (Affinché la CPU funzioni correttamente, anche DS / ES deve essere impostato su voci valide, così come SS). Seleziona anche il livello di privilegio (kernel (anello 0) rispetto all'utente (anello 3)), quindi anche quando ritorna allo spazio utente a 64 bit, il kernel deve comunque provvedere alla modifica di CS, usando iret
o sysret
invece di un normale istruzione jump o ret.
In x86-64, il syscall
punto di ingresso usa swapgs
per girare GS dal GS dello spazio utente a quello del kernel, che usa per trovare lo stack del kernel per questo thread. (Un caso specializzato di archiviazione locale thread). L' syscall
istruzione non modifica il puntatore dello stack in modo che punti allo stack del kernel; punta ancora allo stack dell'utente quando il kernel raggiunge il punto di ingresso 1 .
DS / ES / SS devono anche essere impostati su descrittori di segmento validi affinché la CPU funzioni in modalità protetta / modalità lunga, anche se la base / limite di tali descrittori viene ignorata in modalità lunga.
Quindi, in pratica, la segmentazione x86 viene utilizzata per TLS e per le cose obbligatorie per osdev x86 che l'hardware richiede di fare.
Nota a piè di pagina 1: Storia divertente: ci sono archivi di mailing list di messaggi tra sviluppatori del kernel e architetti AMD risalenti a un paio di anni prima che il silicio AMD64 fosse rilasciato, con conseguenti modifiche alla progettazione e syscall
quindi utilizzabili. Vedi i collegamenti in questa risposta per i dettagli.