От boot-кода до shell: несколько месяцев разработки ОС на Rust с ИИ-агентами. AArch64.. AArch64. codex.. AArch64. codex. genrt.. AArch64. codex. genrt. Open source.. AArch64. codex. genrt. Open source. qemu.. AArch64. codex. genrt. Open source. qemu. rust.. AArch64. codex. genrt. Open source. qemu. rust. userspace.. AArch64. codex. genrt. Open source. qemu. rust. userspace. ии-агенты.. AArch64. codex. genrt. Open source. qemu. rust. userspace. ии-агенты. операционные системы.. AArch64. codex. genrt. Open source. qemu. rust. userspace. ии-агенты. операционные системы. разработка ос.. AArch64. codex. genrt. Open source. qemu. rust. userspace. ии-агенты. операционные системы. разработка ос. Системное программирование.. AArch64. codex. genrt. Open source. qemu. rust. userspace. ии-агенты. операционные системы. разработка ос. Системное программирование. ядро ОС.

Ещё полгода назад я всерьёз считал, что собственная операционная система — слишком большой проект для одного человека, который может заниматься ею только в свободное от основной работы время. Для старта нужно было одновременно разбираться в AArch64, MMU, исключениях, прерываниях, планировании, ELF, CPIO — и при этом как-то отлаживать код в среде, которой ещё толком нет.

Сейчас мой экспериментальный проект genrt загружает high-half-ядро в QEMU, запускает вытесняющий планировщик, пользовательские процессы на EL0 и интерактивную командную оболочку из initramfs. В этой статье я расскажу, как пришёл к такому результату, где именно мне помогли ИИ-инструменты и почему это всё равно не было историей про «написал промпт — получил ОС». Во второй половине статьи разберу архитектуру genrt, последовательность загрузки и ограничения, которые пока не позволяют называть систему полноценной RTOS.

Что получилось за 4 месяца

Начну с результата. genrt — небольшая экспериментальная операционная система для AArch64. Ядро написано преимущественно на Rust, ранняя загрузка и переключение контекста — на AArch64-ассемблере, а пользовательские программы — на freestanding C.

На момент релиза v0.1.0-alpha.2 в проекте около 13 тысяч строк кода ядра, 4,7 тысячи строк инфраструктуры и тестов и ещё примерно 600 строк userspace. Само по себе количество строк мало о чём говорит, но масштаб уже достаточный, чтобы пройти полный путь от собственной точки входа до запуска отдельных ELF-программ.

Пока целевая платформа у проекта одна: одноядерная виртуальная машина QEMU virt с Cortex-A72 и GICv2. Сейчас genrt умеет:

  • загружаться на AArch64 и переходить из физически адресуемого boot-кода в high-half-ядро;

  • строить отдельные виртуальные адресные пространства ядра и пользовательских процессов;

  • вытесняюще планировать потоки по алгоритму Round Robin;

  • обрабатывать таймерные прерывания, исключения из EL0 и ввод с UART;

  • монтировать read-only initramfs в формате CPIO newc;

  • загружать статические ELF-файлы и запускать их на EL0;

  • выполнять цепочку fork → execve → waitpid;

  • запускать интерактивную командную оболочку и небольшие утилиты echo, cat, ls и pwd;

  • проходить воспроизводимые QEMU-тесты локально и в CI.

Долгосрочное направление проекта — эксперименты в области жёсткого реального времени. Но считать текущую версию genrt полноценной RTOS было бы преждевременно: пока нет приоритетного планирования, измеренных верхних границ задержек и даже поддержки реальной аппаратной платформы. Это цель развития, а не характеристика текущего релиза.

Пример загрузки и работы в shell
$ cargo xtask run-aarch64
...
[INFO ] memory: switched to runtime kernel page tables; TTBR0 cleared
[INFO ] initramfs: mounted 8 files, 3 directories
[INFO ] sched: irq-return preemptive switching initialized
[INFO ] init: spawning first EL0 process
[INFO ] init: loading /init from initramfs

genrt shell
> ls
bin
etc
hello.txt
init
readme.txt
> cat /etc/banner
genrt initramfs
> cd bin
> pwd
/bin
> ls
cat
echo
ls
pwd
> exit
[INFO ] init: user process exited code=0

Почему я вообще взялся за свою ОС

С Linux я познакомился около пяти лет назад. Для меня это стало входом не только в системное программирование, но и в аппаратную инженерию: постепенно операционная система перестала выглядеть как набор пользовательских программ и превратилась в большой слой, который связывает код с процессором, памятью и периферией.

Тогда же появилась мысль когда-нибудь написать собственную ОС. Правда, довольно долго она оставалась именно мыслью. Причины были приблизительно следующими:

  • нужно было освоить слишком много направлений системного программирования, причём многие из них зависят друг от друга;

  • ошибки в ядре трудно локализовать, особенно пока нет привычных средств диагностики;

  • сделать проект основной работой я не мог;

  • команды, готовой вместе пройти этот путь, рядом не было;

  • последовательное изучение всего необходимого стека заняло бы больше свободного времени, чем я был готов выделить.

И дело не только в количестве кода. Даже небольшой этап вроде запуска первого процесса требует изучить теорию, посмотреть, как похожую задачу решают другие разработчики, определить границы подсистем, написать код и тесты, отладить результат и не забыть обновить документацию.

ИИ не убрал ни один из этих этапов, но он сильно сократил расстояние между вопросом и результатом, который можно собрать, запустить и проверить. Именно это сделало старую идею реалистичной.

Как ИИ встроился в разработку

В моём процессе браузерный чат и кодовые агенты решают разные задачи. Чат нужен в основном до того, как начинается изменение репозитория: с его помощью я изучаю незнакомую или плохо знакомую область знания, обсуждаю варианты архитектуры и превращаю идею в ясное техническое задание. Агенты подключаются позже — читают реальный код, вносят изменения, запускают проверки и разбирают результат.

Чат как интерактивный учебник и собеседник по архитектуре

Больше всего времени чат сэкономил мне там, где обычный поиск быстро превращается в десятки вкладок. Например, при изучении AArch64 я мог начать с общего вопроса про уровни исключений, затем перейти к конкретным регистрам, таблице векторов, устройству GICv2 или последовательности включения MMU — и уточнять каждое непонятное место, не теряя общий контекст.

Обычно я использовал чат, чтобы:

  • разбирать архитектуру AArch64 — от системных регистров до MMU, GICv2 и обработки исключений;

  • сравнивать несколько вариантов устройства новой подсистемы и заранее обсуждать их слабые места;

  • смотреть, как похожие проблемы решены в других ОС, не копируя чужую архитектуру целиком;

  • делить большой замысел на небольшие этапы;

  • превращать выбранное решение в техническое задание с инвариантами и критериями приёмки;

  • анализировать результаты отладки, когда наблюдаемое поведение не совпадало с моей моделью системы.

Для меня особенно важен именно диалоговый режим. При работе с новой темой я часто ещё не знаю, какой поисковый запрос будет правильным. В чате можно начать с неточного вопроса и постепенно дойти до модели, которой уже хватает для конкретного решения в коде.

При этом ответ модели для меня не является архитектурным решением сам по себе. Он скорее помогает быстрее собрать варианты и увидеть вопросы, которые я мог пропустить. Окончательный выбор я всё равно сверяю с кодом и текущими ограничениями проекта.

Агенты как разработчики, тестировщики и ревьюеры

Для работы непосредственно с репозиторием я использовал Codex. Со временем у меня сложилось несколько ролей:

Роль

Что делает

Исследователь

Находит реальные точки входа, вызовы и зависимости между затронутыми подсистемами

Архитектор

Формулирует границы решения, инварианты, варианты реализации и необходимые ADR

Разработчик

Вносит изменения в основной код по согласованному техническому заданию

Тестировщик

Проектирует проверки, добавляет тесты и разбирает сбои

Ревьюер

Ищет дефекты, нарушения инвариантов, архитектурных границ и стиля проекта

Это не означает, что пять агентов одновременно правят одни и те же файлы. Такой режим быстро привёл бы к конфликтам и размыванию ответственности. Исследование и проектирование можно выполнять параллельно, но основной код меняет один разработчик. После этого результат отдельно смотрят тестировщик и ревьюер.

Типичный цикл выглядит примерно так:

идея этапа
    ↓
изучение теории и существующего кода
    ↓
архитектурное решение и критерии приёмки
    ↓
реализация одним агентом
    ↓
format / lint / build / QEMU-контракты
    ↓
независимое ревью
    ↓
исправления → повторная проверка → merge

На практике цикл редко проходит идеально с первого раза. Ревью может обнаружить, что решение нарушает инвариант планировщика, QEMU-тест — поймать ошибку на границе ядра и userspace, а отладка — показать, что исходное техническое задание было неполным. Тогда задача возвращается на предыдущий этап, но уже с конкретной ошибкой, которую можно воспроизвести, а не с общим ощущением «что-то не работает».

Какие решения я не делегирую

ИИ заметно ускоряет работу, но не определяет направление проекта. Я по-прежнему выбираю следующий этап, ограничиваю объём изменения, принимаю архитектурные компромиссы и решаю, можно ли считать результат готовым.

Особенно осторожно я отношусь к изменениям в управлении памятью, обработке исключений и переключении потоков. Там правдоподобная ошибка может успешно скомпилироваться и проявиться намного позже. Поэтому важнее не качество отдельного промпта, а наличие явных инвариантов, небольших изменений и тестов, которые проходят через затронутые границы системы.

Иными словами, ИИ расширил объём работы, который я могу сделать один, но не снял с меня ответственность за результат.

Что пришлось построить вокруг агентов

Чем дольше жил проект, тем заметнее становилась одна проблема: агенту недостаточно открыть репозиторий и сказать «реализуй правильно». Он может предложить вполне разумное локальное решение, которое не соответствует уже принятой архитектуре, выделяет память в неподходящем контексте или незаметно меняет контракт между подсистемами.

Поэтому часть инфраструктуры genrt посвящена не самой ОС, а тому, чтобы следующий исполнитель — человек или агент — мог восстановить правила проекта из репозитория.

Правила в AGENTS.md

В корневом AGENTS.md лежат общие правила работы:

  • какие файлы считаются источниками проектного контекста и в каком порядке их читать;

  • какие архитектурные и RT-инварианты нельзя нарушать;

  • какими командами собирать и проверять проект;

  • как разделяется работа между агентами;

  • когда нужно обновлять документацию и ADR;

  • что считается завершённой задачей.

Для отдельных частей дерева правила уточняются во вложенных файлах. Например, kernel/AGENTS.md описывает допустимые контексты выполнения, запрещает аллокации в IRQ и требует держать доступ к системным регистрам, архитектурным инструкциям и MMIO внутри AArch64-слоя.

Такая вложенность оказалась удобной. Агент, который меняет echo, не обязан загружать в контекст все детали планировщика. Но при работе с IRQ-обработчиком локальные ограничения ядра уже нельзя случайно пропустить.

Skills как готовые рабочие процедуры

AGENTS.md отвечает на вопрос «какие правила здесь действуют», а skills — «как обычно выполнять такой класс задач». В описании агентного процесса сейчас зафиксировано шесть таких процедур:

  • genrt-change-workflow — путь нетривиального изменения от исследования до закрытия задачи;

  • genrt-qemu-test — добавление и изменение QEMU-контрактов и тестового машинного протокола;

  • genrt-verify — выбор проверок в зависимости от риска изменения;

  • genrt-review — независимое ревью с упором на конкретные дефекты и доказательства;

  • genrt-adr — создание и замещение архитектурных решений;

  • genrt-docs-sync — поиск документации, которую нужно обновить вместе с кодом.

По сути, skill — это короткая воспроизводимая инструкция или чек-лист. Это не отдельная роль и не обязательный ритуал для каждой правки. Например, изменение только документации не требует полного запуска QEMU, а изменение syscall ABI должно одновременно затронуть userspace-заголовки, документацию и интеграционные контракты.

Память проекта вместо памяти конкретного чата

Всё, что должно пережить отдельную сессию с моделью, хранится в репозитории:

  • memory/current-state.md описывает уже реализованные возможности и текущие границы;

  • memory/invariants.md собирает сквозные инварианты;

  • memory/decisions/ содержит ADR и историю архитектурных решений;

  • README внутри подсистем объясняют, кто владеет ресурсами и как выглядит их жизненный цикл.

Так новый агент может восстановить состояние проекта из версионируемых файлов. Это надёжнее, чем рассчитывать на историю старого диалога или на то, что я сам вспомню все причины решения, принятого несколько месяцев назад.

Одна точка входа для сборки и тестов

Единой точкой входа в сборку, запуск, тестирование и выпуск артефактов служит xtask:

# Проверить форматирование, lint, тесты инструментов и обычную сборку
cargo xtask check

# Показать список QEMU-контрактов и запустить их
cargo xtask test-aarch64 --list
cargo xtask test-aarch64

# Выполнить полный набор локальных и CI-проверок
cargo xtask ci

# Собрать образ и запустить интерактивную командную оболочку
cargo xtask run-aarch64

QEMU поднимает одноядерную AArch64-платформу без сети и графики. Ядро, DTB и initramfs загружаются как отдельные артефакты:

Упрощённая команда запуска QEMU
qemu-system-aarch64 
  -machine virt,gic-version=2 
  -cpu cortex-a72 
  -smp 1 
  -display none 
  -monitor none 
  -nic none 
  -serial stdio 
  -no-reboot 
  -kernel target/aarch64-unknown-none-softfloat/debug/genrt-aarch64.elf 
  -device loader,file=target/aarch64-unknown-none-softfloat/debug/qemu-virt.dtb,addr=0x40000000 
  -device loader,file=target/aarch64-unknown-none-softfloat/debug/initramfs.cpio,addr=0x47000000,force-raw=on

Одни и те же команды запускаются локально и в CI. Это принципиальный момент для агентной разработки: после изменения агент должен не написать, что код «выглядит рабочим», а выполнить тот же сквозной сценарий, который будет обязательным перед слиянием.

Четыре QEMU-контракта

Проверять систему по произвольным строкам обычного лога оказалось ненадёжно: текст сообщения может измениться, хотя поведение останется прежним. Поэтому в тестовых образах используется отдельный машинный протокол GTRT/1, а релизные артефакты дополнительно проверяются на отсутствие его маркеров.

Сейчас есть четыре интеграционных контракта:

Контракт

Что он проверяет

kernel-contract

Планировщик, таймеры, ожидания, mailbox, аллокаторы и гонки между событием и тайм-аутом внутри специального тестового ядра

user-fault

Классификацию исключения из EL0 и завершение только процесса-источника без падения ядра

userspace-contract

Цепочку fork → execve → waitpid, файловые и процессные системные вызовы и запуск обычных утилит echo, cat, ls и pwd

shell-contract

Релизную командную оболочку, разбор argv, наследование cwd, UART RX и восстановление после неизвестной команды или ненулевого статуса дочернего процесса

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

Машинный протокол, состав тестовых образов и раннер на стороне хоста подробнее описаны в docs/testing.md.

Теперь о самой ОС

Агентная инфраструктура — не цель проекта сама по себе. Теперь о том, что получилось внутри ОС. Архитектурно genrt разделена на AArch64-зависимый слой, общее ядро и freestanding userspace. Более формальное описание текущих возможностей хранится в memory/current-state.md.

Основные подсистемы genrt и связи между ними

Основные подсистемы genrt

AArch64-слой

Архитектурный слой отвечает за раннюю загрузку, таблицу векторов исключений, вход и выход из trap-контекста, настройку MMU и доступ к системным регистрам. Там же находятся низкоуровневые части драйверов GICv2, PL011 и ARM Generic Timer.

Я старался не разносить архитектурные детали по общему коду ядра. Планировщик, менеджер памяти или подсистема процессов должны оперировать своими абстракциями, а не напрямую читать TTBR0_EL1 или программировать регистр таймера. Для единственной платформы это может казаться лишним слоем. Зато уже сейчас понятно, какой код придётся менять/дорабатывать при переносе на другую AArch64-плату.

Планировщик, время и ожидания

Планировщик пока довольно простой: одноядерный Round Robin без приоритетов. Единственная планируемая сущность — поток, который может находиться в состояниях Ready, Running, Blocked или Exited.

Таблица потоков и очередь готовых потоков имеют фиксированную вместимость. Слоты со временем переиспользуются, поэтому одного индекса недостаточно: идентификатор потока содержит ещё и номер поколения. Благодаря этому устаревшая ссылка не сможет случайно обратиться к новому потоку, занявшему тот же слот.

Вытеснение выполняется на возврате из IRQ. ARM Generic Timer работает в режиме one-shot: ядро каждый раз программирует его на ближайшее событие — окончание кванта, пробуждение после sleep или тайм-аут ожидания. События времени лежат в заранее выделенной deadline queue на основе минимальной кучи; сам IRQ-путь не меняет размер контейнеров и не выделяет память.

Для блокирующих операций используется WaitToken, в котором есть поколение потока и номер конкретного ожидания. Это защищает от неприятного класса ошибок: поздний тайм-аут или повторное пробуждение не должны воздействовать на уже следующее ожидание потока.

В ядре также есть блокирующий mailbox с тайм-аутом. Пока он недоступен из userspace, но используется для проверки общих механизмов блокировки и пробуждения.

Управление памятью

Во время ранней загрузки AArch64-код строит временные таблицы страниц, которых достаточно для включения MMU и перехода в high-half. После запуска аллокатора физических страниц ядро создаёт постоянные отображения TTBR1, переключается на них и очищает TTBR0 до запуска первого пользовательского процесса.

У каждого процесса собственное TTBR0-пространство. При fork пользовательская память копируется сразу. Copy-on-write и demand paging в проекте пока нет — это дороже в момент создания процесса, зато в текущей модели не переносит скрытое копирование и выделение памяти на более поздний page fault.

Размер кучи ядра задаётся при загрузке. Аллокации допустимы во время bootstrap и в обычном контексте потока, но запрещены в IRQ, ядре планировщика, обработчиках событий времени и на пути передачи trap frame между обработчиком исключения и планировщиком.

Здесь хорошо видно направление проекта: я не пытаюсь максимально эффективно использовать каждый байт памяти. На данном этапе важнее понимать, где и когда ресурс может понадобиться, и не вносить неожиданную работу в невытесняемые участки.

Процессы, ELF и системные вызовы

Таблица процессов тоже ограничена заранее. Процесс владеет адресным пространством, файловыми дескрипторами, текущим каталогом, отношениями родитель–потомок и статусом завершения. Пока у одного процесса может быть только один пользовательский поток.

ELF loader принимает статический AArch64 ELF, проверяет заголовки, отображает загружаемые сегменты, строит пользовательский стек и передаёт управление на EL0 через ERET. Динамического загрузчика, ELF interpreter и libc в системе нет.

Syscall ABI точнее называть Linux-подобным и POSIX-ориентированным, а не POSIX-совместимым. В текущем диспетчере реализованы open, read, write, close, fork, execve, waitpid, getdents64, chdir, getcwd и exit, но только в тех вариантах, которые сейчас нужны проекту.

Например, getdents64 — Linux-специфичный интерфейс, waitpid принимает только конкретный положительный PID при options == 0, а запись в обычные файлы пока не поддерживается. Совпадение номеров и сигнатур отдельных вызовов ещё не делает систему POSIX-совместимой — и в документации я стараюсь не создавать такого впечатления.

Initramfs и userspace

QEMU загружает несжатый CPIO-архив newc в заранее зарезервированный диапазон физической памяти. При старте ядро один раз разбирает архив и строит индекс файлов и каталогов в read-only ramfs.

Полноценной VFS здесь пока нет. Нет writable-файлов, mount API, символьных ссылок, блочных устройств, page cache и большинства привычных метаданных. Файловая подсистема поддерживает абсолютные и относительные пути, текущий каталог, последовательное чтение и Linux-подобные записи dirent64. У каждого процесса есть таблица на 32 файловых дескриптора; первые три заняты stdin, stdout и stderr.

В обычном образе /init — это интерактивная командная оболочка. В /bin лежат отдельные статически слинкованные программы:

  • echo выводит аргументы в stdout;

  • cat читает файл;

  • ls перечисляет содержимое каталога;

  • pwd печатает текущий каталог.

Это, разумеется, сильно урезанные версии привычных утилит. cd реализована внутри командной оболочки (built-in utility): если запустить её как отдельный дочерний процесс, изменится каталог только этого процесса, а после его завершения родительская оболочка останется на прежнем месте.

Ввод приходит через ограниченный кольцевой буфер, который заполняет IRQ-драйвер PL011. Полноценной TTY-подсистемы и line discipline пока нет, поэтому интерфейс остаётся намеренно простым.

Как система доходит от _start до оболочки >

Вся цепочка загрузки в сокращённом виде выглядит так:

xtask → QEMU → _start → boot page tables → high-half
      → kernel_main → scheduler → /init → shell

Полноценного загрузчика здесь нет: его роль частично выполняют xtask и фиксированная конфигурация QEMU. Последовательность получается такой:

  1. xtask собирает kernel ELF и userspace, получает нормализованный DTB для QEMU virt и упаковывает initramfs.

  2. QEMU размещает DTB по физическому адресу 0x4000_0000, точку входа ядра — по 0x4008_0000, а initramfs — по 0x4700_0000.

  3. Низко расположенный trampoline _start оставляет активным только boot CPU, подготавливает физический bootstrap stack и вызывает Rust-код до включения MMU.

  4. Ранний Rust-код читает DTB по фиксированному адресу, строит временные таблицы TTBR0/TTBR1, включает MMU и переходит в high-half со смещением 0xffff_0000_0000_0000.

  5. rust_entry устанавливает VBAR_EL1, восстанавливает сведения о платформе и инициализирует UART, GICv2 и ARM Generic Timer.

  6. kernel_main запускает frame allocator и фиксированную кучу, создаёт постоянные TTBR1-таблицы, очищает TTBR0, монтирует initramfs и подготавливает планировщик.

  7. Планировщик запускает kernel_init_thread. Уже из этого потока ядро читает /init, создаёт для него TTBR0-пространство и начальный EL0-контекст, а затем блокируется в ожидании завершения процесса.

  8. Планировщик выбирает пользовательский поток, активирует его TTBR0 и через ERET передаёт управление в /init. Командная оболочка запускает внешние программы по цепочке fork → execve → waitpid.

Где система пока заканчивается

genrt уже проходит путь от boot-кода до интерактивного пользовательского окружения, но остаётся исследовательским проектом. Самые заметные ограничения сейчас такие:

  • только один CPU: нет SMP-планирования, IPI, TLB shootdown и межъядерных блокировок;

  • только QEMU virt: реальная ARM-плата пока не поддерживается;

  • read-only initramfs вместо VFS, writable-файловой системы и постоянного хранилища;

  • небольшой syscall ABI без сигналов, сокетов, mmap и многих привычных POSIX-интерфейсов;

  • один пользовательский поток на процесс и полное копирование памяти при fork;

  • нет libc, динамического загрузчика, TLS и стандартной userspace-среды;

  • Round Robin без приоритетов, priority inheritance и измеренной верхней границы latency;

  • нет сохранения FP/SIMD-контекста, а само ядро собирается для soft-float target;

  • фиксированное количество процессов, потоков, файловых дескрипторов и событий времени;

  • UART вместо TTY и полноценного терминала.

Не всё из этого я воспринимаю как временные слабые стороны. Фиксированные лимиты и отсутствие отложенного выделения памяти отчасти выбраны сознательно: они удерживают систему небольшой и делают владение ресурсами более явным. А вот SMP, реальное оборудование, приоритетный планировщик и постоянное хранилище — уже естественные кандидаты на следующие крупные этапы.

Как запустить genrt

Статья описывает релиз v0.1.0-alpha.2. Для воспроизведения лучше переключиться именно на этот тег, а не использовать меняющуюся ветку main:

git clone https://github.com/redeemed-sis/genrt.git
cd genrt
git checkout v0.1.0-alpha.2
./scripts/setup/install-deps.sh
cargo xtask run-aarch64

После появления приглашения > можно выполнить ls, pwd, cat /etc/banner, echo hello и exit.

Полный набор проверок запускается отдельно:

cargo xtask ci
Демонстрация работы с релизной genrt

Демонстрация работы с релизной genrt

Что я вынес из этих четырех месяцев

Главный вывод для меня не в том, что ИИ теперь умеет писать операционные системы. Без понимания архитектуры, ограничений и того, как система должна вести себя на практике, с его помощью так же легко построить правдоподобную, но неработающую конструкцию.

В текущий момент меняется другое — цикл от незнакомого вопроса до проверяемого решения стал намного короче. Я могу быстрее разобраться в новой области, обсудить несколько вариантов, сформулировать небольшую задачу, реализовать её и прогнать через воспроизводимые проверки. Благодаря этому проект, который раньше казался слишком широким для одного человека, удалось разбить на последовательность посильных этапов.

При этом расширение возможностей не уменьшает ответственность. Решение о том, что строить, какие компромиссы принимать и почему тестам можно доверять, всё равно остаётся за инженером.

genrt ещё далека от практического применения: ей не хватает SMP, постоянного хранилища, развитого ABI, RT-политик и поддержки реального оборудования. Но это уже не набор разрозненных экспериментов. Собственный boot-код приводит к запуску изолированного userspace, командная оболочка действительно запускает отдельные ELF-программы, а изменения проходят через сквозные тестовые контракты.

Исходный код находится в репозитории genrt, а состояние, описанное в статье, зафиксировано в релизе v0.1.0-alpha.2. Буду рад технической критике, найденным ошибкам и обсуждению архитектурных решений в GitHub.

Автор: redeemed-sis

Источник