Весь исходный код на C#, даже то, что обычно пишут на ассемблере.
MSBuild и PowerShell только оркестрируют сборку. Обработчики прерываний, write barrier'ы, трамплины interface dispatch, чтение CR3: всё решается одним из трёх способов.
Ярусы 0 и 1, то есть ядро и все нативные приложения, не содержат ни строки на C, C++ или ассемблере. Единственное исключение, ярус 2: стоковому PowerShell нужны JIT и рефлексия, поэтому он работает на опциональном форке CoreCLR. Форк написан на C++ и для сборки и загрузки системы не нужен.
Насколько известно, других managed-ОС с таким инвариантом нет: Cosmos на нижнем уровне опускается до X#, собственного диалекта ассемблера, а Singularity держала загрузчик и HAL на C++ и ассемблере. Если задача кажется нерешаемой на C#, значит, она неверно поставлена.
Сам C#
[RuntimeExport], [UnmanagedCallersOnly], delegate* unmanaged, fixed, арифметика указателей.
Ассемблер Iced: при сборке и на лету
Source generator превращает 16 стабов раннего старта в байтовые шаблоны. Когда куча поднята, Iced вызывается напрямую.
var a = new Assembler(64);
a.mov(rax, rcx);
a.Assemble(writer, rip);
COFF-объекты без единой строки на C
Если линкеру нужен символ, который ILC не эмитит, MSBuild-задача сама создаёт .obj.
[BootAsm.CoffDataSymbol(
"__security_cookie",
Section = ".data")]
public static ulong SecurityCookie
= 0x2B992DDFA232UL;
Три яруса исполнения
От голого железа до стоковых .NET-сборок, которые грузятся байт-в-байт. Unikernel: одно адресное пространство, свой планировщик, свой GC.
Kernel-AOT
Ядро, загрузка, драйверы, планировщик. Прямой доступ к CR3, PCI, MMIO, IDT, local APIC.
- Где
- OS/
- Сборка
- NativeAOT + NoStdLib + MinimalRuntime
PE-приложение
Freestanding-приложения: лаунчер, оболочка, DOOM, эмуляторы NES. У каждого свой сборщик мусора и своя куча.
- Где
- apps_native/
- Сборка
- NativeAOT + NoStdLib + общий SDK
CoreCLR-hosted
Стоковые .NET DLL байт-в-байт: JIT, рефлексия, полный BCL. Так работает PowerShell.
- Где
- \sharpos\*.dll на FAT
- Сборка
- опциональный форк CoreCLR (C++), слинкован с ядром
Это не hello world: PowerShell и DOOM на голом железе.
Самые требовательные тесты всего стека сразу: TPL, исключения, рефлексия, GC, FAT32, ANSI-консоль, framebuffer.

PowerShell 7.6.5
Стоковый pwsh грузится с FAT32 до интерактивного приглашения и выполняет настоящие командлеты. PSReadLine работает полностью: цвета, Tab, история, Ctrl+C.
DOOM
ManagedDoom собран как PE-приложение поверх своей std. WAD читается с FAT32, картинка через GOP с увеличением 2×, PS/2-клавиатура, 35 Гц по HPET.

Своя среда
Лаунчер на Terminal.Gui, оболочка с синтаксисом bash и историей, до 16 процессов через Process.Start, свой FAT32 с записью, USB-стек на xHCI, эмуляторы NES. Весь набор, ядро, оболочка, приложения, трубы и CoreCLR с PowerShell, тестируется на реальном ноутбуке, HP 255 G10 с Ryzen 3 7320U, 8 ГБ памяти и прошивкой AMI, и полный набор проб там проходит.
Программы передают объекты, а не байты.
PIPEGEN | PIPEFILT 3 | PIPECNT работает как в bash, но каждая стадия получает настоящий объект с полями. Объект лежит в общей памяти обмена: без парсинга и без копирования в кучу получателя.
Обе стадии PIPESPY показывают один и тот же адрес: один объект проходит через четыре программы без копирования.
- Ноль выделений на сообщениеКопия, пересылка, чтение на месте. На графе из 100 объектов в 5–6 раз быстрее, чем
BinaryWriter+BinaryReader. - Запросы прямо в конвейереKusto Query Language над потоком сообщений:
where,summarize,extend,project. Запрос связывается до прихода первых данных. - Один атрибут, и тип ходит по трубеПометьте тип атрибутом
[Message], и его можно читать, фильтровать и писать обратно без копии.Pipe.Read<LogEntry>() .Where(e => e.Level >= min) .WriteTo(); // без копии - JSON, JSON Lines, CBOR
CONVERTработает потоково, в памяти одно сообщение: файл на 100 МиБ проходит через кучу в 64 МиБ.
Что уже работает
Каждая отметка «работает» подтверждена прогоном проб в ядре. Полная матрица с комментариями лежит в README.
| Возможность | Kernel-AOT | PE-прил. | CoreCLR |
|---|---|---|---|
| Управляемая куча, GC с точным сканированием стека | работает | работает | работает |
try / catch / finally / фильтры when | работает | работает | работает |
async / await, yield return, лямбды | работает | работает | работает |
| Коллекции BCL, LINQ | работает | работает | работает |
Task.Run, lock, многопоточные процессы | работает | работает | работает |
Math с правильным округлением (SharpLibm, CORE-MATH) | работает | работает | частично |
dynamic | пока нет | частично | работает |
| Рефлексия | пока нет | пока нет | работает |
| USB (xHCI): клавиатура, флешки, аппаратный ключ U2F | частично | частично | неприменимо |
| Вытесняющая многозадачность | частично | частично | частично |
| SMP | запланировано | запланировано | запланировано |
| Сеть | пока нет | пока нет | пока нет |
Соберите сами
Собирается обычным dotnet publish -r win-x64. Зависимости ставит mise или nix, образ запускается в QEMU.
- Поставьте mise или nix. Все версии инструментов закреплены в
mise.tomlиflake.lock. - Клонируйте с подмодулями. Форк CoreCLR необязателен, без него нужен флаг
-SkipCoreClr. - Соберите и запустите.
run_build.ps1собирает ядро, делает образ и запускает QEMU. - Положите payloads. DOOM1.WAD, картриджи
.nesи PowerShell кладутся вpayloads/.
# macOS / Linux / Windows
# или на Windows: winget install jdx.mise
brew install mise
git clone --recurse-submodules \
https://github.com/daniilvaino/SharpOS.git
cd SharpOS
mise trust && mise bootstrap
mise exec -- pwsh
# дальше в PowerShell
./build.ps1 launcher
$env:SHARPOS_GUI = 1
./run_build.ps1 -UsbOnly -SkipCoreClr
# NixOS или любой Linux с включёнными флейками
git clone --recurse-submodules \
https://github.com/daniilvaino/SharpOS.git
cd SharpOS
nix develop --command pwsh
# дальше в PowerShell
./build.ps1 launcher
$env:SHARPOS_GUI = 1
./run_build.ps1 -UsbOnly -SkipCoreClr
Библиотеки и вдохновение
Чужой код, который попадает в образ, и проекты, на которых SharpOS учился.
- zerosharpстарт
UEFI hello world на NativeAOT, отсюда всё началось. - dotnet/runtimeMIT
Toolchain NativeAOT, форк CoreCLR, сотни портов BCL. - IcedMIT
Кодировщик x86-64: весь ассемблер проекта. - ManagedDoomGPL-2.0
DOOM на C#, изолирован в отдельном приложении. - Terminal.GuiMIT
Текстовый интерфейс, на нём написан лаунчер. - XtermSharpMIT
Движок эмулятора терминала: ANSI/VT, прокрутка. - Kusto.LanguageApache-2.0
Разбор и связывание KQL для запросов в трубах. - Cosmosидея
Сама идея managed-операционной системы. Её нижний уровень написан на X#, диалекте ассемблера, поэтому инвариант «всё на C#» она не держит.
C# от первой инструкции до DOOM.
SharpOS в общественном достоянии по лицензии CC0. Используйте, изменяйте, присылайте pull request'ы.