12 KiB
Ты задал отличный вопрос, который вскрывает один из самых интересных нюансов системного программирования.
Действительно, Moss kernel (и некоторые другие проекты вроде Hubris от Oxide) используют async/await прямо внутри ядра. Это не противоречие, а смена подхода к тому, что такое «многозадачность ядра».
Давай разберем, почему они так сделали и почему это отличается от того async, который мы используем в вебе.
1. Ядро — это и есть «самый большой асинхронный рантайм»
Подумай, что делает ядро ОС:
- Ждет нажатия клавиши (прерывание).
- Ждет, пока диск дочитает сектор (DMA).
- Ждет пакета от сетевой карты.
Традиционные ядра (Linux, Windows) решают это через сложную систему прерываний, колбэков, workqueues и конечных автоматов, написанных вручную на C. Это сложно читать и легко ошибиться.
В чем идея Moss: Вместо того чтобы писать эти конечные автоматы (state machines) вручную, они используют Rust-компилятор.
async fn read_disk()в ядре Moss — это не запуск Tokio. Это способ сказать компилятору: «сгенерируй для меня state machine, которая приостановится, пока контроллер диска не пришлет прерывание».- Это позволяет писать драйверы так, будто код выполняется линейно, хотя на самом деле он разбит на микро-кусочки, ожидающие железа.
2. Async в ядре != Async в вебе
В Moss нет «тяжелого» рантайма типа Tokio.
- В вебе (Tokio): Рантайм использует потоки ОС (threads), work-stealing, и сложные очереди, чтобы утилизировать все ядра CPU.
- В ядре (Moss/no_std):
Executor— это часть планировщика ядра. Он очень примитивный.- Когда драйвер делает
.await, он просто говорит планировщику: «Не буди меня, пока не сработает вот это прерывание». - Это Cooperative Multitasking (кооперативная многозадачность) внутри ядра. Это дешевле, чем создавать отдельный поток ядра (kernel thread) для каждого драйвера.
- Когда драйвер делает
3. Почему это круто (Best Practices & Philosophy)
Использование async в ядре решает проблему Complexity Management.
- Без async: Ты пишешь обработчик прерывания, который ставит флаг, потом таймер, который проверяет флаг, потом функцию, которая доделывает работу... Код размазан по файлу.
- С async: Ты пишешь:
Логика линейна. Компилятор сам нарежет это на состояния. Это безопаснее и понятнее.// Псевдокод драйвера в Moss async fn handle_packet() { let header = nic.wait_for_header().await; // Ждем железо let data = nic.dma_read(header.len).await; // Ждем DMA process(data); }
Резюме для менти
То, что Moss написан на async — это не про производительность (как в Nginx/Tokio), а про эргономику кода драйверов.
- Они используют
async/awaitкак синтаксический сахар для управления сложными конечными автоматами железа. - Для твоих задач (Backend/Systems Tools) правило остается прежним: если ты не пишешь ядро или встроенное ПО для микроконтроллера (Embedded Rust), выбирай инструмент по задаче (I/O -> Async, CPU -> Sync).
В ядре они вынуждены быть асинхронными, потому что железо асинхронно по своей природе. Rust просто дал им удобный синтаксис для этого.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Это отличный вопрос, который показывает, что ты начинаешь мыслить архитектурно.
Короткий ответ: В Redox OS изначально не было async/await, но сейчас он внедряется — но иначе, чем в Moss.
В Moss async/await — это основа ядра (монолитный подход к драйверам внутри ядра). В Redox OS — это инструмент для драйверов в userspace и оптимизации системных вызовов (через io_uring).
Вот подробности, почему так и в чем разница:
1. Архитектура диктует инструменты
- Moss Kernel: Это (в основном) монолитный подход в плане исполнения драйверов. Драйверы работают в пространстве ядра. Чтобы они не блокировали ядро, используется кооперативная многозадачность через
async. - Redox OS: Это микроядро.
- Драйверы — это обычные процессы (userspace daemons).
- Файловая система — это процесс.
- Сетевой стек — это процесс.
- Ядро очень маленькое, его задача — перекладывать сообщения (Message Passing) между процессами.
2. Как работает Redox (Схемы vs Async)
В Redox все построено на концепции URL Schemes (file:, tcp:, http:).
Изначально Redox использовал блокирующие системные вызовы. Процесс делает read(), ядро блокирует его, посылает сообщение драйверу, ждет ответа.
Почему это стало проблемой? В userspace-драйверах это неудобно. Если драйвер сетевой карты обрабатывает 1000 пакетов, ему нужно либо 1000 потоков (дорого), либо сложный event loop (сложно).
Что изменилось (2024-2025):
Redox активно внедряет поддержку асинхронности, вдохновляясь Linux io_uring.
- Userspace Drivers: Драйверы в Redox начинают переписывать на
async/await, чтобы они могли эффективно обрабатывать очереди сообщений от ядра, не плодя потоки. - Kernel Interface: Ядро учится принимать "пачки" запросов асинхронно, чтобы уменьшить оверхед на переключение контекста (syscall overhead), который в микроядрах всегда высок.
3. Сравнение подходов (Mental Model)
| Характеристика | Moss Kernel | Redox OS |
|---|---|---|
| Где живет Async? | Внутри ядра (Kernel Space). | В драйверах (User Space) + интерфейс ядра. |
| Роль Async | Замена прерываниям и сложным State Machines железа. | Оптимизация Message Passing между процессами. |
| Аналогия | Как JavaScript в браузере (Single thread, non-blocking). | Как микросервисы (gRPC), которые общаются асинхронно. |
Вывод для тебя
Redox не написан на async так тотально, как Moss. Redox — это про изоляцию. Но он использует async там, где это нужно для производительности I/O.
Если ты хочешь поковырять "Async в ядре" — смотри Moss или Embassy (для embedded). Если хочешь понять "Async в системной архитектуре" (как построить ОС из микросервисов) — смотри Redox.