
Kernel de Linux en espacio de usuario basado en JIT que ejecuta contenedores de forma nativa en macOS con Apple Silicon sin una máquina virtual. Reemplazo directo de la API del motor Docker con aislamiento de contenedores, imágenes superpuestas y publicación de puertos.
Ejecuta contenedores de Linux en macOS — sin VM.
dd ejecuta contenedores de Linux de forma nativa en macOS con Apple Silicon sin una máquina virtual. No hay núcleo de Linux ni hipervisor subyacente: un JIT traduce el código del contenedor y procesa sus llamadas al sistema de Linux en espacio de usuario (linaje gVisor / PRoot). El JIT es el núcleo de Linux del invitado: los namespaces, cgroups, capas de imágenes overlay y la red se mantienen como estado de espacio de usuario. Habla la API del Motor Docker, por lo que el CLI docker habitual lo maneja.
La computación del contenedor se ejecuta como instrucciones nativas de Apple Silicon; solo sus llamadas al sistema son interpretadas. Sin VM que arrancar, sin demonio en una VM, sin costo de virtualización.
Sitio web y documentación: https://ricccrd.github.io/dd/
make jit # build.rs compila + firma los JITs
DD_IMAGES=/path/to/images cargo run -p dd-daemon # inicia el demonio
export DOCKER_HOST=unix://$PWD/dd.sock
docker run -p 8080:80 -m 256m alpine sh -c 'echo hi from $(hostname)'
DOCKER_HOST a su socket y tus comandos existentes docker run / ps / images / build funcionan sin cambios.jit86) que decodifica x86, sintetiza sus flags y baja SSE/x87 a NEON (los binarios glibc funcionan); e invitados macOS arm64 (ddcli mac) — sin VM en ninguno de ellos..wh., getdents fusionados), un VFS de jaula de rutas libre de TOCTOU, namespaces PID / UTS / USER, una netns de loopback privada con publicación de puertos -p, y límites de cgroup de memoria + pids (OOM en el límite).dd instalan un demonio en segundo plano por usuario y un — todo bajo , nunca .Cualquier otra forma de ejecutar contenedores de Linux en un Mac — Docker Desktop, Colima, Rancher, OrbStack — arranca una VM de Linux bajo un hipervisor y ejecuta el demonio dentro de ella. Esa VM es un impuesto que pagas todo el día. dd la elimina: un contenedor es un proceso normal de macOS cuyas llamadas al sistema son gestionadas por un núcleo de Linux en espacio de usuario.
La ventaja es estructural: la computación del invitado se ejecuta como instrucciones nativas de Apple Silicon (sin capa de virtualización de hardware en la ruta crítica), y el conocido cuello de botella de intercambio de archivos de Docker Desktop — el puente virtiofs/FUSE entre macOS y la VM — simplemente no existe, porque el VFS de dd es el sistema de archivos del anfitrión detrás de una jaula de rutas.
Compensación honesta: un núcleo en espacio de usuario solo es tan completo como las llamadas al sistema que implementa, y hoy Por defecto, el invitado se ejecuta en un proceso — rápido, y la decisión correcta para código de confianza (tu entorno de desarrollo, CI, tus propias herramientas). Para código no confiable ahora hay una división de centinela optativa (
DDJIT_UNTRUSTED): el invitado se ejecuta en una caja de arena Seatbelt con denegación por defecto que no tiene autoridad de sistema de archivos/red del anfitrión, mientras que un proceso centinela de confianza posee los recursos reales y sirve las llamadas al sistema a través de un anillo de memoria compartida — la forma de gVisor. Es temprano (las llamadas al sistema de archivos principales — read/write/open/close/lseek — se reenvían hoy; sockets/exec/fork están llegando), así que para código completamente hostil, una VM todavía expone una superficie más reducida.
El mismo binario estático de Linux, ejecutado de dos formas en un Apple M5 Pro (macOS 26.3): dentro de la VM de Linux (cómo los Docker basados en VM ejecutan contenedores) vs. a través del JIT de dd en el anfitrión sin ninguna VM. Mediana de 7 (make bench). Un tiempo menor es mejor; "dd vs VM" > 1× significa que dd es más rápido. El carril de dd incluso paga un pequeño impuesto de puente entre procesos que la aplicación real no tiene — por lo que son conservadores.
Contenedores x86-64 — dd vs emulación en VM (qemu-user; ejecutar x86 en Apple Silicon significa traducirlo de cualquier manera). El JIT de dd supera a qemu en 9 de 10 cargas de trabajo, dramáticamente en punto flotante:
Contenedores aarch64 — dd vs una VM nativa (la VM ejecuta arm64 a velocidad nativa total — la barra más difícil):
dd ejecuta la computación arm64 a velocidad nativa — adelante en int sieve + mandelbrot, en paridad en SHA-256, matmul, memcpy, n-body y base64. Las brechas restantes son trabajo intensivo en ramas indirectas / llamadas al sistema — qsort (~1.3×), text-scan (~1.35×) y SQLite (~1.5×) — reducidas drásticamente por los últimos pases (§B-off + x16/x17 robado llevó a SQLite de ~1.9× a ~1.5×). Cerrar el resto (despacho VDBE) es la frontera activa; consulta docs/design/arm-sqlite-parity.md. (Cada carga de trabajo está dimensionada para ejecutarse ≥0.45s, por lo que el pequeño impuesto de puente por ejecución del arnés es insignificante aquí).
Estos son microbenchmarks de computación — ni siquiera capturan las ventajas estructurales de dd (sin VM que arrancar, sin RAM residente, E/S directa del sistema de archivos del anfitrión). Todos los números medidos, mediana de 7. Reproduce: make bench.
El objetivo es superar a la VM en todos los benchmarks. dd ya gana en cada carga de trabajo x86-64 anterior e iguala o supera a arm64 nativo; donde todavía está detrás — SQLite arm64 intensivo en syscall/asignación, y exprimir más del traductor x86 — es exactamente la frontera de optimización (el optimizador de trazas de nivel 2 y el trabajo de rendimiento jit86). La paridad o mejor en todas partes es el estándar.
dd ejecuta un contenedor de Linux al ser su núcleo en espacio de usuario. Un JIT traduce el código máquina del invitado e intercepta cada instrucción de llamada al sistema; el manejador de intercepción — service() en dd-jit/src/runtime/os/linux/ — es la ABI de llamadas al sistema de Linux, implementada contra el anfitrión macOS.
ld.so) y construir la pila inicial.# 1. Inicia el demonio, apunta docker hacia él
make jit
DD_IMAGES=/path/to/images cargo run -p dd-daemon
export DOCKER_HOST=unix://$PWD/dd.sock
# 2. Es simplemente Docker
docker run -p 8080:80 -m 256m alpine sh -c 'echo hi from $(hostname)'
docker ps
docker images
docker run --rm -it ubuntu bash
# 3. O mediante la aplicación de escritorio instalada (por usuario, sin root)
dd install # LaunchAgent + docker context
dd app # abre la GUI
docker --context dd run alpine echo hi
dd está dirigido a macOS con Apple Silicon (arm64, macOS 12+). El JIT necesita las Herramientas de Línea de Comandos de Xcode (clang + codesign).
Obtén el último .dmg de la página de versiones, ábrelo y arrastra dd a Aplicaciones. Luego en un terminal:
dd install # árbol ~/.dd + LaunchAgent por usuario + `docker context create dd`
dd app # abre la GUI
dd doctor # verifica socket / agente / contexto / cuarentena de la app
Gatekeeper: el DMG no está firmado (ad-hoc). En el primer inicio, haz clic derecho en la aplicación → Abrir, o ejecuta
xattr -dr com.apple.quarantine /Applications/dd-app.app(dd doctordetecta esto e imprime la solución).
xcode-select --install # clang + codesign
# instala Rust (estable) y Nix (para el shell de desarrollo GTK4)
git clone https://github.com/ricccrd/dd && cd dd
make app # compila + ensambla & firma ad-hoc target/dd-app.app
make dmg # -> target/dist/dd-<ver>-<arch>.dmg
make install # copia a /Applications y ejecuta `dd install`
make app/dmg ejecutan el empaquetado dentro del shell de desarrollo Nix (nix/flake.nix), que proporciona GTK4 + dylibbundler / create-dmg. El paquete reubica el grafo de dylib de GTK en Contents/Frameworks, prepara los datos de tiempo de ejecución de GTK y firma ad-hoc de adentro hacia afuera.
Un espacio de trabajo de Cargo.
dd-jit/ — el entorno de ejecución JIT (C, bajo src/runtime/) más sus enlaces Rust.
build.rs compila y firma un binario JIT por arquitectura invitada (aarch64, x86_64);
src/lib.rs expone Guest + el contrato de lanzamiento tipado SpawnConfig. El invitado aarch64 está completamente descompuesto (motor jit/ + personalidad os/linux/ + frontend frontend/aarch64/); el invitado x86-64 (jit86) comparte la capa os/linux/.dd-daemon/ — el demonio de la API del Motor Docker. Detecta la arquitectura invitada de cada imagen desde su ELF, elige el JIT correspondiente y lo lanza mediante .El demonio escucha en ~/.dd/run/docker.sock; tanto la GUI como docker --context dd lo utilizan. El estado persiste en ~/.dd/state.json.
make test # la matriz motor × caso, informe agrupado
make test ENGINE=x86_64 # un motor
make test FILTER=container # un grupo / casos que coinciden con un nombre
cargo run -p dd-tests -- --list # lista grupos + casos
make test-ci # la ruta de cargo-test (CI)
Los casos se declaran en dd-tests/src/cases/. Un caso es un programa invitado + aserciones; los invitados aarch64 se compilan sobre la marcha (gcc -static-pie) y se comparan con un oráculo nativo, los invitados x86-64 provienen de fixtures precompilados. Cada caso se ejecuta en cada motor para el que tiene un invitado.
clang + codesign (Xcode CLT).-p), netns loopback privada, límites de cgroup de memoria+pids, namespaces UTS/PID/USER.docs/ para los informes detallados.Richard Hutta — [email protected]
MIT.
docker context$HOMEsudo| dd — núcleo en espacio de usuario (JIT) | Docker basado en VM (Desktop / Colima / …) |
|---|
| Modelo subyacente | Un JIT procesa llamadas al sistema de Linux en espacio de usuario (linaje gVisor) | Un núcleo completo de Linux dentro de una VM de hipervisor |
| RAM residente en reposo | Ninguna — por contenedor, liberada al salir | Gigabytes reservados para la VM, siempre encendida |
| Arranque | Spawn de proceso — sin VM que arrancar | Arrancar primero una VM Linux + el demonio dentro de la VM |
| Montaje enlace / E/S archivos | Sistema de archivos anfitrión directo a través de una jaula de rutas | Puente virtiofs/gRPC-FUSE a través del límite de la VM |
| Publicación de puertos | Directo a sockets del anfitrión | A través de la capa NAT/reenvío de la VM |
| Costo de batería / fondo | Nada ejecutándose cuando no hay contenedor | Una VM inactiva que agota la batería |
| Espacio para distribuir y parchear | Sin núcleo de Linux — nada que rastrear por CVE | Distribuye, parchea y rastrea un núcleo completo de Linux |
| Observabilidad | Un proceso normal de macOS — samplear, depurar, Monitor de Actividad | Una VM opaca; la carga de trabajo es invisible para las herramientas del anfitrión |
| Carga de trabajo | VM (qemu) | dd (sin VM) | dd vs VM |
|---|
| float n-body | 5.39s | 0.23s | 24× más rápido |
| mandelbrot | 7.81s | 0.83s | 9.4× más rápido |
| matmul | 8.21s | 1.37s | 6.0× más rápido |
| SQLite (600k filas) | 2.99s | 1.01s | 3.0× más rápido |
| qsort | 3.91s | 1.68s | 2.3× más rápido |
| memcpy | 2.40s | 1.10s | 2.2× más rápido |
| text-scan (wc/grep) | 1.42s | 1.11s | 1.3× más rápido |
| int sieve | 1.31s | 1.04s | 1.25× más rápido |
| SHA-256 | 2.72s | 2.44s | 1.1× más rápido |
| base64 | 4.28s | 5.39s | 0.79× (1.26× más lento) |
| Carga de trabajo | VM (nativa) | dd (sin VM) | dd vs VM |
|---|
| int sieve | 0.75s | 0.48s | 1.58× más rápido |
| mandelbrot | 0.79s | 0.77s | 1.03× más rápido |
| matmul | 0.66s | 0.66s | ~paridad |
| memcpy | 0.55s | 0.56s | ~paridad |
| base64 | 0.68s | 0.68s | ~paridad |
| float n-body | 0.17s | 0.17s | ~paridad |
| SHA-256 | 0.80s | 0.82s | ~paridad |
| qsort | 0.83s | 1.10s | 1.33× más lento |
| text-scan (wc/grep) | 0.51s | 0.68s | 1.35× más lento |
| SQLite (600k filas) | 0.36s | 0.62s | 1.71× más lento |
SpawnConfigdd-tests/ — un arnés de pruebas declarativo; los casos se ejecutan en cada motor con un informe agrupado.dd-client/ — un pequeño cliente tipado de la API del Motor Docker sobre el socket Unix del demonio (la única fuente de verdad para el formato de cable, compartido por la GUI y el CLI).dd-gui/ (binario dd-app) — una interfaz de escritorio GTK4. Se compila solo en macOS mediante el shell de desarrollo Nix.dd-cli/ (binario dd) — la superficie de instalación/control, todo sin root.