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 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 (por ejemplo, coprocesador x87 , APIC ), en los primeros días esto solía ser por chip separado, que luego se integró en la CPU. Pero el término todavía se usa.
Conceptos generales
Las direcciones lógicas son las direcciones de memoria utilizadas en el código de territorio de usuario "normal" (por ejemplo, el contenido de rsi
in 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:
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 el PG
bit del cr0
registro está establecido.
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 encajar 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 lo fija 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 (vía 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áginas 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 una versión simplificada 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
y PT2
: posición inicial de la tabla 1 y 2 en RAM.
Los valores de la 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 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 fragmento 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 lo fija 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
así que 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 si solo se necesitan 21 bits en este caso, los diseñadores de hardware probablemente elegirían L = 32
hacer el acceso más rápido y reservar los bits restantes para uso posterior. El valor real para L
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, el hardware realiza la traducción de direcciones entre direcciones lineales y físicas .
Cuando el sistema operativo quiere activar el proceso 1, establece el cr3
a PT1
, el inicio de la tabla para el proceso uno.
Si el Proceso 1 desea acceder a la dirección lineal 0x00000001
, el circuito de hardware de búsqueda automáticamente hace lo siguiente para el SO:
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 1 de la página porque cr3
apunta a ella.
mira la 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
según la tabla de páginas, la ubicación del número de página 0x00000
está en 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 tabla y 001
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 forma, 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 que 00001
el hardware sabe que su entrada de la tabla de páginas está ubicada en la dirección RAM: PT1 + 1 * L
( 1
debido a la parte de la página), y ahí es donde la buscará.
Cuando el sistema operativo quiere cambiar al proceso 2, todo lo que necesita hacer es cr3
señalar 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 0
y terminen en FFFFFFFF
, sin preocuparse por las 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 mediante una excepción de error de página.
Por lo general, depende del sistema operativo 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ó al disco.
El sistema operativo tendrá que hacer un 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 desee.
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. Esta es la razón por la que los agradables desarrolladores de IA32 establecen el valor de cr2
en esa dirección cada vez que ocurre un error de página. El manejador de excepciones puede simplemente buscar cr2
la dirección.
Simplificaciones
Simplificaciones a 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áginas contenían solo dos campos: una dirección de 20 bits y un indicador de presencia de 1 bit.
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 de entradas por proceso. Si cada entrada tiene 4 bytes de longitud, eso sería 4M por proceso , lo cual 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 1GB de mi RAM.
Por esta razón, los desarrolladores de x86 decidieron utilizar un esquema multinivel 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 un solo 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 2^10 = 1K
páginas tiene entradas de directorio de páginas
Los directorios de páginas contienen ... entradas del 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 4 bytes de longitud. Entonces, las 2 ^ 10 entradas de los directorios de páginas y las tablas de páginas encajarán perfectamente en 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 dadas 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 dadas 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
y PT2
: 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 acabamos 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 está ubicada 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, lo que requeriría 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, comúnmente 3 o 4.
x86_64 usa 4 niveles en un 9 | 9 | 9 | 12
esquema, de modo que el nivel superior solo 2^9
acepta 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 altera si PAE está activado. La forma exacta en que se modifica depende de si PSE está activado o desactivado.
PAE se activa y desactiva mediante el PAE
bit 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.
PSE se enciende y se apaga mediante el PAE
bit 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 apunta a directorios de página y completa la dirección lineal de 32 bits. Los PDPT también tienen 64 bits de ancho.
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 ahora cr3
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 una caché, comparte muchos de los problemas de diseño de la 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
El >
indica la entrada actual que se reemplazará.
y después de que 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
a 00009
se convierte en:
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 el TLB ya que no contiene ninguna entrada válida 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
agregar 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 en cada momento
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
cambia, 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 la modificación de una entrada de la tabla de páginas y su uso, cuando aún no estaba almacenada en caché en la 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 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 la PG
posición del bit:
#define X86_CR0_PG_BIT 31 /* Paging */
Bibliografía
Gratis:
No libre: