Una unidad de gestión de memoria ( MMU ), a veces llamada unidad de gestión de memoria paginada ( PMMU ), [1] es una unidad de hardware de computadora que examina todas las referencias de memoria en el bus de memoria , traduciendo estas solicitudes, conocidas como direcciones de memoria virtual , en direcciones físicas en la memoria principal .
En los sistemas modernos, los programas generalmente tienen direcciones que acceden a la memoria máxima teórica de la arquitectura de la computadora , 32 o 64 bits. La MMU asigna las direcciones de cada programa a áreas separadas en la memoria física, que generalmente es mucho más pequeña que el máximo teórico. Esto es posible porque los programas rara vez usan grandes cantidades de memoria al mismo tiempo.
La mayoría de los sistemas operativos (OS) modernos funcionan en conjunto con una MMU para proporcionar compatibilidad con memoria virtual (VM). La MMU rastrea el uso de la memoria en bloques de tamaño fijo conocidos como páginas y, si un programa hace referencia a una ubicación en una página que no está en la memoria física, la MMU provocará una interrupción en el sistema operativo . El SO seleccionará entonces un bloque menos utilizado en la memoria, lo escribirá en un almacenamiento de respaldo, como un disco duro , si se ha modificado desde que se leyó, leerá la página del almacenamiento de respaldo en ese bloque y configurará la MMU para asignar el bloque a la página solicitada originalmente para que el programa pueda usarlo. Esto se conoce como paginación a demanda .
Las MMU modernas generalmente también realizan tareas adicionales relacionadas con la memoria. La protección de la memoria bloquea los intentos de un programa de acceder a la memoria que no ha solicitado previamente, lo que evita que un programa que se comporta mal utilice toda la memoria o que un código malicioso lea datos de otro programa. También suelen administrar una caché de procesador , que almacena los datos a los que se accedió recientemente en una memoria muy rápida y, por lo tanto, reduce la necesidad de comunicarse con la memoria principal más lenta. En algunas implementaciones, también son responsables del arbitraje de bus , que controla el acceso al bus de memoria entre las muchas partes de la computadora que desean acceder.
Antes de que los sistemas VM se generalizaran en la década de 1990, los diseños de MMU anteriores eran más variados. Uno de ellos era la traducción paginada, que era similar a la paginación por demanda moderna en el sentido de que utilizaba bloques de tamaño fijo, pero tenía una lista de páginas de tamaño fijo que dividía la memoria; esto significaba que el tamaño del bloque era una función del número de páginas y la memoria instalada. Otra técnica común, que se encontraba principalmente en máquinas más grandes, era la traducción segmentada, que permitía bloques de memoria de tamaño variable que se asignaban mejor a las solicitudes del programa. Esto era eficiente, pero no se asignaba tan bien a la memoria virtual. Algunos de los primeros sistemas, especialmente los sistemas de 8 bits , utilizaban MMU muy simples para realizar la conmutación de bancos .
Las MMU modernas suelen dividir el espacio de direcciones virtuales (el rango de direcciones que utiliza el procesador) en páginas , cada una de las cuales tiene un tamaño que es una potencia de 2, normalmente unos pocos kilobytes , pero pueden ser mucho más grandes. Los programas hacen referencia a la memoria utilizando el tamaño de dirección natural de la máquina, normalmente 32 o 64 bits en los sistemas modernos. Los bits inferiores de la dirección (el desplazamiento dentro de una página) no se modifican. Los bits de dirección superiores son los números de página virtuales. [3]
La mayoría de las MMU utilizan una tabla de elementos en memoria denominada tabla de páginas , que contiene una entrada de tabla de páginas (PTE) por página virtual, para asignar números de páginas virtuales a números de páginas físicas en la memoria principal. Las tablas de páginas de varios niveles se utilizan a menudo para reducir el tamaño de la tabla de páginas. Una caché asociativa de PTE se denomina búfer de búsqueda de traducción (TLB) y se utiliza para evitar la necesidad de acceder a la memoria principal cada vez que se asigna una dirección virtual. [4]
Otras MMU pueden tener una matriz privada de memoria, [5] registros, [6] o RAM estática [7] que contiene un conjunto de información de mapeo.
El número de página virtual se puede utilizar directamente como un índice en la tabla de páginas u otra información de mapeo, o se puede dividir aún más, con bits en un nivel dado utilizados como un índice en una tabla de tablas de nivel inferior en las que los bits en el nivel siguiente hacia abajo se utilizan como un índice, con dos o más niveles de indexación.
El número de página física se combina con el desplazamiento de página para proporcionar la dirección física completa. [3]
Una entrada de la tabla de páginas u otra información por página también puede incluir información sobre si se ha escrito en la página (el bit sucio ), cuándo se utilizó por última vez (el bit accedido , para un algoritmo de reemplazo de página menos utilizada recientemente (LRU ), qué tipo de procesos ( modo de usuario o modo supervisor ) pueden leerla y escribirla, y si debe almacenarse en caché . [8]
A veces, una entrada de la tabla de páginas u otra información por página prohíbe el acceso a una página virtual en particular, tal vez porque no se ha asignado memoria física de acceso aleatorio (RAM) a esa página virtual. En este caso, la MMU envía una señal de error de página a la CPU. El sistema operativo (OS) entonces maneja la situación, tal vez tratando de encontrar un marco libre de RAM y configurando el mapa de páginas para asignarlo a la dirección virtual solicitada. Si no hay RAM libre, puede ser necesario elegir una página existente (conocida como víctima ), utilizando algún algoritmo de reemplazo , y guardarla en el disco (un proceso llamado paginación ). Con algunas MMU, también puede haber escasez de PTE, en cuyo caso el SO tendrá que liberar uno para la nueva asignación. [8] [3]
La MMU también puede generar condiciones de error de acceso ilegal o fallas de página no válida en caso de accesos a memoria ilegal o inexistente, respectivamente, lo que genera condiciones de error de segmentación o error de bus cuando los maneja el sistema operativo.
En algunos casos, un error de página puede indicar un error de software , que se puede prevenir utilizando la protección de memoria como uno de los beneficios clave de una MMU: un sistema operativo puede utilizarla para protegerse contra programas erróneos al prohibir el acceso a la memoria a la que un programa en particular no debería tener acceso. Normalmente, un sistema operativo asigna a cada programa su propio espacio de direcciones virtuales. [3]
Una MMU paginada también mitiga el problema de la fragmentación externa de la memoria. Después de que se han asignado y liberado bloques de memoria, la memoria libre puede fragmentarse (ser discontinua) de modo que el bloque contiguo más grande de memoria libre puede ser mucho más pequeño que la cantidad total. Con la memoria virtual, un rango contiguo de direcciones virtuales se puede asignar a varios bloques no contiguos de memoria física; esta asignación no contigua es uno de los beneficios de la paginación . [8] [3]
Sin embargo, el mapeo paginado causa otro problema, la fragmentación interna . Esto ocurre cuando un programa solicita un bloque de memoria que no se mapea limpiamente en una página, por ejemplo, si un programa solicita un buffer de 1 KB para realizar trabajo de archivo. En este caso, la solicitud da como resultado que se reserve una página entera a pesar de que solo se utilizará 1 KB de la página; si las páginas son más grandes que 1 KB, se desperdicia el resto de la página. Si se realizan muchas asignaciones pequeñas de este tipo, la memoria se puede utilizar a pesar de que gran parte de ella permanece vacía. [4]
En algunos de los primeros diseños de microprocesadores , la gestión de la memoria la realizaba un circuito integrado independiente , como el VLSI Technology VI475 (1986), el Motorola 68851 (1984) utilizado con la CPU Motorola 68020 en el Macintosh II , o el Z8010 [9] y el Z8015 (1985) [10] [11] utilizados con la familia de procesadores Zilog Z8000 . Los microprocesadores posteriores (como el Motorola 68030 y el Zilog Z280 ) colocaban la MMU junto con la CPU en el mismo circuito integrado, al igual que los microprocesadores Intel 80286 y posteriores x86 .
Si bien este artículo se centra en las MMU modernas, que suelen basarse en paginación a demanda, los primeros sistemas utilizaban un direccionamiento de base y límites que luego se desarrolló hasta convertirse en segmentación , o utilizaban un conjunto fijo de bloques en lugar de cargarlos a demanda. La diferencia entre estos dos enfoques es el tamaño del bloque contiguo de memoria; los sistemas paginados dividen la memoria principal en una serie de bloques de igual tamaño, mientras que los sistemas segmentados generalmente permiten tamaños variables. [4]
Los primeros sistemas de gestión de memoria, a menudo implementados en software, reservaban una parte de la memoria para almacenar una serie de asignaciones. Estas consistían en pares de valores, la base y el límite , aunque se han utilizado muchos otros términos. Cuando el sistema operativo solicitaba memoria para cargar un programa, o un programa solicitaba más memoria para almacenar datos de un archivo, por ejemplo, llamaba a la biblioteca de manejo de memoria . Esta examinaba las asignaciones para buscar un área en la memoria principal lo suficientemente grande como para contener la solicitud. Si se encontraba dicho bloque, se ingresaba una nueva entrada en la tabla. A partir de ese momento, cuando ese programa accedía a la memoria, todas sus direcciones se compensaban con el valor base. Cuando el programa terminaba con la memoria que solicitaba y la liberaba, o el programa salía, se liberaban las entradas asociadas con él. [4]
Este estilo de acceso, con el tiempo, se volvió común en el mercado de mainframes [ cita requerida ] y se conocía como traducción segmentada , aunque aquí también se utilizan una variedad de términos. Este estilo tiene la ventaja de la simplicidad; los bloques de memoria son continuos y, por lo tanto, solo es necesario almacenar los dos valores, base y límite. Cada entrada corresponde a un bloque de memoria utilizado por un solo programa, y la traducción es invisible para el programa, que ve que la memoria principal comienza en la dirección cero y se extiende hasta algún valor fijo. [4]
La desventaja de este enfoque es que conduce a un efecto conocido como fragmentación externa . Esto ocurre cuando se liberan asignaciones de memoria pero no son contiguas. En este caso, puede haber suficiente memoria disponible para manejar una solicitud, pero está dispersa y no se puede asignar. En sistemas donde los programas se inician y se detienen con el tiempo, esto puede eventualmente llevar a que la memoria esté altamente fragmentada y no queden bloques grandes. Se desarrollaron varios algoritmos para abordar este problema. [4]
La segmentación se utilizó ampliamente en las plataformas de microcomputadoras de la década de 1980. Entre las MMU que utilizaron este concepto se encuentran la Motorola 68451 y la Signetics 68905, [4] pero existen muchos otros ejemplos. También se admitió en implementaciones de software; un ejemplo es MultiFinder de Apple , lanzado en 1987 para la plataforma Macintosh . A cada programa se le asignaba una cantidad de memoria que se preseleccionaba en el Finder y la traducción de virtual a física se lograba dentro de los programas utilizando identificadores . [12]
Un ejemplo más común es el Intel 8088 utilizado en el IBM PC . Este implementó una MMU muy simple dentro de la CPU, con cuatro registros de procesador que contenían valores base a los que accedía directamente el programa. Estos asignaban solo los 4 bits superiores de la dirección de 20 bits, y no había un equivalente de un límite, que era simplemente los 16 bits inferiores de la dirección y, por lo tanto, un valor fijo de 64 kB. [13] Las entradas posteriores en la serie de arquitectura x86 utilizaron enfoques diferentes.
Algunos sistemas, como el GE 645 y sus sucesores, utilizaban tanto la segmentación como la paginación. La tabla de segmentos, en lugar de contener entradas por segmento que indican la dirección base física y la longitud del segmento, contiene entradas que indican la dirección base física de una tabla de páginas para el segmento, además de la longitud del segmento. La memoria física se divide en páginas de tamaño fijo, y las mismas técnicas utilizadas para la paginación por demanda basada puramente en páginas se utilizan para la paginación por demanda basada en segmentos y páginas.
Otro enfoque para el manejo de la memoria es dividir la memoria principal en una serie contigua de bloques de tamaño fijo. Esto es similar al sistema de paginación por demanda moderno en el que el resultado es una serie de páginas, pero en estos sistemas anteriores la lista de páginas tiene un tamaño fijo y normalmente se almacena en algún tipo de memoria rápida como la RAM estática para mejorar el rendimiento. En este caso, las dos partes de la dirección almacenadas por la MMU se conocen como número de segmento e índice de página . [4]
Consideremos un diseño de procesador con direccionamiento de 24 bits, como el Motorola 68000 original . En un sistema de este tipo, la MMU divide la dirección virtual en partes, por ejemplo, los 13 bits menos significativos para el índice de página y los 11 bits más significativos restantes como el número de segmento. Esto da como resultado una lista de 2048 páginas de 8 kB cada una. [4] En este enfoque, las solicitudes de memoria dan como resultado que se otorguen una o más páginas a ese programa, que pueden no ser contiguas en la memoria principal. La MMU mantiene una lista de números de página expresados originalmente por el programa y el número de página real en la memoria principal. Cuando intenta acceder a la memoria, la MMU lee el número de segmento del bus de memoria del procesador, encuentra la entrada correspondiente para ese programa en su memoria interna y expresa la versión mapeada del valor en el bus de memoria mientras que los bits inferiores de la dirección original se pasan sin cambios. Al igual que en el caso segmentado, los programas ven su memoria como un solo bloque contiguo. [4]
Este enfoque tiene dos desventajas. La primera es que, a medida que se expande el espacio de direcciones virtuales, también aumenta la cantidad de memoria necesaria para almacenar la asignación. Por ejemplo, en el 68020, las direcciones tienen 32 bits de ancho, lo que significa que el número de segmento para el mismo tamaño de página de 8 kB ahora es de los 19 bits superiores y la tabla de asignación se expande a 512 kB de tamaño, [4] mucho más allá de lo que se podía implementar en hardware por un costo razonable en la década de 1980. Este problema se puede reducir haciendo que las páginas sean más grandes, digamos 64 kB en lugar de 8. Ahora el índice de página usa 16 bits y la tabla de páginas resultante es de 64 kB, lo que es más manejable. Pasar a un tamaño de página mayor conduce al segundo problema, una mayor fragmentación interna. A un programa que genera una serie de solicitudes de bloques pequeños se le asignarán bloques grandes y, por lo tanto, desperdiciará grandes cantidades de memoria. [4]
El método de traducción paginada fue ampliamente utilizado por las unidades de procesamiento multimedios (MMU) con microprocesadores en los años 70 y principios de los 80, incluido el Signetics 68905 (que podía funcionar en ambos modos). Tanto Signetics como Philips produjeron una versión del 68000 que combinaba el 68905 en el mismo chip físico, el 68070. [4]
Otro uso de esta técnica es ampliar el tamaño de la dirección física cuando la dirección virtual es demasiado pequeña. Por ejemplo, el PDP-11 originalmente tenía una dirección de 16 bits que la hacía demasiado pequeña a medida que aumentaban los tamaños de memoria en la década de 1970. Esto se solucionó ampliando el bus de memoria física a 18 bits y utilizando una MMU para agregar dos bits más en función de otros pines en el bus del procesador para indicar qué programa estaba accediendo a la memoria. [14]
Otro uso de esta misma técnica, aunque no se denomina paginación sino conmutación de bancos , fue ampliamente utilizado por los primeros microprocesadores de 8 bits como el MOS 6502. Por ejemplo, la MMU de Atari expresaba bits adicionales en el bus de direcciones para seleccionar entre varios bancos de memoria DRAM en función de cuál de los chips estaba activo en ese momento, normalmente la CPU o ANTIC . Esto se utilizó para ampliar la memoria disponible en el Atari 130XE a 128 kB. [15] El Commodore 128 utilizó un enfoque similar.
La mayoría de los sistemas modernos dividen la memoria en páginas de entre 4 y 64 KB de tamaño, a menudo con la capacidad de utilizar las denominadas páginas enormes de 2 MB o 1 GB de tamaño (a menudo son posibles ambas variantes). Las traducciones de páginas se almacenan en caché en un búfer de traducción de páginas (TLB). Algunos sistemas, principalmente los diseños RISC más antiguos , realizan una captura en el sistema operativo cuando no se encuentra una traducción de página en el TLB. La mayoría de los sistemas utilizan un rastreador de árboles basado en hardware. La mayoría de los sistemas permiten desactivar la MMU, pero algunos la desactivan cuando realizan una captura en el código del sistema operativo.
El IBM System/360 Modelo 67 , que se presentó en agosto de 1965, incluía una MMU llamada caja de traducción dinámica de direcciones (DAT). [16] [17] Tiene la característica inusual de almacenar bits accedidos y sucios fuera de la tabla de páginas (junto con la clave de protección de cuatro bits para todos los procesadores S/360). Se refieren a la memoria física en lugar de a la memoria virtual, y se accede a ellos mediante instrucciones de propósito especial. [17] Esto reduce la sobrecarga para el SO, que de otro modo necesitaría propagar bits accedidos y sucios desde las tablas de páginas a una estructura de datos más orientada físicamente. Esto hace que la virtualización a nivel de SO , más tarde llamada paravirtualización , sea más fácil.
A partir de agosto de 1972, el IBM System/370 tiene una MMU similar, aunque inicialmente sólo admitía un espacio de direcciones virtuales de 24 bits en lugar del espacio de direcciones virtuales de 32 bits del System/360 Modelo 67. También almacena los bits accedidos y sucios fuera de la tabla de páginas. A principios de 1983, la arquitectura System/370-XA amplió el espacio de direcciones virtuales a 31 bits y, en 2000, se introdujo la arquitectura z/Architecture de 64 bits , con el espacio de direcciones ampliado a 64 bits; estos siguen almacenando los bits accedidos y sucios fuera de la tabla de páginas.
Las páginas VAX ocupan 512 bytes, [18] : 199 , lo que es muy poco. Un sistema operativo puede tratar varias páginas como si fueran una sola página más grande. Por ejemplo, Linux en VAX agrupa ocho páginas. Por lo tanto, se considera que el sistema tiene páginas de 4 KB . VAX divide la memoria en cuatro regiones de propósito fijo, cada una de 1 GB de tamaño. Son: [18] : 200–201
Las tablas de páginas son grandes matrices lineales. [18] : 209–215 Normalmente, esto sería un gran desperdicio cuando se utilizan direcciones en ambos extremos del rango posible, pero las tablas de páginas para el espacio P0 y P1 se almacenan en el espacio paginado S0. [18] : 211–212 Por lo tanto, existe efectivamente un árbol de dos niveles , lo que permite que las aplicaciones tengan un diseño de memoria dispersa sin desperdiciar mucho espacio en entradas de tabla de páginas no utilizadas. A diferencia de las entradas de tabla de páginas en la mayoría de MMU, las entradas de tabla de páginas en la MMU VAX carecen de un bit de acceso. [18] : 203–205 Los sistemas operativos que implementan paginación deben encontrar alguna forma de emular el bit de acceso si quieren funcionar de manera eficiente. Normalmente, el sistema operativo desasignará páginas periódicamente para que los fallos de página no presente se puedan utilizar para permitir que el sistema operativo establezca un bit de acceso.
Los procesadores de aplicaciones basados en la arquitectura ARM implementan una MMU definida por la arquitectura del sistema de memoria virtual de ARM. La arquitectura actual define PTE para describir páginas de 4 KB y 64 KB , secciones de 1 MB y supersecciones de 16 MB ; las versiones anteriores también definían una página diminuta de 1 KB . ARM utiliza una tabla de páginas de dos niveles si se utilizan páginas de 4 KB y 64 KB , o solo una tabla de páginas de un nivel para secciones de 1 MB y secciones de 16 MB .
Las actualizaciones de TLB se realizan automáticamente mediante hardware que recorre la tabla de páginas. Las PTE incluyen permiso de acceso de lectura/escritura basado en privilegios, información de capacidad de almacenamiento en caché, un bit NX y un bit no seguro. [19]
Los procesadores DEC Alpha dividen la memoria en 8 KB , 16 KB , 32 KB o 64 KB ; el tamaño de la página depende del procesador. [20] : 3–2 [21] : 3–2 páginas. Después de un error de TLB, el código de máquina de firmware de bajo nivel (aquí llamado PALcode ) recorre una tabla de páginas.
El código PALcode de OpenVMS AXP y el código PALcode de DEC OSF/1 recorren una tabla de páginas estructurada en árbol de tres niveles. Las direcciones se dividen en un conjunto de bits no utilizados (que contienen el mismo valor que el bit superior del índice en el nivel raíz del árbol), un conjunto de bits para indexar el nivel raíz del árbol, un conjunto de bits para indexar el nivel medio del árbol, un conjunto de bits para indexar el nivel de hoja del árbol y los bits restantes que pasan a la dirección física sin modificación, indexando un byte dentro de la página. Los tamaños de los campos dependen del tamaño de la página; los tres campos de índice del árbol son del mismo tamaño. [20] : 3-2–3-3 [21] : 3-1–3-2 El código PALcode de OpenVMS AXP admite bits de permiso de lectura y escritura completos para los modos de usuario, supervisor, ejecutivo y kernel, y también admite errores en los bits de lectura/escritura/ejecución. [20] : 3-3–3-6 El código PAL del DEC OSF/1 admite bits de permiso de lectura y escritura completos para los modos de usuario y kernel, y también admite errores en los bits de lectura/escritura/ejecución. [21] : (II-B) 3-3-3-6
El código PAL de Windows NT AXP puede recorrer una tabla de páginas de un solo nivel en un espacio de direcciones virtuales o una tabla de páginas de dos niveles en un espacio de direcciones físicas. Los 32 bits superiores de una dirección se ignoran. Para una tabla de páginas de un solo nivel, las direcciones se dividen en un conjunto de bits para indexar la tabla de páginas y los bits restantes que pasan a la dirección física sin modificación, indexando un byte dentro de la página. Para una tabla de páginas de dos niveles, las direcciones se dividen en un conjunto de bits para indexar el nivel raíz del árbol, un conjunto de bits para indexar el nivel superior del árbol, un conjunto de bits para indexar el nivel de hoja del árbol y los bits restantes que pasan a la dirección física sin modificación, indexando un byte dentro de la página. Los tamaños de los campos dependen del tamaño de la página. [22] : 3-2–3-4 El código PAL de Windows NT AXP admite que una página sea accesible solo desde el modo kernel o que sea accesible desde el modo usuario y kernel, y también admite un bit de error en escritura. [22] : 3-5
La arquitectura MIPS admite de una a 64 entradas en la TLB. La cantidad de entradas de la TLB se puede configurar en la configuración de la CPU antes de la síntesis. Las entradas de la TLB son duales. Cada entrada de la TLB asigna un número de página virtual (VPN2) a uno de los dos números de marco de página (PFN0 o PFN1), según el bit menos significativo de la dirección virtual que no forma parte de la máscara de página . Este bit y los bits de máscara de página no se almacenan en la VPN2. Cada entrada de la TLB tiene su propio tamaño de página, que puede ser cualquier valor desde 1 KB hasta 256 MB en múltiplos de cuatro. Cada PFN en una entrada de la TLB tiene un atributo de almacenamiento en caché, un bit de estado válido y uno sucio. Una VPN2 tiene un bit de estado global y una ID asignada por el SO que participa en la coincidencia de la entrada de la TLB de la dirección virtual, si el bit de estado global se establece en cero. Una PFN almacena la dirección física sin los bits de máscara de página.
Se genera una excepción de recarga de TLB cuando no hay entradas en la TLB que coincidan con la dirección virtual asignada. Se genera una excepción de TLB no válida cuando hay una coincidencia pero la entrada está marcada como no válida. Se genera una excepción de TLB modificada cuando una instrucción de almacenamiento hace referencia a una dirección asignada y el estado sucio de la entrada coincidente no está establecido. Si se produce una excepción de TLB al procesar una excepción de TLB, una excepción de TLB de doble error, se envía a su propio controlador de excepciones .
MIPS32 y MIPS32r2 admiten 32 bits de espacio de direcciones virtuales y hasta 36 bits de espacio de direcciones físicas. MIPS64 admite hasta 64 bits de espacio de direcciones virtuales y hasta 59 bits de espacio de direcciones físicas.
El Sun-1 original es un ordenador de placa única construido alrededor del microprocesador Motorola 68000 y presentado en 1982. Incluye la unidad de gestión de memoria Sun 1 original que proporciona traducción de direcciones, protección de memoria, uso compartido de memoria y asignación de memoria para múltiples procesos que se ejecutan en la CPU. Todo el acceso de la CPU a la RAM privada incorporada, la memoria Multibus externa, la E/S incorporada y la E/S Multibus se realiza a través de la MMU, donde la traducción de direcciones y la protección se realizan de manera uniforme. La MMU está implementada en hardware en la placa de la CPU.
La MMU consta de un registro de contexto, un mapa de segmentos y un mapa de páginas. Las direcciones virtuales de la CPU se traducen en direcciones intermedias mediante el mapa de segmentos, que a su vez se traducen en direcciones físicas mediante el mapa de páginas. El tamaño de página es de 2 KB y el tamaño de segmento es de 32 KB , lo que da 16 páginas por segmento. Se pueden mapear hasta 16 contextos simultáneamente. El espacio máximo de direcciones lógicas para un contexto es de 1024 páginas o 2 MB. La dirección física máxima que se puede mapear simultáneamente también es de 2 MB.
El registro de contexto es importante en un sistema operativo multitarea porque permite que la CPU cambie entre procesos sin tener que volver a cargar toda la información del estado de traducción. El registro de contexto de 4 bits puede cambiar entre 16 secciones del mapa de segmentos bajo el control del supervisor, lo que permite que se asignen 16 contextos simultáneamente. Cada contexto tiene su propio espacio de direcciones virtuales. Se puede compartir el espacio de direcciones virtuales y las comunicaciones entre contextos escribiendo los mismos valores en los mapas de segmentos o páginas de diferentes contextos. Se pueden manejar contextos adicionales tratando el mapa de segmentos como un caché de contexto y reemplazando los contextos obsoletos según el uso menos reciente.
El registro de contexto no hace distinción entre los estados de usuario y supervisor. Las interrupciones y las trampas no cambian de contexto, lo que requiere que todos los vectores de interrupción válidos siempre se asignen a la página 0 del contexto, así como a la pila de supervisores válidos. [23]
Las estaciones de trabajo Sun-2 son similares; están construidas alrededor del microprocesador Motorola 68010 y tienen una unidad de administración de memoria similar, con páginas de 2 KB y segmentos de 32 KB . El registro de contexto tiene un contexto de sistema de 3 bits utilizado en el estado de supervisor y un contexto de usuario de 3 bits utilizado en el estado de usuario. [24]
Las estaciones de trabajo Sun-3 , a excepción de Sun-3/80, Sun-3/460, Sun-3/470 y Sun-3/480, están construidas en torno al Motorola 68020 y tienen una unidad de gestión de memoria similar. El tamaño de página se ha incrementado a 8 KB . (Los modelos posteriores están construidos en torno al Motorola 68030 y utilizan la MMU integrada del 68030).
Las estaciones de trabajo Sun-4 están construidas alrededor de varios microprocesadores SPARC y tienen una unidad de gestión de memoria similar a la de las estaciones de trabajo Sun-3.
En PowerPC G1, G2, G3 y G4, las páginas normalmente ocupan 4 KB. Después de un error de TLB, la MMU PowerPC estándar comienza dos búsquedas simultáneas. Una de las búsquedas intenta hacer coincidir la dirección con uno de los cuatro u ocho registros de traducción de direcciones de bloques de datos (DBAT), o cuatro u ocho registros de traducción de direcciones de bloques de instrucciones (IBAT), según corresponda. Los registros BAT pueden asignar fragmentos lineales de memoria de hasta 256 MB, y normalmente los utiliza un sistema operativo para asignar grandes porciones del espacio de direcciones para el propio uso del núcleo del sistema operativo. Si la búsqueda BAT tiene éxito, la otra búsqueda se detiene y se ignora.
La otra búsqueda, que no es compatible directamente con todos los procesadores de esta familia, se realiza a través de una denominada tabla de páginas invertida , que actúa como una extensión hash de la TLB fuera del chip. En primer lugar, se utilizan los cuatro bits superiores de la dirección para seleccionar uno de los 16 registros de segmento . A continuación, 24 bits del registro de segmento reemplazan esos cuatro bits, lo que produce una dirección de 52 bits. El uso de registros de segmento permite que varios procesos compartan la misma tabla hash .
La dirección de 52 bits se codifica y luego se utiliza como índice en la tabla externa al chip. Allí, se escanea un grupo de entradas de la tabla de ocho páginas en busca de una que coincida. Si ninguna coincide debido a colisiones de hash excesivas , el procesador vuelve a intentarlo con una función hash ligeramente diferente . Si esto también falla, la CPU entra en el SO (con la MMU deshabilitada) para que se pueda resolver el problema. El SO debe descartar una entrada de la tabla hash para hacer espacio para una nueva entrada. El SO puede generar la nueva entrada a partir de una tabla de páginas similar a un árbol más normal o de estructuras de datos por mapeo que probablemente sean más lentas y más eficientes en términos de espacio. El soporte para el control de no ejecución se encuentra en los registros de segmento, lo que genera una granularidad de 256 MB .
Un problema importante de este diseño es la deficiente localización de la caché causada por la función hash. Los diseños basados en árboles evitan este problema colocando las entradas de la tabla de páginas para páginas adyacentes en ubicaciones adyacentes. Un sistema operativo que se ejecute en PowerPC puede minimizar el tamaño de la tabla hash para reducir este problema.
También es algo lento eliminar las entradas de la tabla de páginas de un proceso. El sistema operativo puede evitar la reutilización de valores de segmento para retrasar la tarea, o puede optar por sufrir el desperdicio de memoria asociado con las tablas hash por proceso. Los chips G1 no buscan entradas de la tabla de páginas, pero sí generan el hash, con la expectativa de que un sistema operativo busque la tabla hash estándar a través del software. El sistema operativo puede escribir en la TLB. Los chips G2, G3 y los primeros G4 utilizan hardware para buscar en la tabla hash. Los chips más recientes permiten al sistema operativo elegir cualquiera de los dos métodos. En los chips que hacen que esto sea opcional o que no lo admitan en absoluto, el sistema operativo puede optar por utilizar exclusivamente una tabla de páginas basada en árbol.
La arquitectura x86 ha evolucionado durante mucho tiempo manteniendo la compatibilidad total del software, incluso para el código del sistema operativo. Por ello, la MMU es extremadamente compleja, con muchos modos de funcionamiento posibles diferentes. Aquí se describe el funcionamiento normal de la CPU 80386 tradicional y sus sucesoras ( IA-32 ).
La CPU divide principalmente la memoria en páginas de 4 KB . Los registros de segmento, fundamentales para los diseños de MMU 8088 y 80286 más antiguos, no se utilizan en los sistemas operativos modernos, con una excepción importante: el acceso a datos específicos de subprocesos para aplicaciones o datos específicos de CPU para núcleos de sistemas operativos, que se realiza con el uso explícito de los registros de segmento FS y GS. Todo acceso a la memoria implica un registro de segmento, elegido de acuerdo con el código que se está ejecutando. El registro de segmento actúa como un índice en una tabla, que proporciona un desplazamiento que se agregará a la dirección virtual. Excepto cuando se usa FS o GS, el sistema operativo garantiza que el desplazamiento será cero.
Después de añadir el desplazamiento, la dirección se enmascara para que no supere los 32 bits. El resultado se puede buscar a través de una tabla de páginas con estructura de árbol, en la que los bits de la dirección se dividen de la siguiente manera: 10 bits para la rama del árbol, 10 bits para las hojas de la rama y los 12 bits más bajos se copian directamente al resultado. Algunos sistemas operativos, como OpenBSD con su función W^X y Linux con los parches Exec Shield o PaX , también pueden limitar la longitud del segmento de código, según lo especificado por el registro CS, para no permitir la ejecución de código en regiones modificables del espacio de direcciones.
Las revisiones menores de la MMU introducidas con el Pentium han permitido páginas muy grandes de 4 MB saltando el nivel inferior del árbol (esto deja 10 bits para indexar el primer nivel de la jerarquía de páginas y los 10+12 bits restantes se copian directamente al resultado). Las revisiones menores de la MMU introducidas con el Pentium Pro introdujeron la característica de extensión de dirección física (PAE), que permite direcciones físicas de 36 bits con 2+9+9 bits para tablas de páginas de tres niveles y los 12 bits más bajos se copian directamente al resultado. Las páginas grandes ( 2 MB ) también están disponibles saltando el nivel inferior del árbol (lo que da como resultado 2+9 bits para la jerarquía de tablas de dos niveles y los 9+12 bits más bajos restantes se copian directamente). Además, la tabla de atributos de página permitió la especificación de la capacidad de almacenamiento en caché buscando algunos bits altos en una pequeña tabla en la CPU.
El soporte de no ejecución originalmente solo se proporcionaba por segmento, lo que lo hacía muy difícil de usar. Los chips x86 más recientes proporcionan un bit de no ejecución por página en el modo PAE. Los mecanismos W^X , Exec Shield y PaX descritos anteriormente emulan el soporte de no ejecución por página en máquinas con procesadores x86 que carecen del bit NX al configurar la longitud del segmento de código, con una pérdida de rendimiento y una reducción en el espacio de direcciones disponible.
x86-64 es una extensión de 64 bits de x86 que elimina casi por completo la segmentación en favor del modelo de memoria plana que utilizan casi todos los sistemas operativos para los procesadores 386 o más nuevos. En el modo largo, se ignoran todos los desplazamientos de segmento, excepto los segmentos FS y GS. Cuando se utiliza con páginas de 4 KB , el árbol de la tabla de páginas tiene cuatro niveles en lugar de tres.
Las direcciones virtuales se dividen de la siguiente manera: 16 bits sin utilizar, nueve bits para cada uno de los cuatro niveles del árbol (para un total de 36 bits) y los 12 bits más bajos copiados directamente al resultado. Con páginas de 2 MB , solo hay tres niveles de tabla de páginas, para un total de 27 bits utilizados en paginación y 21 bits de desplazamiento. Algunas CPU más nuevas también admiten una página de 1 GB con dos niveles de paginación y 30 bits de desplazamiento. [25]
Se puede utilizar CPUID para determinar si se admiten páginas de 1 GB . En los tres casos, se requiere que los 16 bits más altos sean iguales al bit 48, o en otras palabras, los 48 bits más bajos se extienden a los bits más altos. Esto se hace para permitir una expansión futura del rango direccionable, sin comprometer la compatibilidad con versiones anteriores. En todos los niveles de la tabla de páginas, la entrada de la tabla de páginas incluye un bit de no ejecución .
El Burroughs B5000 de 1961 fue el primer sistema comercial que admitió memoria virtual (después del Atlas ), no necesita una MMU externa. [26] El B5000 y sus sucesores hasta los sistemas actuales Unisys ClearPath MCP (Libra) proporcionan las dos funciones de una MMU (direcciones de memoria virtual y protección de memoria) con un enfoque arquitectónico diferente. En lugar de agregar memoria virtual a un procesador no diseñado para memoria virtual, se integra en el diseño central del procesador/sistema, por lo que no hay necesidad de una unidad externa para agregar funcionalidad de traducción de direcciones o verificación de límites, lo que resulta en una seguridad de memoria sin precedentes.
En primer lugar, en el mapeo de direcciones de memoria virtual, en lugar de necesitar una MMU, las máquinas están basadas en descriptores . [27] [28] El bit superior de una palabra de memoria de 48 bits en los sistemas de la serie Burroughs B5000 indica si la palabra es un dato de usuario o una palabra descriptor/de control; los descriptores eran de solo lectura para los procesos de usuario y solo podían ser actualizados por el sistema (hardware o sistema operativo). Las palabras de memoria en los sistemas B6x00/B7x00/B5900/A-series/Unisys MCP tienen 48 bits de datos y 3 bits de etiqueta (y los sistemas posteriores a la actualidad tienen 4 bits de etiqueta); las palabras cuya etiqueta es un número impar son de solo lectura para los procesos de usuario; los descriptores tienen una etiqueta de 5 y las palabras de código tienen una etiqueta de 3.
Cada bloque de memoria asignado recibe un descriptor maestro con las propiedades del bloque: dirección física, tamaño y si está presente en la memoria principal o no, en cuyo caso, en el primer acceso, el bloque debe asignarse o, si hay una dirección, es una dirección en el almacenamiento secundario y debe cargarse en la memoria principal. Todas las referencias a código y datos se realizan mediante un descriptor. Cuando se realiza una solicitud para acceder al bloque para leer o escribir, el hardware verifica su presencia a través del bit de presencia (pbit) en el descriptor.
Un pbit [29] de 1 indica la presencia del bloque. En este caso, se puede acceder al bloque a través de la dirección de memoria principal física en el descriptor. Si el pbit es cero, se genera una interrupción pbit [30] para que el MCP (sistema operativo) haga que el bloque esté presente. Si el campo de dirección es cero, este es el primer acceso a este bloque y se le asigna (un pbit init (inicial)). Si el campo de dirección no es cero, es una dirección de disco del bloque, que se ha implementado previamente: el bloque se obtiene del disco, el pbit se establece en uno y la dirección de memoria física se actualiza para apuntar al bloque en la memoria. Esto hace que los descriptores sean equivalentes a una entrada de tabla de páginas en un sistema MMU, pero los descriptores están libres de una tabla.
El rendimiento del sistema se puede monitorear a través de la cantidad de interrupciones pbit, que es el recuento de accesos a descriptores con un bit cero. Los pbits 'Init' indican asignaciones iniciales o nuevos bloques de memoria, pero un alto nivel de pbits 'other' indica que el sistema tuvo que cargar un bloque desde el almacenamiento secundario de memoria virtual. En un funcionamiento normal, esto no ocurrirá con mucha frecuencia. Si hay una cantidad superior a la media de otros pbits que ocurren, el sistema podría estar fallando, lo que indica una sobrecarga del sistema. En sistemas de producción, la carga de la máquina será más constante y, por lo tanto, el sistema estará configurado o ajustado para evitar fallas.
Por lo tanto, toda la asignación de memoria es completamente automática (una de las características de los sistemas modernos [31] ) y no hay otra forma de asignar bloques que este mecanismo. No existen llamadas como malloc o dealloc, ya que los bloques de memoria también se asignan automáticamente en caso de interrupción de pbit o se descartan. El esquema también es perezoso , ya que un bloque no se asignará hasta que se haga referencia a él. Cuando la memoria está casi llena, el MCP examina el conjunto de trabajo, intentando la compactación (ya que el sistema está segmentado, no paginado), desasignando segmentos de solo lectura (como segmentos de código que se pueden restaurar a partir de su copia original) y, como último recurso, sacando al disco los segmentos de datos sucios (es decir, actualizados).
Otra forma en que estos sistemas proporcionan una función de MMU es en la protección. Dado que todos los accesos se realizan a través del descriptor, el hardware puede verificar que todos los accesos estén dentro de los límites y, en el caso de una escritura, que el proceso tenga permiso de escritura. El sistema MCP es inherentemente seguro y, por lo tanto, no necesita una MMU para proporcionar este nivel de protección de la memoria.
Los bloques se pueden compartir entre procesos a través de descriptores de copia en pilas de procesos (una pila es una estructura especial del sistema que representa el estado de ejecución de un proceso). Por lo tanto, algunos procesos pueden tener permiso de escritura, mientras que otros no. Los segmentos de código son de solo lectura, por lo tanto reentrantes y compartidos entre procesos. Los descriptores de copia contienen un campo de dirección de 20 bits que proporciona el índice del descriptor maestro en la matriz de descriptores maestros. Esto también implementa un mecanismo de comunicación entre procesos (IPC) muy eficiente y seguro. Los bloques se pueden reubicar fácilmente, ya que solo es necesario actualizar el descriptor maestro cuando cambia el estado de un bloque.
Otro aspecto es el rendimiento: ¿los sistemas basados en MMU o no basados en MMU ofrecen un mejor rendimiento? Los sistemas MCP pueden implementarse sobre hardware estándar que sí tenga una MMU (por ejemplo, una PC estándar). Incluso si la implementación del sistema utiliza la MMU de alguna manera, esto no será visible en absoluto a nivel de MCP.