x86 paging x86-64 virtual-memory tlb

x86 - Cuándo hacer o no hacer INVLPG, MOV a CR3 para minimizar el lavado de TLB



paging x86-64 (2)

A su primera pregunta:

  1. Siempre puedes usar INVLPG y puedes hacer cualquier cambio posible. El uso de INVLPG siempre se guarda.
  2. Recargar CR3 no invalida las páginas globales en la TLB. Entonces, a veces, debe usar INVLPG, ya que la recarga de CR3 no tiene ningún efecto.
  3. Se debe utilizar INVLPG para cada página involucrada. Si está cambiando varias páginas a la vez, llega un momento en el que recargar CR3 es más rápido que una multitud de llamadas INVLPG.
  4. No olvide la extensión IDentifier del espacio de direcciones en las CPU modernas.

A tu segunda pregunta:

Una página que no está asignada no puede almacenarse en caché en el TLB (suponiendo que la haya invalidado correctamente cuando la haya desasignado previamente). Por lo tanto, cualquier cambio de no-presente no necesita recarga INVLPG o CR3.

Prólogo

Soy un aficionado al sistema operativo, y mi kernel se ejecuta en 80486+ y ya es compatible con la memoria virtual.

A partir de 80386, la familia de procesadores x86 de Intel y varios clones de la misma han admitido memoria virtual con paginación. Es bien sabido que cuando se establece el bit PG en CR0 , el procesador utiliza la traducción de direcciones virtuales. Luego, el registro CR3 apunta al directorio de páginas de nivel superior, que es la raíz para 2-4 niveles de estructuras de tablas de páginas que asignan las direcciones virtuales a direcciones físicas.

El procesador no consulta estas tablas para cada dirección virtual generada, en su lugar, las almacena en caché en una estructura llamada Translation Lookaside Buffer , o TLB. Sin embargo, cuando se realizan cambios en las tablas de páginas, se debe vaciar el TLB. En los procesadores 80386, esta descarga se haría recargando ( MOV ) CR3 con la dirección del directorio de la página de nivel superior, o un interruptor de tarea. Esto supuestamente incondicionalmente borra todas las entradas de TLB. Según tengo entendido, sería perfectamente válido que un sistema de memoria virtual siempre vuelva a cargar CR3 después de cualquier cambio.

Esto es un desperdicio, ya que el TLB ahora arrojaría entradas completamente buenas, por lo tanto, en los procesadores 80486 se introdujo la instrucción INVLPG . INVLPG invalidará la entrada de TLB que coincida con la dirección del operando de origen.

Sin embargo, comenzando con Pentium Pro, también tenemos páginas globales que no se vacían con los movimientos a CR3 o al interruptor de tareas; y AMD x86-64 ISA dice que algunas estructuras de tablas de páginas de nivel superior pueden ser almacenadas en caché y no invalidadas por INVLPG . Para obtener una imagen coherente de lo que se necesita y lo que no se necesita en cada ISA, uno realmente tendría que descargar una hoja de datos de 1000 páginas para una multitud de ISA lanzados desde los 80 para leer un par de páginas, e incluso entonces los documentos parecen ser particularmente vago en cuanto a la invalidación de TLB y qué sucede si la TLB no se invalida correctamente.

Pregunta

Por simplicidad, uno puede asumir que estamos hablando de un sistema con un solo procesador . Además, se puede suponer que no se requiere un cambio de tarea después de cambiar las estructuras de la página . (por lo tanto, INVLPG siempre es, al INVLPG , una opción tan buena como la de volver a cargar el registro CR3 ).

El supuesto básico es que sería necesario volver a cargar CR3 después de cada cambio en las tablas de páginas y directorios de páginas, y tal sistema sería correcto. Sin embargo, si uno quiere evitar vaciar el TLB innecesariamente, necesita respuestas a las 2 preguntas:

  1. Siempre que INVLPG sea ​​compatible con la ISA, ¿después de qué tipo de cambios se puede usar de manera segura en lugar de volver a cargar el CR3 ? Por ejemplo, "Si uno deselecciona un marco de página (establezca que la entrada de la tabla correspondiente no esté presente), siempre se puede usar INVLPG "?

  2. ¿Qué tipo de cambios se pueden hacer en las tablas y directorios sin tocar CR3 o ejecutar INVLPG ? Por ejemplo, "Si una página no está asignada en absoluto (no está presente), se puede escribir una PTE con Present=1 para ella sin limpiar el TLB"?

Incluso después de leer una gran cantidad de documentos ISA y todo lo relacionado con INVLPG aquí en Stack Overflow, no estoy personalmente seguro de ninguno de los ejemplos que presenté allí. De hecho, un post notable lo expresó de inmediato: "No sé exactamente cuándo debes usarlo y cuándo no". Por lo tanto, se agradecen todos los ejemplos correctos, preferiblemente documentados, y para IA32 o x86-64, que puede dar.


En los términos más simples posibles; el requisito es que cualquier cosa que la TLB de la CPU haya recordado que haya cambiado debe ser invalidada antes de que ocurra algo que se base en el cambio.

Las cosas que las CPU''s podrían haber recordado incluyen:

  • los permisos finales para la página (la combinación de permisos de lectura / escritura / ejecución de la entrada de la tabla de la página, la entrada del directorio de la página, etc.); incluyendo si la página está presente o no (vea la advertencia a continuación)
  • La dirección física de la página.
  • Las banderas "accedidas" y "sucias".
  • Las banderas que efectúan el almacenamiento en caché.
  • ya sea una página normal o una página grande (2 o 4 MiB) o una página enorme (1 GiB)

ADVERTENCIA: debido a que las CPU de Intel no recuerdan las páginas "no presentes", la documentación de Intel puede indicar que no es necesario invalidar cuando se cambia una página de "no presente" a "presente". La documentación de Intel solo es correcta para las CPU de Intel. No es correcto para todas las CPU 80x86. Algunas CPU (en su mayoría, Cyrix) recuerdan cuando una página "no estaba presente" y debido a esas CPU, tiene que invalidar cuando se cambia una página de "no presente" a "presente".

Tenga en cuenta que debido a la ejecución especulativa no puede cortar esquinas. Por ejemplo, si sabe que nunca se ha accedido a una página, no puede asumir que no está en la TLB porque es posible que la TLB haya sido obtenida de forma especulativa.

He elegido las palabras "antes de que todo lo que se base en el cambio suceda" con mucho cuidado. Las CPU modernas (especialmente para el modo largo) almacenan en caché las estructuras de paginación de nivel superior (por ejemplo, entradas PDPT) y no solo las páginas finales. Esto significa que si cambia una estructura de paginación de nivel superior, pero las entradas de la tabla de páginas siguen siendo las mismas, todavía debe invalidar.

También significa que es posible omitir la invalidación si nada se basa en el cambio. Un ejemplo simple de esto es con las banderas sucias y accedidas: si no está confiando en estas banderas (para determinar "menos recientemente usado" y qué páginas enviar para intercambiar espacio), entonces no importa mucho si la CPU no lo hace. No te des cuenta de que los has cambiado. También es posible (no recomendado para una sola CPU pero muy recomendado para múltiples CPU) omitir la invalidación de TLB en los casos en los que obtendría un error de página si la CPU está usando la información antigua / obsoleta de TLB, donde la página falla el controlador invalida si y solo si es realmente necesario.

Adicionalmente; "cualquier cosa que la TLB de la CPU pudiera haber recordado" es un poco difícil. A menudo, un sistema operativo asignará las propias estructuras de paginación en el espacio de direcciones virtuales para permitirles un acceso rápido / fácil a ellas (por ejemplo, el truco común de "mapeo recursivo" en el que pretende que el directorio de páginas sea una tabla de páginas). En este caso, cuando cambia una entrada de directorio de página, debe invalidar las páginas normales efectuadas (como es de esperar), pero también debe invalidar cualquier cosa que se haya efectuado el cambio en cualquier asignación.

Para lo cual usar (INVLPG o CR3 de recarga) hay varios problemas. Para una sola página, INVLPG será más rápido. Si cambia un directorio de páginas (con 1024 páginas o 512 páginas, según el tipo de paginación), el uso de INVLPG en un bucle puede o no ser más costoso que la recarga de CR3 (depende de la CPU / hardware y los patrones de acceso). para el código siguiente a la invalidación).

Hay 2 temas más que entran en esto. El primero es el cambio de tareas. Al cambiar entre tareas que utilizan diferentes espacios de direcciones virtuales, debe cambiar CR3. Esto significa que si cambia algo que afecta a un área grande (por ejemplo, un directorio de páginas) puede mejorar el rendimiento general haciendo un cambio de tarea antes, en lugar de recargar CR3 ahora (para anularlo) y luego recargar CR3 poco después (para el interruptor de tarea ). Básicamente, es una optimización de "matar 2 pájaros con una piedra".

La otra cosa es "páginas globales". Normalmente, hay páginas que son iguales en todos los espacios de direcciones virtuales (por ejemplo, el núcleo). Cuando vuelve a cargar CR3 (p. Ej., Durante un cambio de tarea) no desea que se invaliden los TLB de las páginas que siguen siendo las mismas sin ningún motivo, ya que eso perjudicaría el rendimiento más de lo necesario. Para corregir eso y mejorar el rendimiento, (para Pentium y versiones posteriores) hay una función llamada "páginas globales" en la que puedes marcar estas páginas comunes como globales y no se invalidan cuando recargas CR3. En ese caso, si necesita invalidar las páginas globales, debe usar INVPLG o cambiar CR4 (por ejemplo, deshabilitar y luego volver a activar la función de páginas globales). Para áreas más grandes (por ejemplo, cambiar un directorio de página y no solo una página) es lo mismo que antes (jugar con CR4 puede ser más rápido o más lento que INVLPG en un bucle).