Операционная система (ОС)
Операционная система (ОС) представляет собой сложный программный комплекс, управляющий ресурсами компьютера и предоставляющий прикладным программам унифицированный интерфейс. По мере роста функциональ
Операционная система (ОС) представляет собой сложный программный комплекс, управляющий ресурсами компьютера и предоставляющий прикладным программам унифицированный интерфейс. По мере роста функциональности и объёма кода (современные ядра содержат миллионы строк) возникла необходимость в системном подходе к организации компонентов. Структурирование ОС — это способ разбиения системы на части (модули, уровни, серверы) с чётко определёнными интерфейсами и правилами взаимодействия.
Основные принципы структурирования:
- Модульность — разделение на логически завершённые компоненты с минимизацией связей.
- Иерархическая организация (уровни абстракции) — каждый нижележащий уровень предоставляет услуги вышележащему, скрывая детали реализации.
- Скрытие информации (инкапсуляция) — компонент предоставляет интерфейс, но не раскрывает внутренние структуры.
- Виртуализация ресурсов — преобразование физических ресурсов в удобные логические абстракции (процессы, виртуальная память, файлы).
В этой статье мы рассмотрим классические и современные архитектуры ОС, их организацию, достоинства и недостатки, а также проиллюстрируем ключевые принципы диаграммами и примерами.
Английские сокращения
| Сокращение | Английское обозначение | Русское обозначение |
| OS | Operating System | операционная система |
| API | Application Programming Interface | интерфейс прикладного программирования |
| ABI | Application Binary Interface | интерфейс двоичного уровня |
| IPC | Inter-Process Communication | межпроцессное взаимодействие |
| SMP | Symmetric Multiprocessing | симметричная многопроцессорная обработка |
| HAL | Hardware Abstraction Layer | уровень абстракции от аппаратуры |
| LKM | Loadable Kernel Module | загружаемый модуль ядра |
| VM | Virtual Machine | виртуальная машина |
| VMM | Virtual Machine Monitor | монитор виртуальных машин (гипервизор) |
| RPC | Remote Procedure Call | удалённый вызов процедур |
Русские сокращения
| Сокращение | Обозначение |
| ОС | операционная система |
| Ядро | центральная часть ОС |
| Модуль | загружаемый компонент ядра |
| ПЗУ | память только для чтения (ROM) |
| В/В | ввод-вывод |
Модульность
Модуль — это функционально законченная часть системы, имеющая чёткий интерфейс (набор функций или системных вызовов). Модули могут компилироваться отдельно, загружаться динамически (LKM в Linux) и заменяться без перекомпиляции всей системы.
Преимущества модульности:
- Упрощение разработки и отладки.
- Повышение надёжности (сбой в одном модуле реже приводит к краху всей системы).
- Переносимость (аппаратно-зависимые модули изолированы).
Иерархическая организация (слои)
Принцип, впервые строго реализованный в системе THE (Dijkstra, 1968). Система разбивается на уровни (layers), где уровень использует услуги только уровней . Каждый уровень скрывает детали нижележащих уровней.
Пример иерархии (классическая схема ОС):
| Уровень | Назначение |
| 0 | Аппаратура (процессор, память, устройства) |
| 1 | Диспетчеризация процессов, базовое управление памятью |
| 2 | Механизмы IPC, синхронизация |
| 3 | Драйверы устройств (прерывания, ввод-вывод) |
| 4 | Файловые системы, виртуальная память |
| 5 | Системные вызовы (интерфейс для приложений) |
Скрытие информации и абстракция
Каждый компонент ОС должен предоставлять абстракцию ресурса:
- Физический процессор → абстракция «процесс» или «поток».
- Физическая память → абстракция «виртуальное адресное пространство».
- Дисковое устройство → абстракция «файл» или «блочное устройство».
Виртуализация
Виртуализация расширяет принцип абстракции: на одном физическом ресурсе создаётся несколько логических (виртуальных) экземпляров, изолированных друг от друга. Современные гипервизоры (VMware ESXi, KVM, Xen) — пример полной виртуализации аппаратуры.
Монолитное ядро (Monolithic Kernel)
Вся ОС (управление процессами, памятью, файловыми системами, драйверы) работает в одном адресном пространстве ядра, в режиме супервизора (ring 0 на x86). Компоненты вызывают друг друга напрямую.
@startuml
rectangle "Пользовательский режим" {
[Приложения]
}
rectangle "Режим ядра (одно адресное пространство)" {
component "Управление процессами" as proc
component "Управление памятью" as mem
component "Файловая система" as fs
component "Драйверы устройств" as drv
component "Сетевой стек" as net
proc -[hidden]- mem
mem -[hidden]- fs
fs -[hidden]- drv
drv -[hidden]- net
}
[Приложения] --> proc : системные вызовы
[Приложения] --> mem
[Приложения] --> fs
@enduml
Преимущества:
- Высокая производительность (отсутствие переключений контекста между компонентами ядра).
- Простота реализации (по сравнению с микроядром).
Недостатки:
- Отсутствие изоляции: ошибка в любом драйвере может разрушить всё ядро (паника системы).
- Плохая расширяемость (замена драйвера требует перекомпиляции ядра, хотя в современных монолитных ядрах есть модули).
- Переносимость требует переработки больших частей кода.
Примеры: традиционные Unix (до микроядренных экспериментов), Linux (однако Linux поддерживает загружаемые модули, что частично решает проблему расширяемости), FreeBSD, MS-DOS, классические Windows 9x.
Многоуровневая (слоевая) архитектура (Layered)
Строгое соблюдение иерархии уровней. Каждый уровень реализован как отдельная абстрактная машина. Система THE (Technische Hogeschool Eindhoven) — эталон.
Преимущества:
- Простота отладки (можно тестировать уровень за уровнем).
- Высокая надёжность.
Недостатки:
- Снижение производительности из-за многократного пересечения границ уровней.
- Трудность определения «правильной» иерархии (циклические зависимости возможны).
Примеры: THE, некоторые исследовательские ОС (например, Hydra). В чистом виде сейчас почти не применяется, но принцип уровней используется при проектировании подсистем (например, стек TCP/IP — уровни, драйверы слой за слоем).
Микроядро (Microkernel)
Микроядро содержит только самые минимальные механизмы: управление процессами (базовое), межпроцессное взаимодействие (IPC), базовую адресацию памяти и обработку прерываний. Все остальные службы (файловые системы, драйверы, сетевой стек) работают как серверы в пользовательском режиме. Взаимодействие происходит через IPC.
@startuml
rectangle "Пользовательский режим" {
component "Приложение" as app
component "Сервер файловой системы" as fs_server
component "Сервер драйверов" as drv_server
component "Сервер сети" as net_server
app -[hidden]r- fs_server
fs_server -[hidden]r- drv_server
drv_server -[hidden]r- net_server
}
rectangle "Режим ядра" {
component "Микроядро\n(IPC, базовая планировка,\nуправление адресными пространствами)" as micro
}
app --> micro : системные вызовы
fs_server --> micro
drv_server --> micro
net_server --> micro
app <--> fs_server : IPC
fs_server <--> drv_server : IPC
@enduml
Преимущества:
- Высокая надёжность и отказоустойчивость: сбой сервера не разрушает микроядро, сервер можно перезапустить.
- Модульность и расширяемость: добавлять серверы легко.
- Безопасность: серверы изолированы (каждый в своём адресном пространстве).
Недостатки:
- Низкая производительность: частые переключения контекста (пользователь ↔ ядро) и копирование данных при IPC.
- Сложность разработки (необходимость в хорошем дизайне IPC, избегании лишних копирований).
Примеры: QNX (используется в автомобилях, медицинском оборудовании), L4 (семейство высокопроизводительных микроядер), Minix 3 (учебная, но надёжная), Mach (основа для OS X/macOS, но там гибрид).
Гибридное ядро (Hybrid kernel)
Компромисс: часть драйверов и подсистем работает в пространстве ядра (для производительности), а часть — в пользовательском (для безопасности или лицензионных причин). Часто имеет архитектуру «монолитное ядро с модулями», но с вынесенными компонентами.
Примеры:
- Windows NT (и все последующие Windows): ядро гибридное, но большая часть драйверов в режиме ядра, есть также подсистемы в пользовательском режиме (CSRSS, Win32k.sys частично).
- XNU (ядро macOS, iOS): основано на Mach (микроядро) + компоненты BSD (монолитные) + драйверы I/O Kit в пользовательском режиме.
Экзоядро (Exokernel)
Минимальное ядро, которое только мультиплексирует аппаратные ресурсы (без их абстрагирования). Библиотеки операционной системы (libOS) работают в пользовательском режиме и напрямую, безопасно через экзоядро, управляют ресурсами. Обеспечивает максимальную производительность за счёт низких накладных расходов, но требует переписывания приложений.
Примеры: ExOS (MIT), Nemesis (Кембридж). В основном исследовательские.
Наноядро (Nanokernel)
Ядро ещё меньше, чем микроядро: предоставляет только аппаратные прерывания, таймеры, минимальный планировщик. Всё остальное — на более высоких уровнях. Практического распространения не получило.
Независимо от общей архитектуры, внутри ОС выделяют следующие подсистемы, которые могут быть организованы модульно:
- Управление процессами и потоками
- Структура: планировщик, диспетчер, синхронизация (мьютексы, семафоры).
- Принципы: вытесняющая многозадачность, поддержка SMP, приоритеты.
- Управление памятью
- Физическое управление (страничные кадры, сегменты).
- Виртуальная память (отображение, подкачка, кэш TLB).
- Абстракция: адресное пространство процесса.
- Файловая система
- Организация: VFS (Virtual File System) — уровень абстракции над конкретными ФС (ext4, FAT, NTFS).
- Кэширование, журналирование, права доступа.
- Подсистема ввода-вывода и драйверы
- Иерархия: обработчики прерываний, драйверы (блочные, символьные, сетевые), универсальные интерфейсы (ioctl, read, write).
- Модель plug-and-play, управление питанием.
- Сетевая подсистема
- Стек протоколов (обычно слойный: сокеты → TCP/UDP → IP → драйвер).
- Модульность: загружаемые протоколы, фильтры пакетов.
- Безопасность и защита
- Разграничение доступа (дискреционное / мандатное).
- Аудит, шифрование, механизмы изоляции (капсулы, контейнеры).
Каждая подсистема проектируется с использованием тех же принципов (модульность, интерфейсы, уровни). Например, VFS — это классический слой абстракции.
Монолитные ядра (Linux, FreeBSD) реализуют динамическую загрузку модулей для устранения недостатка жёсткой связности. Модули могут добавлять драйверы, файловые системы, системные вызовы без перекомпиляции ядра.
Принципы:
- Модуль компилируется отдельно, содержит таблицу экспортируемых символов.
- Загрузка: команды `insmod` (Linux), `kldload` (FreeBSD).
- Выгрузка: `rmmod`, `kldunload`.
- Защита от выгрузки используемых модулей (счётчик ссылок).
Недостаток: модуль работает в адресном пространстве ядра и может его разрушить (нет изоляции).
Гипервизоры (VMM) добавляют новый уровень — виртуальные машины. Аппаратные ресурсы виртуализируются, и на одной физической машине работают несколько «гостевых» ОС. Гипервизоры делятся на два типа:
- Тип 1 (bare-metal): гипервизор работает непосредственно на аппаратуре (VMware ESXi, Xen, Microsoft Hyper-V).
- Тип 2 (хостированные): гипервизор — приложение хост-ОС (VirtualBox, VMware Workstation, QEMU).
Принципы структурирования гипервизора: прослойка между аппаратурой и гостевыми ОС, перехват привилегированных инструкций, управление памятью с аппаратной поддержкой (EPT, NPT).
@startuml
rectangle "Гостевая ОС 1" as guest1
rectangle "Гостевая ОС 2" as guest2
rectangle "Гипервизор (тип 1)" as hyper
rectangle "Аппаратура" as hw
guest1 -down-> hyper
guest2 -down-> hyper
hyper -down-> hw
@enduml
| Характеристика | Монолитное ядро | Микроядро | Гибридное |
| Производительность | высокая | низкая (из-за IPC) | средняя |
| Надёжность | низкая (один сбой — паника) | высокая (серверы перезапускаются) | средняя |
| Расширяемость | средняя (модули) | высокая | высокая |
| Сложность разработки | средняя | высокая (IPC, синхронизация) | высокая |
| Размер ядра (минимальный) | ~1-10 МБ (Linux) | ~10-100 КБ (L4) | ~500 КБ – несколько МБ |
| Примеры | Linux, FreeBSD, Windows 9x | QNX, L4, Minix 3 | Windows NT, XNU |
Напишем простую программу, имитирующую вызовы «уровней» ОС: от приложения через системный вызов к драйверу. Это абстракция, не настоящие syscalls.
class Hardware:
@staticmethod
def write_console(msg):
print(f"[HW] Вывод: {msg}")
class KernelDriver:
def write(self, msg):
Hardware.write_console(msg)
class SyscallInterface:
def __init__(self):
self.driver = KernelDriver()
def sys_write(self, msg):
self.driver.write(msg)
class Application:
def __init__(self):
self.sys = SyscallInterface()
def run(self):
self.sys.sys_write("Hello, OS structure!")
app = Application()
app.run()
Этот пример иллюстрирует скрытие информации: приложение не знает о драйвере и аппаратуре, оно вызывает только системный интерфейс.
- Унификация ядер для разных архитектур (Device Tree, ACPI) — вынос описания аппаратуры из ядра.
- Контейнеризация — изоляция на уровне ОС (Linux namespaces, cgroups) без полноценной виртуализации; добавляет новый слой абстракции между процессами и ядром.
- Микроядреные тенденции в embedded и IoT (Zephyr RTOS, Fuchsia — Google использует микроядро Zircon).
- Распределённые ОС и одноуровневые архитектуры (Plan 9, Inferno) — все ресурсы представлены как файлы в едином пространстве имён.
- Формальная верификация ядер (seL4 — первое микроядро с математическим доказательством корректности).