← Gestión de Memoria Bloque 4 · Segmentación · Tema 15 de 52

Tema 15

Segmentación

La asignación contigua trata el programa como un bloque único. La segmentación lo divide en segmentos lógicos (código, datos, pila, heap) que se asignan de forma independiente. La dirección deja de ser un número y pasa a ser un par (segmento, desplazamiento). En este tema verás la idea, sus ventajas y por qué fue un paso intermedio hacia la paginación.

1. El problema: la asignación contigua es rígida

Con base y límite (tema 11) y particiones (tema 12), el programa es un bloque contiguo en la RAM. Esto tiene inconvenientes:

La segmentación resuelve estos problemas dividiendo el programa en partes lógicas independientes.

2. La idea: segmentos lógicos

Un programa no es un bloque uniforme: tiene código, datos, pila y heap, cada uno con tamaño y permisos distintos. La segmentación trata cada parte como un segmento independiente, con su propia base y límite.

Cada segmento se carga en un bloque contiguo de la RAM, pero los segmentos de un mismo proceso pueden estar en posiciones distintas (no contiguos entre sí). Así, el código puede estar en una zona y la pila en otra.

Segmentación es la técnica que divide el espacio de direcciones de un proceso en segmentos lógicos (código, datos, pila, heap), cada uno con su propia base, límite y permisos, asignables de forma independiente en la RAM.

3. Dirección lógica: segmento + desplazamiento

La dirección deja de ser un número y pasa a ser un par (s, d):

La MMU traduce: si d < límite[s], la dirección física es base[s] + d. Si no, excepción de protección.

En hardware, la dirección lógica suele codificarse con los bits altos indicando el segmento y los bajos el desplazamiento.

Traducción con segmentación: segmento + desplazamiento → tabla → base + desplazamiento Dirección lógica (s=2, d=0x100) Tabla de segmentos base[2] = 0x20000 límite[2] = 0x5000 RAM física 0x20100 s física
La MMU usa el número de segmento para indexar la tabla, comprueba el desplazamiento contra el límite y suma la base.

4. Segmentos típicos

Un programa suele tener estos segmentos:

SegmentoContenidoPermisosCrecimiento
Código (text)Instruccionesr-xNo (fijo)
Datos (data)Globales inicializadasrw-No (fijo)
BSSGlobales no inicializadasrw-No (fijo)
HeapMemoria dinámicarw-Hacia arriba
Pila (stack)Variables localesrw-Hacia abajo

El heap y la pila crecen en direcciones opuestas dentro de sus propios segmentos, sin afectar a los demás.

5. Ventajas

6. Limitaciones

Estas limitaciones motivaron la paginación (tema 18), que combina segmentación y paginación (segmentación paginada).

7. Ejemplo numérico

Un proceso tiene 3 segmentos:

SegmentoBaseLímitePermisos
0 (código)0x100000x3000r-x
1 (datos)0x200000x2000rw-
2 (pila)0x300000x1000rw-

Traducción de direcciones:

Dirección lógicaSegmentoDesplazamiento¿Válida?Física
(0, 0x0500)00x0500Sí0x10500
(1, 0x1FFF)10x1FFFSí0x21FFF
(2, 0x0FFF)20x0FFFSí0x30FFF
(0, 0x3000)00x3000No (límite)—
(1, 0x2000)10x2000No (límite)—

8. Experimento en C

El programa siguiente simula la traducción con segmentación:

segmentacion.c
#include <stdio.h>

typedef struct { unsigned long base, limite; int perm; } Segmento;

static int traducir(Segmento *t, int s, unsigned long d, unsigned long *fis)
{
    if (s < 0 || s >= 3) return 0;          /* segmento inválido */
    if (d >= t[s].limite) return 0;         /* desplazamiento fuera de rango */
    *fis = t[s].base + d;
    return 1;
}

int main(void)
{
    Segmento t[3] = {
        {0x10000, 0x3000, 5},  /* código: r-x */
        {0x20000, 0x2000, 6},  /* datos: rw- */
        {0x30000, 0x1000, 6}   /* pila: rw- */
    };
    int segs[] = {0, 1, 2, 0, 1};
    unsigned long offs[] = {0x0500, 0x1FFF, 0x0FFF, 0x3000, 0x2000};
    for (int i = 0; i < 5; i++) {
        unsigned long f;
        if (traducir(t, segs[i], offs[i], &f))
            printf("seg %d off 0x%04lx -> fisica 0x%04lx\\n", segs[i], offs[i], f);
        else
            printf("seg %d off 0x%04lx -> EXCEPCION\\n", segs[i], offs[i]);
    }
    return 0;
}
terminal · traducción con segmentación
$ gcc -std=c17 -Wall -Wextra -o segmentacion segmentacion.c
$ ./segmentacion
seg 0 off 0x0500 -> fisica 0x10500
seg 1 off 0x1fff -> fisica 0x21fff
seg 2 off 0x0fff -> fisica 0x30fff
seg 0 off 0x3000 -> EXCEPCION
seg 1 off 0x2000 -> EXCEPCIONseg 1 off 0x2000 -> EXCEPCION

9. Probar con un simulador

Este simulador traduce direcciones lógicas (segmento, desplazamiento) usando una tabla de segmentos editable.

Simulador · segmentación

CampoValor
Base del segmento
Límite del segmento
¿Dentro del límite?
Dirección física

Tabla de segmentos (editable):

10. En Linux y x86 hoy

En x86-64, la segmentación se usa de forma plana: todos los segmentos (CS, DS, SS, ES) tienen base 0 y límite máximo. La protección y la traducción real se hacen con paginación. Pero el concepto de segmentos sigue presente en:

11. Resumen

12. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué es un segmento?

Una parte lógica del proceso (código, datos, pila, heap) con su propia base, límite y permisos.

2. ¿Cómo se forma una dirección lógica en segmentación?

Como un par (segmento, desplazamiento): el número de segmento indexa la tabla, y el desplazamiento se suma a la base.

3. ¿Qué ventaja tiene la segmentación sobre la asignación contigua?

Permite asignar cada segmento de forma independiente, compartir código y proteger por segmento.

4. ¿Qué limitación tiene la segmentación?

Fragmentación externa (cada segmento es un bloque contiguo) y la necesidad de bloques grandes para segmentos grandes.

5. ¿Cómo se traduce una dirección (s, d)?

Si d < límite[s], la física es base[s] + d; si no, excepción de protección.

6. ¿Por qué la pila y el heap pueden crecer independientemente?

Porque son segmentos separados con sus propias bases y límites; crecer uno no afecta al otro.

7. ¿Se usa la segmentación en x86-64 moderno?

De forma plana (base 0, límite máximo) para CS/DS/SS/ES; FS y GS se usan para TLS y datos del núcleo.

8. ¿Qué problema resuelve la paginación que la segmentación no?

La fragmentación externa y la necesidad de bloques contiguos grandes.

Para practicar

  1. Modifica segmentacion.c para añadir un segmento de heap (segmento 3) con base 0x40000 y límite 0x2000.
  2. Traduce a mano las direcciones (0, 0x2FFF), (3, 0x1FFF) y (1, 0x2000). ¿Cuáles son válidas?
  3. Explica por qué la compartición de código es natural con segmentación.
  4. Dibuja la RAM con 3 segmentos de un proceso y huecos entre ellos. ¿Hay fragmentación externa?
  5. Investiga cómo usa Linux los segmentos FS y GS en x86-64.
  6. Compara la segmentación con la paginación: ¿qué ventaja tiene cada una?
  7. Implementa en C una tabla de segmentos con permisos (r, w, x) y comprueba los accesos.