La interfaz de programación de Linux muestra el diseño de un espacio de direcciones virtual de un proceso. ¿Cada región en el diagrama es un segmento?
De comprender el kernel de Linux ,
¿es correcto que lo siguiente significa que la unidad de segmentación en MMU mapea los segmentos y las compensaciones dentro de los segmentos en la dirección de memoria virtual, y la unidad de paginación luego mapea la dirección de memoria virtual en la dirección de memoria física?
La Unidad de Administración de Memoria (MMU) transforma una dirección lógica en una dirección lineal por medio de un circuito de hardware llamado unidad de segmentación; posteriormente, un segundo circuito de hardware llamado unidad de paginación transforma la dirección lineal en una dirección física (consulte la Figura 2-1).
Entonces, ¿por qué dice que Linux no usa segmentación sino solo paginación?
La segmentación se ha incluido en microprocesadores de 80x86 para alentar a los programadores a dividir sus aplicaciones en entidades lógicamente relacionadas, como subrutinas o áreas de datos globales y locales. Sin embargo, Linux usa la segmentación de una manera muy limitada. De hecho, la segmentación y la paginación son algo redundantes, porque ambos se pueden usar para separar los espacios de direcciones físicas de los procesos: la segmentación puede asignar un espacio de direcciones lineal diferente a cada proceso, mientras que la paginación puede mapear el mismo espacio de direcciones lineales en diferentes espacios de direcciones físicas . Linux prefiere la paginación a la segmentación por las siguientes razones:
• La gestión de la memoria es más simple cuando todos los procesos usan los mismos valores de registro de segmento, es decir, cuando comparten el mismo conjunto de direcciones lineales.
• Uno de los objetivos de diseño de Linux es la portabilidad a una amplia gama de arquitecturas; Las arquitecturas RISC, en particular, tienen soporte limitado para la segmentación.
La versión 2.6 de Linux usa segmentación solo cuando lo requiere la arquitectura 80x86.
Respuestas:
La arquitectura x86-64 no utiliza segmentación en modo largo (modo de 64 bits).
Cuatro de los registros de segmento: CS, SS, DS y ES están forzados a 0, y el límite a 2 ^ 64.
https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments
Ya no es posible que el sistema operativo limite qué rangos de las "direcciones lineales" están disponibles. Por lo tanto, no puede usar la segmentación para la protección de la memoria; debe depender completamente de la paginación.
No se preocupe por los detalles de las CPU x86 que solo se aplicarían cuando se ejecute en los modos heredados de 32 bits. Linux para los modos de 32 bits no se usa tanto. Incluso puede considerarse "en un estado de negligencia benigna durante varios años". Ver soporte x86 de 32 bits en Fedora [LWN.net, 2017].
(Sucede que Linux de 32 bits tampoco usa la segmentación. Pero no necesita confiar en mí en eso, simplemente puede ignorarlo :-).
fuente
mov eax, [fs:rdi + 16]
). El kernel usa GS (afterswapgs
) para encontrar la pila de kernel del proceso actual en elsyscall
punto de entrada. Pero sí, la segmentación no se usa como parte del mecanismo principal de gestión de memoria / protección de memoria del sistema operativo.Como el x86 tiene segmentos, no es posible no usarlos. Pero tanto
cs
(segmento de código) comods
direcciones base (segmento de datos) se establecen en cero, por lo que la segmentación no se usa realmente. Una excepción son los datos locales de subprocesos, uno de los segmentos de segmento normalmente no utilizados registra los puntos para enlazar datos locales. Pero eso es principalmente para evitar reservar uno de los registros de propósito general para esta tarea.No dice que Linux no use la segmentación en el x86, ya que eso no sería posible. Ya destacó una parte, Linux usa la segmentación de una manera muy limitada . La segunda parte es que Linux usa la segmentación solo cuando lo requiere la arquitectura 80x86
Ya mencionó las razones, la paginación es más fácil y más portátil.
fuente
No.
Mientras que el sistema de segmentación (en modo protegido de 32 bits en un x86) está diseñado para admitir segmentos separados de código, datos y pila, en la práctica todos los segmentos están configurados en la misma área de memoria. Es decir, comienzan en 0 y terminan en el final de la memoria (*) . Eso hace que las direcciones lógicas y las direcciones lineales sean iguales.
Esto se llama un modelo de memoria "plana", y es algo más simple que el modelo donde tiene segmentos distintos y luego punteros dentro de ellos. En particular, un modelo segmentado requiere punteros más largos, ya que se debe incluir el selector de segmento además del puntero de desplazamiento. (Selector de segmento de 16 bits + desplazamiento de 32 bits para un puntero total de 48 bits; frente a un puntero plano de 32 bits).
El modo largo de 64 bits realmente ni siquiera admite segmentación que no sea el modelo de memoria plana.
Si programara en modo protegido de 16 bits en el 286, tendría más necesidad de segmentos, ya que el espacio de direcciones es de 24 bits pero los punteros son solo de 16 bits.
(* Tenga en cuenta que no recuerdo cómo Linux de 32 bits maneja la separación kernel / espacio de usuario. La segmentación lo permitiría mediante la configuración de los límites de segmentos de espacio de usuario para que no incluyan el espacio del kernel. La paginación lo permite ya que proporciona un nivel de protección por página).
El x86 todavía tiene los segmentos y no puede deshabilitarlos. Solo se usan lo menos posible. En el modo protegido de 32 bits, los segmentos deben configurarse para el modelo plano, e incluso en el modo de 64 bits todavía existen.
fuente
wrfsbase
es ilegal en modo protegido / compat, solo modo largo, por lo que en un espacio de usuario de kernel de 32 bits no se pudo establecer la base FS alta.Linux x86 / 32 no utiliza la segmentación en el sentido de que inicializa todos los segmentos a la misma dirección lineal y límite. La arquitectura x86 requiere que el programa tenga segmentos: el código solo puede ejecutarse desde el segmento de código, la pila solo puede ubicarse en el segmento de pila, los datos solo pueden manipularse en uno de los segmentos de datos. Linux omite este mecanismo al configurar todos los segmentos de la misma manera (con excepciones que su libro no menciona de todos modos), de modo que la misma dirección lógica sea válida en cualquier segmento. De hecho, esto es equivalente a no tener segmentos en absoluto.
fuente
Estos son 2 usos casi totalmente diferentes de la palabra "segmento"
Los usos tienen un origen común: si estaba utilizando un modelo de memoria segmentada (especialmente sin memoria virtual paginada), es posible que los datos y las direcciones BSS sean relativos a la base del segmento DS, la pila relativa a la base SS y el código relativo a la Dirección base de CS.
Por lo tanto, se pueden cargar múltiples programas diferentes en diferentes direcciones lineales, o incluso moverlos después de comenzar, sin cambiar los desplazamientos de 16 o 32 bits en relación con las bases de segmento.
Pero luego debe saber a qué segmento se refiere un puntero, por lo que tiene "punteros lejanos" y así sucesivamente. (Los programas x86 de 16 bits reales a menudo no necesitaban acceder a su código como datos, por lo que podrían usar un segmento de código de 64k en algún lugar, y tal vez otro bloque de 64k con DS = SS, con la pila creciendo desde grandes compensaciones y datos en la parte inferior, o un pequeño modelo de código con todas las bases de segmentos iguales).
Cómo interactúa la segmentación x86 con la paginación
La asignación de direcciones en modo de 32/64 bits es:
Las tablas de páginas (almacenadas en caché por TLB) se asignan linealmente a 32 (modo heredado), 36 (PAE heredado) o dirección física de 52 bits (x86-64). ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).
Este paso es opcional: la paginación debe habilitarse durante el arranque configurando un bit en un registro de control. Sin paginación, las direcciones lineales son direcciones físicas.
Tenga en cuenta que la segmentación no le permite usar más de 32 o 64 bits de espacio de direcciones virtuales en un solo proceso (o subproceso) , porque el espacio de direcciones plano (lineal) en el que se asigna todo solo tiene el mismo número de bits que los propios desplazamientos. (Este no fue el caso para x86 de 16 bits, donde la segmentación fue realmente útil para usar más de 64k de memoria con registros y compensaciones en su mayoría de 16 bits).
La CPU almacena en caché los descriptores de segmento cargados desde el GDT (o LDT), incluida la base del segmento. Cuando desreferencia un puntero, según el registro en el que se encuentre, el valor predeterminado es DS o SS como segmento. El valor del registro (puntero) se trata como un desplazamiento desde la base del segmento.
Como la base del segmento es normalmente cero, las CPU hacen esto en casos especiales. O desde otro punto de vista, si hacer una base de segmento distinto de cero, las cargas tienen una latencia adicional porque el caso (normal) "especial" de pasar por la adición de la dirección de base no se aplica.
Cómo Linux configura los registros de segmento x86:
La base y el límite de CS / DS / ES / SS son todos 0 / -1 en modo de 32 y 64 bits. Esto se denomina modelo de memoria plana porque todos los punteros apuntan al mismo espacio de direcciones.
(Los arquitectos de CPU de AMD neutralizaron la segmentación al aplicar un modelo de memoria plana para el modo de 64 bits porque los sistemas operativos principales no lo usaban de todos modos, excepto por la protección no ejecutiva que se proporcionó de una manera mucho mejor al paginar con el PAE o x86- Formato de tabla de 64 páginas).
TLS (Thread Local Storage): FS y GS no están fijos en base = 0 en modo largo. (Eran nuevos con 386, y no fueron utilizados implícitamente por ninguna instrucción, ni siquiera las
rep
instrucciones de cadena que usan ES). x86-64 Linux establece la dirección base FS para cada subproceso en la dirección del bloque TLS.Por ejemplo,
mov eax, [fs: 16]
carga un valor de 32 bits de 16 bytes en el bloque TLS para este hilo.el descriptor de segmento CS elige en qué modo se encuentra la CPU (modo protegido de 16/32/64 bits / modo largo). Linux usa una sola entrada GDT para todos los procesos de espacio de usuario de 64 bits y otra entrada GDT para todos los procesos de espacio de usuario de 32 bits. (Para que la CPU funcione correctamente, DS / ES también debe establecerse en entradas válidas, y también lo hace SS). También elige el nivel de privilegio (kernel (anillo 0) vs. usuario (anillo 3)), por lo que incluso al regresar al espacio de usuario de 64 bits, el núcleo todavía tiene que organizar el cambio de CS, usando
iret
o ensysret
lugar de un normal instrucción de salto o ret.En x86-64, el
syscall
punto de entrada usaswapgs
para voltear GS del GS del espacio de usuario al kernel, que usa para encontrar la pila de kernel para este hilo. (Un caso especializado de almacenamiento local de subprocesos). Lasyscall
instrucción no cambia el puntero de la pila para apuntar a la pila del núcleo; todavía apunta a la pila de usuarios cuando el núcleo alcanza el punto de entrada 1 .DS / ES / SS también debe establecerse en descriptores de segmento válidos para que la CPU funcione en modo protegido / modo largo, aunque la base / límite de esos descriptores se ignore en modo largo.
Básicamente, la segmentación x86 se usa para TLS y para el material osdev x86 obligatorio que el hardware requiere que haga.
Nota al pie 1: Historia divertida: hay archivos de listas de correo de mensajes entre desarrolladores de kernel y arquitectos de AMD de un par de años antes del lanzamiento del silicio AMD64, lo que da como resultado ajustes en el diseño de
syscall
modo que sea utilizable. Vea los enlaces en esta respuesta para más detalles.fuente