Операционные системы История ОС: от Multics и Unix до BSD, Linux и наших дней
0%

История ОС: от Multics и Unix до BSD, Linux и наших дней

История ОС: от Multics и Unix до BSD, Linux и наших дней

История ОС — не музейная витрина. Это единственный способ объяснить, почему creat() пишется без «e», почему ps aux и ps -ef — это два разных синтаксиса одной команды, почему на Linux вы пишете epoll, а на FreeBSD kqueue, и почему #define _GNU_SOURCE вообще существует. Каждая странность современного системного API — это окаменевшее решение, принятое конкретным человеком в конкретный год под конкретное железо. Разобравшись в цепочке решений, вы перестаёте зубрить API и начинаете его выводить.

Эта статья — карта родословной. Дальше по треку мы будем разбирать устройство ядер, планировщиков и файловых систем; здесь мы выясняем, откуда взялись сами понятия и почему семейства ОС различаются именно так, а не иначе.

Что было до Unix: пакетная обработка и дефицит машин

В 1950-х компьютер был редким и дорогим ресурсом, а «операционная система» — набором библиотек для ввода-вывода. Работа шла пакетами (batch): вы приносили колоду перфокарт оператору, он ставил её в очередь, и через сутки вы получали распечатку. Ошибка в одной карте стоила дня.

Ключевая идея, изменившая всё, — разделение времени (time-sharing): пусть машина быстро переключается между пользователями, и каждому кажется, что она его. Джон Маккарти предложил это в 1959-м, а первая работающая система — CTSS (Compatible Time-Sharing System, MIT, Фернандо Корбато, 1961) — доказала, что идея реализуема. CTSS дала нам вещи, которые сегодня кажутся воздухом: интерактивный терминал, пользовательские каталоги, команду для отправки сообщения другому залогиненному пользователю (прародитель почты) и понятие «редактор текста».

Из CTSS выросло всё остальное — в том числе и амбиции, которые породили Multics.

Multics: великолепный провал, из которого выросло всё

В 1964-м MIT, Bell Labs и General Electric начали проект Multics (MULTiplexed Information and Computing Service). Цель формулировалась как «computing utility» — вычисления как коммунальная услуга: подключаетесь к розетке и берёте столько процессора, сколько нужно, платите по счётчику. В 1964 году это звучало как научная фантастика; в 2026-м это называется AWS.

Multics был технически амбициозен настолько, что многие его идеи вернулись в мейнстрим только через 30-40 лет:

  • Сегментированная виртуальная память с отображением файлов. В Multics файл — это сегмент, который вы просто адресуете. Никакого read()/write() — вы обращаетесь к данным как к памяти. Идея вернулась как mmap(2) в 4.2BSD и как основа современных баз данных.
  • Кольца защиты (8 колец, а не два режима). Промежуточные уровни привилегий позволяли писать «полупривилегированные» подсистемы. x86 унаследовал 4 кольца от этой линии мышления — и индустрия де-факто использует два.
  • Динамическое связывание. Символ разрешался при первом обращении. Наши .so и .dll — оттуда.
  • Иерархическая файловая система с произвольной вложенностью и списки контроля доступа (ACL).
  • Написан на языке высокого уровня (PL/I), а не на ассемблере — тогда это было почти ересью.
  • Защита от переполнения стека на уровне архитектуры: сегменты имели границы, стек рос в сторону, где переполнение упиралось в нечитаемую область.

Кольца защиты Multics против двух режимов Unix

Проблема была одна: Multics опаздывал и был чудовищно сложен. Bell Labs вышла из проекта в апреле 1969 года, не получив ничего, кроме опыта. Сам Multics дожил до 30 октября 2000 года — последняя система была выключена в министерстве обороны Канады, — и это единственная ОС, получившая рейтинг безопасности B2 по «Оранжевой книге» при живом сетевом использовании.

Но главное наследие Multics — не код, а фрустрация двух инженеров, которые вернулись из проекта в Bell Labs и решили сделать то же самое, только маленькое.

1969: Unix на списанном PDP-7

Летом 1969-го Кен Томпсон остался на три недели один (жена уехала с ребёнком к родителям) и написал на списанном PDP-7 ядро, командный интерпретатор, редактор и ассемблер — по неделе на компонент. Формальным поводом была игра Space Travel, которую он портировал с GE-645; настоящим — желание получить обратно комфортную интерактивную среду, которая была в Multics.

Название UNICS (позже UNIX) придумал Брайан Керниган — как каламбур: «кастрированный Multics», система «на одного» вместо «на многих».

Ключевое отличие от Multics — сознательное упрощение до предела:

Multics Unix
Сегментная память, файлы отображаются в адресное пространство Файл — просто последовательность байт, читается через read()
8 колец защиты 2 режима: user / kernel
ACL на каждом сегменте 9 бит прав: rwxrwxrwx
PL/I, сложный компилятор ассемблер → B → C
Устройства — особый класс объектов Устройства — файлы в /dev
Мощный, но монолитный набор возможностей Много маленьких программ, соединяемых конвейером

«Файл — это просто поток байт без структуры» — самое важное решение во всей истории Unix. Multics, VMS, MVS, а позже и Windows имели типизированные файлы с записями. Unix сказал: типизацию делает прикладная программа, ядро не знает и не хочет знать. Именно поэтому grep работает и с исходником, и с логом, и с выводом ps — а на VMS каждая утилита должна была знать формат.

В 1970-м проект получил финансирование под предлогом системы подготовки документов для патентного отдела Bell Labs (отсюда troff, ed и вообще вся текстовая культура Unix) и переехал на PDP-11/20. Первое издание руководства — 3 ноября 1971 года; отсюда мы отсчитываем UNIX v1.

Адресное пространство процесса в Unix V6 на PDP-11 и его следы в современном ядре

1973: переписывание на C — момент, который изменил индустрию

Деннис Ритчи развил язык B (сам производный от BCPL) в C, добавив типы и структуры. В 1973 году ядро Unix переписали на C — примерно 90% кода перестало быть машинно-зависимым.

До этого момента ОС была неотделима от железа: чтобы получить ОС на новой машине, её писали заново. После — ОС стала портируемой. В 1977-1978 Unix перенесли на Interdata 8/32, доказав, что перенос возможен, и с этого момента любой производитель железа мог получить приличную ОС за месяцы, а не годы.

Практический след этого решения виден прямо сейчас:

# Заголовки системных вызовов Linux — до сих пор C ABI, придуманный в 1973-м
$ grep -n "SYSCALL_DEFINE3(read" /usr/src/linux/fs/read_write.c
622:SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)

# Каждый язык — Go, Rust, Python — в конечном счёте вызывает этот C-совместимый интерфейс
$ strace -e trace=read python3 -c "open('/etc/hostname').read()" 2>&1 | head -3
read(3, "myhost\n", 4096)               = 7
read(3, "", 4096)                       = 0

Статья Ритчи и Томпсона «The UNIX Time-Sharing System» (CACM, июль 1974) — до сих пор лучшие 11 страниц про то, что такое ОС. Прочитайте её; она читается легче большинства современных блогпостов.

Как Unix вышел из Bell Labs: юридическая случайность

AT&T была монополистом связи и по антимонопольному постановлению 1956 года не имела права торговать компьютерными продуктами. Поэтому Unix раздавали университетам практически бесплатно — вместе с исходниками, потому что «это не продукт, это исследовательский артефакт».

Так родилась уникальная ситуация: тысячи студентов получили полный исходник работающей ОС. Джон Лайонс в Университете Нового Южного Уэльса напечатал в 1976-м Lions’ Commentary on UNIX 6th Edition — построчный разбор всех ~9000 строк ядра V6. AT&T запретила распространение книги, и она стала самой копируемой самиздатовской книгой в истории CS. Сегодня она легально доступна, и это до сих пор лучший способ увидеть ядро целиком.

Version 7 Unix (1979) — последняя «настоящая» research-версия от Bell Labs: Bourne shell, awk, make, find, полноценный набор системных вызовов. Практически всё, что вы набираете в терминале, существовало в V7.

Раскол первый: Berkeley против AT&T

Университет Беркли получил V6 в 1974-м. Билл Джой и коллеги начали доводить систему, и распространяли результат как BSD (Berkeley Software Distribution). Хронология важна, потому что каждая версия принесла что-то, чем вы пользуетесь ежедневно:

  • 1BSD (1978) — редактор ex, компилятор Pascal.
  • 2BSD (1979)vi и C shell (csh). Да, vim — это правнук патча 1979 года.
  • 3BSD (1979) — порт на VAX и страничная виртуальная память (до этого Unix только свопил процессы целиком).
  • 4.2BSD (1983) — переломный релиз, профинансированный DARPA: сокеты и TCP/IP в ядре, Fast File System (FFS) с длинными именами файлов и цилиндрическими группами, надёжные сигналы, select(2).
  • 4.3BSD (1986), 4.3BSD-Tahoe (1988), 4.3BSD-Reno (1990).
  • 4.4BSD (1993) и 4.4BSD-Lite (1994) — последние релизы группы CSRG.

Сокеты Беркли — это API, которым вы пишете сеть в 2026 году, вне зависимости от языка и ОС. Даже Windows Sockets (Winsock) — это калька с BSD-интерфейса.

Параллельно AT&T, после разделения компании в 1984-м получившая право продавать софт, коммерциализировала свою линию: System III (1982) → System V (1983) → SVR2 → SVR3 (1987, STREAMS, разделяемая память SysV IPC) → SVR4 (1988).

SVR4 был попыткой примирения: AT&T и Sun совместно слили System V, BSD, SunOS и Xenix в одну систему. Технически удалось; политически — вызвало панику остальных вендоров и запустило Unix wars: коалиция OSF (IBM, DEC, HP) против UNIX International (AT&T, Sun). Пока Unix-вендоры судились и фрагментировали рынок, Microsoft спокойно занимала desktop.

Что от раскола осталось в вашем терминале

# BSD-синтаксис (без дефиса) и System V-синтаксис (с дефисом) — оба живы в procps
$ ps aux | head -2
USER   PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root     1  0.0  0.1 168404 12996 ?     Ss   09:12   0:03 /sbin/init

$ ps -ef | head -2
UID    PID  PPID  C STIME TTY      TIME CMD
root     1     0  0 09:12 ?    00:00:03 /sbin/init

# Флаг -a у echo, поведение echo -n, семантика tail -f — всё это точки расхождения BSD/SysV.
# Поэтому в скриптах используют printf, а не echo:
$ printf '%s\n' "переносимо всегда"

Другой живой след — семантика сигналов. В System V обработчик сбрасывался в SIG_DFL после первого срабатывания (ненадёжные сигналы), в BSD — нет. Поэтому signal(2) до сих пор считается непортируемым, и правильный код выглядит так:

#include <signal.h>
#include <stdio.h>

static volatile sig_atomic_t stop = 0;   /* только sig_atomic_t безопасен в обработчике */

static void on_sigint(int signo) {
    (void)signo;
    stop = 1;                            /* никакого printf: он не async-signal-safe */
}

int main(void) {
    struct sigaction sa;
    sa.sa_handler = on_sigint;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;            /* перезапускать прерванные системные вызовы */

    /* sigaction(2) стандартизован POSIX и ведёт себя одинаково на Linux,
       FreeBSD, OpenBSD, NetBSD и macOS. signal(2) — нет. */
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        perror("sigaction");
        return 1;
    }
    while (!stop) pause();
    puts("завершились чисто");
    return 0;
}

POSIX: попытка склеить осколки

Когда стало ясно, что «Unix» — это дюжина несовместимых систем (SunOS, HP-UX, AIX, Ultrix, Xenix, Irix, Dynix…), IEEE выпустил стандарт POSIX 1003.1-1988. Название придумал Ричард Столлман. Смысл: зафиксировать общее подмножество, чтобы исходник компилировался везде.

POSIX не столько создал будущее, сколько зафиксировал прошлое — и это важно понимать. Он не описывает epoll, kqueue, cgroups, namespaces, io_uring. Всё интересное, что появилось после 1990-х, стандартом не покрыто. Отсюда практическое правило: POSIX — это ваш фундамент переносимости, но продакшн-производительность всегда живёт в непортируемых расширениях.

# Какую версию POSIX заявляет ваша система
$ getconf _POSIX_VERSION
200809          # Linux/glibc: POSIX.1-2008
# FreeBSD 14:   200112
# macOS 15:     200112
# OpenBSD 7.x:  200809

# Полный список ограничений, которые обязана декларировать система
$ getconf -a | grep -E 'PATH_MAX|ARG_MAX|OPEN_MAX' 

Feature-test-макросы — прямое следствие исторического раскола. В одном и том же <unistd.h> живут разные наборы функций в зависимости от того, какое наследство вы просите:

#define _POSIX_C_SOURCE 200809L   /* только POSIX.1-2008 — максимальная переносимость */
/* #define _DEFAULT_SOURCE */     /* + традиционные BSD/SysV-расширения (бывший _BSD_SOURCE) */
/* #define _GNU_SOURCE */         /* + всё, что есть в glibc: memfd_create, pipe2, gettid... */
#include <unistd.h>

Раскол второй: суд, из-за которого мир получил Linux, а не BSD

К 1991 году в Беркли шла работа по вычищению всего AT&T-кода из BSD, чтобы систему можно было распространять свободно. Результат — Networking Release 2 (1991), из которого Билл и Линн Джолиц сделали 386BSD (1992) — первый свободный Unix для массового железа.

И тут случился суд. USL (Unix System Laboratories, дочерняя структура AT&T) подала иск против BSDi и Университета Беркли в 1992 году, утверждая, что в BSD остался их код. Дело тянулось до начала 1994 года и закончилось мировым соглашением: из 18 000 файлов спорными признали три, ещё несколько потребовали правок в копирайтах. Появился 4.4BSD-Lite — юридически чистая база.

Но два года неопределённости оказались решающими. Именно в это окно — 25 августа 1991 года — студент из Хельсинки Линус Торвальдс написал в comp.os.minix:

«I’m doing a (free) operating system (just a hobby, won’t be big and professional like gnu) for 386(486) AT clones.»

Почему Linux, а не BSD? Не из-за технического превосходства. BSD в 1992-м был зрелее по всем параметрам. Причины были такие:

  1. Юридическая чистота. Никто не хотел строить бизнес на системе, которую судят.
  2. Лицензия GPL. Торвальдс переключил Linux на GPL в версии 0.12 (январь 1992). Копилефт заставлял корпорации возвращать доработки в общий ствол — Linux не фрагментировался так, как коммерческие Unix.
  3. Готовое окружение GNU. Проект GNU Ричарда Столлмана (анонс — сентябрь 1983) к 1991 году имел gcc, bash, binutils, coreutils, emacs — всё, кроме ядра (Hurd, начатый в 1990-м, до сих пор не готов к продакшену). Linux оказался ровно недостающим куском.
  4. Модель разработки. «Release early, release often» — Торвальдс выпускал версии несколько раз в неделю, принимая патчи от кого угодно. Эрик Реймонд описал это в «The Cathedral and the Bazaar» (1997).

Показательный эпизод — дебаты Танненбаума и Торвальдса (январь 1992, тред «LINUX is obsolete»). Автор MINIX утверждал, что монолитное ядро — устаревшая архитектура, а будущее за микроядрами. Технически Танненбаум был во многом прав; практически победил тот, кто отдавал работающий код каждую неделю. Полная переписка — обязательное чтение перед статьёй про https://courses.digitable.life/post/operating-systems/02-kernel-architectures/.

Родословная: одна картинка

Обратите внимание на единственную стрелку слияния — SVR4 (1988) действительно вобрал код и идеи линии BSD (сокеты, FFS, работу с сигналами), и на этой базе выросла Solaris 2. Все остальные ветвления в истории Unix были расколами, а не слияниями: код расходился, а обратно сходились только стандарты.

Хронология ключевых событий

Ветви, живые сегодня

Linux

Хронология развития самого ядра важна для понимания, откуда взялись знакомые механизмы:

Версия Год Что принесла
0.01 1991 10 239 строк, только 386, только Minix FS
1.0 1994 сеть TCP/IP, ~176 000 строк
2.0 1996 SMP (правда, через один big kernel lock), загружаемые модули
2.4 2001 USB, ext3, iptables/netfilter
2.6 2003 O(1)-планировщик, epoll, futex, NPTL, sysfs, cgroups (2007), namespaces
2005 переход на git, написанный Торвальдсом за две недели после разрыва с BitKeeper
3.x–6.x 2011–… CFS→EEVDF, eBPF, io_uring (5.1, 2019), Rust (6.1, 2023)

Ключевая вещь, которую стоит понимать про Linux: у него нет спецификации. Есть код и правило «we do not break userspace». Торвальдс формулирует его жёстко: если обновление ядра ломает работающую программу — это баг ядра, а не программы. Это правило — причина, по которой бинарник, собранный в 2005 году, до сих пор запускается.

# Проверить границу стабильности API: syscall-таблица никогда не переиспользует номера
$ ausyscall --dump | head -5      # Linux
0       read
1       write
2       open
3       close
4       stat

# Номер 0 = read с 1969 года — по сути, тот же контракт, что в V1

Семейство BSD

386BSD распался на две ветви в 1993-м, а из NetBSD в 1995-м Тео де Раадт (после конфликта с core team) отпочковал OpenBSD. Различие философий стоит запомнить, оно последовательно проявляется во всём:

Практическая разница, которую вы почувствуете первой, — модель асинхронного I/O. kqueue(2) появился во FreeBSD 4.1 (2000, Джонатан Лемон), epoll — в Linux 2.5.44 (2002). Это разные API с разной семантикой:

/* FreeBSD / macOS / OpenBSD / NetBSD: kqueue — единый механизм
   для сокетов, файлов, сигналов, таймеров, процессов и событий FS */
int kq = kqueue();
struct kevent ev;
EV_SET(&ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
kevent(kq, &ev, 1, NULL, 0, NULL);          /* регистрация */
struct kevent out[64];
int n = kevent(kq, NULL, 0, out, 64, NULL); /* ожидание */

/* Linux: epoll — только для файловых дескрипторов; сигналы, таймеры
   и файловые события пришлось отдельно «одескрипторивать»:
   signalfd(2), timerfd_create(2), inotify(7), eventfd(2), pidfd_open(2) */
int ep = epoll_create1(0);
struct epoll_event e = { .events = EPOLLIN, .data.fd = fd };
epoll_ctl(ep, EPOLL_CTL_ADD, fd, &e);
struct epoll_event outs[64];
int m = epoll_wait(ep, outs, 64, -1);

Это не вкусовщина: BSD решил задачу обобщённо и сразу, Linux — точечно и итеративно. Обе стратегии имеют цену. У kqueue — сложный интерфейс с 6-полевой структурой; у Linux — зоопарк из шести разных *fd-механизмов, который в итоге и подтолкнул к созданию io_uring. Библиотеки вроде libuv и libevent существуют ровно затем, чтобы этот раскол спрятать.

macOS и XNU: третья линия

Отдельная ветвь тянется от Mach — микроядра, разработанного в Carnegie Mellon (Рик Рашид, Ави Тевэниан, 1985-1994). Стив Джобс, уйдя из Apple, построил на Mach 2.5 + BSD 4.3 систему NeXTSTEP (1989). Apple купила NeXT в конце 1996-го, и NeXTSTEP стал основой Mac OS X 10.0 (март 2001).

Ядро называется XNU («X is Not Unix») и представляет собой гибрид: Mach отвечает за IPC, задачи, память и порты; BSD-слой поверх него даёт POSIX-процессы, сокеты, VFS и системные вызовы; драйверы живут в IOKit — объектной подсистеме на подмножестве C++.

# macOS: где видны обе половины
$ uname -a
Darwin host 24.0.0 Darwin Kernel Version 24.0.0: ... root:xnu-11215.1.10~2/RELEASE_ARM64_T6000 arm64

$ sysctl kern.ostype kern.osrelease
kern.ostype: Darwin
kern.osrelease: 24.0.0

# Mach-порты — не POSIX. Это IPC-примитив уровня микроядра
$ lsmp -p $$ | head -5

# А вот kqueue — чистое BSD-наследие, работает точно как во FreeBSD
$ man 2 kqueue

macOS формально сертифицирован как UNIX 03 — то есть это один из немногих оставшихся «настоящих» UNIX по букве стандарта, в отличие от Linux, который UNIX-подобен, но не сертифицирован.

Windows NT: линия, идущая не от Unix вообще

Важно понимать: NT — \ast \ast не\ast \ast ветвь Unix. Его родословная идёт от \ast \ast VMS\ast \ast (DEC), а конкретно — от Дэйва Катлера, архитектора VMS и RSX-11M, которого Microsoft переманила в 1988-м вместе с командой. Windows NT 3.1 вышла в июле 1993-го.

Отличия архитектурные, а не косметические:

Аспект Unix-линия Windows NT
Создание процесса fork() + exec() — два шага, наследование по умолчанию CreateProcess() — один вызов с 10 параметрами, наследование явное
Дескрипторы малые целые числа int fd, наследуются при fork непрозрачные HANDLE, наследование задаётся флагом
Асинхронный I/O readiness-модель (select/epoll/kqueue) completion-модель (\ast \ast IOCP\ast \ast ) — ближе к io_uring
Ядро монолитное с модулями гибридное, слой HAL, подсистемы (Win32, POSIX, OS/2)
Пути /, чувствительность к регистру, единое дерево \, регистронезависимость, буквы дисков, объектный менеджер \??\
Права 9 бит + ACL опционально ACL с самого начала, SID вместо uid

Ирония в том, что в 2016 году круг замкнулся: Microsoft выпустила \ast \ast WSL1\ast \ast — подсистему, транслировавшую Linux-системные вызовы в NT-вызовы (ровно то, для чего в NT изначально был механизм подсистем), а в 2019-м \ast \ast WSL2\ast \ast — с настоящим ядром Linux в лёгкой виртуальной машине. Транслировать оказалось дороже, чем виртуализировать: fork() принципиально плохо ложится на модель NT.

Solaris, illumos и то, что они подарили всем

Sun прошла интересный путь: SunOS 1-4 были BSD-based, а SunOS 5 / \ast \ast Solaris 2\ast \ast (1992) перешли на SVR4. В Solaris 10 (2005) появились три вещи, которые изменили индустрию:

  • \ast \ast ZFS\ast \ast — файловая система с контрольными суммами, снапшотами и встроенным RAID (сегодня в FreeBSD, Linux через OpenZFS, TrueNAS);
  • \ast \ast DTrace\ast \ast — динамическая трассировка живого ядра без перекомпиляции (идейный предок Linux eBPF);
  • \ast \ast Zones\ast \ast — контейнеры за 8 лет до Docker.

Oracle закрыла исходники в 2010-м, но сообщество форкнуло последнюю открытую версию как \ast \ast illumos\ast \ast — на нём живут OmniOS и SmartOS. Подробнее об этой ветви — в статье https://courses.digitable.life/post/operating-systems/16-other-operating-systems/, а о ZFS — в https://courses.digitable.life/post/operating-systems/05-filesystems/.

Практическая археология: как история проступает в вашем коде

Это самая полезная часть. Пройдёмся по вещам, которые вы видите ежедневно, и объясним их происхождение.

\ast \ast /dev/tty и слово «tty».\ast \ast TTY = teletype, механический телетайп. Все настройки терминала — скорость, эхо, обработка ^H — это эмуляция железки 1960-х.

$ stty -a
speed 38400 baud; rows 50; columns 204; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; ...
# «speed 38400 baud» на локальном терминале — чистая фикция, поле осталось от модемов

$ stty sane      # рецепт «починить сломанный терминал», которому 40 лет

\ast \ast ETXTBSY — «Text file busy».\ast \ast «Text» здесь означает сегмент кода (см. SVG выше). Ядро V6 запрещало писать в файл, который кто-то исполняет. Живо до сих пор:

$ cp /bin/sleep ./mysleep && ./mysleep 100 &
$ echo x > ./mysleep
bash: ./mysleep: Text file busy      # errno 26 — привет из 1975 года

\ast \ast creat() без «e».\ast \ast Кен Томпсон, отвечая на вопрос, о чём он жалеет в дизайне Unix: «I’d spell creat with an e». Функция до сих пор в POSIX, хотя это просто open(path, O_WRONLY|O_CREAT|O_TRUNC, mode).

\ast \ast Разделение /bin и /usr/bin.\ast \ast В 1971-м диск RK05 на PDP-11 кончился, и часть системы перенесли на второй диск, смонтированный в /usr. Позже придумали обоснование («/usr — для не-критичных программ»), и это разделение прожило 50 лет. Только около 2012 года дистрибутивы начали делать /usr merge — сливать их обратно симлинками.

$ ls -ld /bin
lrwxrwxrwx 1 root root 7 Apr  3 2025 /bin -> usr/bin    # современный Debian/Fedora/Arch
$ man hier                                              # каноническое описание иерархии

\ast \ast dd с его if=/of=/bs=.\ast \ast Единственная утилита Unix с синтаксисом в стиле IBM JCL — потому что её писали для чтения лент с мейнфреймов, и автор скопировал привычный операторам синтаксис.

\ast \ast /proc.\ast \ast Придуман Томом Килианом для 8-го издания Research Unix (1984), развит в Plan 9 до тотальности («всё — файл, действительно всё»), скопирован в Solaris и Linux. Разные семейства пришли к разным выводам:

# Linux: /proc — основной интерфейс наблюдаемости, разросшийся далеко за процессы
$ cat /proc/self/status | head -4
$ cat /proc/meminfo | head -3
$ cat /proc/sys/vm/swappiness

# FreeBSD: procfs объявлен устаревшим, данные берутся через sysctl(3)
$ sysctl kern.proc.pid.$$
$ sysctl vm.stats.vm.v_free_count

# OpenBSD: /proc удалён полностью в 5.7 (2015) как источник уязвимостей
$ ls /proc
ls: /proc: No such file or directory

# macOS: /proc нет, есть sysctl и proc_pidinfo(3)
$ sysctl kern.boottime

Это отличная иллюстрация философского различия: Linux наращивает интроспекцию, OpenBSD её сокращает как поверхность атаки. Подробно — в https://courses.digitable.life/post/operating-systems/14-observability-and-performance/.

Init. BSD имел один скрипт /etc/rc, System V — /etc/inittab с runlevel’ами и /etc/rc.d/. Раскол дожил до наших дней: FreeBSD использует rc.d в BSD-стиле, Linux в основном перешёл на systemd, macOS — на launchd. Об этом целая статья: https://courses.digitable.life/post/operating-systems/11-init-systems/.

Как выбирать API сегодня, зная историю

Правило, выведенное из истории: пишите бизнес-логику на POSIX, а горячий путь — на нативном API конкретной ОС, спрятанном за интерфейсом. Именно так устроены PostgreSQL (src/backend/port/), nginx (src/event/modules/), Go runtime (runtime/netpoll_epoll.go и netpoll_kqueue.go).

Типичные заблуждения

«Linux — это Unix». Нет. Linux — Unix-подобная система, написанная с нуля, без единой строки AT&T-кода, и не сертифицированная по Single UNIX Specification. Сертифицированы macOS, AIX, HP-UX, z/OS UNIX System Services и (исторически) Solaris. Это юридический факт, а не оценка качества.

«BSD проиграл Linux технически». В 1992-м BSD был зрелее: лучше сеть, лучше VM, лучше документация. Решили лицензия, юридический риск и модель разработки. Технические аргументы в истории ОС редко бывают решающими.

«GPL всегда лучше BSD-лицензии для проекта». Это компромисс, а не превосходство. GPL защищает от форков-без-возврата и удерживает ствол единым — так Linux избежал фрагментации, погубившей коммерческий Unix. BSD-лицензия обеспечивает максимальное распространение кода: сетевой стек BSD оказался в Windows, стек и libc — в PlayStation, kqueue — в macOS. Разные цели.

«Микроядра проиграли». Не проиграли. Mach живёт в каждом iPhone и Mac. L4 крутится в baseband-процессорах миллиардов телефонов. seL4 — формально верифицированное микроядро — используется в авиации и обороне. QNX управляет автомобилями. Проиграла лишь идея «микроядро для универсального сервера общего назначения». Разбор — в https://courses.digitable.life/post/operating-systems/02-kernel-architectures/.

«Всё это устарело, сейчас всё в контейнерах». Контейнеры — это namespaces + cgroups + chroot, то есть механизмы, растущие прямо из Unix-модели процессов и файловой системы. chroot(2) появился в V7 в 1979 году. FreeBSD jails — 2000 год. Solaris Zones — 2004. Docker (2013) сделал упаковку удобной, не изобретя изоляции. См. https://courses.digitable.life/post/operating-systems/18-virtualization-and-containers/.

«POSIX гарантирует переносимость». POSIX гарантирует переносимость исходника в рамках описанного подмножества. Он не описывает производительность, не описывает поведение при исчерпании ресурсов, оставляет десятки мест «implementation-defined» и не покрывает ничего из того, что вы реально используете в высоконагруженном сервисе.

Что действительно стоит унести из этой истории

  1. Простой интерфейс переживает сложный. «Файл — поток байт» победил типизированные записи Multics и VMS. read/write пережили всё.
  2. Портируемость важнее оптимальности. Переписывание Unix на C стоило производительности, но дало ОС полвека жизни.
  3. Лицензия — архитектурное решение. GPL против BSD определила судьбу двух систем сильнее, чем любой технический выбор.
  4. Ограничения железа превращаются в стиль. 64 KiB на PDP-11 породили культуру маленьких программ и конвейеров, которая пережила железо в тысячи раз.
  5. Обратная совместимость — это функция. «We do not break userspace» — причина, по которой Linux захватил серверы.
  6. Хорошие идеи возвращаются. Multics ждал 40 лет своей реабилитации: computing utility стал облаком, сегменты — mmap, кольца — SMEP/SMAP и виртуализации, ACL — POSIX ACL и SELinux.

Источники

Книги:

  • Dennis Ritchie, Ken Thompson. The UNIX Time-Sharing System. CACM, 1974 — первоисточник, 11 страниц.
  • John Lions. Lions’ Commentary on UNIX 6th Edition, 1976. Доступна онлайн — построчный разбор всего ядра.
  • Marshall Kirk McKusick et al. The Design and Implementation of the FreeBSD Operating System, 2nd ed., 2014 — каноническая книга по BSD-линии.
  • Peter Salus. A Quarter Century of UNIX, 1994 — лучшая историческая монография.
  • Andrew Tanenbaum. Modern Operating Systems, 5th ed. — исторические главы и разбор архитектурных развилок.
  • Eric Raymond. The Art of Unix Programming, 2003 — про философию, местами спорно, но исторические главы сильные.

Первоисточники и архивы:

Man-страницы, которые стоит прочитать целиком: man 7 standards (FreeBSD), man 7 feature_test_macros (Linux), man 7 hier, man 2 intro.

Что дальше

История объяснила, почему ядра выглядят так, как выглядят. Следующий шаг — разобрать, как они устроены внутри: чем монолит отличается от микроядра, что такое гибрид XNU и NT на самом деле, зачем нужны экзоядра и unikernel’ы, и почему спор Танненбаума с Торвальдсом не закончен до сих пор.

Архитектуры ядер: монолитное, микроядро, гибридное, экзоядро, unikernel

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов