wandres.dev
EL C DEL KERNEL · Freestanding, sin libc

El C del kernel: otro lenguaje

El kernel se escribe en C, pero no el C que conoces: GNU C freestanding, sin libc, sin coma flotante libre, con su propio universo de APIs. Las reglas que lo cambian todo.

⏱ 13 min

El kernel está escrito en C, así que sabes el lenguaje. Pero programar el kernel es como mudarte a un país donde hablan tu idioma con reglas distintas: no hay librería estándar, ciertas cosas están prohibidas, y hay un universo entero de funciones propias. Este nivel te aclimata.

🎯 Al terminar esta lección sabrás
  • Por qué el C del kernel es freestanding.
  • No hay libc: printk en vez de printf.
  • Las restricciones (coma flotante, stack pequeño).
  • El universo de APIs propias del kernel.

Freestanding: sin suelo debajo

¿Recuerdas el nivel 23 de C23, freestanding? El kernel es ese entorno. No hay sistema operativo debajo del kernel — el kernel es el sistema operativo. Por tanto:

  • No hay libc. Nada de stdio.h, stdlib.h, string.h de userspace. Si quieres imprimir, reservar memoria o copiar cadenas, usas las funciones del kernel.
  • El kernel se compila -ffreestanding con su propia colección de headers en include/linux/.
// userspace          →   kernel
printf("hola\n");printk(KERN_INFO "hola\n");
malloc(size);kmalloc(size, GFP_KERNEL);
free(p);kfree(p);
strcpy / memcpy        →   strscpy / memcpy (versiones del kernel)
El kernel reimplementa su propio mundo

Esto sorprende a todo el que llega: el kernel tiene su propia versión de casi todo lo que la libc te daba — sus funciones de cadenas, su asignador de memoria, sus estructuras de datos, sus macros de utilidad. ¿Por qué? Porque la libc de userspace hace syscalls al kernel para funcionar, y el kernel no puede hacerse syscalls a sí mismo. Así que se lo implementa todo internamente, adaptado a sus necesidades (contextos sin dormir, sin coma flotante, escalable a miles de núcleos). Aprender el kernel es, en buena parte, aprender ese universo paralelo de APIs: printk, kmalloc, list_head, spinlock_t. La sintaxis de C la sabes; el vocabulario es nuevo. Los headers de include/linux/ son tu nuevo diccionario.

Las restricciones

El kernel impone límites que en userspace no existen:

🚫

Sin coma flotante (casi)

El kernel normalmente no salva los registros FPU en cada cambio de contexto, así que usar float/double está prohibido salvo en secciones especiales. Trabajas con enteros.

📏

Stack diminuto

El stack del kernel es pequeño (unos pocos KB), no los megabytes de userspace. Nada de arrays enormes en el stack ni recursión profunda: reserva en el heap con kmalloc.

💥

Un fallo lo cuelga todo

No hay red de seguridad. Un puntero nulo dereferenciado es un oops o un panic que puede colgar la máquina, no un segfault de un proceso.

🧵

Concurrencia por defecto

Tu código puede ejecutarse en varios núcleos a la vez y ser interrumpido en cualquier momento. La sincronización no es opcional (niveles 15-18).

GNU C, no C estándar puro

El kernel usa GNU C: C con extensiones de GCC (y compatibles con Clang). Verás construcciones que no son C estándar:

// expresiones de sentencia, typeof, atributos... extensiones GNU muy usadas
#define min(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })

// atributos de sección, alineación, likely/unlikely para el predictor de saltos
if (unlikely(error)) { /* … */ }
💡
El tooling iguala a C23 sin ser C23

El kernel no usa C23 (necesita compilar con toolchains muy variados y estables), pero con GNU C y su tooling logra lo mismo que verías en C moderno: genéricos con typeof y macros, chequeos estáticos con sparse (su analizador propio), sanitizers adaptados como KASAN (nivel 27), y una disciplina de estilo férrea. No echarás de menos constexpr o nullptr: el kernel tiene equivalentes propios (NULL, constantes, macros) forjados en décadas de experiencia. Es C llevado a su extremo profesional.

⚔️ Aclimátate al C del kernel
  1. Explora include/linux/ en el árbol de fuentes: abre string.h, slab.h, printk.h y ve las APIs del kernel.
  2. Anota las equivalencias: printf→printk, malloc→kmalloc, free→kfree.
  3. Investiga por qué no puedes usar double en el kernel (pista: registros FPU y context switch).
  4. Busca likely() / unlikely() en el código y entiende qué le dicen al compilador.