¿Cómo funciona la paginación x86?


92

Esta pregunta está destinada a llenar el vacío de buena información gratuita sobre el tema.

Creo que una buena respuesta encajará en una gran respuesta SO o al menos en algunas respuestas.

El objetivo principal es brindar a los principiantes la información suficiente para que puedan tomar el manual por sí mismos y puedan comprender los conceptos básicos del sistema operativo relacionados con la paginación.

Pautas sugeridas:

  • las respuestas deben ser amigables para principiantes:
    • ejemplos concretos, pero posiblemente simplificados son muy importantes
    • las aplicaciones de los conceptos mostrados son bienvenidas
  • citar recursos útiles es bueno
  • son bienvenidas pequeñas digresiones sobre cómo los sistemas operativos utilizan las funciones de paginación
  • Las explicaciones de PAE y PSE son bienvenidas
  • pequeñas digresiones en x86_64 son bienvenidas

Preguntas relacionadas y por qué creo que no son incautos:


1
Esto debe etiquetarse como "faq" y marcarse como "community-wiki".
Kerrek SB

@KerrekSB Realmente no sé cómo manejar este tipo de preguntas. Las respuestas deben ser wikis comunitarios, ¿es así? No pude encontrar una faqetiqueta.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3
Yo diría que la respuesta corta es "lea el Vol. 3, Capítulo 4: Paginación en el Manual de Intel". Es bastante claro, conciso y bien escrito, y no tiene más autoridad.
Kerrek SB

4
@KerrekSB Estoy de acuerdo en que el manual es claro y con autoridad, pero fue un poco demasiado duro como primera lectura para mí, necesitaba algunos ejemplos simples y concretos + fundamento para entender mejor las cosas.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:


144

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 rsiin 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 PGbit del cr0registro 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:

  • PT1y 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 = 32hacer el acceso más rápido y reservar los bits restantes para uso posterior. El valor real para Lx86 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 cr3a 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 cr3apunta a ella.

  • mira la entrada 0x00000porque 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 0x00000está en 0x00001 * 4K = 0x00001000.

  • para encontrar la dirección física final solo necesitamos agregar el desplazamiento:

      00001 000
    + 00000 001
      -----------
      00001 001
    

    porque 00001es la dirección física de la página buscada en la tabla y 001es 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 00001el hardware sabe que su entrada de la tabla de páginas está ubicada en la dirección RAM: PT1 + 1 * L( 1debido 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 cr3señ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 0y 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 cr2en esa dirección cada vez que ocurre un error de página. El manejador de excepciones puede simplemente buscar cr2la 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 -ldice 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 = 1Kentradas 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 = 1Kpá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.
  • PT1y 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 0x00801004paso 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 | 12da:

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 = 256Kentradas, 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 | 12esquema, de modo que el nivel superior solo 2^9acepta 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 PAEbit 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 PAEbit 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.

    cr3ahora 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 cr3tiene 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 00003se 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 00007a 00009se convierte en:

  valid   linear   physical
  ------  -------  ---------
  1       00003    00005
  1       00007    00009
> 0       00000    00000
  0       00000    00000

Ahora, si 00003necesita 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, 00000no está en el TLB ya que no contiene ninguna entrada válida 00000como 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 -> 0000Adarí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 cr3cambia, 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 invlpginstrucció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.hes 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.hdefine CR0, y en particular la PGposición del 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.


Gran respuesta, pero todavía no tengo claro cómo se decide LRU. Invocar el sistema operativo cada vez que se accede a una página que no sea MRU parece caro. Alternativamente, pude ver que el hardware reordena la tabla de páginas para LRU, lo que puede ser peligroso para los programas simultáneos. ¿Alguno de estos es correcto? ¿Cómo sabe el sistema operativo qué página es la LRU cuando se produce un error de página?
Keynan

@Keynan Creo que es el hardware el que lo hace, por lo que el tiempo necesario no es una preocupación. En cuanto a la concurrencia, no sé cómo se gestiona. Creo que hay un CR3 y caché por procesador, y el sistema operativo solo debe asegurarse de que las páginas de memoria no se superpongan.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
real TLBs are not usually fully associativeThe TLB is usually implemented as … CAM¿No son estas dos declaraciones contradictorias?
a3f

>>> x86_64 utiliza 4 niveles en 9 | 9 | 9 | 12 esquema debería ser 9 | 9 | 9 | 9 | 12?
monklof

@monklof Creo que esto es correcto: 9 9 9 12 ya permite 512 GB de RAM. El esquema de 5 niveles es un desarrollo más reciente dirigido solo a servidores, esto se menciona en la respuesta en mi sitio web, que está más actualizado.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

22

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 (por ejemplo mov DWORD PTR [eax], 25, que almacena un dwordentero de 32 bits (también conocido como ) de valor 25 en la memoria cuya dirección se almacena en el eaxregistro de 32 bits). En el direccionamiento lineal plano, eaxse permite que este número se ejecute en un único rango contiguo, desde cero hasta el valor máximo (en nuestro caso, es 2 32  - 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 la paginación, la unidad de gestión de memoria del procesador (o MMU) alimenta de forma transparente la dirección deseada (ahora denominada dirección virtual ) en un mecanismo de búsqueda, las llamadas tablas de página , 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 tareas". 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 espacio de direcciones virtuales 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 privilegios (llamados "anillos") y el código de usuario se ejecuta a un nivel de privilegios que es demasiado bajo para permitir para modificar las tablas de páginas de la CPU.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.