3 puntos por GN⁺ 2024-05-25 | 1 comentarios | Compartir por WhatsApp
  • Bunnix, que comenzó como un proyecto personal de descanso, experimentó con qué tan lejos se podía llegar en alrededor de un mes creando un sistema operativo tipo Unix para x86_64, y tomó 27 días de trabajo efectivo
  • El kernel se escribió principalmente en Hare, junto con componentes en C como lwext4 para soporte de ext4 y libvterm para la terminal de video del kernel
  • Soporta tanto legacy boot como EFI y se probó en algunas laptops reales, pero debido a la falta de soporte para USB, requiere un teclado PS/2 o emulación PS/2 del BIOS
  • El espacio de usuario se basa principalmente en software de terceros como dash, Doom, gzip, less, mandoc, sbase, tcc y Vim 5.7; libc es una versión de musl libc modificada para Bunnix
  • Bunnix funciona, pero tiene muchos bugs y sigue siendo un sistema de un solo usuario; más que apuntar al mantenimiento a largo plazo, es un experimento que deriva en una reelaboración de Helios y mejoras en el diseño del kernel

Alcance y ejecución de Bunnix

  • Bunnix es un proyecto de sistema operativo tipo Unix para x86_64 iniciado el 21 de abril de 2024
  • Excluyendo los días en que no se trabajó realmente, se invirtieron en total 27 días
  • Se ofrece una ISO de Bunnix 0.0.0 que se puede ejecutar directamente
  • En qemu, se puede arrancar la ISO con el siguiente comando
qemu-system-x86_64 -cdrom bunnix.iso -display sdl -serial stdio
  • También se puede grabar la ISO en una memoria USB y arrancarla en hardware real
    • Es probable que funcione en la mayoría de las máquinas AMD64
    • Se probó en una ThinkPad X220 y una Starlabs Starbook Mk IV
    • Soporta tanto legacy boot como EFI
  • La mayor limitación de ejecución es la falta de soporte para USB
    • Se necesita un teclado PS/2 o emulación PS/2 del BIOS
    • La mayoría de los teclados de laptop están conectados mediante PS/2
    • Que funcione la emulación PS/2 de un teclado USB depende del entorno
  • El port de Doom tiene limitaciones en los atajos de teclado y en el comportamiento de salida
    • Movimiento con WASD
    • Disparo con Shift derecho
    • Abrir puertas con Space
    • Salir del juego no funciona, así que hay que reiniciar después de jugar

Estructura del kernel y funciones soportadas

  • El kernel de Bunnix está escrito mayormente en Hare y usa algunos componentes en C
    • Para soporte del sistema de archivos ext4 usa lwext4
    • Para la terminal de video del kernel usa libvterm
  • Los drivers soportados se concentran en lo necesario para hardware básico y arranque desde almacenamiento
    • PCI legacy
    • Dispositivos de bloque AHCI
    • Tablas de particiones GPT y MBR
    • Teclado PS/2
    • Puertos seriales de plataforma
    • Reloj CMOS
    • Framebuffer configurado por el bootloader
    • Sistemas de archivos ext4 y memfs
  • También incluye funciones básicas del kernel necesarias para un sistema tipo Unix
    • Sistema de archivos virtual y dispositivos

      • Proporciona /dev con dispositivos de bloque, pseudodispositivos null, zero y full, /dev/kbd, /dev/fb0, TTY seriales y de video, y la terminal de control /dev/tty
      • Incluye un emulador de terminal relativamente completo y soporte de termios que funciona en cierta medida
    • Llamadas al sistema y modelo de usuario

      • Soporta unas 40 llamadas al sistema, incluidas clock_gettime, poll, openat, fork, exec, pipe, dup, dup2, ioctl, entre otras
      • Actualmente Bunnix es un sistema de un solo usuario
      • No aplica los modos de archivo ni la propiedad de Unix
      • Con unos días más de trabajo, está en condiciones de convertirse en un sistema multiusuario

Bootloaders y espacio de usuario

  • Bunnix incluye dos bootloaders
    • El bootloader para legacy boot es compatible con multiboot y está escrito en Hare
    • El bootloader para EFI está escrito en C
  • Ambos bootloaders cargan el kernel como archivo ELF e initramfs cuando es necesario
    • El bootloader EFI incluye zlib para descomprimir initramfs
    • El bootloader compatible con multiboot se encarga de la descompresión por su cuenta
  • El espacio de usuario está compuesto en su mayor parte por fuentes de terceros
    • Colossal Cave Adventure advent
    • dash /bin/sh
    • Doom
    • gzip
    • less
    • lok /bin/awk
    • lolcat
    • mandoc
    • sbase core utils
    • Compilador de C tcc
    • Vim 5.7
  • libc deriva de musl libc y tiene varias modificaciones para ajustarse a las necesidades de Bunnix
  • La biblioteca curses está basada en netbsd-curses
  • El sistema funciona, pero tiene muchos bugs y algunas implementaciones se hicieron con prisa, así que hay que estar preparado para fallos

Factores que permitieron una implementación rápida y dificultades

  • Parte del código de Bunnix proviene del proyecto anterior Helios
    • Incluye parte del código de kernel relacionado con configuración general de CPU, como GDT e IDT
    • Algunos drivers, como AHCI, también se adaptaron al sistema Bunnix
    • Se considera que, sin la experiencia con Helios, habría sido difícil crear Bunnix tan rápido
  • La integración de soporte ext4 y terminal virtual fue especialmente difícil
    • Se incorporaron dependencias externas: lwext4 y libvterm
    • La capa de sistema de archivos se reescribió varias veces y aún conserva bugs
    • Para implementar correctamente el diseño de sistemas de archivos Unix, incluido openat y el manejo de inodes, fue necesario examinar más a fondo el interior de lwext4
  • También se ganó experiencia enlazando fuentes en Hare, assembly y C dentro del proyecto Hare
    • En general funcionó bien, pero hubo incomodidades al construir mecanismos de integración de ABI
    • Surgió la necesidad de convertir automáticamente headers de C en módulos de declaraciones forward para Hare
    • Parte de ese trabajo existe en hare-c, pero todavía hace falta más
  • Apuntar al port de Vim elevó la dificultad de la implementación de la terminal
    • libvterm es una buena biblioteca de máquina de estados de terminal, pero tiene poca documentación
    • Hicieron falta muchos ajustes finos para una integración correcta
    • También se dedicó tiempo a optimización de rendimiento para que funcionara con fluidez
  • El scheduler fue un área en la que se descartó gran parte del código basado en Helios y se reescribió
    • Tanto Helios como Bunnix son sistemas de una sola CPU
    • A diferencia de Helios, Bunnix permite cambios de contexto dentro del kernel
    • Los cambios de tarea preventivos también entran y salen a través del kernel
    • Esta estructura requiere varias pilas de kernel y otro método de cambio de tareas
    • Con un scheduler lo suficientemente robusto, las operaciones bloqueantes como lecturas de disco o pipe(2) pueden implementarse de forma sencilla con wait queues
  • La implementación de señales fue necesaria para compatibilidad con Unix
    • Helios no apunta a Unix, por lo que funciona sin señales
    • En Bunnix se ajustó principalmente para que SIGCHLD funcionara correctamente para el port de dash
    • La implementación final de señales es de nivel muy básico

Lecciones de diseño que se trasladan a Helios

  • Bunnix es un kernel monolítico, mientras que Helios tiene un diseño de microkernel que no es Unix
  • En el sistema de archivos se confirmó la importancia del caching
    • Helios divide la implementación del sistema de archivos entre varios drivers y procesos separados
    • Aunque solo sea para rastrear objetos vivos, el caching en la capa del sistema de archivos es importante
    • Al reelaborar Helios, habrá mucho trabajo de refactorización o reescritura del código del sistema de archivos
  • El acceso a drivers es naturalmente más simple en un kernel monolítico
    • Sin embargo, no hay satisfacción completa con haber puesto tantas cosas en ring 0
    • Hay margen para reflejar algunos elementos del flujo de control de un diseño monolítico en el scheduler de Helios
  • En gestión de memoria, el asignador por bitmap funcionó mejor de lo esperado
    • En Helios se había intentado evitar el asignador por bitmap, y la gestión de memoria era un punto muy incómodo
    • Bunnix usa un asignador por bitmap simple para todas las páginas generales del sistema
    • La sobrecarga fue menor de lo temido y funcionó muy bien
  • Se considera que crear Bunnix en menos de 30 días no habría sido posible con un diseño de microkernel
    • Un kernel monolítico es mucho más simple de implementar
    • Las ventajas del diseño de microkernel también son atractivas, y una mejor respuesta podría ser un kernel híbrido

Estado del proyecto y posibles mejoras pendientes

  • Bunnix es más bien un proyecto artístico casi terminado que algo a lo que se le vaya a dedicar mucho más tiempo
    • Ocasionalmente podrían dedicarle algunos días de trabajo
    • Las mejoras de la comunidad pueden enviarse como parches a public inbox
  • El desarrollo posterior de sistemas operativos irá en la dirección de volver a Helios y realizar rediseños importantes a partir de las lecciones obtenidas con Bunnix
  • Las posibles prioridades de mejora son las siguientes
    • Caché de directorios para el sistema de archivos y mejoras generales de caching
    • Corrección de bugs de ext4
    • procfs y top
    • mmap de archivos
    • Señales adicionales como SIGSEGV
    • Soporte multiusuario
    • Dispositivos de bloque NVMe
    • Dispositivos de bloque IDE
    • Soporte de ATAPI e ISO 9660
    • Soporte de Intel HD audio
    • Stack de red
    • Toolchain de Hare en el sistema base
    • Self-hosting

1 comentarios

 
GN⁺ 2024-05-25
Opiniones de Hacker News
  • Realmente genial. Me recuerda la historia de que el Unix original también se creó durante unas semanas en las que la familia de Ritchie se fue de vacaciones a California para visitar a sus suegros.
    La fuente es UNIX: A History and a Memoir, de Brian W. Kernighan.

    • Es importante señalar que, antes de Unix, ya habían trabajado durante mucho tiempo en Multics. Si no recuerdo mal, Unix era más bien una versión “simplificada” de eso, y no surgió de la nada de repente.
    • Probablemente se refiera a Ken Thompson. Me da flojera buscar la entrevista en YouTube, pero recuerdo que contó varias veces algo como que ya tenía el controlador de disco, algunos programas y otros componentes, y que pensó que mientras su esposa estaba de viaje tendría tiempo de llenar los huecos y convertirlo en un sistema operativo completo.
    • Unix en sí debería considerarse algo que tomó bastante tiempo. Si tomamos V7 como el “Unix realmente terminado”, tardó varios años, y la primera versión era, por ejemplo, poco más que el sistema de archivos.
    • Según tengo entendido, la historia era sobre 3 programas que faltaban, y uno de ellos era un editor de texto.
      Ahora lo recuerdo medio borroso, así que tendría que comprobarlo.
    • Creo que confundiste a Dennis Ritchie con Ken Thompson.
  • Me gustaría ver material más detallado sobre la parte que dice: “Por fin aprendí cómo funcionan las señales de arriba abajo, y son realmente feas. Siempre sentí que eran una de las partes más débiles del diseño de Unix, y este proyecto no me hizo cambiar de opinión”. Si algún usuario de HN o el autor sabe algo, me interesa.

    • Si todavía no lo has visto, empezaría con Advanced Programming in the Unix Environment, de Stevens.
      https://www.amazon.com/Advanced-Programming-UNIX-Environment...
      Cubre las responsabilidades de trabajar con la API de Unix en espacio de usuario, incluidas señales y procesos.
      Si quieres implementar señales dentro del kernel, no estoy seguro de qué recomendar, pero quizá algo como https://pdos.csail.mit.edu/6.828/2012/xv6.html se me ocurre.
      Leer un libro que explica con claridad y de forma sistemática cómo funciona Unix, con ejemplos autocontenidos, es realmente refrescante. No saber C puede ser una barrera, pero lo mismo aplica al leer la entrada del blog.
      No creo que exista información equivalente en algún lugar de la web. En mi blog también hay mucho conocimiento suelto sobre Unix que la gente todavía lee, pero no está al mismo nivel.
      Creo que entender las señales de Unix es uno de esos temas para los que acercarse mediante entradas de blog, Google o LLMs es muy ineficiente. Incluso de segunda mano no diría que es “barato”, pero es un libro cuyo precio se mantiene alto porque la información lo vale, y para un programador en actividad es relativamente barato.
    • Las señales están en el punto de encuentro entre entrada/salida asíncrona/llamadas al sistema y comunicación entre procesos. La asincronía y el IPC también eran debilidades del diseño original de Unix y no fueron elementos presentes desde el principio.
      Las señales son un intento torpe de añadir a la fuerza IPC asíncrono al diseño, por lo que son propensas a condiciones de carrera. Queda ambiguo qué pasa si llega otra señal mientras se está manejando una señal, o qué hacer con una señal cuando el proceso está en medio de una llamada al sistema. Hay que decidir si diferirla, ponerla en una cola o sacar al proceso de la llamada al sistema.
      Si todas las llamadas al sistema fueran asíncronas, como principio de diseño que siguen muchos sistemas operativos modernos, ese aspecto quedaría resuelto. Si el IPC tuviera un sistema como canales confiables, se podrían implementar no solo señales, sino también comunicación asíncrona entre procesos o llamadas a procedimientos más sofisticadas.
    • Creo que a algunas personas no les gustan las señales de Unix porque cargan con demasiados conceptos distintos.
      SIGSTOP/SIGCONT/SIGKILL en realidad hacen control de procesos, como pausar, reanudar y terminar, más que enviar una señal al proceso.
      Los mensajes asíncronos simples como SIGHUP, SIGUSR1, SIGUSR2, SIGTTIN, SIGTTOU se abusan para cosas como recargar configuración, y se les agregan atajos tipo hack como nohup para demonizar procesos. gunicorn también usa las dos últimas para escalar dinámicamente hacia arriba y hacia abajo. En esta categoría también hay cosas extrañamente específicas como SIGWINCH.
      Además están las que indican instrucciones inválidas, violaciones de segmentación y excepciones de punto flotante, como SIGILL, SIGSEGV y SIGFPE. También hay casos como SIGSYS, donde de entrada es discutible si conviene que sean asíncronas.
      Otros enfoques también tienen trade-offs. Windows tiene eventos, SEH, rutinas de manejo para CTRL+C/CTRL+BREAK/cierre, IOCP, callbacks, etc., y las notes de Plan 9 son cadenas, así que es bueno que puedan enviar datos arbitrarios a otro proceso, pero usar el mismo mecanismo para control de procesos tiene las mismas desventajas que en *nix; en mi opinión, solo son cadenas en vez de números.
    • “signalfd is useless” es un buen artículo: https://ldpreload.com/blog/signalfd-is-useless
      Trata los problemas de las señales de Unix y también explica por qué signalfd, que Linux creó para intentar resolverlos, no funciona bien.
    • Al escribir aplicaciones portables, las diferencias en el manejo de señales entre BSD y SYSV eran un problema.
      https://pubs.opengroup.org/onlinepubs/009604499/functions/bs...
      También es importante que el código dentro de un manejador de señales sea reentrante. “Por lo general, las funciones no reentrantes no son seguras para llamarse desde un manejador de señales”.
      https://man7.org/linux/man-pages/man7/signal-safety.7.html
  • Me interesaba Hare, pero al ver esta entrada del FAQ me pareció una política tremendamente autodestructiva: https://harelang.org/documentation/faq.html#will-hare-suppor...
    Básicamente, apoyo que los desarrolladores usen la licencia que quieran, apunten al sistema operativo que quieran y escriban el código que quieran
    Pero eso no convierte a esta política específica en una buena idea. Incluso la FSF, considerada uno de los grupos más extremos o principistas de la filosofía del software libre, da soporte a Windows y POSIX. Uno puede quejarse y llamarlo Woe32, pero Stallman ha argumentado de forma bastante convincente que hacer que los proyectos de software libre también funcionen en sistemas propietarios ayuda más en la lucha por un mundo sin software propietario
    El código de las bibliotecas está licenciado bajo MPL, así que usar Hare no te ata a una licencia específica. Pero me pregunto qué vida puede tener un lenguaje con una actitud de “no damos soporte, no preguntes en el foro, no vengas aquí” hacia más del 95% de los escritorios
    Irónicamente, si buscas “harelang repo” en Google, el primer resultado es un port no oficial para macOS, y el repositorio real de SourceHut ni siquiera aparece en la primera página
    Los lenguajes crecen como una bola de nieve o desaparecen. Estoy escribiendo esto desde una Mac, pero si quisiera podría usar una máquina Linux ahora mismo. Entonces, ¿por qué debería aprender un lenguaje que impone a los desarrolladores una prueba de pureza que ni siquiera la FSF exige? Una parte considerable del open source y del software libre se escribe en Mac, y más de lo que uno cree también se escribe en Windows
    A mi juicio, lo que distingue a Hare de Odin o Zig es precisamente esa actitud de pureza y exclusión. Les deseo hacking feliz y éxito, pero soy pesimista respecto de lo segundo

    • Por un lado, puedo respetar que los autores se mantengan firmes en lo que quieren lograr y no acepten todas las demandas
      Por otro lado, eso no es lo único del FAQ que hace levantar una ceja
      “No hay gestor de paquetes, y como valor compartido se fomenta menos la reutilización de código”
      “qbe genera código más lento que LLVM, y el rendimiento está en el rango de 25 a 75% del rendimiento en tiempo de ejecución frente a código similar generado por LLVM”
      “¿Se puede usar multithreading en Hare? Probablemente no”
      “¿Entonces tengo que implementar mi propia tabla hash? Sí. Las tablas hash son una estructura de datos común que muchos programas en Hare tendrán que implementar desde cero”
      Por ahora, definitivamente no es un lenguaje diseñado con la adopción masiva como objetivo. Está bien, y al menos son honestos al respecto
    • No estoy de acuerdo con la palabra “impone”. Si alguien hace algo gratis, mientras no sea malintencionado, no tiene obligación de hacerlo de una forma específica
      Tienes libertad para expresar tu opinión, pero cuando los desarrolladores ya fijaron sus lineamientos, este tipo de crítica no me parece constructiva
    • Creo que tú y la gente de Hare tienen una definición de éxito distinta. Decir que “los lenguajes crecen como una bola de nieve o desaparecen” también me suena a menospreciar bastante a muchos lenguajes que, sin tener popularidad de rockstar, han avanzado de forma constante durante décadas
      No todas las bandas tienen que aparecer en las listas de Billboard para valer la pena escucharlas
    • Significa que oficialmente no van a dar soporte a Windows ni a macOS. Si alguien quiere, ¿no puede otro proyecto intentar hacer un port? Me parece bien que sean honestos sobre el nivel de soporte que pretenden dar
      Dar soporte a sistemas operativos que los desarrolladores no usan es pedir mucho
    • “Los lenguajes crecen como una bola de nieve o desaparecen” no es cierto y es una afirmación ingenua
      Hay bastantes lenguajes que, aunque no son populares en general, prosperan en nichos sólidos y cumplen roles importantes
  • Impresionante, muy genial e inspirador. Ejemplos como “construir algo impresionante en X días” requieren experiencia y talento acumulados durante años

    • Comparado con eso, hoy en día a mí me tomó casi una semana cambiar el texto de un solo botón con cadenas de internacionalización
      Tuve que poner la cadena en inglés en el catálogo, actualizar varias pruebas, correr las pruebas en el sistema local, subir los cambios a un clúster de staging, arreglar fallas inesperadas en las pruebas, desplegar a producción, pedirle al equipo de traducción que la tradujera a varios idiomas y actualizar la documentación
    • También es el creador de KnightOS, escrito hace más de 12 años únicamente en ensamblador Z80
      https://www.ticalc.org/archives/files/fileinfo/463/46387.htm...
    • Drew es inteligente y el plazo fue corto, pero creo que verlo simplemente como alguien a quien hay que poner en un pedestal es una perspectiva equivocada. Hacer un clon de UNIX es, en la mayoría de las universidades, un proyecto de grado común
      Lo que se necesita para expandirlo hasta algo pulido es más perseverancia que una genialidad especial
    • Además, ya existía Helios, una implementación previa del kernel, que proporcionó buena parte del código de más bajo nivel. No quiero restarle mérito al logro, pero DD ha sido bastante abierto en que gran parte de la velocidad de este proyecto se debió a haber hecho Helios primero y reutilizar ese código
    • Como ya estaba Helios, básicamente se integraron algunas partes faltantes
  • Fue realmente genial ver las actualizaciones que publicaba casi todos los días en Mastodon. Se podía ver cómo una persona experimentada iba encajando poco a poco software complejo

  • El código está aquí: https://git.sr.ht/~sircmpwn/bunnix/tree/master
    Tiene licencia GPLv3

  • El espacio de usuario está ensamblado en su mayoría a partir de fuentes de terceros
    Al principio me sorprendió que al hacer clic en la ISO apareciera una descarga de 60 MB, pero ahora entiendo por qué
    A modo de comparación, Linux 0.01 era una descarga de 71 KB, aunque solo incluía el código fuente del kernel

  • Hare parece un lenguaje interesante
    Sin embargo, en esta era multinúcleo, creo que la siguiente limitación podría restringir su adopción
    Según las FAQ https://harelang.org/documentation/faq.html, ante la pregunta de si se puede usar multithreading en Hare, la respuesta es “probablemente no”
    Para multiplexar operaciones de entrada/salida recomiendan un event loop, y cuando se necesita usar recursos de CPU en paralelo, recomiendan múltiples procesos con memoria compartida
    Estrictamente hablando, se pueden crear threads en un programa Hare. Se puede enlazar con libc y usar pthreads, o usar directamente la llamada al sistema clone(2). Los sistemas operativos implementados en Hare, como Helios, suelen implementar multithreading
    Pero como la biblioteca estándar upstream no garantiza reentrancia, es completamente tu responsabilidad evitar pegarte un tiro en el pie

    • “Múltiples procesos con memoria compartida si necesitas usar recursos de CPU en paralelo” en realidad es bastante potente
      Personalmente prefiero este enfoque para la mayoría de los usos, porque limita la posibilidad de data races únicamente a las regiones de memoria compartida. Desde el punto de vista de las data races, se siente como un “unsafe block” de la memoria
    • Si la biblioteca estándar upstream no garantiza reentrancia, eso excluye no solo el multithreading, sino también su uso dentro de interrupciones. Me parece una limitación bastante grande para un “lenguaje de programación de sistemas”
    • Me gustaría que tuviera closures
  • Esto viene de “Linux System Call Table – Chromiumos” https://www.chromium.org/chromium-os/developer-library/refer... https://news.ycombinator.com/item?id=33395777
    google/syzkalleR
    Llamadas al sistema de Fuschia / Zircon: https://fuchsia.dev/fuchsia-src/reference/syscalls