Дайджесты новостей
Изометрическая архитектурная схема GoNIX с ядром Linux и чистым пространством пользователя на Go

GoNIX: UNIX-подобная операционная система с пространством пользователя на Go

Если ты хоть раз собирал минималистичный образ Linux под Docker или настраивал виртуальную машину для микросервиса, то наверняка знаешь этот джентльменский набор: ядро Linux, компактная стандартная библиотека musl, швейцарский нож BusyBox и пачка shell-скриптов, которые связывают всё это хозяйство воедино. Эта архитектура живет десятилетиями, и кажется, что ничего нового здесь уже не придумать. Но у классической связки на Си есть свои родовые травмы: хрупкие bash-скрипты на этапе инициализации, ручное управление памятью в системных утилитах и постоянный страх перед переполнением буфера.

Недавно на радарах системных инженеров появился любопытный проект — GoNIX (официальный сайт gonix.dev). Его авторы задались радикальным вопросом: а что, если оставить стандартное ядро Linux ради поддержки железа и виртуализации, но полностью выбросить Си из пространства пользователя (userspace) и переписать все базовые компоненты на Go?

Я решил покопаться в архитектуре дистрибутива, и должен признать: концепция выглядит на удивление стройной и практичной.

Как устроен userspace без Си

В GoNIX нет привычного демона systemd, громоздких скриптов /etc/init.d/ или утилит вроде udev. Практически каждый системный кирпичик здесь скомпилирован статически на Go:

  1. Процесс init (PID 1). Первый процесс, которому ядро передает управление, написан на чистом Go. Он монтирует псевдо-файловые системы (/proc, /sys, /dev), инициализирует базовые каталоги, контролирует жизненный цикл служб и аккуратно собирает сигналы завершения дочерних процессов. Никаких интерпретаторов sh/bash на этапе загрузки не запускается вовсе — логика инициализации жестко типизирована и исполняется в нативном машинном коде.
  2. Единый мультикомандный бинарник coreutils. Подобно тому, как устроен BusyBox, десятки стандартных команд (ls, cat, cp, ps, mkdir, kill) объединены в один исполняемый файл. В системе они представлены набором символических ссылок. Это позволяет многократно снизить накладные расходы на размер Go runtime.
  3. Прямая работа с ядром через netlink. Для поднятия сети, настройки маршрутов и работы встроенного DHCP-клиента GoNIX не вызывает сторонние утилиты пакета iproute2. Код общается с подсистемой сети ядра напрямую через сокеты netlink.
  4. Встроенный SSH-сервер. Вместо тяжелого OpenSSH или Dropbear в систему интегрирован легковесный SSH-демон на базе стандартного пакета golang.org/x/crypto/ssh.

При этом разработчики внедрили бескопийную работу с initramfs: изменяемый слой файловой системы накладывается поверх базового образа без дублирования файлов в оперативной памяти. В итоге от момента передачи управления ядром до появления приветствия терминала (login prompt) проходит всего около 20 миллисекунд.

Зачем это нужно: ниша микро-ВМ и Serverless

Ты можешь резонно спросить: зачем вообще городить огород, если Alpine Linux с BusyBox весит считанные мегабайты? Дело в том, что современная инфраструктура все чаще уходит от тяжелых долгоживущих виртуалок в сторону эфемерных микро-ВМ (micro-VM на базе Firecracker, Cloud Hypervisor или QEMU). Для таких задач время холодного старта и безопасность имеют решающее значение:

  • Безопасность памяти (Memory Safety). Системные утилиты и демоны, написанные на Go, защищены от классических уязвимостей переполнения буфера и повреждения указателей. Аудит системы на одном языке программирования со строгой типизацией на порядок прозрачнее. Ранее мы исследовали, насколько глубоко устроена конкурентность Go в языке C: воссоздаем плановик goroutine, каналы и RCU, и переход на чистый Go в userspace избавляет разработчиков от необходимости балансировать между низкоуровневыми указателями и безопасностью.
  • Идеально для единичных микросервисов. Ты можешь собрать собственный минимальный ISO-образ или диск, куда положить только ядро, GoNIX init и свой Go-бинарник. Получается практически unikernel, но со стандартным и предсказуемым поведением ядра Linux.
Инженерные компромиссы и цена решения

Чудес не бывает, и за выбор Go приходится платить. Во-первых, каждый независимый бинарник на Go несет в себе собственный рантайм со сборщиком мусора, поэтому по потреблению оперативной памяти мелкие Go-демоны будут уступать C-аналогам на musl. Во-вторых, для запуска традиционных программ на Си (например, браузеров или баз данных) в GoNIX используется пакетный менеджер pkg, компилирующий пакеты против musl libc из репозитория pkgs.ulinux.org.

Проект поддерживает архитектуры amd64 и arm64, запускается в QEMU и даже позволяет развернуть графический рабочий стол на базе легковесного композитора Wayland (labwc). Если захочешь пощупать систему вживую без установки, авторы подняли открытый демо-сервер: достаточно выполнить команду ssh root@gonix.dev в своем терминале. Это отличный пример того, как свежий взгляд на базовую архитектуру позволяет выжать максимум скорости из привычного ядра.