Куди рухаються ОС
Навіщо це
Section titled “Навіщо це”Усе попереднє в курсі усталилося десятиліття тому. Тут ітиметься про те, що змінюється просто зараз, і про те, які припущення класичної теорії перестають бути вірними.
Мотив у більшості пунктів спільний: межа між ядром і простором користувача, така чітка в модулі 3, поступово розмивається. Код користувача потрапляє всередину ядра, ядро віддає роботу в спільну пам’ять, а частину гарантій забирає собі апаратура.
eBPF: ядро стало програмованим
Section titled “eBPF: ядро стало програмованим”Раніше змінити поведінку ядра можна було двома способами: написати модуль, який має право повалити систему, або чекати наступного релізу.
eBPF дозволяє завантажити в ядро маленьку програму й приєднати її до події — системного виклику, функції ядра, мережевого пакета, планувального рішення. Перед завантаженням програму перевіряє верифікатор: доводить, що вона завершиться, не звернеться за межі пам’яті й не зациклиться. Не довів — не завантажив.
Ось у цьому й полягає ключова відмінність від модуля ядра. eBPF-програма не може повалити ядро за побудовою, а не за домовленістю.
| Застосування | Що робить |
|---|---|
| Трасування | bpftrace, профілювання ядра без перезбирання і без зупинки |
| Мережа | XDP обробляє пакет ще до мережевого стека — фільтрація й балансування на мільйонах пакетів за секунду |
| Безпека | політики на рівні виклику з повним контекстом, чого seccomp не вміє |
| Планування | sched_ext: планувальник CPU як eBPF-програма |
Останній рядок таблиці варто перечитати уважно. sched_ext, який з’явився
в ядрі 6.12, дозволяє замінити планувальник — той самий, що описаний
у модулі 7, — завантажуваною програмою, а якщо вона
поводиться погано, перемкнутися назад. Експеримент, який раніше вимагав
перезбирання ядра й перезавантаження, тепер триває секунди.
io_uring: менше меж
Section titled “io_uring: менше меж”Кожен системний виклик коштує сотні наносекунд (модуль 3), і за мільйона операцій на секунду ця вартість стає основною статтею витрат.
io_uring прибирає виклик із гарячого шляху. Програма й ядро діляться
двома кільцевими чергами в спільній пам’яті: заявки кладуться в одну,
результати забираються з іншої, а в режимі опитування межу можна взагалі
не перетинати.
За цим стоїть ширший принцип, який видно і тут, і в XDP: не робити перехід швидшим, а прибрати його.
Rust у ядрі
Section titled “Rust у ядрі”Більшість критичних уразливостей у ядрі Linux є помилками роботи з пам’яттю: звернення після звільнення, вихід за межі, гонитви даних. C їх не ловить.
Від ядра 6.1 Rust є офіційно підтримуваною мовою для драйверів. Перевірки часу компіляції прибирають цілий клас помилок і нічого не коштують під час виконання. Написане ядро ніхто переписувати не збирається, зате нові драйвери можуть бути безпечними за побудовою.
Зміна ця йде повільно й супроводжується суперечками, причому причина не технічна. Змішувати дві мови в проєкті на тридцять мільйонів рядків із двома тисячами розробників дорого суто організаційно.
Уніядра
Section titled “Уніядра”Коли в машині працює один застосунок, а в хмарі так буває часто, поділ на режими не захищає ні від чого. Уніядро збирає застосунок разом із потрібними частинами ядра в один образ, який працює в одному адресному просторі.
Системних викликів немає, перемикань контексту теж, образ вимірюється мегабайтами, старт мілісекундами. Ізоляцію бере на себе гіпервізор (модуль 17).
Платити доводиться всім іншим: налагодженням, звичними інструментами, можливістю підключитися по SSH і подивитися, що там відбувається. Тому ніша й лишається вузькою.
Реальний час і вбудовані системи
Section titled “Реальний час і вбудовані системи”Пристроїв зовсім без ОС стає дедалі менше: навіть дешевий мікроконтролер сьогодні отримує Zephyr чи FreeRTOS, тобто планувальник, драйвери й мережевий стек у кілька десятків кілобайтів.
Паралельно й сам Linux став придатнішим для реального часу. PREEMPT_RT,
який десятиліттями жив набором латок, увійшов в основне ядро у версії 6.12.
Він робить витісненими майже всі ділянки ядра й зменшує найгіршу затримку
до передбачуваних одиниць мікросекунд, щоправда ціною невеликої втрати
пропускної здатності.
Конфіденційні обчислення
Section titled “Конфіденційні обчислення”Класична модель захисту (модуль 16) виходить із того, що ядру й гіпервізору можна довіряти. У хмарі це припущення виглядає сумнівно, бо ваші дані обробляються на чужій машині під чужим гіпервізором.
Конфіденційні обчислення прибирають господаря з кола довіри. AMD SEV-SNP та Intel TDX шифрують пам’ять віртуальної машини ключем, недоступним гіпервізору, і дають змогу зробити атестацію — криптографічний доказ, що запущено саме той образ, який ви очікували.
Межа довіри так переїжджає з програмного забезпечення в апаратуру. Обмеження при цьому визнані чесно: побічні канали за часом (модуль 16) нікуди не зникають, а довіряти доводиться виробникові процесора.
Навантаження AI
Section titled “Навантаження AI”Прискорювачі зламали одразу кілька припущень класичної ОС.
Почнімо з планування. Планувальник операційної системи керує процесорним часом (модуль 7), а GPU має власну чергу, до якої ядро має мало стосунку. Хто вирішує, чий шматок обчислень піде першим, і що робити з чесністю — питання, на яке усталеної відповіді поки немає.
Далі пам’ять. У прискорювача вона є окремим адресним простором. Уніфікована
пам’ять і HMM намагаються це приховати так само, як віртуальна пам’ять
приховує диск (модуль 12), з тими самими наслідками:
те, що виглядає як звернення до пам’яті, цілком може означати передавання
по шині.
І нарешті масштаб. Модель, яка не вміщається в одну машину, робить одиницею планування кластер, а не процес. На цьому рівні класична теорія ОС відповідей не має взагалі.
Що з цього лишиться
Section titled “Що з цього лишиться”Варто розділяти те, що змінюється, і те, що ні.
Не змінюються процес як одиниця ізоляції, віртуальна пам’ять, файлові абстракції, необхідність синхронізації й компроміс між пропускною здатністю та часом відгуку. Це наслідки фізики й математики, а не моди.
Змінюються натомість інші речі: де саме проходить межа ядра, скільки вона коштує, кому доводиться довіряти й що вважати одиницею планування.
Тому курс і побудований навколо механізмів, а не інтерфейсів. Інтерфейси застаріють, а питання «хто чекає, хто платить і чому саме так» лишиться.
Перевір себе
Куди далі
Section titled “Куди далі”Курс закінчився, а тема ні. Природних напрямків три:
- Углиб ядра. Зібрати власне ядро з
PREEMPT_RT, написати простий модуль, розібрати якусь підсистему за документацією й вихідним кодом. - Убік розподілених систем. Туди, де класична ОС закінчується: консенсус, реплікація, відмовостійкість поза межами однієї машини.
- Убік продуктивності.
perf,bpftrace, аналіз затримок, тобто застосувати механізми з цього курсу до реальних систем.
Джерела
Section titled “Джерела”- Brendan Gregg, BPF Performance Tools
- Documentation/bpf і sched_ext
- Efficient IO with io_uring
- Rust for Linux
- LWN.net — найкраще джерело про те, що відбувається в ядрі просто зараз