InquirerThe state of Philippine news: Trust falls amid changing media habitsDaily MaverickBrazil may turn right-wing again — this is why South Africans should careBollywood HungamaTiger Shroff team shares video of his injuries after Juhu firing incident; actor reportedly sustained cuts and bruisesPunchMy wife eager to know Ballon d’Or result, says KaneCNN TürkMevduat faizi düşecek, kredi faizi ne olacak?ESPNTransfer rumors, news: Como's Paz destroyed Man United, now they want to sign himZDF heuteAktuelle Pressemitteilungen des ZDFThe Hollywood Reporter‘Biermann’ Director Explains Why His Doc About a German Political Singer Is a Film for the Trump EraFutura SciencesLes Romains n'ont jamais réussi à protéger leurs soldats de ces parasites qui rongeaient leurs intestinsWirtualna PolskaGorzka reakcja Tuska. Tak premier mówił o odejściu Pełczyńskiej-NałęczNHK 社会鹿児島 鹿屋 飲食店経営者殺害事件で容疑者を逮捕 容疑は否認InteriaUkraina chce zmian na granicy z Polską. "Tak szybko, jak to możliwe"
The Daily Newsstand · Free, Always
Friday, October 23, 2026

Как неудавшийся клон GodotOS стал Unix на Godot

Translate

У каждой домашней сети начала 2000-х была своя география. До соседнего компьютера — двадцать метров витой пары, до сервера провайдера — DSL-модем и неизвестность за ним. Но мерили мы это пространство не метрами, а мегабитами: 10 через хаб, 100 через свитч, 256 килобит до остального мира. И внутри этих мегабит помещался целый маленький интернет — чьи-то домашние страницы, IRC-канал, FTP с демками, почтовый сервер, который вечно лежал.

AgoraOS — моя попытка снова построить такое пространство. Это Unix-подобная операционная система, написанная на GDScript внутри Godot: с ядром, процессами, пайпами, сетевым стеком и маленьким интернетом 2003 года вокруг, который пока статичный, но об этом позже. Хотя задумывалась она совсем не так. Начиналось всё с желания аккуратно переписать чужой проект — а закончилось тем, что я перестал рисовать операционную систему и начал её строить.

Смена курса

У меня давняя любовь к глубоким симуляциям — к играм и системам, где вещи работают не потому, что так задано скриптами, а потому, что так устроена архитектура.

Поэтому, когда я сел за реализацию, сразу стало понятно: обычная имитация мне не нужна. Окно терминала, которое печатает заготовленные ответы на заготовленные команды, — это та же красивая картинка. Я хотел, чтобы процессы работали в связке. Чтобы ls | grep был настоящим пайпом между двумя процессами, ping — настоящими ICMP-пакетами, а выдернутый кабель — настоящей проблемой для всего, что через него шло.

Всё есть файл!

Так родилась идея сделать ОС по всем принципам Unix, только на Godot. И оказалось, что изобретать почти ничего не нужно. Unix уже полвека назад ответил на большинство вопросов, которые возникают при проектировании такой системы. Каждый принцип становился кирпичом, на который ложился следующий.

Всё — файл. Аккаунты лежат в /etc/passwd и /etc/shadow, поэтому useradd просто дописывает строку, login её читает, а игрок может отредактировать файл руками — и это тоже сработает. Процессы видны в /proc, устройства — в /dev. Даже программы в /bin — файлы: их можно скопировать, переименовать или снять с них x-бит. А значит, пакетному менеджеру не нужно ничего, кроме копирования файлов.

Процессы и пайпы. Каждая программа — корутина GDScript, без потоков. Блокирующее чтение, sleep или ожидание дочернего процесса просто приостанавливают её через await. Вот yes целиком:

extends AgoraProgram


func summary() -> String:
	return "print a string forever"


func main(p: AgoraProcess) -> int:
	var text := " ".join(p.argv.slice(1)) if p.argv.size() > 1 else "y"
	while p.outln(text):
		await p.tick()
	return 0

Когда у пайпа не остаётся читателей, outln возвращает false, писатель получает SIGPIPE, и yes | head останавливается так же, как в настоящей системе. p.tick() отдаёт кадр движку не чаще раза в 8 мс, поэтому while true; do :; done не вешает игру.

Здесь был единственный по-настоящему коварный момент. Godot возобновляет ожидающие корутины синхронно, прямо внутри emit(). Если запустить стадии пайплайна слева направо, первая начнёт писать раньше, чем вторая встанет в ожидание. Решение — создать все процессы пайплайна заранее, а запускать их с конца:

for i in range(stages.size() - 1, -1, -1):
		if stages[i].proc:
			procs.append(stages[i].proc)
			(stages[i].proc as AgoraProcess).start()
	var foreground: bool = interactive and p.tty != null and not io.get("bg", false)

pid 1. Ядро загружается, монтирует /proc, /dev, /sys и запускает /sbin/init. Каким он будет — systemd с юнитами или sysvinit с /etc/inittab — решает установленная система, а не ядро. Если init умирает — kernel panic.

Конфигурация в файлах, а не в памяти. Этот принцип сэкономил больше всего работы. При сохранении мира пишутся только диски, CMOS и часы. Процессы, маршруты, правила файрвола не сохраняются вообще: загруженная машина просто загружается заново и настраивается из своих /etc, как настоящая. Не нужно сериализовать живое ядро — нужно честно реализовать загрузку.

В итоге слои повторяют настоящий компьютер. Ядро — чистый GDScript без нод и интерфейса, оно работает и в headless-режиме. Рабочий стол — лишь один из фронтендов, как X11 или Wayland.

Когда кирпичи начинают работать вместе

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

Лучше всего это видно на сети. Её первая версия была ровно тем, от чего я уходил: класс AgoraNetwork с плоской таблицей «адрес → машина». Одна плоская сеть, без маршрутизации и без пакетов. По сути, это была маленькая GodotOS внутри моего проекта: сеть, нарисованная поверх, а не устроенная изнутри.

Дальше сеть собрали снизу вверх. Ethernet и ARP, ip и ifconfig, ICMP, ifupdown, мост, DHCP, форвардинг, netfilter, NAT, TCP, DNS. Соединения находят друг друга только по адресам и портам. Никаких ссылок на объекты в пакетах, никаких обходных каналов.

Симуляция вместо сценария

После этого начались вещи, которых нет ни в одном отдельном объекте.

Выдёргиваешь кабель — и открытая ssh-сессия не обрывается мгновенно, а подвисает. TCP перепосылает неподтверждённые данные с удваивающимся таймаутом и только потом сдаётся с ETIMEDOUT. Никто не писал код «если кабель выдернут, сломать ssh». Его сломала физика.

Та же история с кабелем, который игрок обжал сам в 3D-режиме. Коробка с кабелями, обжимка, тестер, битовые ошибки в кабеле, счётчики ошибок в ядре и повторная передача в TCP. Связались они сами, потому что каждый слой делает только то, что делает его настоящий прототип.

Ещё несколько таких цепочек:

  • Тонкий коаксиал. Забыл терминатор на конце шины — и сеть ложится целиком, потому что каждый кадр сталкивается с собственным эхо. Ровно так это и было в 10BASE2.

  • Домашняя страничка. Кладёшь index.html в ~/public_html — apache с UserDir отдаёт её, краулер поисковика Ferret находит её по HTTP, и через какое-то время она появляется в поиске.

  • traceroute через NAT. NAT на роутере откатывает трансляцию не только у ответов, но и у ICMP-ошибок, включая заголовок, процитированный внутри. Поэтому traceroute из-за домашнего роутера показывает честный маршрут.

  • Забытый пароль BIOS. Открываешь корпус, переставляешь перемычку Clear CMOS или вынимаешь батарейку вместе со шнуром питания — и настройки вместе с паролем пропадают. Способ из 2003 года работает, потому что пароль лежит в CMOS, а CMOS питается от батарейки.

Честность иногда возвращала и настоящие баги. Когда TCP стал передавать данные сегментами, оказалось, что nc, через который прогоняли 25 000 строк, отправляет 25 000 сегментов — по одному на каждую запись. Решение в итоге оказалось тем же, что и в реальных стеках: алгоритм Нейгла, а ssh и sshd выставляют TCP_NODELAY, как это делает OpenSSH.

Маленький интернет

Когда кирпичей стало достаточно, из них сложился целый интернет. Демо-мир — октябрьский вечер 2003 года. На столе ПК с монитором и домашний роутер, за ним провайдер LanLink, который выдаёт адрес по DHCP и держит DNS, а дальше — серверы с сайтами той эпохи.

Каждый сервер — такая же машина, как ПК игрока, с тем же ядром, тем же init и тем же apache. Сайты — это обычные HTML-файлы в /var/www и ~/public_html, написанные в духе времени. Есть Linux user group, гиковские новости, e-zine, сайт демосцены с анонимным FTP. Есть поисковик Ferret, чей краулер действительно обходит все эти сайты, мессенджер Ping! в духе ICQ, почта провайдера и IRC, где в #lanlink дежурит бот техподдержки.

GUI клиент IRC

GUI клиент IRC

Каждая машина при этом загружается по-настоящему: BIOS с POST и Setup, GRUB 0.97 с menu.lst, ядро, init, login:. Первый раз ПК пустой, в приводе лежит установочный диск, и систему нужно поставить текстовым инсталлятором. А в 3D-режиме всё это стоит на столе: индикаторы мигают от трафика, на мониторе — настоящий экран, корпус открывается, а кабели можно переткнуть или обжать заново.

Загрузка системы

Загрузка системы

Когда интернет оживёт

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

Когда я доделаю мультиплеер, этот интернет сможет ожить.

Метры и мегабиты

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

Сейчас внутри AgoraOS можно собрать полноценный интернет. Пока он маленький и тихий. Но в нём уже есть то, ради чего всё затевалось: пространство, размер которого считается в метрах кабеля, а ощущается в мегабитах в секунду. Хаб на 10, свитч на 100, коаксиал, который ложится от одного забытого терминатора. Если мультиплеер получится, может быть, кто-то снова почувствует тот самый — маленький локальный интернет, в котором ты знаешь каждый сервер по имени.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.