conceptC++ Moderno~5 min de lecturaActualizado 2026-07-02#cpp#serenityos#operating-systems#kernel#systems

SerenityOS: un sistema operativo escrito en C++

SerenityOS importa porque no es un ejemplo de juguete en un blog post. Es un sistema operativo gráfico Unix-like, con kernel, userland, services, applications, libraries, ports y tooling en un único source tree público. Su codebase muestra el trato real de C++ en OS work: podés codificar ownership, errores e invariantes en tipos, pero igual tenés que hacerte cargo del ABI, allocator, runtime, build system y borde de hardware.

El reset: C++ no vuelve menos low-level a un OS. En un kernel, cada abstracción todavía tiene que justificar su object layout, failure mode, initialization order y código generado.

Cómo funciona de verdad

El repositorio de SerenityOS describe el proyecto como un OS gráfico Unix-like para computadoras x86 64-bit, Arm y RISC-V. El README también deja clara la arquitectura: kernel, userland POSIX-like, services, IPC, libraries, GUI applications, ports, build tools y flow de launch en QEMU forman parte del sistema.

La lección interesante de C++ no es "usá la standard library en todos lados". SerenityOS tiene libraries de proyecto como AK y tipos custom para ownership, formatting, containers y errores. Un kernel no puede depender ciegamente de supuestos hosted como política de exceptions del proceso, comportamiento normal del heap, comodidad de static initialization o libc de la plataforma host. Incluso en userland, el proyecto tiene reglas de estilo que uniforman el código: miembros privados con m_, naming conventions explícitas, preferencia por range-for, C++ casts en vez de C-style casts y spelling claro de virtual override.

La realidad de build también es parte de la lección. Las build instructions actuales piden un host compiler moderno para host tools, CMake, Ninja, QEMU y un flow de cross-compilation desde Linux, macOS, Windows con WSL2 y otros hosts Unix-like. El camino default de build lanza QEMU. Ese es exactamente el tipo de entorno donde tu subset de C++ no es solo estilo; es parte de la portabilidad.

Artefacto ejecutable: un subset systems estilo Serenity

El demo ejecutable vive en examples/modern-cpp/serenityos-an-os-written-in-cpp/. No es código de SerenityOS. Es un artefacto chico C++20 que espeja la forma de decisiones systems C++: RAII para un lock guard, un return type estilo ErrorOr<T>, storage de capacidad fija y build con -fno-exceptions -fno-rtti.

cd examples/modern-cpp/serenityos-an-os-written-in-cpp
./run.sh

El guard es la misma idea que cualquier scoped lock en un kernel:

class SpinGuard {
public:
    explicit SpinGuard(SpinLock& lock)
        : m_lock(lock)
    {
        m_lock.lock();
    }

    ~SpinGuard()
    {
        m_lock.unlock();
    }

    SpinGuard(SpinGuard const&) = delete;
    SpinGuard& operator=(SpinGuard const&) = delete;

private:
    SpinLock& m_lock;
};

El fixed vector devuelve un error explícito en vez de throwear o crecer a escondidas:

ErrorOr<std::size_t> append(T value)
{
    if (m_size == Capacity)
        return Error::NoSpace;

    m_storage[m_size] = value;
    return m_size++;
}

El script emite demo.O2.s. El destructor del lock guard se vuelve un punto ordinario de cleanup. El resultado ErrorOr se vuelve estado ordinario de valor. Ese es el núcleo del argumento OS-C++: usá tipos para mantener protocolos locales, después inspeccioná el código bajado.

Por qué SerenityOS es un buen objetivo de estudio

  • Es full-stack. Kernel, capas tipo libc, IPC, services, GUI, libraries y applications fuerzan trade-offs distintos de C++ en un solo tree.
  • Es cross-compiled. Ves host tools, target tools, image building y emulator launch como un workflow.
  • Usa vocabulary custom. Tipos como NonnullOwnPtr y ErrorOr muestran que C++ systems serio suele definir formas de ownership y error propias del proyecto.
  • Está limitado por estilo. El coding style es lo bastante explícito para hacer legible C++ a escala entre contribuidores.
  • Tiene bordes OS reales. Syscalls, process isolation, memory mappings, filesystem code y drivers son donde la abstracción tiene que encontrarse con hechos ABI.
  • Es código actual. El proyecto cambia, así que tratalo como un source tree para leer, no como un tutorial congelado.

Fallas típicas y trade-offs

  • Asumir que aplican reglas hosted C++ en el kernel. Exceptions, RTTI, allocation, threads y servicios libc dependen de soporte de runtime.
  • Inventar accidentalmente una standard library peor. Containers y smart pointers custom se justifican en kernels, pero igual tienen que estar documentados, testeados y ser aburridos.
  • Dejar que el estilo se vuelva folklore. Un OS grande en C++ necesita reglas escritas porque los contribuidores importan idioms incompatibles de application C++.
  • Ignorar boot e initialization order. Global constructors, estado static y uso temprano del allocator pueden ser inocuos en una app y dolorosos antes de que el kernel esté vivo.
  • Drift de build cross-platform. El conjunto de hosts soportados, versiones requeridas de compiler, flags de emulator y scripts de toolchain son parte de la superficie del proyecto.
  • Leer sin buildar. OS code suele esconder hechos importantes en generated headers, compiler flags, linker scripts y scripts de image-building.

En la práctica

Leé SerenityOS como un mapa de decisiones. Empezá por el README y las build instructions, después inspeccioná tipos de ownership/error en AK, bordes de syscalls del kernel y un servicio userland chico. Hacé las mismas preguntas en todos lados: quién posee este objeto, quién puede fallar, quién aloca, quién puede bloquear, qué cruza un borde ABI C y qué código corre durante cleanup?

Para tu propio proyecto de OS, la lección no es copiar SerenityOS completo. La lección es hacer un subset de C++ que matchee tu runtime. Si tu kernel no tiene exception runtime, usá errores explícitos. Si tu allocator no siempre está disponible, evitá crecimiento escondido. Si tu ABI tiene que ser C-shaped en el borde syscall, envolvelo deliberadamente. C++ puede ayudar, pero solo cuando el subset es honesto sobre la máquina.

Conecta con: Qué subset de C++ usar · Error handling: exceptions vs expected · RAII: la idea que cambia todo · Smart pointers: unique_ptr, shared_ptr, ownership · Translation units, declarations y linkage · El pipeline: preprocess, compile, assemble, link · El syscall: cruzar al kernel · Craftsmanship para low-level programmers

Fuentes

  • SerenityOS README - overview actual del proyecto, arquitecturas soportadas, features y puntos de entrada de build/run. https://github.com/SerenityOS/serenity
  • SerenityOS Build Instructions - dependencies actuales de host, requisitos de compiler, flow de cross-build y expectativas de QEMU. https://github.com/SerenityOS/serenity/blob/master/Documentation/BuildInstructions.md
  • SerenityOS Coding Style - convenciones C++ específicas del proyecto para naming, formatting, pointer/reference, casts y virtual override. https://github.com/SerenityOS/serenity/blob/master/Documentation/CodingStyle.md
  • SerenityOS NonnullOwnPtr source - vocabulary concreto de ownership en la AK library. https://github.com/SerenityOS/serenity/blob/master/AK/NonnullOwnPtr.h
  • SerenityOS syscall API source - ejemplo de tipos C++ encontrándose con bordes syscall kernel/userland. https://github.com/SerenityOS/serenity/blob/master/Kernel/API/Syscall.h
  • SerenityOS top-level CMake - muestra checks del target system, expectativas de toolchain, image targets e integración de build. https://github.com/SerenityOS/serenity/blob/master/CMakeLists.txt
  • C++ Core Guidelines - contraste útil para guía C++ hosted versus el subset más angosto que puede elegir un kernel de OS. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines