Solución:
Versión de esta respuesta con un buen TOC y más contenido.
Corregiré cualquier error informado. Si desea hacer grandes modificaciones o agregar un aspecto faltante, hágalo con sus propias respuestas para obtener una reputación bien merecida. Las ediciones menores se pueden combinar directamente en.
Código de muestra
Ejemplo mínimo: https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S
Como todo lo demás en programación, la única forma de entender esto realmente es jugar con ejemplos mínimos.
Lo que hace que este sea un tema “difícil” es que el ejemplo mínimo es grande porque necesita crear su propio sistema operativo pequeño.
Manual de Intel
Aunque es imposible de entender sin tener ejemplos en mente, intente familiarizarse con los manuales lo antes posible.
Intel describe la paginación en el Manual de Intel Volumen 3 Guía de programación del sistema – 325384-056ES Septiembre de 2015 Capítulo 4 “Paginación”.
Es especialmente interesante la Figura 4-4 “Formatos de CR3 y entradas de estructura de paginación con paginación de 32 bits”, que muestra las estructuras de datos clave.
MMU
La paginación la realiza la unidad de gestión de memoria (MMU) de la CPU. Como muchos otros (p. Ej., Coprocesador x87, APIC), este solía ser por chip separado en los primeros días, que luego se integró en la CPU. Pero el término todavía se usa.
Hechos generales
Las direcciones lógicas son las direcciones de memoria utilizadas en el código de la tierra del usuario “normal” (por ejemplo, el contenido de rsi
en mov eax, [rsi]
).
Primero, la segmentación las traduce en direcciones lineales y luego la paginación luego traduce las direcciones lineales en direcciones físicas.
(logical) ------------------> (linear) ------------> (physical)
segmentation paging
La mayoría de las veces, podemos pensar en las direcciones físicas como indexando celdas de memoria de hardware RAM reales, pero esto no es 100% cierto debido a:
- Regiones de E / S asignadas en memoria
- memoria multicanal
La localización solo está disponible en modo protegido. El uso de paginación en modo protegido es opcional. La paginación está activada si PG
un poco del cr0
El registro está configurado.
Paginación vs segmentación
Una diferencia importante entre la paginación y la segmentación es que:
- la paginación divide la RAM en trozos de igual tamaño llamados páginas
- la segmentación divide la memoria en trozos de tamaños arbitrarios
Esta es la principal ventaja de la paginación, ya que los trozos de igual tamaño hacen que las cosas sean más manejables.
La paginación se ha vuelto mucho más popular que el soporte para la segmentación se eliminó en x86-64 en modo de 64 bits, el modo de operación principal para el nuevo software, donde solo existe en modo de compatibilidad, que emula IA32.
Solicitud
La paginación se utiliza para implementar procesos de espacios de direcciones virtuales en el sistema operativo moderno. Con direcciones virtuales, el sistema operativo puede adaptarse a dos o más procesos simultáneos en una sola RAM de manera que:
- Ambos programas no necesitan saber nada sobre el otro.
- la memoria de ambos programas puede crecer y reducirse según sea necesario
- el cambio entre programas es muy rápido
- un programa nunca puede acceder a la memoria de otro proceso
Históricamente, la paginación vino después de la segmentación y la reemplazó en gran medida para la implementación de memoria virtual en sistemas operativos modernos como Linux, ya que es más fácil administrar los fragmentos de memoria de tamaño fijo de las páginas en lugar de los segmentos de longitud variable.
Implementación de hardware
Al igual que la segmentación en modo protegido (donde la modificación de un registro de segmento desencadena una carga desde el GDT o LDT), el hardware de paginación utiliza estructuras de datos en la memoria para hacer su trabajo (tablas de páginas, directorios de páginas, etc.).
El formato de esas estructuras de datos es fijo por el hardware, pero depende del sistema operativo configurar y administrar esas estructuras de datos en la RAM correctamente, y decirle al hardware dónde encontrarlas (a través de cr3
).
Algunas otras arquitecturas dejan la paginación casi por completo en manos del software, por lo que una TLB ejecuta una función proporcionada por el sistema operativo para recorrer las tablas de páginas e insertar la nueva asignación en la TLB. Esto deja que los formatos de tabla de página sean elegidos por el sistema operativo, pero hace que sea poco probable que el hardware pueda superponer los recorridos de página con la ejecución desordenada de otras instrucciones, como hace x86.
Ejemplo: esquema de paginación de un solo nivel simplificado
Este es un ejemplo de cómo funciona la paginación en un simplificado versión de la arquitectura x86 para implementar un espacio de memoria virtual.
Tablas de páginas
El sistema operativo podría proporcionarles las siguientes tablas de páginas:
Tabla de páginas dada al proceso 1 por el SO:
RAM location physical address present
----------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0x00000 1
PT1 + 2 * L 0x00003 1
PT1 + 3 * L 0
... ...
PT1 + 0xFFFFF * L 0x00005 1
Tabla de páginas dada al proceso 2 por el SO:
RAM location physical address present
----------------- ----------------- --------
PT2 + 0 * L 0x0000A 1
PT2 + 1 * L 0x0000B 1
PT2 + 2 * L 0
PT2 + 3 * L 0x00003 1
... ... ...
PT2 + 0xFFFFF * L 0x00004 1
Dónde:
-
PT1
yPT2
: posición inicial de la tabla 1 y 2 en RAM.Valores de muestra:
0x00000000
,0x12345678
etc.Es el sistema operativo el que decide esos valores.
-
L
: longitud de una entrada de tabla de páginas. -
present
: indica que la página está presente en la memoria.
Las tablas de páginas se encuentran en la RAM. Por ejemplo, podrían ubicarse como:
--------------> 0xFFFFFFFF
--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1
--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2
--------------> 0x0
Las ubicaciones iniciales en la RAM para ambas tablas de páginas son arbitrarias y están controladas por el sistema operativo. ¡Depende del sistema operativo asegurarse de que no se superpongan!
Cada proceso no puede tocar ninguna tabla de página directamente, aunque puede realizar solicitudes al sistema operativo que provoquen la modificación de las tablas de página, por ejemplo, solicitando segmentos de pila o montón más grandes.
Una página es un trozo de 4 KB (12 bits) y, dado que las direcciones tienen 32 bits, solo se requieren 20 bits (20 + 12 = 32, por lo tanto, 5 caracteres en notación hexadecimal) se requieren para identificar cada página. Este valor lo fija el hardware.
Entradas de la tabla de páginas
Una tabla de páginas es … ¡una tabla de entradas de tablas de páginas!
El formato exacto de las entradas de la tabla es fijo. por el hardware.
En este ejemplo simplificado, las entradas de la tabla de páginas contienen solo dos campos:
bits function
----- -----------------------------------------
20 physical address of the start of the page
1 present flag
en este ejemplo, los diseñadores de hardware podrían haber elegido L = 21
.
La mayoría de las entradas de la tabla de páginas reales tienen otros campos.
No sería práctico alinear las cosas a 21 bits, ya que la memoria es direccionable por bytes y no por bits. Por lo tanto, incluso en este caso solo se necesitan 21 bits, los diseñadores de hardware probablemente elegirían L = 32
para agilizar el acceso y reservar los bits restantes para su uso posterior. El valor real de L
en x86 es de 32 bits.
Traducción de direcciones en esquema de un solo nivel
Una vez que el sistema operativo ha configurado las tablas de páginas, se realiza la traducción de direcciones entre direcciones lineales y físicas. por el hardware.
Cuando el sistema operativo quiere activar el proceso 1, establece el cr3
para PT1
, el inicio de la tabla para el proceso uno.
Si el Proceso 1 quiere acceder a la dirección lineal 0x00000001
, la paginación hardware El circuito hace automáticamente lo siguiente para el sistema operativo:
-
divida la dirección lineal en dos partes:
| page (20 bits) | offset (12 bits) |
Entonces en este caso tendríamos:
- página = 0x00000
- desplazamiento = 0x001
-
Mire en la tabla de páginas 1 porque
cr3
lo señala. -
mirar entrada
0x00000
porque esa es la parte de la página.El hardware sabe que esta entrada está ubicada en la dirección RAM
PT1 + 0 * L = PT1
. -
ya que está presente, el acceso es válido
-
por la tabla de páginas, la ubicación del número de página
0x00000
Me senté0x00001 * 4K = 0x00001000
. -
para encontrar la dirección física final solo necesitamos agregar el desplazamiento:
00001 000 + 00000 001 ----------- 00001 001
porque
00001
es la dirección física de la página buscada en la mesa y001
es el desplazamiento.Como su nombre indica, el desplazamiento siempre se agrega simplemente a la dirección física de la página.
-
luego, el hardware obtiene la memoria en esa ubicación física.
De la misma manera, ocurrirían las siguientes traducciones para el proceso 1:
linear physical
--------- ---------
00000 002 00001 002
00000 003 00001 003
00000 FFF 00001 FFF
00001 000 00000 000
00001 001 00000 001
00001 FFF 00000 FFF
00002 000 00002 000
FFFFF 000 00005 000
Por ejemplo, al acceder a la dirección 00001000
, la parte de la página es 00001
el hardware sabe que la entrada de la tabla de páginas se encuentra en la dirección RAM: PT1 + 1 * L
(1
debido a la parte de la página), y ahí es donde lo buscará.
Cuando el sistema operativo quiere cambiar al proceso 2, todo lo que necesita hacer es hacer cr3
apunte a la página 2. ¡Es así de simple!
Ahora las siguientes traducciones ocurrirían para el proceso 2:
linear physical
--------- ---------
00000 002 00001 002
00000 003 00001 003
00000 FFF 00001 FFF
00001 000 00000 000
00001 001 00000 001
00001 FFF 00000 FFF
00003 000 00003 000
FFFFF 000 00004 000
La misma dirección lineal se traduce en diferentes direcciones físicas para diferentes procesos., dependiendo solo del valor dentro cr3
.
De esta manera, cada programa puede esperar que sus datos comiencen en 0
y terminar en FFFFFFFF
, sin preocuparse por direcciones físicas exactas.
Fallo de página
¿Qué pasa si el Proceso 1 intenta acceder a una dirección dentro de una página que no está presente?
El hardware notifica al software a través de una excepción de error de página.
Por lo general, es el sistema operativo el que debe registrar un controlador de excepciones para decidir qué se debe hacer.
Es posible que acceder a una página que no está en la mesa sea un error de programación:
int is[1];
is[2] = 1;
pero puede haber casos en los que sea aceptable, por ejemplo en Linux cuando:
-
el programa quiere aumentar su pila.
Simplemente intenta acceder a un determinado byte en un rango posible dado, y si el sistema operativo está satisfecho, agrega esa página al espacio de direcciones del proceso.
-
la página se cambió a disco.
El sistema operativo tendrá que hacer algo de trabajo detrás de los procesos para que la página vuelva a la RAM.
El sistema operativo puede descubrir que este es el caso basándose en el contenido del resto de la entrada de la tabla de páginas, ya que si la bandera actual está clara, las otras entradas de la entrada de la tabla de páginas se dejan completamente para que el sistema operativo haga lo que quiera.
En Linux, por ejemplo, cuando está presente = 0:
-
si todos los campos de la entrada de la tabla de páginas son 0, dirección inválida.
-
de lo contrario, la página se ha cambiado al disco y los valores reales de esos campos codifican la posición de la página en el disco.
-
En cualquier caso, el sistema operativo necesita saber qué dirección generó el error de página para poder solucionar el problema. Es por eso que los agradables desarrolladores de IA32 establecieron el valor de cr2
a esa dirección siempre que se produzca un error de página. El manejador de excepciones puede entonces mirar en cr2
para obtener la dirección.
Simplificaciones
Simplificaciones de la realidad que facilitan la comprensión de este ejemplo:
-
Todos los circuitos de paginación reales utilizan paginación de varios niveles para ahorrar espacio, pero esto mostró un esquema simple de un solo nivel.
-
Las tablas de página contenían solo dos campos: una dirección de 20 bits y una dirección de 1 bit. bandera actual.
Las tablas de páginas reales contienen un total de 12 campos y, por lo tanto, otras características que se han omitido.
Ejemplo: esquema de paginación de varios niveles
El problema con un esquema de paginación de un solo nivel es que ocuparía demasiada RAM: 4G / 4K = 1M entradas por proceso. Si cada entrada tiene 4 bytes de longitud, sería 4M por proceso, que es demasiado incluso para una computadora de escritorio: ps -A | wc -l
dice que estoy ejecutando 244 procesos en este momento, ¡por lo que tomaría alrededor de 1 GB de mi RAM!
Por esta razón, los desarrolladores de x86 decidieron utilizar un esquema de niveles múltiples que reduce el uso de RAM.
La desventaja de este sistema es que tiene un tiempo de acceso ligeramente mayor.
En el esquema de paginación simple de 3 niveles utilizado para procesadores de 32 bits sin PAE, los 32 bits de dirección se dividen de la siguiente manera:
| directory (10 bits) | table (10 bits) | offset (12 bits) |
Cada proceso debe tener uno y solo un directorio de páginas asociado, por lo que contendrá al menos 2^10 = 1K
entradas de directorio de páginas, mucho mejor que el mínimo de 1M requerido en un esquema de un solo nivel.
Las tablas de páginas solo se asignan según las necesidades del sistema operativo. Cada tabla de página tiene 2^10 = 1K
entradas del directorio de páginas
Los directorios de páginas contienen … entradas de directorio de páginas. Las entradas del directorio de páginas son las mismas que las entradas de la tabla de páginas, excepto que apuntan a direcciones RAM de tablas de páginas en lugar de direcciones físicas de tablas. Dado que esas direcciones tienen solo 20 bits de ancho, las tablas de páginas deben estar al comienzo de las páginas de 4 KB.
cr3
ahora apunta a la ubicación en la RAM del directorio de páginas del proceso actual en lugar de a las tablas de páginas.
Las entradas de las tablas de páginas no cambian en absoluto desde un esquema de un solo nivel.
Las tablas de páginas cambian de un esquema de un solo nivel porque:
- cada proceso puede tener hasta tablas de 1K páginas, una por entrada de directorio de página.
- cada tabla de página contiene exactamente 1K entradas en lugar de 1M entradas.
La razón para usar 10 bits en los dos primeros niveles (y no, digamos, 12 | 8 | 12
) es que cada entrada de la tabla de páginas tiene una longitud de 4 bytes. Entonces, las 2 ^ 10 entradas de los directorios de páginas y las tablas de páginas se ajustarán perfectamente a las páginas de 4 KB. Esto significa que es más rápido y sencillo asignar y desasignar páginas para ese propósito.
Traducción de direcciones en esquema multinivel
Directorio de páginas dado al proceso 1 por el sistema operativo:
RAM location physical address present
--------------- ----------------- --------
PD1 + 0 * L 0x10000 1
PD1 + 1 * L 0
PD1 + 2 * L 0x80000 1
PD1 + 3 * L 0
... ...
PD1 + 0x3FF * L 0
Tablas de páginas proporcionadas al proceso 1 por el sistema operativo en PT1 = 0x10000000
(0x10000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0
PT1 + 2 * L 0x0000D 1
... ...
PT1 + 0x3FF * L 0x00005 1
Tablas de páginas proporcionadas al proceso 1 por el sistema operativo en PT2 = 0x80000000
(0x80000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT2 + 0 * L 0x0000A 1
PT2 + 1 * L 0x0000C 1
PT2 + 2 * L 0
... ...
PT2 + 0x3FF * L 0x00003 1
dónde:
PD1
: posición inicial del directorio de páginas del proceso 1 en la RAM.PT1
yPT2
: posición inicial de la tabla de la página 1 y la tabla de la página 2 para el proceso 1 en la RAM.
Entonces, en este ejemplo, el directorio de la página y la tabla de la página podrían almacenarse en la RAM de forma similar a:
----------------> 0xFFFFFFFF
----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2
----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1
----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1
----------------> 0x0
Traduzcamos la dirección lineal 0x00801004
paso a paso.
Suponemos que cr3 = PD1
, es decir, apunta al directorio de páginas que se acaba de describir.
En binario, la dirección lineal es:
0 0 8 0 1 0 0 4
0000 0000 1000 0000 0001 0000 0000 0100
Agrupando como 10 | 10 | 12
da:
0000000010 0000000001 000000000100
0x2 0x1 0x4
lo que da:
- entrada de directorio de página = 0x2
- entrada de la tabla de páginas = 0x1
- desplazamiento = 0x4
Entonces, el hardware busca la entrada 2 del directorio de la página.
La tabla del directorio de páginas dice que la tabla de páginas se encuentra en 0x80000 * 4K = 0x80000000
. Este es el primer acceso a RAM del proceso.
Dado que la entrada de la tabla de páginas es 0x1
, el hardware mira la entrada 1 de la tabla de páginas en 0x80000000
, que le dice que la página física se encuentra en la dirección 0x0000C * 4K = 0x0000C000
. Este es el segundo acceso a RAM del proceso.
Finalmente, el hardware de paginación agrega el desplazamiento y la dirección final es 0x0000C004
.
Otros ejemplos de direcciones traducidas son:
linear 10 10 12 split physical
-------- --------------- ----------
00000001 000 000 001 00001001
00001001 000 001 001 page fault
003FF001 000 3FF 001 00005001
00400000 001 000 000 page fault
00800001 002 000 001 0000A001
00801008 002 001 008 0000C008
00802008 002 002 008 page fault
00B00001 003 000 000 page fault
Los errores de página se producen si no hay una entrada de directorio de páginas o una entrada de tabla de páginas.
Si el sistema operativo desea ejecutar otro proceso al mismo tiempo, le daría al segundo proceso un directorio de página separado y vincularía ese directorio a tablas de página separadas.
Arquitecturas de 64 bits
64 bits sigue siendo demasiada dirección para los tamaños de RAM actuales, por lo que la mayoría de las arquitecturas utilizarán menos bits.
x86_64 usa 48 bits (256 TiB) y el PAE del modo heredado ya permite direcciones de 52 bits (4 PiB).
12 de esos 48 bits ya están reservados para el desplazamiento, lo que deja 36 bits.
Si se adopta un enfoque de 2 niveles, la mejor división sería dos niveles de 18 bits.
Pero eso significaría que el directorio de la página tendría 2^18 = 256K
entradas, que requerirían demasiada RAM: ¡cerca de una paginación de un solo nivel para arquitecturas de 32 bits!
Por lo tanto, las arquitecturas de 64 bits crean aún más niveles de página, normalmente 3 o 4.
x86_64 usa 4 niveles en una 9 | 9 | 9 | 12
esquema, de modo que el nivel superior sólo ocupa sólo 2^9
entradas de nivel superior.
PAE
Extensión de dirección física.
Con 32 bits, solo se pueden direccionar 4 GB de RAM.
Esto comenzó a convertirse en una limitación para los servidores grandes, por lo que Intel introdujo el mecanismo PAE en Pentium Pro.
Para aliviar el problema, Intel agregó 4 nuevas líneas de dirección, de modo que se pudieran abordar 64 GB.
La estructura de la tabla de páginas también se modifica si PAE está activado. La forma exacta en que se modifica depende de si PSE está activado o desactivado.
PAE se enciende y apaga a través del PAE
un poco de cr4
.
Incluso si la memoria direccionable total es de 64 GB, el proceso individual solo puede usar hasta 4 GB. Sin embargo, el sistema operativo puede poner diferentes procesos en diferentes fragmentos de 4GB.
PSE
Extensión de tamaño de página.
Permite que las páginas tengan una longitud de 4 M (o 2 M si PAE está activado) en lugar de 4K.
El PSE se enciende y apaga a través del PAE
un poco de cr4
.
Esquemas de tablas de páginas PAE y PSE
Si PAE y PSE están activos, se utilizan diferentes esquemas de nivel de paginación:
-
sin PAE y sin PSE:
10 | 10 | 12
-
sin PAE y PSE:
10 | 22
.22 es el desplazamiento dentro de la página de 4Mb, ya que 22 bits se dirigen a 4Mb.
-
PAE y sin PSE:
2 | 9 | 9 | 12
La razón de diseño por la que 9 se usa dos veces en lugar de 10 es que ahora las entradas ya no pueden caber en 32 bits, que se completaron con 20 bits de dirección y 12 bits de bandera significativos o reservados.
La razón es que 20 bits ya no son suficientes para representar la dirección de las tablas de páginas: ahora se necesitan 24 bits debido a los 4 cables adicionales agregados al procesador.
Por lo tanto, los diseñadores decidieron aumentar el tamaño de las entradas a 64 bits y, para que quepan en una tabla de una sola página, es necesario reducir el número de entradas a 2 ^ 9 en lugar de 2 ^ 10.
El 2 inicial es un nuevo nivel de página llamado Tabla de puntero de directorio de página (PDPT), ya que puntos para paginar directorios y completar la dirección lineal de 32 bits. Los PDPT también tienen un ancho de 64 bits.
cr3
ahora apunta a los PDPT que deben estar en los primeros cuatro 4 GB de memoria y alineados en múltiplos de 32 bits para la eficiencia del direccionamiento. Esto significa que ahoracr3
tiene 27 bits significativos en lugar de 20: 2 ^ 5 para los 32 múltiplos * 2 ^ 27 para completar el 2 ^ 32 de los primeros 4GB. -
PAE y PSE:
2 | 9 | 21
Los diseñadores decidieron mantener un campo de 9 bits de ancho para que quepa en una sola página.
Esto deja 23 bits. Dejar 2 para el PDPT para mantener las cosas uniformes con el caso PAE sin PSE deja 21 para offset, lo que significa que las páginas tienen 2M de ancho en lugar de 4M.
TLB
El búfer de búsqueda anticipada de traducción (TLB) es un caché para direcciones de paginación.
Dado que es un caché, comparte muchos de los problemas de diseño del caché de la CPU, como el nivel de asociatividad.
Esta sección describirá un TLB completamente asociativo simplificado con 4 entradas de dirección única. Tenga en cuenta que, al igual que otros cachés, los TLB reales no suelen ser completamente asociativos.
Operación básica
Después de que ocurre una traducción entre la dirección lineal y física, se almacena en el TLB. Por ejemplo, un TLB de 4 entradas comienza en el siguiente estado:
valid linear physical
------ ------- ---------
> 0 00000 00000
0 00000 00000
0 00000 00000
0 00000 00000
los >
indica la entrada actual que se reemplazará.
y después de una dirección lineal de página 00003
se traduce a una dirección física 00005
, el TLB se convierte en:
valid linear physical
------ ------- ---------
1 00003 00005
> 0 00000 00000
0 00000 00000
0 00000 00000
y después de una segunda traducción de 00007
para 00009
se vuelve:
valid linear physical
------ ------- ---------
1 00003 00005
1 00007 00009
> 0 00000 00000
0 00000 00000
Ahora si 00003
necesita ser traducido nuevamente, el hardware primero busca el TLB y encuentra su dirección con un solo acceso a la RAM 00003 --> 00005
.
Por supuesto, 00000
no está en la TLB ya que ninguna entrada válida contiene 00000
como clave.
Política de reemplazo
Cuando se llena TLB, se sobrescriben las direcciones más antiguas. Al igual que para la memoria caché de la CPU, la política de reemplazo es una operación potencialmente compleja, pero una heurística simple y razonable es eliminar la entrada utilizada menos recientemente (LRU).
Con LRU, a partir del estado:
valid linear physical
------ ------- ---------
> 1 00003 00005
1 00007 00009
1 00009 00001
1 0000B 00003
agregando 0000D -> 0000A
daría:
valid linear physical
------ ------- ---------
1 0000D 0000A
> 1 00007 00009
1 00009 00001
1 0000B 00003
LEVA
El uso de TLB hace que la traducción sea más rápida, porque la traducción inicial requiere un acceso por nivel de TLB, lo que significa 2 en un esquema simple de 32 bits, pero 3 o 4 en arquitecturas de 64 bits.
La TLB generalmente se implementa como un tipo costoso de RAM llamado memoria direccionable por contenido (CAM). CAM implementa un mapa asociativo en hardware, es decir, una estructura que dada una clave (dirección lineal), recupera un valor.
Las asignaciones también podrían implementarse en direcciones RAM, pero las asignaciones CAM pueden requerir muchas menos entradas que una asignación RAM.
Por ejemplo, un mapa en el que:
- tanto las claves como los valores tienen 20 bits (el caso de un esquema de paginación simple)
- como máximo es necesario almacenar 4 valores a la vez
podría almacenarse en un TLB con 4 entradas:
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
FFFFF 00000
Sin embargo, para implementar esto con RAM, sería necesario tener 2 ^ 20 direcciones:
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
... (from 00011 to FFFFE)
FFFFF 00000
que sería incluso más caro que usar un TLB.
Invalidar entradas
Cuando cr3
cambios, todas las entradas de TLB se invalidan, porque se va a utilizar una nueva tabla de páginas para un nuevo proceso, por lo que es poco probable que alguna de las entradas antiguas tenga algún significado.
El x86 también ofrece la invlpg
instrucción que invalida explícitamente una sola entrada TLB. Otras arquitecturas ofrecen aún más instrucciones para las entradas TLB invalidadas, como invalidar todas las entradas en un rango determinado.
Algunas CPU x86 van más allá de los requisitos de la especificación x86 y brindan más coherencia de la que garantiza, entre modificar una entrada de la tabla de páginas y usándolo, cuando aún no estaba almacenado en caché en el TLB. Aparentemente, Windows 9x se basó en eso para la corrección, pero las CPU AMD modernas no proporcionan recorridos de página coherentes. Las CPU de Intel lo hacen, a pesar de que tienen que detectar especulaciones erróneas para hacerlo. Aprovechar esto probablemente sea una mala idea, ya que probablemente no hay mucho que ganar y existe un gran riesgo de causar problemas sutiles sensibles al tiempo que serán difíciles de depurar.
Uso del kernel de Linux
El kernel de Linux hace un uso extensivo de las funciones de paginación de x86 para permitir cambios de proceso rápidos con una pequeña fragmentación de datos.
En v4.2
, Mira por debajo arch/x86/
:
include/asm/pgtable*
include/asm/page*
mm/pgtable*
mm/page*
Parece que no hay estructuras definidas para representar las páginas, solo macros: include/asm/page_types.h
es especialmente interesante. Extracto:
#define _PAGE_BIT_PRESENT 0 /* is present */
#define _PAGE_BIT_RW 1 /* writeable */
#define _PAGE_BIT_USER 2 /* userspace addressable */
#define _PAGE_BIT_PWT 3 /* page write through */
arch/x86/include/uapi/asm/processor-flags.h
define CR0
, y en particular el PG
posición de bit:
#define X86_CR0_PG_BIT 31 /* Paging */
Bibliografía
Gratis:
-
rutgers-pxk-416 capítulo “Gestión de la memoria: notas de clase”
Buena revisión histórica de las técnicas de organización de la memoria utilizadas por sistemas operativos más antiguos.
No libre:
-
bovet05 capítulo “Direccionamiento de memoria”
Introducción razonable al direccionamiento de memoria x86. Faltan algunos buenos y sencillos ejemplos.
Aquí hay una respuesta muy breve y de alto nivel:
Un procesador x86 funciona en uno de varios modos posibles (aproximadamente: real, protegido, 64 bits). Cada modo puede utilizar uno de varios modelos posibles de direccionamiento de memoria (pero no todos los modos pueden utilizar todos los modelos), a saber: direccionamiento en modo real, direccionamiento segmentado y direccionamiento lineal plano.
En el mundo moderno, solo el direccionamiento lineal plano en modo protegido o de 64 bits es relevante, y los dos modos son esencialmente los mismos, siendo la principal diferencia el tamaño de la palabra de máquina y, por lo tanto, la cantidad de memoria direccionable.
Ahora, el modo de direccionamiento de memoria da significado a los operandos de memoria de las instrucciones de la máquina (como mov DWORD PTR [eax], 25
, que almacena un 32 bits (también conocido como dword
) entero de valor 25 en la memoria cuya dirección se almacena en el eax
Registro de 32 bits). En direccionamiento plano-lineal, este número en eax
se permite que se ejecute en un solo rango contiguo, desde cero hasta el valor máximo (en nuestro caso, eso es 232 – 1).
Sin embargo, el direccionamiento lineal plano puede ser paginado o no paginado. Sin paginación, la dirección se refiere directamente a la memoria física. Con paginación, la unidad de gestión de memoria del procesador (o MMU) alimenta de forma transparente la dirección deseada (ahora llamada dirección virtual) en un mecanismo de búsqueda, el llamado tablas de páginas, y obtiene un nuevo valor, que se interpreta como una dirección física. La operación original ahora opera en esta nueva dirección traducida en la memoria física, aunque el usuario solo ve la dirección virtual.
El beneficio clave de la paginación es que el sistema operativo administra las tablas de página. Por lo tanto, el sistema operativo puede modificar y reemplazar las tablas de páginas de forma arbitraria, como cuando “cambia de tarea”. Puede mantener una colección completa de tablas de páginas, una para cada “proceso”, y cada vez que decide que un proceso en particular se ejecutará en una CPU determinada, carga las tablas de páginas del proceso en la MMU de esa CPU (cada CPU tiene su propia conjunto de tablas de páginas). El resultado es que cada proceso ve su propio virtual espacio de direcciones que se ve igual independientemente de qué páginas físicas estaban libres cuando el sistema operativo tuvo que asignarle memoria. Nunca conoce la memoria de ningún otro proceso, ya que no puede acceder directamente a la memoria física.
Las tablas de páginas son estructuras de datos anidadas en forma de árbol almacenadas en la memoria normal, escritas por el sistema operativo pero leídas directamente por el hardware, por lo que el formato es fijo. Se “cargan” en la MMU configurando un registro de control de CPU especial para que apunte a la tabla de nivel superior. La CPU usa una caché llamada TLB para recordar las búsquedas, por lo que los accesos repetidos a las mismas pocas páginas son mucho más rápidos que los accesos dispersos, por razones de TLB-miss así como por razones habituales de caché de datos. Es común ver que el término “entrada de TLB” se usa para referirse a las entradas de la tabla de páginas, incluso cuando no están almacenadas en caché en la TLB.
Y en caso de que le preocupe que un proceso pueda deshabilitar la paginación o intentar modificar las tablas de la página: esto no está permitido, ya que x86 implementa niveles de privilegio (llamados “anillos”), y el código de usuario se ejecuta a un nivel de privilegio que es demasiado bajo para permitirle modificar las tablas de páginas de la CPU.