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

Unix, Linux, дистрибутиви

Linux, macOS і Android — три різні системи, які поводяться підозріло схоже. Ті самі fork і exec, ті самі права rwx, ті самі конвеєри. При цьому спільного коду між ними майже немає.

Розбиратися, звідки взялася ця схожість, варто не з історичного інтересу. Це пояснює цілком практичні речі: чому програма збирається на одній системі й не збирається на іншій, чому Apple взяла код BSD, а не Linux, і чому ls на вашій машині може виявитися трьома різними програмами.

Multics, спільний проєкт MIT, Bell Labs і General Electric, мав стати системою поділу часу для сотень користувачів. Він розростався, запізнювався, і врешті Bell Labs із проєкту вийшла.

Кен Томпсон і Денніс Рітчі почали писати щось радикально простіше. Назва Unics була жартом над Multics: той робив складно і для всіх, а цей просто і для кількох.

У 1973-му ухвалили рішення, яке все й визначило: ядро переписали на C. Доти ядра писали асемблером, і кожне було прив’язане до однієї моделі машини. Ядро мовою високого рівня вдалося перенести на іншу архітектуру, і саме переносимість, а не витонченість дизайну, зробила Unix основою всього, що прийшло потім.

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

З сімдесятих Unix розійшовся на дві гілки. System V була комерційною лінією AT&T, а BSD — розробкою Берклі, яка додала мережевий стек TCP/IP, сокети й віртуальну пам’ять. Чимало з того, чим користуються сьогодні, прийшло саме звідти.

У 1983-му Річард Столмен почав GNU, проєкт зі створення повністю вільного Unix-сумісного набору програм. На початок дев’яностих GNU мав компілятор, оболонку, бібліотеку C і сотні утиліт, але не мав робочого ядра.

Ядро написав у 1991-му Лінус Торвальдс. Коду Unix у ньому не було жодного рядка: це була незалежна реалізація тих самих інтерфейсів.

Ліцензія як технічне рішення

Section titled “Ліцензія як технічне рішення”

Долю систем ліцензія визначила не менше, ніж якість коду.

GPL, під якою вийшли Linux і GNU, вимагає відкривати зміни, якщо ви їх розповсюджуєте. Через це в Linux потекли вклади компаній, які не могли тримати свої правки закритими, і ядро стало спільним активом конкурентів.

BSD-ліцензія, навпаки, дозволяє брати код у закриті продукти без жодних зобов’язань. Тому мережевий стек BSD опинився всюди, а Apple побудувала macOS на основі BSD і Mach: узяти Linux означало б відкрити систему.

З тієї ж причини Android бере ядро Linux, бо GPL стосується ядра і зміни до нього відкриті, але GNU не бере: замість glibc там Bionic під BSD-ліцензією, та й решта простору користувача не є GPL.

З чого складається дистрибутив

Section titled “З чого складається дистрибутив”

Ядро саме по собі нічого не робить, у нього немає ні оболонки, ні утиліт. Дистрибутивом називають ядро плюс усе інше.

Складова Приклади Роль
Ядро Linux процеси, пам’ять, ФС, драйвери
Бібліотека C glibc, musl, Bionic обгортки над системними викликами
Система ініціалізації systemd, OpenRC, runit PID 1, запуск служб
Базові утиліти GNU coreutils, BusyBox ls, cp, cat
Оболонка bash, zsh, ash інтерфейс командного рядка
Менеджер пакетів apt, dnf, pacman, apk встановлення й оновлення

Замініть будь-який рядок цієї таблиці — отримаєте іншу систему. Alpine Linux бере musl замість glibc і BusyBox замість coreutils, через що образ виходить у десятки разів меншим, зате програми, зібрані під glibc, там просто не запускаються. Це, до речі, найчастіша причина славнозвісного «у Docker не працює, а локально працює».

Debian і похідні (Ubuntu, Mint) використовують apt і формат .deb, а роблять ставку на стабільність і величезний репозиторій.

Red Hat і похідні (RHEL, Fedora, Rocky) — це dnf і формат .rpm, де Fedora слугує полігоном для того, що згодом потрапляє в RHEL.

Arch віддає пакети одразу після релізу, а систему користувач збирає собі сам із мінімуму.

Alpine побудований на musl і BusyBox заради мінімального розміру й став через це стандартом для контейнерів.

NixOS описує конфігурацію системи декларативно, тому оновлення відкочується цілком.

macOS є сертифікованим UNIX. Ядро XNU поєднує мікроядро Mach і підсистему BSD, а простір користувача походить від BSD, не від GNU.

Android використовує ядро Linux зі значними змінами: wakelocks, Binder для міжпроцесного зв’язку, окрема модель дозволів. Замість glibc там Bionic, а замість звичного набору утиліт — середовище виконання ART.

WSL2 запускає справжнє ядро Linux у полегшеній віртуальній машині поверх Hyper-V. Перша версія WSL пробувала транслювати системні виклики Linux у виклики Windows і наштовхнулася рівно на те, про що йшлося в модулі 3: відтворити чуже ABI повністю неймовірно важко.

Terminal window
uname -r; cat /etc/os-release | head -3

Версія ядра і версія дистрибутива — незалежні числа. Одне ядро працює під десятками дистрибутивів.

Terminal window
ldd --version | head -1; ldd /bin/ls

Яка бібліотека C використовується і з чим злінковано ls. На Alpine ці команди дадуть зовсім інший результат.

Terminal window
readlink -f /bin/sh

Куди насправді вказує /bin/sh. У Debian це dash, а не bash — і скрипт із #!/bin/sh, який використовує розширення bash, зламається.

Terminal window
ls /usr/bin | wc -l; dpkg -S /bin/ls 2>/dev/null || rpm -qf /bin/ls 2>/dev/null

Скільки програм у системі і з якого пакета прийшла конкретна. Ядро не має до жодної з них стосунку.

Terminal window
ps -p 1 -o comm=

Що працює як PID 1 — systemd, init чи щось інше (модуль 5).

Terminal window
getconf _POSIX_VERSION

Версія POSIX, якої дотримується система.

Типові помилки розуміння

Section titled “Типові помилки розуміння”

«Linux — це операційна система». Linux є ядром. Операційною системою буде дистрибутив: ядро плюс бібліотека C, ініціалізація, утиліти й тисячі пакетів.

«Linux походить від Unix». Спільного коду в них немає. Linux — незалежна реалізація тих самих інтерфейсів, тож спорідненість тут інтерфейсна, а не генеалогічна.

«POSIX означає, що програма запуститься скрізь». Означає, що запуститься програма, яка за межі POSIX не виходить. Більшість реального коду використовує розширення конкретної системи.

«Android — дистрибутив Linux». Ядро Linux там справді є, а GNU немає: інша бібліотека C, інше середовище виконання, інша модель дозволів.

«macOS побудований на Linux». Він побудований на BSD і Mach, а схожість команд пояснюється спільним предком, не спільним кодом.

«У контейнері не працює, бо Docker зламався». Найімовірніше, образ зібраний на Alpine з musl, а програма — під glibc.

Перевір себе

1. Яке рішення 1973 року зробило Unix основою для всього, що прийшло далі?
2. Чому Linux поводиться як Unix, не містячи жодного рядка його коду?
3. Чому Apple побудувала macOS на BSD, а не на Linux?
4. Програма працює локально, але падає в контейнері на базі Alpine. Найімовірніша причина?
5. Скрипт із #!/bin/sh використовує синтаксис bash і падає на Debian. Чому?
6. Що таке дистрибутив Linux?
  • The UNIX Time-Sharing System — Рітчі й Томпсон, 1974
  • Раймонд, The Art of Unix Programming — про філософію Unix
  • Айзексон, Інноватори — історія галузі як історія людей
  • Nemeth та ін., UNIX and Linux System Administration Handbook
  • man 7 standards, getconf _POSIX_VERSION