👑 Nivel Élite — contenido
C y programación de sistemas, ensamblador y ABI, construir un sistema operativo, el kernel de Linux, compiladores e intérpretes, databases internals y redes de bajo nivel. Teoría y práctica con proyectos.
Nivel Élite — contenido
Este es el nivel donde ya no hay cursos: se lee el código fuente primario y se construyen los sistemas. Es el camino de Linus Torvalds: entender el kernel, el compilador, el sistema operativo y la base de datos desde dentro. Cada tema se sella con un proyecto: escribir un OS, un compilador, un DBMS.
El principio rector: leer las fuentes primarias y construir los sistemas, no consumir cursos. Los libros clásicos no caducan: CSAPP (2ª/3ª ed.), OSTEP, K&R, Dragon Book, TCP/IP Illustrated siguen siendo correctos. El hábito final es leer código real (Linux, Go runtime, CPython, Node core) y contribuir.
C y la programación de sistemas
C es el lenguaje del kernel, de Unix y de todo lo que funciona a bajo nivel. Antes de cualquier proyecto élite hay que hablar C con fluidez.
Punteros, memoria y structs
Ya tienes la teoría en wiki: Memoria y punteros en C. Los elementos que te convierten en programador de sistemas:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char nombre[32];
int edad;
} Persona;
Persona* crear(const char* nombre, int edad) {
Persona* p = malloc(sizeof(Persona));
if (!p) return NULL; // comprobar malloc SIEMPRE
strncpy(p->nombre, nombre, sizeof(p->nombre) - 1);
p->edad = edad;
return p;
}
int main(void) {
Persona* ana = crear("Ana", 30);
printf("%s, %d\n", ana->nombre, ana->edad);
free(ana); // y liberar SIEMPRE
return 0;
}
💡 El contrato C: cada
mallocsufree, cada recurso su liberación. Los leaks y los use-after-free son los bugs de C. Herramientas:valgrind, AddressSanitizer (-fsanitize=address).
Syscalls: la interfaz con el kernel
C programa sistemas porque puede llamar al kernel directamente:
#include <fcntl.h>
#include <unistd.h>
int main(void) {
int fd = open("/tmp/miarchivo", O_WRONLY | O_CREAT, 0644);
if (fd < 0) return 1;
write(fd, "hola\n", 5);
close(fd);
return 0;
}
| Syscall | Qué hace |
|---|---|
fork |
Crea un proceso hijo (copia del padre) |
exec |
Reemplaza el proceso actual por un programa |
wait |
Espera a que un hijo termine |
open/read/write/close |
Archivos |
mmap |
Mapear memoria o archivos |
socket/bind/listen/accept |
Red |
kill |
Enviar señal a un proceso |
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
pid_t pid = fork();
if (pid == 0) {
printf("soy el hijo (%d)\n", getpid());
execlp("ls", "ls", "-l", NULL); // el hijo se convierte en ls
} else {
wait(NULL); // el padre espera al hijo
printf("soy el padre (%d)\n", getpid());
}
return 0;
}
⚠️ El flujo completo:
forkduplica el proceso → el hijo usaexecpara correr otro programa → el padrewaital hijo. Así crea un shell cada comando que ejecutas.
El modelo de ejecución: de C a ensamblador
En Fundamentos: sistemas viste el ciclo fetch-decode-execute. A nivel élite lo llevas al byte:
gcc -S -O2 programa.c # genera el ensamblador
objdump -d programa # desensambla el binario
readelf -h programa # cabecera ELF (formato de binarios Linux)
Stack frames y la ABI
Una ABI (Application Binary Interface) define cómo se llaman las funciones a nivel de máquina: qué registros llevan los argumentos, quién limpia el stack, cómo se devuelve el valor.
Llamada a foo(a, b):
args → registros (rdi, rsi) → call foo → push return addr
foo: push rbp → mov rbp, rsp → [stack frame de foo]
ret: pop return addr → saltar de vuelta al caller
💡 Entender la ABI es lo que te permite leer ensamblador de verdad, depurar con gdb a nivel de instrucción y escribir código seguro contra buffer overflows (el return address vive en el stack — viste el peligro en la wiki de memoria).
Construir un sistema operativo
De la teoría del OSTEP al kernel real. El camino tiene dos variantes:
- MIT 6.S081 (xv6): modificas un kernel real de RISC-V (labs: syscalls, páginas, traps, COW, threads, filesystem, mmap). El estándar académico.
- OS desde cero (phil-opp en Rust, o la wiki OSDev): escribes el boot y el kernel tú mismo. El rito moderno.
Las piezas de un kernel
[Bootloader] carga el kernel en memoria
[GDT/IDT] estructuras de memoria y de interrupciones
[MEMORIA] gestión de páginas (virtual → física)
[PROCESOS] tabla de procesos, scheduling
[SYSCOLLS] la interfaz que tu OS expone a los programas
[DRIVERS] puerto serie, teclado, video, disco
// La syscall mínima: un "hello" que el kernel expone
void sys_hello(void) {
printk("hello desde el kernel\n");
}
💡 El proyecto vale oro: no hay mejor forma de entender procesos, memoria virtual e interrupciones que haberlos escrito.
El kernel de Linux
El paso final: leer y contribuir al proyecto más grande del mundo.
Cómo leer el kernel sin morir en el intento
- Empieza por lo familiar:
fs/(filesystems),kernel/(scheduling, syscalls),mm/(memoria),net/(red). - Usa LWN y los posts de maintainers para el contexto.
- Sigue una ruta: el flujo de una
read()o de unfork()de principio a fin.
Un fork() por dentro (resumen):
sys_fork (kernel/fork.c)
→ copy_process (copia task_struct, mm, fs, files)
→ wake_up_new_task (añade al scheduler)
el hijo nace con su propio task_struct apuntando al mismo código
| Término del kernel | Qué es |
|---|---|
| task_struct | La ficha de cada proceso/thread |
| HZ / jiffies | El tick del reloj del kernel |
| Waitqueue | Procesos esperando un evento |
| RCU | Read-Copy-Update: lectura sin locks |
| Sysfs / procfs | Interfaces del kernel a user space |
| Kconfig / Kbuild | El sistema de build del kernel |
| mainline | El kernel de Linus (torvalds/linux) |
💡 Contribuir:
kernelnewbies.org, «first patches» (código que arregla cosas triviales), mailing lists. El proceso es por email (git send-email), no PRs de GitHub.
Compiladores e intérpretes
Escribir tu propio lenguaje es el proyecto élite por excelencia. El camino moderno es Crafting Interpreters: construyes dos intérpretes completos (un tree-walker y una bytecode VM con GC).
Las fases de un compilador
código fuente
↓ [Lexer/Scanner] → tokens (identificadores, palabras clave, números)
↓ [Parser] → árbol de sintaxis (AST)
↓ [Análisis semántico] → tipos, referencias válidas
↓ [Generación de código] → bytecode, C, ensamblador
↓ [Optimización] → código más eficiente
Un mini-intérprete para entenderlo
# Los 3 pasos en miniatura
# 1. LEXER: de texto a tokens
def tokenizar(src):
tokens = []
for palabra in src.split():
if palabra.isdigit():
tokens.append(("NUM", int(palabra)))
elif palabra in ("+", "-", "*"):
tokens.append(("OP", palabra))
else:
tokens.append(("ID", palabra))
return tokens
# 2. PARSER: de tokens a árbol (expresión: número [op número]*)
def parsear(tokens):
i = 0
num = tokens[i][1]; i += 1
operaciones = []
while i < len(tokens):
op = tokens[i][1]; num2 = tokens[i+1][1]
operaciones.append((op, num2)); i += 2
return num, operaciones
# 3. EVALUACIÓN: recorrer el árbol
def evaluar(num, operaciones):
for op, n in operaciones:
if op == "+": num += n
if op == "-": num -= n
if op == "*": num *= n
return num
print(evaluar(*parsear(tokenizar("3 + 4 * 2")))) # 14
Terminología de compiladores
| Término | Qué es |
|---|---|
| Token | Unidad léxica (número, identificador, operador) |
| AST | Árbol de sintaxis abstracta |
| Lexer / Parser | Tokeniza / construye el árbol |
| Tree-walker | Intérprete que recorre el AST |
| Bytecode | Código intermedio compacto para una VM |
| Stack machine | La VM clásica (como la JVM, CPython) |
| GC (garbage collector) | Recolecta memoria que ya no se usa |
| Front-end / back-end | Análisis del código / generación de código |
💡 La joya: escribir una VM con garbage collector te conecta con CPython, la JVM, Lua y V8. Entender GC es entender la mitad de los runtimes del mundo.
Bases de datos internals
El proyecto: construir un DBMS (CMU 15-445, BusTub). Las piezas que tocas:
[SQL Parser] ──▶ [Query Planner/Optimizer] ──▶ [Executor]
│
[Buffer Pool] ──│── (páginas en RAM, con eviction)─│
[Storage] ──│── (tablas, índices B+Tree/LSM) ─│
[Concurrencia] ──│── (locks, MVCC) ────────────────│
[Recovery] ──│── (WAL, undo/redo) ────────────│
| Componente | Qué hace |
|---|---|
| Buffer pool | Caché de páginas del disco en RAM (LRU) |
| B+Tree | El índice: búsqueda O(log n) en disco |
| LSM-tree | El índice de las NoSQL (escritura secuencial rápida) |
| WAL (Write-Ahead Log) | Registrar los cambios ANTES de aplicarlos (durabilidad) |
| MVCC | Versiones para que lectores y escritores no se bloqueen |
| Executor | Ejecuta el plan de la query (joins, agregaciones) |
💡 Después de construir uno,
EXPLAIN ANALYZEde PostgreSQL deja de ser magia: ya sabes qué pasa por dentro. Detalle de teoría en Fundamentos: BBDD.
Redes de bajo nivel
De «usar HTTP» a «entender el paquete hasta el byte». La teoría está en Fundamentos: Redes y wiki: TCP/IP. A nivel élite:
- TCP/IP Illustrated vol. 1 (Stevens): el protocolo a fondo, con diagramas.
- Los RFC (fuente primaria): RFC 9293 (TCP), RFC 791 (IP), RFC 768 (UDP).
- Sockets en C (Beej): implementar un cliente/servidor a mano.
// Servidor TCP mínimo en C (sockets)
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(void) {
int srv = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = { .sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = INADDR_ANY };
bind(srv, (struct sockaddr*)&addr, sizeof(addr));
listen(srv, 10);
int cli = accept(srv, NULL, NULL);
char buf[1024];
read(cli, buf, sizeof(buf)); // recibe la petición
write(cli, "HTTP/1.1 200 OK\r\n\r\nhola", 22);
close(cli); close(srv);
return 0;
}
⚠️ Ese servidor no maneja concurrencia (atiende una conexión y se acaba). Añadir
fork/threads por conexión, timeouts y parseo HTTP real es exactamente el camino de los labs de sistemas.
El hábito final: leer código fuente real
| Proyecto | Qué aprenderás |
|---|---|
| Linux kernel | El sistema operativo entero |
| Go runtime | Scheduler, goroutines, GC |
| CPython | Interprete, bytecode, GIL, GC |
| Node core | Event loop, libuv |
| Redis | Un servidor de datos completo y legible (~60k líneas) |
| SQLite | Una base de datos completa en un archivo |
| Git | Modelo de objetos, algoritmos de diff/merge |
💡 Método: elige UN proyecto y rastrea un flujo completo de principio a fin (una petición HTTP en Node, un
GETen Redis, unaread()en Linux). Escribe apuntes. Eso es leer código de verdad — no ojear.
Terminología del nivel élite
| Término | En una frase |
|---|---|
| ABI | El contrato binario entre código y CPU/OS |
| ELF | Formato de binarios y librerías de Linux |
| Syscall | La llamada del programa al kernel |
| fork / exec / wait | Crear proceso / correr otro / esperar |
| task_struct | La ficha de cada proceso del kernel |
| xv6 | El kernel de enseñanza (RISC-V) |
| VM (máquina virtual) | Ejecuta bytecode (CPython, JVM, V8) |
| GC | Recolector de basura |
| AST | Árbol de sintaxis abstracta |
| Bytecode | Código intermedio de una VM |
| Buffer pool | Caché de páginas de BBDD en RAM |
| B+Tree / LSM | Índices de las bases de datos |
| WAL | Log de escritura anticipada (durabilidad) |
| RCU | Lectura sin locks del kernel |
Práctica propuesta (proyectos que valen oro)
- Completa los CSAPP labs (Data Lab, Bomb Lab, Attack Lab, Shell Lab, Malloc Lab). El rito de paso.
- Escribe un shell en C: lee comandos,
fork+exec, redirección>,|,cd,exit. - Modifica xv6 (MIT 6.S081): añade una syscall propia, haz COW fork, un scheduler nuevo.
- Escribe un intérprete (Crafting Interpreters): tokens → AST → evaluador → bytecode VM.
- Construye un servidor HTTP en C con threads y timeouts.
- Implementa un mini-DBMS: buffer pool + B+Tree + WAL en tu lenguaje.
- Lee código real: sigue un
GETcompleto en Redis o el arranque de Node. Escribe apuntes. - Contribuye al kernel o a un proyecto grande: primera patch real.
Para profundizar
- CSAPP Labs: el rito de paso de sistemas.
- MIT 6.S081 — xv6: kernel real, labs de verdad.
- Writing an OS in Rust (phil-opp): OS desde cero, moderno.
- Crafting Interpreters: compiladores en la web, gratis.
- CMU 15-445 — BusTub: construyes un DBMS.
- Database Internals (Petrov): el libro de los motores de datos.
- The Linux Programming Interface (TLPI): programación de sistemas Linux.
- TCP/IP Illustrated vol. 1: redes a fondo.
- build-your-own-x: directorio infinito de proyectos.
- Linux: la parte operativa del sistema.
- Sigue con 💼 Entrevistas y carrera o vuelve al 🗺️ Plan.