Perché (non) segmentazione?


42

Sto studiando i sistemi operativi e l'architettura x86, e mentre leggevo della segmentazione e del paging, ero naturalmente curioso di sapere come i moderni SO gestiscono la gestione della memoria. Da quello che ho trovato Linux e la maggior parte degli altri sistemi operativi essenzialmente evita la segmentazione a favore del paging. Alcuni dei motivi che ho riscontrato sono stati la semplicità e la portabilità.

Quali usi pratici ci sono per la segmentazione (x86 o altro) e vedremo mai sistemi operativi robusti utilizzarlo o continueranno a favorire un sistema di paging.

Ora so che questa è una domanda carica ma sono curioso di sapere come gestire la segmentazione con i sistemi operativi di nuova concezione. Ha molto senso favorire il paging che nessuno prenderà in considerazione un approccio più "segmentato"? Se è così, perché?


E quando dico segmentazione 'shun', intendo che Linux lo usa solo per quanto deve. Solo 4 segmenti per segmenti di codice / dati utente e kernel. Durante la lettura della documentazione Intel ho appena avuto la sensazione che la segmentazione fosse progettata pensando a soluzioni più robuste. Poi di nuovo mi è stato detto in molte occasioni quanto possa essere complicata la x86.


Ho trovato questo aneddoto interessante dopo essere stato collegato all '"annuncio" originale di Linux Torvald per Linux. Lo ha detto qualche post dopo:

Semplicemente, direi che il porting è impossibile. È principalmente in C, ma la maggior parte delle persone non chiamerebbe ciò che scrivo C. Utilizza ogni caratteristica immaginabile del 386 che ho potuto trovare, poiché era anche un progetto per insegnarmi il 386. Come già accennato, utilizza un MMU , sia per il paging (non ancora su disco) sia per la segmentazione. È la segmentazione che lo rende DAVVERO 386 dipendente (ogni attività ha un segmento di 64 Mb per codice e dati - max 64 attività in 4 GB. Chiunque abbia bisogno di più di 64 Mb / attività - cookie difficili).

Immagino che la mia sperimentazione con x86 mi abbia portato a porre questa domanda. Linus non aveva StackOverflow, quindi l'ha appena implementato per provarlo.


Che libro hai letto?

1
Sto leggendo un numero di libri. Ho iniziato a chiedermelo mentre leggevo il manuale di programmazione dei sistemi Intel (vol 3), ma ho letto un po 'di informazioni sulla gestione della memoria di Linux in "Comprensione del kernel Linux" e altre fonti online.
Mr. Shickadance,

In particolare stavo leggendo la sezione sulle tabelle dei descrittori locali ed ero curioso di sapere come i sistemi operativi li usavano.
Mr. Shickadance,

1
OpenBSD combina segmentazione x86 e paging per ottenere la simulazione di bit NX (funzione di sicurezza che impedisce l'esecuzione di pagine di dati). Può essere usato anche PaX.

Non so quasi nulla sull'argomento. Ho appena inserito una domanda di ricerca per vedere le risposte ai reclami su tutti i sistemi operativi attualmente utilizzati. Osservando i reclami, la maggior parte delle persone usa il PC e ora i tablet per alcune attività specifiche. Quindi, perché non allocare più utilizzo della memoria per fare quelle attività più velocemente invece di dare tutta la merda periferica che sta eseguendo l'accesso ad essa.

Risposte:


31

Con la segmentazione sarebbe, ad esempio, possibile mettere ciascun oggetto allocato dinamicamente (malloc) nel proprio segmento di memoria. L'hardware controlla automaticamente i limiti di segmento e l'intera classe di bug di sicurezza (sovraccarichi del buffer) verrebbe eliminata.

Inoltre, poiché tutti gli offset di segmento iniziano da zero, tutto il codice compilato sarebbe automaticamente indipendente dalla posizione. Chiamare in un'altra DLL si riduce a una chiamata remota con offset costante (a seconda della funzione chiamata). Ciò semplificherebbe notevolmente i linker e i caricatori.

Con 4 anelli di protezione, è possibile escogitare un controllo degli accessi più dettagliato (con il paging hai solo 2 livelli di protezione: utente e supervisore) e kernel del sistema operativo più robusti. Ad esempio, solo l'anello 0 ha pieno accesso all'hardware. Separando il kernel del sistema operativo principale e i driver di dispositivo negli anelli 0 e 1, è possibile creare un sistema operativo microkernel più robusto e molto veloce in cui la maggior parte dei controlli di accesso rilevanti verrebbero eseguiti da HW. (I driver di dispositivo potrebbero accedere all'hardware tramite bitmap di accesso I / O nel TSS.)

Tuttavia .. x86 è un po 'limitato. Ha solo 4 registri di segmenti di dati "liberi"; ricaricarli è piuttosto costoso ed è possibile accedere contemporaneamente solo a 8192 segmenti. (Supponendo che si desideri massimizzare il numero di oggetti accessibili, quindi GDT contiene solo descrittori di sistema e descrittori LDT.)

Ora, con la modalità a 64 bit, la segmentazione viene descritta come "legacy" e i controlli dei limiti hardware vengono eseguiti solo in circostanze limitate. IMHO, un GRANDE errore. In realtà non biasimo Intel, biasimo principalmente gli sviluppatori, la maggior parte dei quali pensava che la segmentazione fosse "troppo complicata" e desiderava uno spazio di indirizzamento piatto. Incolpo anche gli scrittori di sistemi operativi a cui mancava l'immaginazione per mettere a frutto la segmentazione. (AFAIK, OS / 2 era l'unico sistema operativo a sfruttare appieno le funzionalità di segmentazione.)


1
Questo è il motivo per cui ho lasciato questo aperto. Ci saranno sicuramente alcune
interpretazioni

1
@zvrba: che spiegazione superba !!! Grazie per quello. Ora ho un dubbio: non pensi che INTEL avrebbe potuto vincere il grande premio rendendo i segmenti non sovrapposti e 4 GB in grado di aiutare con il paging? Voglio dire, come ho capito, "segmentazione con paging" è in grado di indirizzare un massimo di 4 GB di spazio di memoria virtuale. E questo è "noccioline" !!! Immagina di essere in grado di avere un codice, stack, segmenti di dati grandi quanto 4 GB ciascuno e non sovrapposti o sovrapposti di cui avresti bisogno! E quello sarebbe stato un grande successo in quel momento, senza dover richiedere un'architettura a 64 bit come oggi.
fante,

1
Fantastica spiegazione del perché la segmentazione è buona. È un peccato terribile che sia caduto sul ciglio della strada. Ecco un'elaborazione con maggiori dettagli per coloro che sono curiosi di saperne di più.
PIL2,

1
Non c'è da meravigliarsi che mi piaccia OS / 2! Che triste perdita di una tecnologia davvero preziosa grazie all'ignoranza e al marketing.
illuminerà il

Chiunque pensi che la segmentazione sia una buona idea non deve essere abbastanza grande da ricordare quanto sia terribile la segmentazione. È orribile. Praticamente tutto il codice C mai scritto prevede uno spazio di indirizzi piatto. È conveniente essere in grado di guardare un puntatore e vedere solo il suo indirizzo, non è necessario scavare nella base del segmento, supponendo che sia anche possibile, che non è in segmentazione in modalità protetta x86, a meno che il kernel non ti consenta di vedere in qualche modo, molto probabilmente con una chiamata di sistema molto costosa. Lo scambio non è possibile con i segmenti, a meno che non si sostituiscano interi segmenti. Il paging è di gran lunga superiore.
doug65536

25

La risposta breve è che la segmentazione è un hack, utilizzato per fare in modo che un processore con una capacità limitata di indirizzare la memoria superi tali limiti.

Nel caso dell'8086, c'erano 20 linee di indirizzo sul chip, il che significa che poteva accedere fisicamente a 1 Mb di memoria. Tuttavia, l'architettura interna era basata sull'indirizzamento a 16 bit, probabilmente a causa del desiderio di mantenere la coerenza con l'8080. Quindi il set di istruzioni includeva registri di segmento che sarebbero stati combinati con gli indici a 16 bit per consentire l'indirizzamento dell'intero 1 Mb di memoria . L'80286 ha esteso questo modello con un vero MMU, per supportare la protezione basata sul segmento e l'indirizzamento di più memoria (iirc, 16Mb).

Nel caso del PDP-11, i modelli successivi del processore hanno fornito una segmentazione in spazi di istruzioni e dati, sempre per supportare i limiti di uno spazio di indirizzi a 16 bit.

Il problema con la segmentazione è semplice: il tuo programma deve aggirare esplicitamente i limiti dell'architettura. Nel caso dell'8086, ciò significava che il più grande blocco contiguo di memoria a cui si poteva accedere era 64k. se avessi bisogno di accedere a più di quello, dovresti cambiare i tuoi registri di segmento. Il che significava, per un programmatore C, che dovevi dire al compilatore C che tipo di puntatori avrebbe dovuto generare.

È stato molto più semplice programmare l'MC68k, che aveva un'architettura interna a 32 bit e uno spazio di indirizzamento fisico a 24 bit.


5
Ok, tutto ha un senso. Tuttavia, leggendo i documenti Intel si sarebbe propensi a pensare che i segmenti potessero effettivamente essere utilizzati per una maggiore protezione a livello hardware contro i bug del programma. In particolare la sezione 3.2.3 della Guida alla programmazione dei sistemi - ci sono vantaggi per il modello multi-segmento? Sarebbe corretto dire che Linux usa il modello flat protetto? (sezione 3.2.2)
Mr. Shickadance,

3
È passato molto tempo da quando ho prestato attenzione ai dettagli dell'architettura di memoria Intel, ma non credo che l'architettura segmentata fornirebbe una protezione hardware maggiore. L'unica vera protezione che una MMU può offrirti è quella di separare codice e dati, evitando attacchi di sovraccarico del buffer. E credo che sia controllabile senza segmenti, tramite attributi a livello di pagina. Potresti teoricamente limitare l'accesso agli oggetti creando un segmento separato per ciascuno, ma non credo sia ragionevole.

1
Grazie, hai riportato tutti i ricordi repressi di elaborazione delle immagini sulla memoria segmentata - questo significherà più terapia!
Martin Beckett,

10
Hai frainteso completamente la segmentazione. Nel 8086 potrebbe essere stato un trucco; 80286 introdusse la modalità protetta dove era cruciale per la protezione; nell'80386 è stato ulteriormente ampliato e i segmenti possono essere più grandi di 64 kB, sempre con il vantaggio dei controlli hardware. (A proposito, 80286 NON aveva un MMU.)
zvrba,

2
Nel 1985, quando fu introdotto il 386, uno spazio di indirizzamento di 4 GiB era considerato enorme. Ricorda che un hard disk da 20 MiB era piuttosto grande al momento, e non era ancora del tutto insolito che i sistemi venissero forniti solo con unità floppy. L'FDD da 3,5 "è stato introdotto nel 1983, sfoggiando una capacità formattata di ben 360 KB. (1.44 MB 3.5" FDDs è diventato disponibile nel 1986.) Nell'errore sperimentale, tutti allora pensavano a uno spazio di indirizzi a 32 bit come pensiamo ora di 64 bit: fisicamente accessibile, ma così grande da essere praticamente infinito.
un CVn

15

Per 80x86 ci sono 4 opzioni: "niente", solo segmentazione, solo paginazione e sia segmentazione che paginazione.

Per "niente" (nessuna segmentazione o paginazione) non si ottiene un modo semplice per proteggere un processo da se stesso, un modo semplice per proteggere i processi l'uno dall'altro, un modo per gestire cose come la frammentazione dello spazio degli indirizzi fisici, nessun modo per evitare la posizione codice indipendente, ecc. Nonostante tutti questi problemi potrebbe (in teoria) essere utile in alcune situazioni (ad esempio un dispositivo incorporato che esegue solo un'applicazione; o forse qualcosa che utilizza JIT e virtualizza comunque tutto).

Solo per segmentazione; risolve quasi completamente il problema "proteggere un processo da se stesso", ma sono necessarie molte soluzioni per renderlo utilizzabile quando un processo vuole utilizzare più di 8192 segmenti (presupponendo un LDT per processo), il che lo rende per lo più interrotto. Quasi risolvi il problema "proteggi i processi gli uni dagli altri"; ma diversi software in esecuzione allo stesso livello di privilegio possono caricare / utilizzare i rispettivi segmenti (ci sono modi per aggirare questo problema - modificando le voci GDT durante i trasferimenti di controllo e / o usando LDT). Risolve anche principalmente il problema del "codice indipendente dalla posizione" (può causare un problema "codice dipendente dal segmento" ma è molto meno significativo). Non fa nulla per il problema della "frammentazione dello spazio degli indirizzi fisici".

Solo per cercapersone; non risolve molto il problema di "proteggere un processo da se stesso" (ma siamo onesti qui, questo è davvero solo un problema per il debug / test del codice scritto in linguaggi non sicuri e ci sono comunque strumenti molto più potenti come valgrind). Risolve completamente il problema "proteggi i processi gli uni dagli altri", risolve completamente il problema del "codice indipendente dalla posizione" e risolve completamente il problema della "frammentazione dello spazio degli indirizzi fisici". Come bonus aggiuntivo si aprono alcune tecniche molto potenti che non sono affatto così pratiche senza paging; tra cui cose come "copia su scrittura", file mappati in memoria, gestione efficiente dello spazio di scambio, ecc.

Ora penseresti che l'uso sia della segmentazione che del paging ti darebbe i vantaggi di entrambi; e in teoria può, tranne per il fatto che l'unico vantaggio che si ottiene dalla segmentazione (che non viene fatto meglio tramite il paging) è una soluzione al problema "proteggere un processo da se stesso" di cui a nessuno importa davvero. In pratica, ciò che ottieni è la complessità di entrambi e il sovraccarico di entrambi, per un beneficio molto scarso.

Questo è il motivo per cui quasi tutti i sistemi operativi progettati per 80x86 non usano la segmentazione per la gestione della memoria (lo usano per cose come la CPU e l'archiviazione per attività, ma ciò è principalmente solo per comodità per evitare di consumare un registro di uso generale più utile per questi cose).

Naturalmente i produttori di CPU non sono sciocchi: non spenderanno tempo e denaro per ottimizzare qualcosa che sanno che nessuno usa (ottimizzeranno qualcosa che quasi tutti usano invece). Per questo motivo i produttori di CPU non ottimizzano la segmentazione, il che rende la segmentazione più lenta di quanto potrebbe essere, il che fa sì che gli sviluppatori del sistema operativo vogliano evitarlo ancora di più. Per lo più hanno mantenuto la segmentazione solo per compatibilità con le versioni precedenti (che è importante).

Alla fine, AMD ha progettato la modalità lunga. Non esisteva un codice a 64 bit vecchio / esistente di cui preoccuparsi, quindi (per il codice a 64 bit) AMD si è sbarazzato di tutta la segmentazione possibile. Ciò ha fornito agli sviluppatori del sistema operativo un altro motivo (nessun modo semplice per eseguire il porting del codice progettato per la segmentazione a 64 bit) per continuare a evitare la segmentazione.


13

Sono piuttosto sbalordito dal fatto che, da quando è stata posta questa domanda, nessuno ha menzionato le origini delle architetture di memoria segmentate e il vero potere che possono permettersi.

Il sistema originale che ha inventato, o perfezionato in forma utile, tutte le funzionalità che circondano la progettazione e l'uso di sistemi di memoria virtuale paginata segmentata (insieme a file system multi-elaborazione simmetrici e gerarchici) era Multics (e vedi anche il sito Multicians ). La memoria segmentata consente a Multics di offrire all'utente la visione che tutto è nella memoria (virtuale) e consente il massimo livello di condivisione di tuttoin forma diretta (ovvero indirizzabile direttamente in memoria). Il filesystem diventa semplicemente una mappa per tutti i segmenti in memoria. Se utilizzata correttamente in modo sistematico (come in Multics), la memoria segmentata libera l'utente dai numerosi oneri della gestione dell'archiviazione secondaria, della condivisione dei dati e delle comunicazioni tra processi. Altre risposte hanno fatto alcune affermazioni ondulate a mano che la memoria segmentata è più difficile da usare, ma questo non è semplicemente vero, e Multics lo ha dimostrato con clamoroso successo decenni fa.

Intel ha creato una versione ridotta della memoria segmentata 80286 che, sebbene sia abbastanza potente, i suoi limiti ne hanno impedito l'utilizzo per qualcosa di veramente utile. L'80386 ha migliorato questi limiti, ma le forze di mercato all'epoca hanno praticamente impedito il successo di qualsiasi sistema che potesse davvero trarre vantaggio da questi miglioramenti. Negli anni da quando sembra che troppe persone abbiano imparato a ignorare le lezioni del passato.

Intel ha anche provato all'inizio a costruire un super-micro più capace chiamato iAPX 432 che all'epoca avrebbe superato di gran lunga qualsiasi altra cosa, e aveva un'architettura di memoria segmentata e altre funzionalità fortemente orientate alla programmazione orientata agli oggetti. L'implementazione originale è stata troppo lenta e non sono stati fatti ulteriori tentativi per risolverlo.

Una discussione più dettagliata di come Multics ha usato la segmentazione e il paging può essere trovata nel documento di Paul Green Multics Virtual Memory - Tutorial and Reflections


1
Grandi informazioni e argomenti superbi. Grazie per i collegamenti, non hanno prezzo !!!
fante,

1
Grazie per il collegamento a Multics e per la risposta molto istruttiva! Chiaramente la segmentazione era superiore in molti modi a ciò che facciamo ora.
PIL2,

1
La tua risposta è un vero gioiello grezzo. Grazie mille per aver condiviso queste intuizioni che abbiamo perso. Mi fa venire il desiderio di vedere un ritorno alla segmentazione attraverso lo sviluppo di un sistema operativo adeguato che potrebbe richiedere il perfezionamento dell'hardware. Davvero così tanti problemi potrebbero essere migliorati con questo approccio! Sembra addirittura che potremmo ottenere veri e propri linguaggi OOP a livelli di prestazioni molto più elevati e al bare metal con segmentazione.
illuminerà il

6

La segmentazione era un trucco / soluzione alternativa per consentire a un processore a 16 bit di indirizzare fino a 1 MB di memoria - normalmente sarebbero stati accessibili solo 64 KB di memoria.

Quando sono arrivati ​​i processori a 32 bit, è possibile indirizzare fino a 4 GB di memoria con un modello di memoria piatta e non è più necessario segmentare: i registri di segmento sono stati riutilizzati come selettori per GDT / paging in modalità protetta (anche se è possibile modalità protetta a 16 bit).

Anche una modalità di memoria piatta è molto più conveniente per i compilatori: puoi scrivere programmi segmentati a 16 bit in C , ma è un po 'ingombrante. Un modello di memoria piatta rende tutto più semplice.


C'è molto da dire sulla "protezione" fornita dalla segmentazione quando invece possiamo semplicemente usare il paging?
Mr. Shickadance,

1
@Sig. La segmentazione Shickadance non fornisce alcun tipo di protezione della memoria - Per la protezione della memoria è necessaria la modalità protetta in cui è possibile proteggere la memoria tramite GDT o paging.
Giustino,

5

Alcune architetture (come ARM) non supportano affatto i segmenti di memoria. Se Linux fosse stato dipendente dalla sorgente da segmenti, non avrebbe potuto essere trasferito su quelle architetture molto facilmente.

Guardando l'immagine più ampia, il fallimento dei segmenti di memoria ha a che fare con la continua popolarità dell'aritmetica C e del puntatore. Lo sviluppo C è più pratico su un'architettura con memoria piatta; e se vuoi una memoria piatta, scegli il paging della memoria.

C'è stato un periodo intorno alla svolta degli anni '80 quando Intel, come organizzazione, stava anticipando la popolarità futura di Ada e di altri linguaggi di programmazione di livello superiore. Questo è fondamentalmente da dove provengono alcuni dei loro errori più spettacolari, come la terribile segmentazione della memoria APX432 e 286. Con il 386 capitolavano a programmatori di memoria flat; sono stati aggiunti il ​​paging e un TLB e i segmenti sono stati resi ridimensionabili a 4 GB. E poi AMD ha sostanzialmente rimosso i segmenti con x86_64 rendendo il reg di base un dont-care / implicito-0 (tranne per fs? Per TLS penso?)

Detto questo, i vantaggi dei segmenti di memoria sono evidenti: cambiare gli spazi degli indirizzi senza dover ripopolare un TLB. Forse un giorno qualcuno creerà una CPU competitiva dalle prestazioni che supporta la segmentazione, possiamo programmare un sistema operativo orientato alla segmentazione per esso e i programmatori possono creare Ada / Pascal / D / Rust / un'altra lingua che non richiede piatta -memoria programmi per questo.


1

La segmentazione è un enorme onere per gli sviluppatori di applicazioni. È qui che è arrivata la grande spinta per eliminare la segmentazione.

È interessante notare che spesso mi chiedo quanto potrebbe essere migliore l'i86 se Intel eliminasse tutto il supporto legacy per queste vecchie modalità. Qui meglio implicherebbe una potenza inferiore e un funzionamento forse più veloce.

Immagino che si possa sostenere che Intel abbia inacidito il latte con segmenti a 16 bit, portando a una sorta di rivolta degli sviluppatori. Ammettiamolo, uno spazio di indirizzamento a 64k non è niente, specialmente quando si guarda un'app moderna. Alla fine hanno dovuto fare qualcosa perché la concorrenza poteva e ha commercializzato efficacemente contro i problemi di spazio degli indirizzi di i86.


1

La segmentazione porta a traduzioni e scambi di pagine più lenti

Per questi motivi, la segmentazione è stata in gran parte abbandonata su x86-64.

La principale differenza tra loro è che:

  • il paging divide la memoria in blocchi di dimensioni fisse
  • la segmentazione consente larghezze diverse per ogni blocco

Mentre può sembrare più intelligente avere larghezze di segmento configurabili, quando si aumenta la dimensione della memoria per un processo, la frammentazione è inevitabile, ad esempio:

|   | process 1 |       | process 2 |                        |
     -----------         -----------
0                                                            max

alla fine diventerà man mano che il processo 1 cresce:

|   | process 1        || process 2 |                        |
     ------------------  -------------
0                                                            max

fino a quando una divisione è inevitabile:

|   | process 1 part 1 || process 2 |   | process 1 part 2 | |
     ------------------  -----------     ------------------
0                                                            max

A questo punto:

  • l'unico modo per tradurre le pagine è eseguire ricerche binarie su tutte le pagine del processo 1, che accetta un registro inaccettabile (n)
  • uno scambio fuori processo 1 parte 1 potrebbe essere enorme poiché quel segmento potrebbe essere enorme

Con pagine di dimensioni fisse tuttavia:

  • ogni traduzione a 32 bit legge solo 2 memorie: cammina la directory e la tabella delle pagine
  • ogni scambio è un 4KiB accettabile

Pezzi di memoria di dimensioni fisse sono semplicemente più gestibili e hanno dominato l'attuale progettazione del sistema operativo.

Vedi anche: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

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.