один структурный выбор 1991 года и то, что из него следует до сих порone 1991 structural choice, and what still follows from it today

Архитектура ядра: монолит и модель процессовKernel architecture: the monolith and the process model

Linux — монолитное ядро: весь код ядра работает в одном адресном пространстве, а не разнесён по изолированным серверам, общающимся через сообщения. Это не забытая деталь ранней реализации, а осознанный выбор, публично защищённый Торвальдсом в 1992 году, — и он же определяет, почему в Linux нет отдельного системного вызова для создания потока: clone() создаёт и процессы, и потоки одним и тем же механизмом, различие — просто в том, что из ресурсов родителя расшарено.Linux is a monolithic kernel: all kernel code runs in one address space, rather than being split across isolated servers talking via messages. This isn't a forgotten detail of an early implementation — it's a deliberate choice, publicly defended by Torvalds in 1992 — and it's the same choice behind why Linux has no separate syscall for creating a thread: clone() creates both processes and threads through the same mechanism, the difference being purely which of the parent's resources get shared.

Что этоWhat it is

Архитектура ядра определяет, где физически выполняется код разных подсистем ОС (планировщик, драйверы, файловая система, сеть) и как они друг с другом взаимодействуют. Два полюса спектра:A kernel's architecture determines where the code for different OS subsystems (scheduler, drivers, filesystem, networking) physically runs, and how they talk to each other. Two ends of the spectrum:

монолитное ядроmonolithic kernelмикроядроmicrokernel
где выполняется кодwhere the code runsвесь код ядра — в одном привилегированном адресном пространствеall kernel code — in one privileged address spaceв ядре — только минимум (планировщик, IPC, базовая память); драйверы, ФС — отдельные процессы в пространстве пользователяonly the bare minimum in the kernel (scheduler, IPC, basic memory); drivers, filesystems — separate user-space processes
как подсистемы общаютсяhow subsystems communicateпрямой вызов функции — почти бесплатно по накладным расходамa direct function call — almost free in overheadсообщения через IPC — переключение контекста и копирование данных на каждый вызовmessages via IPC — a context switch and data copy on every call
цена бага в драйвереthe cost of a driver bugпотенциально роняет всё ядро — общее адресное пространствоcan potentially bring down the whole kernel — shared address spaceпадает только сам процесс драйвера, ядро продолжает работатьonly the driver's own process crashes, the kernel keeps running
примерыexamplesLinux, традиционныйtraditional UnixseL4, QNX

Между этими полюсами лежат гибридные ядра — Windows NT и XNU (macOS/iOS) держат в привилегированном режиме больше, чем «чистое» микроядро (ради производительности), но структурно разделяют компоненты сильнее монолита. Linux к гибридным не относится — он монолитный по структуре с самого начала, а модульность реализована другим механизмом (см. ниже).Between these poles sit hybrid kernels — Windows NT and XNU (macOS/iOS) keep more in privileged mode than a "pure" microkernel (for performance), but structurally separate components more than a monolith does. Linux isn't hybrid — it's been monolithic in structure from the start, with modularity handled by a different mechanism (see below).

Спор 1992 года: не историческая деталь, а суть выбораThe 1992 debate: not a historical footnote, but the choice itself

29 января 1992 года Эндрю Таненбаум (Andrew Tanenbaum), автор Minix и профессор, публично раскритиковал молодое ядро Линуса Торвальдса в Usenet-группе comp.os.minix, в посте, озаглавленном «LINUX is obsolete». Его аргумент был предельно конкретным, не общими словами о «плохом дизайне»: Minix была построена как микроядро — файловая система и управление памятью работали как отдельные процессы вне ядра, драйверы ввода-вывода — тоже отдельные процессы. Linux же был монолитным — «весь ОС — один a.out файл, который выполняется в 'kernel mode'», по формулировке Таненбаума — что он назвал «гигантским шагом назад в 1970-е».On January 29, 1992, Andrew Tanenbaum, Minix's author and a professor, publicly criticized Linus Torvalds's young kernel in the Usenet group comp.os.minix, in a post titled "LINUX is obsolete." His argument was precise, not vague hand-waving about "bad design": Minix was built as a microkernel — the filesystem and memory management ran as separate processes outside the kernel, I/O drivers too were separate processes. Linux, meanwhile, was monolithic — "the whole OS is a single a.out file that executes in 'kernel mode'," in Tanenbaum's own words — which he called "a giant step back to the 1970s."

Торвальдс не оспаривал техническую характеристику — он согласился, что Linux монолитный и что «микроядра красивее», и что «с теоретической и эстетической точки зрения Linux проигрывает». Его контраргумент был не архитектурным, а прагматичным: если бы ядро GNU (планировавшееся как микроядро) было готово к тому моменту, он бы вообще не начинал Linux — но оно готово не было, а Linux уже реально работал. «Побеждает тем, что доступен уже сейчас», а не теоретическим изяществом. К дискуссии присоединились и другие ранние разработчики ядра — Питер МакДональд (один из первых мейнтейнеров дистрибутивов), Дэвид Миллер, Теодор Тсо — все на стороне Торвальдса. Смысл этого спора важен не как курьёз давно минувших дней: он и есть точная формулировка компромисса, актуального по сей день — производительность и доступность здесь и сейчас против изоляции и теоретической элегантности.Torvalds didn't dispute the technical characterization — he agreed Linux was monolithic and that "microkernels are nicer," and that "from a theoretical and aesthetical standpoint Linux loses." His counterargument wasn't architectural, it was pragmatic: if the GNU kernel (planned as a microkernel) had been ready by then, he wouldn't have started Linux at all — but it wasn't ready, and Linux was already actually working. It "wins heavily on the point of being available now," not on theoretical elegance. Other early kernel developers joined the discussion too — Peter MacDonald (an early distro maintainer), David S. Miller, Theodore Ts'o — all on Torvalds's side. The debate's substance matters not as a historical curiosity from long ago: it's the exact formulation of a trade-off still live today — performance and availability right now, versus isolation and theoretical elegance.

Как это устроено внутриHow it's built internally

01

LKM: модульность без цены микроядраmodularity without a microkernel's price

Довод Таненбаума про «жёсткость» монолита адресован механизмом, появившимся уже в 2.0 (1996) — Loadable Kernel Modules (LKM). Драйвер устройства, оформленный как модуль, можно подключить к работающему ядру и отключить обратно без перезагрузки и без перекомпиляции всего ядра целиком. Технически модуль после загрузки становится частью того же самого адресного пространства, что и остальное ядро — то есть архитектурно это всё ещё монолит, просто модульно собираемый в рантайме, а не единственный статический бинарник времени сборки. LKM не решает проблему изоляции (баг в модуле всё ещё может уронить всё ядро), но снимает практическую боль «пересобирать ядро ради каждого нового драйвера», на которую и указывал спор 1992 года.Tanenbaum's point about the monolith's "rigidity" is addressed by a mechanism that arrived already in 2.0 (1996) — Loadable Kernel Modules (LKM). A device driver packaged as a module can be attached to a running kernel and detached again without a reboot or recompiling the whole kernel. Technically, once loaded a module becomes part of the exact same address space as the rest of the kernel — architecturally it's still a monolith, just modularly assembled at runtime instead of a single static build-time binary. LKM doesn't solve the isolation problem (a bug in a module can still bring down the whole kernel), but it removes the practical pain of "rebuild the kernel for every new driver" that the 1992 debate pointed at.

02

task_struct: и процесс, и поток — одна и та же сущность ядраa process and a thread — the same kernel entity

Ядро Linux не проводит на своём уровне строгого различия между «процессом» и «потоком» — обе сущности представлены одной и той же структурой task_struct и планируются одним и тем же планировщиком одинаково. Разница между ними — не в типе структуры, а в том, какие ресурсы конкретный task_struct расшаривает с родителем: адресное пространство (память), таблицу открытых файловых дескрипторов, обработчики сигналов. Все эти сущности порождаются одним системным вызовом — clone() — которому передаётся набор флагов, определяющий, что именно унаследовать совместно, а что получить своё собственное.The Linux kernel doesn't draw a strict line at its own level between a "process" and a "thread" — both are represented by the same task_struct structure and scheduled the same way by the same scheduler. The difference between them isn't in the structure's type, but in which resources a given task_struct shares with its parent: the address space (memory), the open file descriptor table, signal handlers. All of these entities are spawned by one system call — clone() — passed a set of flags that determines exactly what to inherit jointly and what to get its own private copy of.

одна и та же функция ядра — два разных результата в зависимости от флаговthe same kernel function — two different outcomes depending on the flags
// fork() по сути — clone() с почти без общих флагов:
// новый task_struct получает свою копию памяти, свои файловые дескрипторы
clone(SIGCHLD, ...);

// pthread_create() по сути — clone() с флагами полного расшаривания:
// новый task_struct работает в ТОЙ ЖЕ памяти, что и родитель — это и есть "поток"
clone(CLONE_VM | CLONE_FILES | CLONE_FS |
      CLONE_SIGHAND | CLONE_THREAD, ...);

CLONE_VM — ключевой флаг: если он установлен, новый task_struct получает не копию адресного пространства родителя, а ссылку на то же самое mm_struct — запись в памяти, сделанная одним, немедленно видна другому. Это буквально и есть определение потока на уровне ядра: не отдельная сущность с урезанными правами, а обычный task, которому просто не выделили собственную память. CLONE_FILES аналогично расшаривает таблицу файловых дескрипторов — открыл файл в одном потоке, он открыт и в другом.CLONE_VM is the key flag: if it's set, the new task_struct doesn't get a copy of the parent's address space — it gets a reference to the exact same mm_struct; a memory write by one is immediately visible to the other. That's literally the kernel-level definition of a thread: not a separate stripped-down entity, but an ordinary task that simply wasn't given its own memory. CLONE_FILES similarly shares the file descriptor table — open a file in one thread, and it's open in the other too.

Грабли и мифыGotchas & myths

«Спор 1992 года доказал, что монолит лучше» — нет, доказал только то, что доступность бьёт теорию"The 1992 debate proved the monolith is better" — no, it only proved availability beats theory

Сам Торвальдс не оспаривал теоретическое превосходство микроядерного дизайна в чистоте изоляции — он согласился с этим прямо. Победа Linux была не архитектурным доказательством, а результатом того, что рабочее ядро существовало здесь и сейчас, пока конкурирующий микроядерный проект (GNU Hurd) ещё не был готов к использованию. Путать «Linux оказался более практичным выбором для 1990-х» с «монолит архитектурно превосходит микроядро» — неточность, которую не совершал даже сам Торвальдс.Torvalds himself didn't dispute microkernel design's theoretical superiority in isolation purity — he agreed with it outright. Linux's win wasn't an architectural proof, it was the result of a working kernel existing right then, while the competing microkernel project (GNU Hurd) wasn't yet ready for use. Conflating "Linux was the more practical choice for the 1990s" with "a monolith architecturally beats a microkernel" is an inaccuracy Torvalds himself never made.

«Загружаемые модули делают Linux микроядром» — нет, это всё ещё один адресный пространство"Loadable modules make Linux a microkernel" — no, it's still one address space

LKM решает проблему удобства (не нужно пересобирать ядро под каждый драйвер), но не проблему изоляции — загруженный модуль исполняется в том же привилегированном адресном пространстве, что и остальное ядро, и его баг способен уронить всю систему точно так же, как баг в статически встроенном коде. Модульность в смысле «можно подгружать и выгружать код в рантайме» и микроядерная изоляция в смысле «баг в одном компоненте не роняет остальные» — независимые оси, а не синонимы.LKM solves the convenience problem (no need to rebuild the kernel for every driver), not the isolation problem — a loaded module executes in the exact same privileged address space as the rest of the kernel, and a bug in it can bring down the whole system exactly as a bug in statically built-in code could. Modularity in the sense of "code can be loaded and unloaded at runtime" and microkernel-style isolation in the sense of "a bug in one component doesn't take down the others" are independent axes, not synonyms.

«Поток в Linux — облегчённый процесс» — точнее сказать наоборот: процесс — это поток без расшаренных ресурсов"a thread in Linux is a lightweight process" — more precisely, the reverse: a process is a thread with nothing shared

Формулировка «поток — облегчённая версия процесса» подразумевает, что процесс первичен, а поток — урезанный вариант. На уровне ядра вернее обратное: базовая единица — task_struct, порождаемая clone(), а «процесс» в привычном смысле — это просто частный случай clone(), где не расшарено вообще ничего из ресурсов родителя. Ни та, ни другая сущность не является урезанной версией другой — обе получаются одним и тем же механизмом с разным набором флагов.The phrasing "a thread is a lightweight version of a process" implies the process is primary and the thread is a stripped-down variant. At the kernel level it's more accurate the other way around: the base unit is task_struct, spawned by clone(), and a "process" in the familiar sense is just a specific case of clone() where none of the parent's resources are shared at all. Neither entity is a cut-down version of the other — both come out of the same mechanism with a different set of flags.

ТерминыTerms

монолитное ядроmonolithic kernel
архитектура, при которой весь код ядра работает в одном привилегированном адресном пространстве, а подсистемы общаются прямыми вызовами функций, а не через IPC. Linux — монолитный с самого начала.an architecture where all kernel code runs in one privileged address space, and subsystems communicate via direct function calls rather than IPC. Linux has been monolithic from the start.
микроядро
архитектура, при которой в привилегированном режиме остаётся только минимум (планировщик, базовая память, IPC), а драйверы и файловые системы работают как обычные изолированные процессы в пространстве пользователя. Примеры: seL4, QNX.an architecture where only the bare minimum (scheduler, basic memory, IPC) runs in privileged mode, while drivers and filesystems run as ordinary isolated user-space processes. Examples: seL4, QNX.
гибридное ядроhybrid kernel
архитектура посередине спектра: держит в привилегированном режиме больше компонентов, чем «чистое» микроядро (ради производительности), но структурно разделяет их сильнее, чем монолит. Примеры: Windows NT, XNU (ядро macOS/iOS, объединяющее Mach-микроядро и BSD-слой).an architecture in the middle of the spectrum: keeps more components in privileged mode than a "pure" microkernel (for performance), but structurally separates them more than a monolith does. Examples: Windows NT, XNU (macOS/iOS's kernel, combining a Mach microkernel with a BSD layer).
LKM (Loadable Kernel Module)
код (обычно драйвер устройства), который можно подключить к работающему ядру и отключить обратно без перезагрузки — появился в Linux 2.0 (1996) как ответ на «жёсткость» статически собранного монолита, но не даёт изоляции: загруженный модуль работает в том же адресном пространстве, что и остальное ядро.code (usually a device driver) that can be attached to a running kernel and detached without a reboot — arrived in Linux 2.0 (1996) as a response to a statically built monolith's "rigidity," but doesn't provide isolation: a loaded module runs in the same address space as the rest of the kernel.
task_struct
структура ядра Linux, представляющая одну исполняемую единицу — и процесс, и поток описываются одной и той же структурой; разница между ними определяется только тем, какие ресурсы конкретный task_struct расшаривает с родителем.the Linux kernel structure representing one executable unit — both a process and a thread are described by the exact same structure; the difference between them comes down purely to which resources a given task_struct shares with its parent.
clone()
системный вызов, лежащий в основе и fork(), и создания потоков в Linux — принимает набор флагов, определяющий, что именно новый task_struct получит общим с родителем (память через CLONE_VM, файловые дескрипторы через CLONE_FILES и т.д.), а что — своё собственное.the system call underlying both fork() and thread creation in Linux — takes a set of flags determining exactly what the new task_struct gets to share with its parent (memory via CLONE_VM, file descriptors via CLONE_FILES, and so on) versus what it gets its own private copy of.
CLONE_VM
флаг clone(), заставляющий новый task_struct использовать не копию, а ту же самую структуру mm_struct (адресное пространство), что и родитель — запись в память, сделанная одним, немедленно видна другому. Это и есть определяющее свойство потока на уровне ядра.a clone() flag making the new task_struct use not a copy but the exact same mm_struct (address space) as the parent — a memory write by one is immediately visible to the other. This is the defining kernel-level property of a thread.
mm_struct
структура ядра, описывающая адресное пространство процесса — область памяти, которую он видит. Потоки одного процесса указывают на один и тот же mm_struct; отдельные процессы получают каждый свой.the kernel structure describing a process's address space — the memory region it can see. Threads of one process point to the same mm_struct; separate processes each get their own.