x86 memory segmentation is a term for the kind of memory segmentation characteristic of the Intel x86 computer instruction set architecture. The x86 architecture has supported memory segmentation since the original Intel 8086 (1978), but x86 memory segmentation is a plainly descriptive retronym. The introduction of memory segmentation mechanisms in this architecture reflects the legacy of earlier 80xx processors, which initially[1] could only address 16 KB, or later[2] 64 KB of memory (16,384 bytes or 65,536 bytes, respectively), and whose instructions and registers were optimised for the latter. Dealing with larger addresses and more memory was thus comparably slower, as that capability was somewhat grafted-on in the Intel 8086. Memory segmentation could keep programs compatible, relocatable in memory, and by confining significant parts of a program's operation to 64 KB segments, the program could still run faster.
In 1982, the Intel 80286 added support for virtual memory and memory protection; the original mode was renamed real mode, and the new version was named protected mode.[3] The x86-64 architecture, introduced in 2003, has largely dropped support for segmentation in 64-bit mode.
In both real and protected modes, the system uses 16-bit segment registers to derive the actual memory address. In real mode, the registers CS, DS, SS, and ES point to the currently used program code segment (CS), the current data segment (DS), the current stack segment (SS), and one extra segment determined by the system programmer (ES). The Intel 80386, introduced in 1985, adds two additional segment registers, FS and GS, with no specific uses defined by the hardware. The way in which the segment registers are used differs between the two modes.[4]
La elección del segmento normalmente la realiza el procesador según la función que se esté ejecutando. Las instrucciones siempre se obtienen del segmento de código. Cualquier referencia de datos a la pila, incluyendo cualquier operación de inserción o extracción, utiliza el segmento de pila; las referencias de datos indirectas a través del registro BP suelen referirse a la pila, por lo que utilizan el segmento de pila por defecto. El segmento adicional es el destino obligatorio para las operaciones de cadena (por ejemplo, MOVS o CMPS); solo para este propósito, el registro de segmento seleccionado automáticamente no se puede sobrescribir. Todas las demás referencias a datos utilizan el segmento de datos por defecto. El segmento de datos es la fuente predeterminada para las operaciones de cadena, pero se puede sobrescribir. El formato de instrucción permite un byte de prefijo de segmento opcional que se puede utilizar para sobrescribir el segmento predeterminado para determinadas instrucciones si se desea. [ 5 ]
Modo real

En el modo real o modo V86 , el tamaño fundamental de un segmento es de 65.536 bytes , y los bytes individuales se direccionan utilizando desplazamientos de 16 bits .
El selector de segmento de 16 bits en el registro de segmento se interpreta como los 16 bits más significativos de una dirección lineal de 20 bits, denominada dirección de segmento, cuyos cuatro bits menos significativos restantes son todos ceros. La dirección de segmento siempre se suma a un desplazamiento de 16 bits en la instrucción para obtener una dirección lineal , que es la misma que la dirección física en este modo. Por ejemplo, la dirección segmentada 06EFh:9234h (donde el sufijo "h" significa hexadecimal ) tiene un selector de segmento de 06EFh, que representa una dirección de segmento de 06EF0h, a la que se le suma el desplazamiento, obteniendo la dirección lineal 06EF0h + 9234h = 10124h.
(Los ceros iniciales de la dirección lineal, las direcciones segmentadas y los campos de segmento y desplazamiento se muestran aquí para mayor claridad. Normalmente se omiten).
Debido a la forma en que se suman la dirección de segmento y el desplazamiento, una única dirección lineal puede asignarse a hasta 2¹² = 4096 pares distintos de segmento:desplazamiento. Por ejemplo, la dirección lineal 08124h puede tener las direcciones segmentadas 06EFh:1234h, 0812h:0004h, 0000h:8124h, etc. Esto podría resultar confuso para los programadores acostumbrados a esquemas de direccionamiento únicos, pero también puede ser ventajoso, por ejemplo, al direccionar múltiples estructuras de datos anidadas.
Aunque técnicamente los segmentos en modo real siempre tienen una longitud de 64 KB , en la práctica esto significa que ningún segmento puede ser más largo que 64 KB, en lugar de que cada segmento utilizado en un programa deba considerarse de 64 KB. Es posible trabajar con segmentos más pequeños: los tamaños utilizables van desde 16 hasta 65 536 bytes, en incrementos de 16 bytes. Dado que en modo real no existe protección ni limitación de privilegios, el programa es quien debe coordinar y mantenerse dentro de los límites de los segmentos. Esto se aplica tanto cuando un segmento se trata programáticamente como menor o igual a los 64 KB, como cuando cualquier programa puede acceder a cualquier memoria simplemente cambiando de segmento, ya que puede configurar arbitrariamente selectores de segmento para cambiar las direcciones de los segmentos sin supervisión alguna. Por lo tanto, si bien se puede pensar que el modo real permite diferentes longitudes de segmento y que los segmentos se superpongan o no según se desee, la CPU no impone ninguna restricción al respecto.
El espacio de direcciones efectivo de 20 bits de las CPU de la generación PC/XT limita la memoria direccionable a 2²⁰ bytes, o 1.048.576 bytes (1 MB ). Esto se derivó directamente del diseño de hardware del Intel 8086 (y, posteriormente, del estrechamente relacionado 8088), que tenía exactamente 20 pines de dirección . (Ambos se presentaban en encapsulados DIP de 40 pines; incluso con solo 20 líneas de dirección, los buses de dirección y datos se multiplexaban para que todas las líneas de dirección y datos cupieran dentro del número limitado de pines).
Cada segmento comienza a una distancia múltiplo de 16 bytes, denominada párrafo , desde el inicio del espacio de direcciones lineal (plano). Es decir, a intervalos de 16 bytes. Dado que todos los segmentos tienen técnicamente 64 KB de longitud, esto explica cómo puede producirse la superposición entre segmentos y por qué se puede acceder a cualquier ubicación en el espacio de direcciones de memoria lineal con múltiples pares segmento:desplazamiento. La ubicación real del inicio de un segmento en el espacio de direcciones lineal se puede calcular multiplicando el tamaño del segmento por 16. Estas traducciones de direcciones las realiza la unidad de segmentación de la CPU.
Peculiaridad al final del espacio de direcciones
The last segment, FFFFh (65535), begins at linear address FFFF0h (1048560), 16 bytes before the end of the 20-bit address space, and thus can access, with an offset of up to 65,536 bytes, up to 65,520 (65536−16) bytes past the end of the 20-bit address space of the 8086 or 8088 CPU. A further 4,094 next-highest 64K-segments also still cross that 1MB-threshold, but by less and less. On the 8086 and 8088 CPUs, these address accesses were wrapped around to the beginning of the address space such that 65535:16 would access address 0, and e.g. 65533:1000 would access address 952 of the linear address space. The fact that some programs written for the 8088 and 8086 relied on this quirky wrap-around as a feature led to the Gate A20 compatibility issues in later CPU generations, with the Intel 286 and above, where the linear address space was expanded past 20 bits.
In 16-bit real mode, enabling applications to make use of multiple memory segments for a single data structure (in order to access more memory than available in any one 64K-segment) is quite complex, but was viewed as a necessary evil for all but the smallest tools (which could do with less memory). The root of the problem is that no appropriate address-arithmetic instructions suitable for flat addressing of the entire memory range are available. Flat addressing is possible by applying multiple instructions, which however leads to slower programs.
The memory model concept derives from the setup of the segment registers. For example, in the tiny model CS=DS=ES=SS, that is the program's code, data, and stack are all contained within a single 64 KB segment. In the small memory model DS=SS, so both data and stack reside in the same segment; CS points to a different code segment of up to 64 KB.
Protected mode

80286 protected mode
El modo protegido del 80286 extiende el espacio de direcciones del procesador a 2²⁴ bytes (16 megabytes), pero no ajustando el valor de desplazamiento utilizado para calcular una dirección de segmento a partir del valor en un registro de segmento. En cambio, cada registro de segmento de 16 bits ahora contiene un índice en una tabla de descriptores de segmento que contienen direcciones base de 24 bits a las que se suman desplazamientos. Para admitir software antiguo, el procesador arranca en "modo real", un modo en el que utiliza el modelo de direccionamiento segmentado del 8086. Sin embargo, hay una pequeña diferencia: la dirección física resultante ya no se trunca a 20 bits, por lo que los punteros del modo real (pero no los punteros del 8086) ahora pueden referirse a direcciones desde 100000¹⁶ hasta 10FFEF¹⁶ . Esta región de memoria de casi 64 kilobytes se conocía como Área de Memoria Alta (HMA), y las versiones posteriores de DOS podían usarla para aumentar la memoria "convencional" disponible (es decir, dentro del primer MB ), moviendo partes de DOS de la memoria convencional a la HMA. Con la adición de la HMA, el espacio de direcciones total es de aproximadamente 1,06 MB. Aunque el 80286 no trunca las direcciones de modo real a 20 bits, un sistema que contiene un 80286 puede hacerlo con hardware externo al procesador, desactivando la línea de dirección 21, la línea A20 . El IBM PC AT proporcionó el hardware para hacer esto (para compatibilidad con versiones anteriores del software de los modelos originales IBM PC y PC/XT ), y todos los clones de PC de " clase AT " posteriores también lo hicieron.
El modo protegido 286 se usaba raramente, ya que habría excluido a la gran cantidad de usuarios con máquinas 8086/88. Además, seguía requiriendo dividir la memoria en segmentos de 64k, como se hacía en el modo real. Esta limitación se puede sortear en las CPU de 32 bits, que permiten el uso de punteros de memoria de más de 64k, pero como el campo Límite de Segmento solo tiene 24 bits, el tamaño máximo de segmento que se puede crear es de 16 MB (aunque se puede usar paginación para asignar más memoria, ningún segmento individual puede superar los 16 MB). Este método se usaba comúnmente en las aplicaciones de Windows 3.x para producir un espacio de memoria plano, aunque como el propio sistema operativo seguía siendo de 16 bits, no se podían realizar llamadas a la API con instrucciones de 32 bits. Por lo tanto, seguía siendo necesario colocar todo el código que realizaba llamadas a la API en segmentos de 64k.
Una vez activado el modo protegido 286, normalmente no se podía salir de él salvo mediante un reinicio del hardware. Las máquinas que seguían el estándar IBM PC/AT podían simular un reinicio de la CPU mediante el controlador de teclado estandarizado, pero esto resultaba muy lento. Windows 3.x solucionó ambos problemas provocando intencionadamente un triple fallo en los mecanismos de gestión de interrupciones de la CPU, lo que hacía que el hardware compatible con IBM AT reiniciara la CPU casi instantáneamente, provocando así que volviera al modo real. [ 6 ]
Flujo de trabajo detallado de la unidad de segmentación
Una dirección lógica consta de un selector de segmento de 16 bits (que proporciona 13+1 bits de dirección) y un desplazamiento de 16 bits. El selector de segmento debe estar ubicado en uno de los registros de segmento. Dicho selector consta de un Nivel de Privilegio Solicitado (RPL) de 2 bits, un Indicador de Tabla (TI) de 1 bit y un índice de 13 bits.
Al intentar la traducción de direcciones de una dirección lógica determinada, el procesador lee la estructura del descriptor de segmento de 64 bits de la Tabla de descriptores globales cuando TI=0 o de la Tabla de descriptores locales cuando TI=1. A continuación, realiza la comprobación de privilegios:
- max(CPL, RPL) ≤ DPL
donde CPL es el nivel de privilegio actual (ubicado en los 2 bits inferiores del registro CS), RPL es el nivel de privilegio solicitado al selector de segmento y DPL es el nivel de privilegio del descriptor del segmento (ubicado en el descriptor). Todos los niveles de privilegio son números enteros en el rango 0-3, donde el número más bajo corresponde al privilegio más alto.
Si la desigualdad es falsa, el procesador genera un fallo de protección general (GP) . De lo contrario, la traducción de direcciones continúa. A continuación, el procesador toma el desplazamiento de 16 bits y lo compara con el límite de segmento especificado en el descriptor de segmento. Si es mayor, se genera un fallo GP. En caso contrario, el procesador suma la base de segmento de 24 bits, especificada en el descriptor, al desplazamiento, creando así una dirección física lineal.
La comprobación de privilegios se realiza únicamente cuando se carga el registro de segmento, ya que los descriptores de segmento se almacenan en caché en partes ocultas de los registros de segmento. [ 4 ]
Modo protegido 80386
In the Intel 80386 and later, protected mode retains the segmentation mechanism of 80286 protected mode, but a paging unit has been added as a second layer of address translation between the segmentation unit and the physical bus. Also, importantly, address offsets are 32-bit (instead of 16-bit), and the segment base in each segment descriptor is also 32-bit (instead of 24-bit). The general operation of the segmentation unit is otherwise unchanged. The paging unit may be enabled or disabled; if disabled, operation is the same as on the 80286. If the paging unit is enabled, addresses in a segment are now virtual addresses, rather than physical addresses as they were on the 80286. That is, the segment starting address, the offset, and the final 32-bit address the segmentation unit derived by adding the two are all virtual (or logical) addresses when the paging unit is enabled. When the segmentation unit generates and validates these 32-bit virtual addresses, the enabled paging unit finally translates these virtual addresses into physical addresses. The physical addresses are 32-bit on the 386, but can be larger on newer processors which support Physical Address Extension.
As mentioned above, the 80386 also introduced two new general-purpose data segment registers, FS and GS, to the original set of four segment registers (CS, DS, ES, and SS).
A 386 CPU can be put back into real mode by clearing a bit in the CR0 control register, however this is a privileged operation in order to enforce security and robustness. By way of comparison, a 286 could only be returned to real mode by forcing a processor reset, e.g. by a triple fault or using external hardware.
Later developments
The x86-64 architecture does not use segmentation in long mode (64-bit mode). Four of the segment registers, CS, SS, DS, and ES, are forced to base address 0, and the limit to 264. The segment registers FS and GS can still have a nonzero base address. This allows operating systems to use these segments for special purposes. Unlike the global descriptor table mechanism used by legacy modes, the base address of these segments is stored in a model-specific register. The x86-64 architecture further provides the special SWAPGS instruction, which allows swapping the kernel mode and user mode base addresses.
Por ejemplo, Microsoft Windows en x86-64 utiliza el segmento GS para apuntar al Thread Environment Block , una pequeña estructura de datos para cada hilo , que contiene información sobre el manejo de excepciones, variables locales del hilo y otros estados por hilo. De manera similar, el kernel de Linux utiliza el segmento GS para almacenar datos por CPU.
GS/FS también se utilizan en el almacenamiento local de subprocesos y en el protector de pila basado en canary de gcc .
Prácticas
Las direcciones lógicas se pueden especificar explícitamente en lenguaje ensamblador x86 , por ejemplo (sintaxis de AT&T):
movl$42,%fs:(%eax); Equivalent to M[fs:eax]<-42) inRTLo en sintaxis Intel :
mov dword [ fs : eax ], 42Sin embargo, los registros de segmento se suelen utilizar de forma implícita.
- Todas las instrucciones de la CPU se obtienen implícitamente del segmento de código especificado por el selector de segmento almacenado en el registro CS.
- La mayoría de las referencias a memoria provienen del segmento de datos especificado por el selector de segmento almacenado en el registro DS. También pueden provenir de cualquier segmento especificado por el selector de segmento almacenado en el registro de segmento correspondiente, si un prefijo de anulación de segmento precede a la instrucción que realiza la referencia a memoria. La mayoría de las instrucciones que utilizan DS por defecto, aunque no todas, aceptan cualquier otro prefijo de anulación.
- Las referencias a la pila del procesador , ya sean implícitas (por ejemplo, las instrucciones push y pop ) o explícitas ( accesos a memoria mediante los registros (E)SP o (E)BP ), utilizan el segmento de pila especificado por el selector de segmento almacenado en el registro SS. Para las referencias explícitas, el segmento puede ser sobrescrito.
- Las instrucciones de cadena (por ejemplo, stos , movs , cmps ) utilizan el segmento adicional para la cadena de destino especificada por el selector de segmento almacenado en el registro ES.
La segmentación no se puede desactivar en los procesadores x86-32 (esto también es cierto para el modo de 64 bits, pero está fuera del alcance de esta discusión), por lo que muchos sistemas operativos de 32 bits simulan un modelo de memoria plana estableciendo las bases de todos los segmentos en 0 para que la segmentación sea neutral para los programas. Por ejemplo, el kernel de Linux configura solo 4 segmentos de propósito general:
Dado que la base se establece en 0 en todos los casos y el límite en 4 GiB, la unidad de segmentación no afecta a las direcciones que el programa asigna antes de que lleguen a la unidad de paginación . (Esto, por supuesto, se refiere a los procesadores 80386 y posteriores, ya que los procesadores x86 anteriores no disponen de una unidad de paginación).
La versión actual de Linux también utiliza GS para apuntar al almacenamiento local de subprocesos .
Los segmentos pueden definirse como segmentos de código, datos o sistema. Existen bits de permisos adicionales para configurar los segmentos como de solo lectura, lectura/escritura, ejecución, etc.
En modo protegido, el código siempre puede modificar todos los registros de segmento excepto CS (el selector de segmento de código ). Esto se debe a que el nivel de privilegio actual (CPL) del procesador se almacena en los 2 bits inferiores del registro CS. Las únicas formas de elevar el nivel de privilegio del procesador (y recargar CS) son mediante las instrucciones lcall (llamada lejana) e int (interrupción) . De manera similar, las únicas formas de reducir el nivel de privilegio (y recargar CS) son mediante las instrucciones lret (retorno lejano) e iret (retorno de interrupción). En modo real, el código también puede modificar el registro CS mediante un salto lejano (o utilizando una POP CSinstrucción no documentada en el 8086 o 8088). [ 7 ] Por supuesto, en modo real, no hay niveles de privilegio; todos los programas tienen acceso absoluto sin control a toda la memoria y a todas las instrucciones de la CPU.
Para obtener más información sobre la segmentación, consulte los manuales IA-32, disponibles gratuitamente en los sitios web de AMD o Intel .
Notas y referencias
- ↑ en el Intel 8008
- ↑ del Intel 8080
- ↑ "Familia de microprocesadores Intel 80286" . www.cpu-world.com . Consultado el 7 de noviembre de 2025 .
- 1 2 "Manual del desarrollador de software para arquitecturas Intel 64 e IA-32", Volumen 3, "Guía de programación del sistema", publicado en 2011, Página "Vol. 3A 3-11", el libro dice: " Cada registro de segmento tiene una parte "visible" y una parte "oculta". (La parte oculta a veces se denomina "caché de descriptores" o "registro de sombra"). Cuando se carga un selector de segmento en la parte visible de un registro de segmento, el procesador también carga la parte oculta del registro de segmento con la dirección base, el límite de segmento y la información de control de acceso del descriptor de segmento al que apunta el selector de segmento. La información almacenada en caché en el registro de segmento (visible y oculta) permite al procesador traducir direcciones sin tomar ciclos de bus adicionales para leer la dirección base y el límite del descriptor de segmento. "
- ↑ Intel Corporation (2004). IA-32 Intel Architecture Software Developer's Manual Volume 1: Basic Architecture (PDF) .
- ↑ "Blogs de desarrolladores" .
- El operador ↑
POP CSdebe usarse con extrema precaución y tiene una utilidad limitada, ya que cambia inmediatamente la dirección efectiva que se calculará a partir del puntero de instrucción para obtener la siguiente instrucción. Generalmente, un salto lejano es mucho más útil. Su existenciaPOP CSes probablemente accidental, ya que sigue un patrón de códigos de operación de instrucciones PUSH y POP para los cuatro registros de segmento en los microprocesadores 8086 y 8088.
Véase también
Enlaces externos
- Página principal del manual del desarrollador de software de la arquitectura Intel IA-32
- Esquema de direccionamiento Segmento:Desplazamiento
- Gestión de memoria X86