Apuntes DAM
Volver al inicio

Plan de particionado: GPT o MBR, UEFI o BIOS y alineación a 1 MiB

Ejercicio de PythonDifícilUnos 75 minutos

Calcula la tabla de particiones de un disco como un instalador: sectores alineados a 1 MiB, la última partición con el resto del disco, extendida y lógicas en MBR, y los avisos de UEFI, BIOS con GPT, el límite de 2 TiB de MBR o la falta de raíz. GB frente a GiB.

  • GPT y MBR
  • UEFI, BIOS y la partición EFI
  • Sectores y alineación a 1 MiB
  • GB frente a GiB
  • Particiones primarias, extendida y lógicas
  • Validación de un plan

Enunciado

Al instalar un sistema operativo hay que decidir la tabla de particiones (GPT, la moderna, o MBR, la antigua) según el firmware (UEFI o BIOS) y repartir el disco. Los instaladores alinean cada partición a 1 MiB (2048 sectores de 512 bytes), porque así coincide con los bloques internos de los SSD y de los discos de sector de 4 KiB, y el rendimiento no se resiente.

Además, los fabricantes venden los discos en GB (10⁹ bytes) y los sistemas muestran GiB (2³⁰ bytes): un disco de 500 GB tiene 465,66 GiB, y no falta nada.

Qué tiene que hacer el programa

  1. DISCO n unidad, TABLA GPT|MBR, ARRANQUE UEFI|BIOS y una línea PARTICION nombre n unidad por partición (unidades MB, GB y TB decimales, y MiB, GiB y TiB binarias, en mayúsculas o minúsculas); la última puede ser PARTICION nombre RESTO. Si una línea no se entiende: Línea N: no se entiende «…»; si falta DISCO, TABLA o ARRANQUE: Faltan DISCO, TABLA o ARRANQUE. Con errores no se escribe nada más.
  2. Primera línea: Disco de T (S sectores de 512 bytes), tabla X, arranque Y, donde T es el tamaño en la mayor unidad binaria que no dé menos de 1, con dos decimales y coma (465,66 GiB).
  3. La primera partición empieza en el byte 1 MiB (sector 2048). Cada tamaño se redondea hacia arriba a MiB enteros, y cada partición empieza donde acaba la anterior. GPT reserva el último MiB del disco para su copia de la tabla, y el espacio utilizable se redondea hacia abajo a MiB enteros. RESTO toma todo lo que queda; solo puede ir una vez y en la última (si no: Error: RESTO solo puede usarse una vez y en la última partición).
  4. Con MBR y más de 4 particiones, las 3 primeras son primarias (1-3), la 4.ª es la extendida que las contiene y las demás son lógicas, numeradas desde el 5; cada lógica empieza 1 MiB más tarde que el final de la anterior (su EBR).
  5. Si una partición no cabe: Error: nombre no cabe: quedan T (y no se escribe la tabla, pero sí los avisos). Si todo cabe, la tabla: cabecera Nº Partición Inicio Fin Tamaño y una fila por partición con el número (3 columnas), el nombre (16), el sector de inicio (13), el de fin (13) y el tamaño; después Sin asignar: T (o 0).
  6. Avisos, en este orden, como Aviso: …: MBR en un disco de más de 2 TiB; UEFI sin /boot/efi; BIOS con GPT sin bios_grub; falta /; /boot/efi de menos de 100 MiB; MBR con más de 4 particiones. Si no hay ninguno: Plan correcto.

Entrada

DISCO 500 GB, TABLA GPT, ARRANQUE UEFI y una línea PARTICION nombre tamaño unidad (o RESTO) por partición.

Ejemplos de ejecución

Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.

Un portátil con UEFI y GPT

Entrada

DISCO 500 GB
TABLA GPT
ARRANQUE UEFI
PARTICION /boot/efi 512 MiB
PARTICION swap 8 GiB
PARTICION / 60 GiB
PARTICION /home RESTO

Salida por consola

Disco de 465,66 GiB (976562500 sectores de 512 bytes), tabla GPT, arranque UEFI
Nº  Partición        Inicio        Fin           Tamaño
1   /boot/efi        2048          1050623       512,00 MiB
2   swap             1050624       17827839      8,00 GiB
3   /                17827840      143656959     60,00 GiB
4   /home            143656960     976560127     397,16 GiB
Sin asignar: 0
Plan correcto

Un equipo antiguo con BIOS y MBR

Entrada

DISCO 120 GiB
TABLA MBR
ARRANQUE BIOS
PARTICION /boot 1 GiB
PARTICION / 40 GiB
PARTICION swap 4 GiB
PARTICION /var 20 GiB
PARTICION /home 30 GiB

Salida por consola

Disco de 120,00 GiB (251658240 sectores de 512 bytes), tabla MBR, arranque BIOS
Nº  Partición        Inicio        Fin           Tamaño
1   /boot            2048          2099199       1,00 GiB
2   /                2099200       85985279      40,00 GiB
3   swap             85985280      94373887      4,00 GiB
5   /var             94375936      136318975     20,00 GiB
6   /home            136321024     199235583     30,00 GiB
Sin asignar: 25,00 GiB
Aviso: MBR admite 4 particiones primarias: la 4.ª será extendida y las 2 últimas, lógicas

Guía paso a paso

Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.

1. Todo en bytes

Convierte cada tamaño a bytes con un diccionario de unidades. Trabajar en bytes y pasar a sectores (÷512) solo al escribir evita errores de redondeo.

2. Redondear a MiB

Hacia arriba para el tamaño de cada partición y hacia abajo para el final del espacio útil. Un truco para redondear hacia arriba con enteros: -(-t // MIB) * MIB.

3. Recorrer las particiones

Lleva una variable inicio en bytes que empieza en 1 MiB y avanza con cada partición. Antes de colocar una, comprueba que cabe hasta el final útil.

4. Los avisos

Son comprobaciones independientes sobre la lista de nombres y la configuración: guárdalos en una lista y escríbelos al final.

Resuélvelo aquí

El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 3 casos ocultos que buscan los errores típicos.

🐍PythonPlan de particionado: GPT o MBR, UEFI o BIOS y alineación a 1 MiBDifícil

Ejemplo

Entrada (lo que se escribe por teclado)
DISCO 500 GB
TABLA GPT
ARRANQUE UEFI
PARTICION /boot/efi 512 MiB
PARTICION swap 8 GiB
PARTICION / 60 GiB
PARTICION /home RESTO
Salida esperada
Disco de 465,66 GiB (976562500 sectores de 512 bytes), tabla GPT, arranque UEFI
Nº  Partición        Inicio        Fin           Tamaño
1   /boot/efi        2048          1050623       512,00 MiB
2   swap             1050624       17827839      8,00 GiB
3   /                17827840      143656959     60,00 GiB
4   /home            143656960     976560127     397,16 GiB
Sin asignar: 0
Plan correcto
⏳
Test oculto #3
⏳
Test oculto #4
⏳
Test oculto #5
0/5 tests pasados · pulsa un test para ver su entrada y su salida esperada

Solución explicada

Ver la solución completa
python
1import re
2import sys
3
4MIB = 1024 * 1024
5SECTOR = 512
6UNIDADES = {"MIB": MIB, "GIB": 1024 * MIB, "TIB": 1024 * 1024 * MIB, "MB": 10 ** 6, "GB": 10 ** 9, "TB": 10 ** 12}
7
8
9def tam(bytes_):
10    """Tamaño legible en binario con dos decimales y coma: 59,99 GiB."""
11    for unidad, valor in [("TiB", UNIDADES["TIB"]), ("GiB", UNIDADES["GIB"]), ("MiB", MIB)]:
12        if bytes_ >= valor:
13            return f"{bytes_ / valor:.2f}".replace(".", ",") + " " + unidad
14    return f"{bytes_} B"
15
16
17def main():
18    disco = None
19    tabla = arranque = None
20    particiones = []
21    errores = []
22    for n, linea in enumerate(sys.stdin.read().split("\n"), start=1):
23        p = linea.split()
24        if not p:
25            continue
26        clave = p[0].upper()
27        if clave == "DISCO" and len(p) == 3 and re.fullmatch(r"\d+", p[1]) and p[2].upper() in UNIDADES:
28            disco = int(p[1]) * UNIDADES[p[2].upper()]
29        elif clave == "TABLA" and len(p) == 2 and p[1].upper() in ("GPT", "MBR"):
30            tabla = p[1].upper()
31        elif clave == "ARRANQUE" and len(p) == 2 and p[1].upper() in ("UEFI", "BIOS"):
32            arranque = p[1].upper()
33        elif clave == "PARTICION" and len(p) == 3 and p[2].upper() == "RESTO":
34            particiones.append((p[1], None))
35        elif clave == "PARTICION" and len(p) == 4 and re.fullmatch(r"\d+", p[2]) and p[3].upper() in UNIDADES:
36            particiones.append((p[1], int(p[2]) * UNIDADES[p[3].upper()]))
37        else:
38            errores.append(f"Línea {n}: no se entiende «{linea.strip()}»")
39    if disco is None or tabla is None or arranque is None:
40        errores.append("Faltan DISCO, TABLA o ARRANQUE")
41    for e in errores:
42        print(e)
43    if errores:
44        return
45    print(f"Disco de {tam(disco)} ({disco // SECTOR} sectores de {SECTOR} bytes), tabla {tabla}, arranque {arranque}")
46    avisos = []
47    if tabla == "MBR" and disco > 2 * UNIDADES["TIB"]:
48        avisos.append("MBR solo direcciona 2 TiB: el resto del disco no se podría usar; usa GPT")
49    puntos = [nombre for nombre, _ in particiones]
50    if arranque == "UEFI" and "/boot/efi" not in puntos:
51        avisos.append("Con UEFI hace falta una partición de sistema EFI (/boot/efi, FAT32, de al menos 100 MiB)")
52    if arranque == "BIOS" and tabla == "GPT" and "bios_grub" not in puntos:
53        avisos.append("Con BIOS y GPT, GRUB necesita una pequeña partición bios_grub (1 MiB)")
54    if "/" not in puntos:
55        avisos.append("No hay partición raíz /")
56    efi = [t for nombre, t in particiones if nombre == "/boot/efi"]
57    if efi and efi[0] is not None and efi[0] < 100 * MIB:
58        avisos.append("La partición EFI debería tener al menos 100 MiB")
59    resto = [i for i, (_, t) in enumerate(particiones) if t is None]
60    if len(resto) > 1 or (resto and resto[0] != len(particiones) - 1):
61        print("Error: RESTO solo puede usarse una vez y en la última partición")
62        return
63    # alineación a 1 MiB: la primera empieza en el sector 2048 y GPT reserva su copia en el último MiB
64    inicio = MIB
65    fin_util = disco - (MIB if tabla == "GPT" else 0)
66    fin_util -= fin_util % MIB
67    if tabla == "MBR" and len(particiones) > 4:
68        avisos.append(f"MBR admite 4 particiones primarias: la 4.ª será extendida y las {len(particiones) - 3} últimas, lógicas")
69    filas = []
70    cabe = True
71    for i, (nombre, t) in enumerate(particiones):
72        if tabla == "MBR" and len(particiones) > 4 and i >= 3:
73            inicio += MIB                       # cada lógica empieza con su EBR, un MiB por alineación
74        tamano = (fin_util - inicio) if t is None else -(-t // MIB) * MIB
75        if tamano <= 0 or inicio + tamano > fin_util:
76            print(f"Error: {nombre} no cabe: quedan {tam(max(0, fin_util - inicio))}")
77            cabe = False
78            break
79        numero = i + 1 if not (tabla == "MBR" and len(particiones) > 4 and i >= 3) else i + 2
80        filas.append((numero, nombre, inicio // SECTOR, (inicio + tamano) // SECTOR - 1, tamano))
81        inicio += tamano
82    if cabe:
83        print("Nº  Partición        Inicio        Fin           Tamaño")
84        for numero, nombre, ini, fin, tamano in filas:
85            print(f"{numero:<3} {nombre:<16} {ini:<13} {fin:<13} {tam(tamano)}")
86        libre = fin_util - inicio
87        print(f"Sin asignar: {tam(libre) if libre else '0'}")
88    for a in avisos:
89        print(f"Aviso: {a}")
90    if cabe and not avisos:
91        print("Plan correcto")
92
93
94main()

GPT guarda dos copias de la tabla (al principio y al final del disco), admite 128 particiones y discos de zettabytes. MBR solo cabe en el primer sector: 4 entradas y direcciones de 32 bits de sector, es decir, 2 TiB con sectores de 512 bytes.

UEFI arranca leyendo ficheros de una partición FAT32 (la ESP, montada en /boot/efi); BIOS ejecuta el código del primer sector. Por eso GRUB, con BIOS y GPT, necesita un pequeño hueco propio (bios_grub) donde guardar el código que en MBR iría entre la tabla y la primera partición.

Alinear a 1 MiB desperdicia como mucho un MiB por partición y evita que una escritura de 4 KiB toque dos bloques físicos del SSD, que la convertiría en dos lecturas y dos escrituras.

La partición extendida de MBR es un contenedor: no tiene sistema de ficheros y dentro van las lógicas, encadenadas con un registro (EBR) delante de cada una.

Para ir más allá

  • Añade el tipo de sistema de ficheros de cada partición y avisa si /boot/efi no es FAT32.
  • Calcula también el tamaño recomendado de la swap según la RAM (con y sin hibernación).
  • Genera las órdenes de sgdisk o parted que crearían ese plan.

Dónde se explica