Перейти до основного вмісту

Про лінукс

· 26 хв. читання
UT3USW
Oleksii
попередження

Увага! Rabbit hole клубок! Шизофренія-mode: ON!

Page cache в Linux - це дуже важлива штука: коли система читає файл з диска, вона не хоче кожного разу йти на диск знову, тому тримає копію сторінок файлу в пам'яті. Запустили su, прочитали бінарник, поклали сторінки в кеш. Наступного разу можна взяти з пам'яті, а не бігать на диск.

І от уявімо баг, який дозволяє користувачу без root-доступу підмінити кілька байтів не у файлі на диску, а в цій закешованій копії. На диску все чесно. Хеші здається чесні. Права доступу здається чесні. Але коли система бере сторінку з пам'яті, там уже лежить трошки інший код. Не треба хачити всю ОС. Іноді достатньо дуже маленької щілини в дуже правильній оптимізації.

Звідси починається клубок про контейнери, linux, jvm, lisp і багато іншої шизофренії. Бо якщо баг живе в ядрі, то питання вже не "чи оновив я пакет усередині контейнера". Питання простіше і неприємніше: а чи контейнер взагалі спасе мені? Якщо ні, то де закінчується його ізоляція?

Copy Fail: чотири байти і спільне ядро

У квітні 2026 року дослідники Xint Code/Theori публічно описали вразливість Copy Fail, CVE-2026-31431. Якщо сильно спростити, це локальна ескалація привілеїв у Linux kernel: користувач без root-доступу міг через AF_ALG, splice() і баг у криптографічному модулі який може дати можливість контрольованого запису кількох байтів у page cache файлу. Ubuntu у своєму бюлетені теж описує це як LPE в algif_aead і дає severity HIGH, CVSS 7.8: Fixes available for CVE-2026-31431. NVD окремо веде сторінку CVE-2026-31431 з посиланнями на патчі ядра.

"О боже, Linux небезпечний", але це ціна оптимізації. Десь у ядрі є шлях для zero-copy, щоб не ганяти байти туди-сюди. Десь є криптографічний інтерфейс, який повинен пришвидшити нормальні речі. Десь є page cache, без якого система була б тупо повільнішою. А потім ці три системи зустрічаються, дивляться одна на одну і кажуть: "А давайте дамо користувачу писати туди, куди він не мав би писати".

Контейнери в цій історії з'являються не тому, що Docker якось пов'язаний із AF_ALG чи splice(). Ні. Він з'являється тому, що контейнерні платформи часто запускають недовірений або напівдовірений код на спільному ядрі. І якщо в цьому спільному ядрі є локальна ескалація привілеїв, то "воно ж у контейнері" перестає бути заспокійливою фразою.

Багато людей досі думають, що контейнер - це така маленька віртуальна машина, тільки легша. Ну, типу, "я ж у Docker, значить, я в будиночку". Ні. Docker-контейнер - це процеси на тому самому ядрі Linux, просто їм підкрутили видимість світу і поставили парканчики.

Офіційна документація Docker прямо каже, що при docker run створюються namespaces і control groups. Namespaces дають ізоляцію видимості: свій список процесів, своя мережа, свій hostname, свої mount points. Cgroups дають контроль ресурсів: скільки CPU, пам'яті, I/O і так далі. Linux man pages окремо описують namespaces і cgroups.

Але ядро - спільне. Якщо у спільному ядрі є локальний privilege escalation, то контейнер не стає повноцінною security boundary рівня VM. Контейнер може суттєво зменшити поверхню атаки за рахунок capabilities, seccomp, AppArmor/SELinux, user namespaces і нормальної конфігурації. Але якщо ваша модель безпеки звучить як "у нас untrusted код, але він у Docker, тому норм", то це не модель безпеки, це надія.

Повноцінна VM має окреме ядро гостьової ОС. Контейнер - ні.

І саме тому розмова про Docker дуже швидко стає розмовою про Linux kernel. Контейнер може намалювати процесу окремий світ, але закон фізики в цьому світі все одно пише ядро. А в Linux kernel є одна майже релігійна ідея, без якої вся ця екосистема розсипалася б значно швидше: не ламай те, на що покладаються програми.

Лінус і головна релігія Linux

У Лінуса Торвальдса прикольна історія з прізвищем. Torvalds - це сконструйоване шведсько-фінське родинне ім'я, яке створив його дід, журналіст і поет Оле Торвальдс, зробивши його зі свого середнього імені. Про це є коротка довідка навіть у Wikipedia: Torvalds. Сам Лінус - фінський програміст зі шведськомовної меншини Фінляндії. Тобто коли хтось каже "відомий фін", а хтось уточнює "ну як би фінський швед", обидва не повністю неправі. Це вже хороший початок для операційної системи.

Але важливіше не прізвище. Важливіше правило, яке стало однією з причин успіху Linux: не ламати userspace.

Linux kernel має стабільний інтерфейс між ядром і userspace. Офіційна документація ядра прямо описує Linux ABI між kernel і userspace. Системні виклики, задокументовані stable-частини /proc і /sys, поведінка, на яку покладаються програми, - це все не можна просто взяти і поламати, бо "нам так архітектурно красивіше". Debug-інтерфейси, Kconfig і внутрішні символи ядра - інша історія, на них таку обіцянку не поширюють.

Але Linux kernel не має стабільного внутрішнього ABI для драйверів і модулів. Це інший рівень. Є навіть знаменита документація Грега Кроа-Гартмана з прекрасною назвою The Linux Kernel Driver Interface, де пояснюється, чому стабільний internal kernel API/ABI для Linux не є метою. Якщо ваш драйвер у дереві ядра, його поправлять разом зі зміною внутрішнього API. Якщо ви принесли зовнішній binary blob, то це вже ваші пригоди.

І от тут видно характер Linux. Для користувача: "не ламайте". Для внутрішньої кухні ядра: "ламаємо, якщо треба, бо інакше система закам'яніє".

Це дуже прагматична позиція - що мільйони машин мають продовжувати працювати після оновлення ядра.

І тут починається головний прикол. Ядро Linux у цьому сенсі поводиться консервативніше, ніж багато хто очікує від open source-проєкту. Але більшість людей не запускає програми "на ядрі". Вони запускають їх у дистрибутиві, а дистрибутив - це вже не тільки kernel. Це цілий userspace-зоопарк.

Чому ж тоді Linux-десктоп іноді відчувається як квест?

Бо "Linux" як ядро і "Linux" як дистрибутив - це не одне й те саме.

Ядро може не ламати userspace ABI, але ваша програма живе не напряму на голому syscall interface. Вона живе серед динамічних бібліотек, loader-ів, пакетних менеджерів, OpenSSL, Qt, GTK, libstdc++, драйверів, systemd, PulseAudio/PipeWire, Mesa і ще купи всього, що не контролює Лінус.

glibc тут дуже зручний кандидат на роль головного винуватця, але картинка трохи ширша. GNU C Library справді фундаментальна: це стандартна C-бібліотека GNU-світу, через яку більшість програм ходить до системних викликів і базових функцій. Вона має symbol versioning і серйозно думає про сумісність, але реальна проблема Linux-дистрибутивів не зводиться до одного пакета: немає одного стабільного "Linux userland ABI", який би всі вендори тримали десятиліттями як контракт для будь-якого бінаря.

У Windows цей контракт історично значно жорсткіший. Microsoft десятиліттями тягне за собою Win32 і application compatibility. Про це дуже добре писав Реймонд Чен у книзі The Old New Thing: backwards compatibility для Windows - одночасно суперсила і тягар.

У Linux-дистрибутивах ситуація інша. У вас може бути бінарник, який прекрасно працює на конкретній версії RHEL, але не заводиться на новішій Ubuntu без правильної версії бібліотеки. І це не тому, що ядро зламалось. Це тому, що весь userland - це федерація проєктів, а не одна компанія з одним великим контрактом сумісності.

Звідси й народжуються три способи виживання. Всі вони насправді відповідають на одне й те саме питання: як зробити так, щоб програма не розвалилася від чужого userspace?

  1. Статично лінкувати все, що можна.
  2. Писати на Go або Zig-подібних інструментах, де легше отримати самодостатній бінарник.
  3. Запакувати весь ваш маленький світ у контейнер.

Перший варіант найелегантніший на папері: зібрати один файл і перестати залежати від зовнішнього світу. Але саме тут починається той технічний біль, через який Docker потім виглядає не таким уже й дурним.

Статична лінковка: стара мрія про один файл

Статична лінковка - це коли всі потрібні шматки коду вшиваються у ваш executable під час збірки. Не "при запуску знайди мені libssl.so.3, libc.so.6, правильний loader і молись, щоб воно було сумісне", а "ось один бінарник, запускай". Звучить як очевидна перемога здорового глузду.

Динамічна лінковка свого часу теж була здоровим глуздом. Диски маленькі, пам'яті мало, одна копія бібліотеки може шаритися між процесами, security update бібліотеки прилетів - і всі програми отримали фікс без перекомпіляції. У світі Unix-дистрибутивів це дуже логічна модель: пакетний менеджер тримає систему як цілісний набір взаємно сумісних пакетів.

Проблема починається тоді, коли ви хочете взяти один бінарник і понести його в інший Linux. Не "інший kernel", а інший userspace. Там інша glibc, інший OpenSSL, інший libstdc++, інший dynamic loader, інші шляхи, інші патчі від дистрибутива. І раптом ваш "просто executable" виявляється списком вимог до археологічного шару операційної системи.

Тут хочеться сказати: "Ну то статично залінкуймо glibc і забудьмо". І от на цьому місці Linux починає дивитися в підлогу.

glibc не дуже любить бути просто вшитою в один самодостатній файл. Частина її поведінки історично розрахована на динамічний світ: Name Service Switch для користувачів, груп і DNS, nsswitch.conf, модулі на кшталт libnss_files, libnss_dns, локалі, resolver-и, dlopen. Ви можете отримати "статичний" бінарник, який при певному виклику все одно хоче shared objects тієї ж версії glibc, з якою його лінкували. Це той момент, де слово "статичний" перестає заспокоювати.

Тому для справді самодостатніх Linux-бінарників часто дивляться в бік musl. musl прямо позиціонується як libc, зручна для robust static-linked applications; в офіційному musl how-to це одна з базових історій, а musl FAQ згадує окремий libc.a для static linking. Через це Alpine Linux і musl-based toolchains стали популярними в маленьких контейнерах і static builds.

Але тут немає безкоштовного сиру. musl - не glibc. Десь може відрізнятися поведінка resolver-а, локалей, threading details, performance characteristics, сумісність із софтом, який неявно очікує саме GNU-екосистему. Тобто "перелінкуй на musl" - це не магічна кнопка, а інженерне рішення з trade-off-ами.

Go став популярним частково тому, що для багатьох задач дає відчуття "ось тобі один бінарник". Якщо не тягнути cgo, Go runtime і стандартна бібліотека можуть дати дуже portable executable. Але щойно ви вмикаєте cgo, підключаєте C-бібліотеки або покладаєтесь на системний resolver, ви знову в світі libc. У Go навіть у коді net прямо видно розділення між pure Go resolver і cgo resolver: net/conf.go. Тому CGO_ENABLED=0 стало майже заклинанням для людей, які хочуть деплоїти прості Go-сервіси одним файлом.

Zig іде ще цікавіше: він хоче бути не тільки мовою, а й toolchain-ом, який контролює cross-compilation, libc, linker і багато деталей, які в C/C++ світі розмазані між системними пакетами. Zig прямо підкреслює, що постачається з підтримкою багатьох libc/targets: Zig overview. Це не просто зручність. Це відповідь на той самий біль: "я хочу зібрати бінарник під іншу систему і не влаштовувати шаманство з sysrootами".

Тобто статична лінковка - це не дрібна технічна деталь. Це філософська альтернатива Docker-образу. Один шлях каже: "зробімо програму самодостатньою". Другий каже: "зробімо самодостатнім усе середовище". Якщо перший шлях працює - чудово, у вас маленький артефакт і простий деплой. Якщо не працює, Docker приходить із валізою userspace і каже: "Не сваріться, я просто заберу всю квартиру".

І от тепер Docker перестає бути "модною штукою для DevOps". Він стає відповіддю на дуже старе питання: що робити, якщо ABI на рівні ядра стабільний, а середовище навколо програми все одно нестабільне гівно?

Docker як статична лінковка для людей, які вже втомилися

Docker не з'явився тому, що людство раптом захотіло "мікро-віртуалки". Він став масовим тому, що вирішив дуже побутову проблему: "у мене працює, а на сервері ні".

Контейнерний образ - це не просто ваш бінарник. Це шматок файлової системи з вашими бібліотеками, runtime, конфігами, Python-ом, Node-ом, OpenSSL-ом і всією цією шафою, яка потрібна програмі, щоб не розвалитися від першого ж apt upgrade.

Тобто Docker у певному сенсі - це статична лінковка на рівні середовища. Не можете нормально вкомпілювати glibc? Не хочете пояснювати, яка саме версія libssl.so потрібна? Окей, беріть весь userspace із собою.

З архітектурної точки зору це трохи смішно. Замість "давайте зробимо чистий, стабільний runtime-контракт" людство сказало: "та давайте просто носити весь сарай із собою". Але з інженерної точки зору це працює. А коли щось працює і знімає біль мільйонам людей, воно перемагає.

Технічно Docker сьогодні - це вже не одна магічна штука. Docker Engine використовує container runtime, нижче живуть containerd, runc, OCI images/specs і Linux primitives. Сам containerd - окремий open source runtime-проєкт. Kubernetes свого часу мав спеціальний dockershim, бо ранній Kubernetes говорив із Docker Engine напряму; потім Kubernetes перейшов до Container Runtime Interface, і в v1.24 dockershim прибрали.

Але "Kubernetes більше не використовує Docker" не означає "Docker-образи померли". Образи лишилися. Ідея лишилася. Просто замість Docker Engine як посередника Kubernetes говорить із containerd або іншим CRI-compatible runtime.

Тобто Docker як бренд може втрачати центральність, але контейнерна модель нікуди не дівається. Бо проблема, яку вона вирішує, нікуди не зникла.

Тут важливо не переплутати дві різні ролі контейнера. Перша - packaging: принести з собою свій userspace. Друга - isolation: зробити вигляд, що процес живе в окремому світі. Перша роль зробила Docker масовим. Друга часто продається в голові людей сильніше, ніж реально гарантується ядром.

Cgroups, namespaces і чому це не VM

Якщо розкласти контейнер на деталі, там немає "контейнерної магії". Там є набір давно земних Linux-механізмів:

  • Namespaces: процес бачить не весь світ, а свій namespace. Свій PID tree, свій network stack, свої mount points, свій hostname.
  • Cgroups: процесу обмежують ресурси. Ось тобі CPU quota, ось memory limit, ось I/O.
  • Capabilities: root більше не є всемогутнім монолітом, його права можна нарізати.
  • Seccomp: можна заборонити частину системних викликів.
  • LSM: AppArmor, SELinux та інші політики доступу.
  • Overlay filesystems і images: спосіб зібрати файлову систему шарами.

Docker зібрав ці штуки у продукт, який нормальні люди могли використати без PhD з Linux internals. І це вже велика справа.

Але саме тому контейнер і не дорівнює VM. VM емулює або віртуалізує машину з окремим ядром. Vagrant, наприклад, історично часто використовував VirtualBox, VMware, Hyper-V, QEMU/libvirt або інші провайдери для керування повними VM. Docker запускає процеси на тому самому kernel. Тому Docker легший, швидший і зручніший. І тому ж він не має тієї самої ізоляції, що повна віртуалізація.

Коротше: Docker - це не "віртуальний комп'ютер". Docker - це добре замаскований(лол шо), по суті, процес.

І отут Docker стає ідеальним прикладом старої фрази, яка звучить як виправдання поганого коду, але насправді описує пів історії комп'ютерів: worse is better.

Worse is better: чому гнусний хак іноді кращий за правильну систему

Тут ідеально лягає есе Річарда Ґабріеля Worse Is Better. Це один із тих текстів, які варто читати не тому, що там "істина", а тому, що після нього ви починаєте бачити повторюваний патерн у всій історії софту.

Ґабріель протиставляв умовний MIT/Stanford style і New Jersey/Berkeley style. Перший хоче "зробити правильно": повно, красиво, консистентно. Другий хоче "щоб працювало, було простим і можна було швидко рознести по світу". І потім раптом виявляється, що Unix і C, які виглядали як прості, місцями грубі, неідеальні інструменти, розповзлися всюди. А красиві Lisp-машини, попри всю їхню технічну сміливість, лишилися в музеї.

Це не означає, що якість не потрібна. Це означає, що adoption - окрема сила. Якщо технологія достатньо проста, щоб її можна було зрозуміти, перенести, зламати, полагодити і запустити на дешевому залізі, вона отримує шанс стати стандартом де-факто. А потім стандарт де-факто починає формувати реальність.

Docker - майже карикатурно хороший приклад. Замість розв'язати проблему сумісності userspace красиво, він сказав: "покладемо userspace у коробку". Замість пояснити всім, як збирати portable Linux binaries, він дав Dockerfile. Замість змусити runtime-екосистеми бути нормальними, він зробив нормальним тягати runtime із собою.

Це не "правильне" рішення з точки зору естетики. Але воно настільки корисне, що всі перестали сміятися і почали будувати на ньому продакшен.

Оце і є worse is better у дикій природі.

А щоб відчути, наскільки це не нова драма, треба подивитися на іншу гілку історії: світ, де люди намагалися зробити не "простий C поверх дешевого заліза", а майже ідеальну машину під ідеальне середовище.

Lisp-машини: коли комп'ютер був зроблений під мову, а не навпаки

Lisp-машини - це не просто "компи для програмістів, які любили дужки". Це була спроба зробити всю машину під високорівневу мову: залізо, операційну систему, середовище розробки, garbage collection, інтроспекцію, все. MIT AI Lab почав цю лінію ще в 1970-х; потім були Symbolics, Lisp Machines Inc., Texas Instruments, Xerox. Короткий історичний огляд є в Lisp machine, а Common Lisp HyperSpec теж згадує, що Lisp Machine Lisp виріс із MacLisp і працював на ранніх MIT Lisp Machines: CLHS 1.1.2.

CADR Lisp Machine у MIT Museum

CADR Lisp Machine кінця 1970-х у MIT Museum. Фото: Daderot, Wikimedia Commons, CC0.

Ідея була дуже красива. Замість того, щоб писати на C, потім компілювати в машинний код, потім вручну менеджити пам'ять і ловити segfault-и, ти живеш у середовищі, де програма - майже живий об'єкт. Можна інспектити runtime, міняти код на льоту, дебажити систему зсередини системи. Для AI-досліджень того часу це було дуже природно: Lisp був не просто мовою, а способом думати про символи, програми і дані як про одне середовище.

Space-cadet keyboard від Symbolics LM-2

Space-cadet keyboard від Symbolics LM-2. Оце не просто клавіатура, а артефакт культури, де середовище розробки було майже окремою цивілізацією. Фото: Dave Fischer / Retro-Computing Society of Rhode Island, Wikimedia Commons, CC BY-SA 3.0/GFDL.

Проблема була в тому, що майбутнє коштувало як майбутнє! Lisp-машини були дорогими спеціалізованими воркстейшонами, а поруч дешевшали Unix-машини і персональні комп'ютери. C був грубіший, Unix був грубіший, але вони працювали на масовішому залізі. І коли дешеве залізо почало швидко прискорюватися, спеціальна машина під прекрасну модель світу стала виглядати не як майбутнє, а як дуже дорога бокова гілка еволюції.

Оце болючий момент у worse is better: "краще" може програти не тому, що воно гірше технічно, а тому, що його складніше купити, складніше поширити і складніше втягнути в уже наявну економіку.

Але ідея керованого, переносимого, багатого runtime-світу не померла разом із Lisp-машинами. Вона просто перестала вимагати спеціальний комп'ютер. Наступна масова версія цієї ідеї приїхала вже як віртуальна машина поверх звичайної операційної системи.

JVM: контейнер, але для байткоду

Після Docker легко подивитися на JVM новими очима. JVM теж каже: "мені не подобається ваш хаос із платформами, я принесу свій світ". Тільки Docker приносить filesystem image з userspace-залежностями, а JVM приносить віртуальну машину, class file format, bytecode verifier, garbage collector, JIT і великий runtime-контракт. Офіційна специфікація прямо описує JVM як абстрактну машину з власним форматом класів і runtime-структурами: The Java Virtual Machine Specification.

У цьому сенсі JVM - більш "правильна" відповідь на частину тієї самої проблеми. Ти не пакуєш весь Linux userspace у контейнер, а компілюєш програму в portable bytecode і запускаєш її там, де є сумісна JVM. Звідси старий лозунг Java про write once, run anywhere. На практиці, звісно, було write once, debug everywhere, бо реальність завжди приходить із мокрою ганчіркою. Але сама архітектурна ідея гарна.

Цікаво, що люди за Java були зовсім не дурні. Джеймс Гослінг до Java зробив Gosling Emacs, ранню реалізацію Emacs на C для Unix. Тобто це людина з тієї ж культури інтерактивних середовищ, редакторів, runtime-ів і "система має бути живою". Але результатом стала не Lisp-машина 2.0, а Java: доволі консервативна мова, зате з VM, GC, bytecode і дуже сильним deployment story для enterprise.

Це теж схоже на компроміс із реальністю. JVM взяла частину мрії Lisp-машин - керований runtime, переносимість, garbage collection, багате середовище - і запакувала її так, щоб це можна було поставити на звичайний комп'ютер, поверх звичайної ОС, без спеціального заліза за космічні гроші. Тобто мрія вижила, але в менш чистому, більш промисловому вигляді.

Docker і JVM стоять поруч не випадково. Це дві відповіді на питання "як зробити так, щоб моя програма не залежала від хаосу навколо". JVM відповідає: "дамо стабільну абстрактну машину". Docker відповідає: "запакуємо хаос у коробку". Перша відповідь красивіша. Друга часто простіша для вже існуючого софту. І тому обидві живі.

Але є ще третій інстинкт: не будувати нову машину і не пакувати весь світ, а копати вниз, до нижчих API, де менше магії і більше контролю. Це вже територія сучасних системних мов.

Zig, Windows і старі API, які раптом кращі за нові

Є ще один смішний поворот у цій же темі: Zig. На Windows він дивиться в бік нижчих API замість того, щоб завжди користуватись "офіційно красивими" Win32-обгортками. І це не просто байка. У Zig Devlog є запис Bypassing Kernel32.dll for Fun and Nonprofit, де Andrew Kelley пояснює, що kernel32.dll часто є вищим wrapper-ом, а реальна робота йде через ntdll.dll. У деяких місцях нижчий API дає менше алокацій, менше прихованих failure mode-ів і чесніші error codes.

Це прекрасно лягає в нашу ABI-історію. У Linux головний стабільний контракт - між userspace і kernel. Ви можете напряму думати про syscalls, і це культурно нормальна частина платформи. У Windows офіційна стабільність для більшості розробників живе вище: Win32, kernel32, user32, вся ця велика compatibility-залупа. Нижче є Native API / ntdll, але Microsoft історично не продає його як головний публічний контракт для всіх задач.

І тут приходить Zig і каже: "А ми подивилися, і нижче місцями чистіше". Це дуже в стилі системного програмування: офіційно правильний шар може бути не найкращим для runtime, який хоче мінімум залежностей, контроль I/O і передбачувану поведінку.

Сюди ж додається ще один культурний факт: Zig у 2025 році офіційно переніс основний репозиторій із GitHub на Codeberg, про що написав у Migrating from GitHub to Codeberg. Це вже не прямо про ABI, але дуже про ту саму інженерну нервову систему: менше залежати від великої платформи, яку ти не контролюєш; менше магії; більше прозорості; тримати toolchain ближче до себе.

Тобто Zig у цій історії - це сучасний приклад тієї самої реакції на складність: якщо шари абстракції стали занадто товстими, повільними або непередбачуваними, системні програмісти починають копати вниз.

І тут варто повернутися до початку Linux-історії. Бо вся ця драма з ABI, libc, userland і toolchain-ами не взялася з повітря. Linux як масова система народився не як завершена операційна система, а як робоче ядро, яке з'єднали з уже наявним GNU-світом.

GNU, Hurd і момент, коли історія пішла не за планом

Щоб зрозуміти Linux, треба пам'ятати, що Linux спочатку був ядром, а не повною операційною системою. Повну вільну Unix-like систему хотів зробити GNU. Проєкт GNU Річард Столлман запустив у 1983 році як спробу створити повністю вільну Unix-сумісну ОС; це описано в офіційному тексті GNU in a Nutshell.

DEC PDP-11/20 у Computer History Museum

DEC PDP-11/20 у Computer History Museum. На таких машинах добре видно, чому Unix і C були не академічною красою, а способом вижити на реальному залізі. Фото: Don DeBold, Wikimedia Commons, CC BY 2.0.

GNU мав компілятори, утиліти, shell, libc і купу іншого добра. Але ядро GNU Hurd довго не ставало практично готовим для масового використання. І тут приходить студент Лінус із ядром для 386, яке "just a hobby", і раптом у GNU-userland з'являється робочий kernel.

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

Сюди ж проситься Таненбаум. Його Operating Systems: Design and Implementation з MINIX - класична книжка для розуміння ОС. А знаменитий Tanenbaum-Torvalds debate - це майже мем про мікроядра, монолітні ядра і різницю між "правильніше в теорії" та "злетіло в реальному світі".

І тепер можна оцінити найіронічніший поворот. Linux довго страждав від того, що не має одного стабільного desktop/application ABI рівня "пишеш програму і вона живе десятиліттями". Але на Linux приходить чужий, дуже старий і дуже стабільний світ Windows API - і раптом саме він може стати клеєм для масового десктопу.

Proton: Linux-десктоп може перемогти, запустивши Windows

Найсмішніший поворот у цій історії: можливо, найстабільніший userspace для масового Linux-десктопу - це Win32 через Proton.

Звучить як жарт, але жарт непоганий. Valve зробила Steam Deck і сильно вклалася в Proton - compatibility layer, який дозволяє Windows-іграм запускатися на Linux. Базово Proton - це Wine плюс купа патчів і компонентів для ігор; короткий технічний опис є на сторінці Proton, а код живе в ValveSoftware/Proton.

Steam Deck спереду

Steam Deck - конкретний шматок заліза, який зробив Proton масовою історією. Фото: Liam Dawe / GamingOnLinux, PNG version by VulcanSphere, Wikimedia Commons, CC BY-SA 4.0.

Чому це важливо? Бо Windows-екосистема десятиліттями звикала, що старі програми мають запускатися. Win32 - це не просто API. Це культурний договір: якщо ти написав програму багато років тому, є шанс, що її все ще можна якось запустити. І цей договір настільки сильний, що Linux може отримати кращу desktop-сумісність, запускаючи Windows-програми через шар сумісності.

Тобто Microsoft роками тягнула backwards compatibility як тягар, а Valve взяла цей тягар і зробила з нього аргумент на користь Linux.

Windows це єдиний стабільний ABI для Linux завдяки Valve!

Смішно? Дуже. Логічно? Теж дуже.

То що, Docker - поганий?

Ні. І так. Нормальна відповідь у системному програмуванні майже завжди така.

Docker поганий, якщо ви думаєте, що це повноцінна безпекова межа для недовіреного коду. Для цього існують VM, sandbox-и, gVisor, Firecracker, Kata Containers, seccomp-профілі, user namespaces, LSM-політики і нормальна threat model, а не віра в слово "container".

Docker хороший, якщо вам треба відтворюване середовище, контроль залежностей і нормальна доставка софту між машинами. Він бере хаос userspace і робить з нього артефакт, який можна зібрати, підписати, покласти в registry і запустити приблизно там, де треба.

Docker поганий, якщо ви пакуєте в образ 2 GB випадкового мотлоху і потім дивуєтесь, чому CI повільний. Docker хороший, якщо ви чесно визнаєте: "Linux userspace сумісність складна, залежності складні, production складний, тому я зафіксую світ хоча б на рівні image".

Docker поганий як архітектурний ідеал. Docker прекрасний як інженерний компроміс.

І от це, здається, головний висновок. Історія софту - це не парад ідеальних архітектур. Це кладовище прекрасних ідей, які програли простішим, дешевшим, грубішим і більш переносимим рішенням. Unix, C, Linux, Docker, Win32 compatibility, Proton - усі вони в різні моменти виглядали як "ну камон, можна ж красивіше". А потім виявлялося, що красивіше можна, але люди вже поїхали на тому, що працює.

І в цьому немає простого моралізаторства. Красиві системи потрібні. Стабільні ABI потрібні. Нормальні runtime-контракти потрібні. Просто реальна індустрія завжди живе в компромісі між тим, як мало б бути, і тим, що можна поставити в продакшен у четвер до обіду.

Тому коли наступного разу хтось скаже "це просто хак", варто спитати: хак, який нікому не потрібен, чи хак, на якому через п'ять років буде стояти пів індустрії?

Бо це дуже різні хаки.

Що почитати далі

Вам не потрібні скіли

· 3 хв. читання
UT3USW
Oleksii

Ілюстрація до нотатки про skills

Вам не потрібні скіли!

Нещодавно читав про Thiele Machine, цікавий твіст на машину тюрінга який додає щось типу "душі" в обчислення. Прикольний ребіт хол. але мене це штовхнуло подумати про практичну штуку.

Декада ШІ агентів

· 5 хв. читання
UT3USW
Oleksii

Поки заголовки щодня обіцяють то сингулярність, то кінець людства, Карпатій дивиться на все це тверезіше. Чувак працював в OpenAI, керував автопілотом у Tesla - він знає, як ця кухня працює зсередини. В інтерв'ю Дваркешу Пателю він каже речі, які збігаються з тим, що я сам думаю: трансформери[^2] - тупик, RL - лайно, а ШІ загалом переоцінений.

Коли всі кричать "рік агентів", Карпатій каже - ні, це буде декада агентів, бо моделі зараз просто недостатньо надійні. Він прямо називає навчання з підкріпленням (RL) жахливим підходом, який видавлює результати по краплі. І навіть статистично модель вважає за краще промовчати, ніж відповісти - бо штраф за помилку більший, ніж за "я не знаю".

Він розповідає про досвід у Tesla, де прогрес - це поступове "вигризання" надійності, і про свою нещодавню роботу над Nanochat[^1], щоб пояснити: код-агенти досі не можуть замінити архітектора. Далі розбираємо головне з цієї розмови - чому справжній інтелект не зводиться до запам'ятовування, чому Карпатій не вірить у різкий "вибух" продуктивності, і навіщо він будує "Starfleet Academy" для освіти людства.

ШІ-бульбашка на стероїдах

· 16 хв. читання
UT3USW
Oleksii

Ілюстрація до теми інвестиційної бульбашки ШІ

Авантюра на $3 трильйони

Революція штучного інтелекту — це не просто технологічний зсув. Це найбільша і, мабуть, найшаленіша інвестиційна лихоманка за всю історію. До 2030 року людство планує вбухати в інфраструктуру ШІ близько $3–4 трильйони. Тільки у США капітальні витрати на обчислення вже дали понад 1.1% зростання ВВП у першій половині 2025-го, випередивши навіть споживчі витрати.

Поки ринок зосереджений на експоненціальному зростанні обчислювальних потужностей і можливостей, він ігнорує фатальну ваду: вся ця екосистема є дивом фінансової інженерії, побудованим на кругових залежностях, нестійкій економіці та неминучому зіткненні з жорсткими фізичними обмеженнями нашої глобальної енергетичної мережі. Бум ШІ — це не просто спекулятивна бульбашка, що нагадує 2000 рік, а системно крихка структура з відлунням кризи 2008 року, де найбільший ризик полягає не у відсутності інновацій, а у відсутності електроенергії.

Нотатки

· 3 хв. читання
UT3USW
Oleksii

Не найкращий приклад нотатків, але флекс кльовим записником

Зараз я працюю в умовах постійної зміни контексту. Мені постійно треба бути на різних зустрічах з різними людьми, та часто зустрічі не мають ні хвилини перерви та відбуваються просто стик в стик. Тому без нотатків можна загубитися в екшенах чи думках. Переважно я веду нотатки старим надійним методом - у звичайному блокноті. Але який процес далі? він різний і хаотичний, про щось я забуваю і ніколи не повертаюсь, але переважно повертаюсь і намагаюсь розібратись. І часто без контексту важко пригадати що за запис в блокноті та про що він. Типу "Розібратись з A\B тестом". Добре якщо я маю дату цього запису, то я можу хоча б провести паралель і відновити ширший контекст. Але все це виглядає не дуже ефективно.

Під люком в асфальті. Річки.

· 3 хв. читання
UT3USW
Oleksii

Перше що хочу сказати - я не “професійний дігер” чи дослідник. Так вийшло, що компанія моїх друзів — це люди, що витратили вже 16(!) років на цю справу.

Вхід під землю через люк у Києві

Тож, хочеш не хочеш, я вже і сам перший раз як заліз під землю 5 років тому, не так активно, як декотрі з них, але певний досвід маю. На моєму “підземному” шляху відбувалось кілька неприємних історій, проте загалом це дуже позитивний і цікавий досвід дослідження міста з, буквально, іншої його сторони.

Радіоаматарство в Україні його стан зараз і перспективи

· 2 хв. читання
UT3USW
Oleksii

AHTUNG! Обережно! Мат.

Фраза що згенерував ChatGPT при підготовці цього документу і я вирішив її залишити just for lulz: Якщо ви не хочете читати мат то вибачайтеся і йдіть нахуй. Я не збираюсь це редагувати. Я не збираюсь це видаляти. Я не збираюсь це читати. Я не збираюсь це перечитувати. Я не збираюсь це переписувати.

Вступ

Радіоаматарство в Україні абсолютно мертве. Завдяки гіпертоксичному комьюніті дідів. Буквально дідів з оцих от умовних КПІ кафедр. Подивіться сайти радіоаматорських організацій. Це блять в прямому сенсі йобаний некролог. Контест — некролог, конест — некролог!