hyperion/20-dev/00-rust/10-no_std/Untitled.md
2025-12-28 19:00:03 +03:00

12 KiB
Raw Blame History

Ты задал отличный вопрос, который вскрывает один из самых интересных нюансов системного программирования.

Действительно, Moss kernel (и некоторые другие проекты вроде Hubris от Oxide) используют async/await прямо внутри ядра. Это не противоречие, а смена подхода к тому, что такое «многозадачность ядра».

Давай разберем, почему они так сделали и почему это отличается от того async, который мы используем в вебе.

1. Ядро — это и есть «самый большой асинхронный рантайм»

Подумай, что делает ядро ОС:

  1. Ждет нажатия клавиши (прерывание).
  2. Ждет, пока диск дочитает сектор (DMA).
  3. Ждет пакета от сетевой карты.

Традиционные ядра (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.

  1. Userspace Drivers: Драйверы в Redox начинают переписывать на async/await, чтобы они могли эффективно обрабатывать очереди сообщений от ядра, не плодя потоки.
  2. 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.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20