Skip to main content

Архитектура UNIX

Основные принципы архитектуры

UNIX строится на нескольких фундаментальных концепциях:

  1. "Все есть файл" - устройства, процессы, сокеты представлены как файлы
  2. Модульность - множество небольших специализированных утилит
  3. Конвейеры - программы взаимодействуют через стандартные потоки ввода/вывода
  4. Иерархическая файловая система - единое дерево каталогов

Слоистая архитектура

┌─────────────────────────────────────┐
│ Пользовательские │
│ приложения │
├─────────────────────────────────────┤
│ Shell и утилиты │
│ (bash, ls, grep, gcc...) │
├─────────────────────────────────────┤
│ Системная библиотека │
│ (libc) │
├─────────────────────────────────────┤
│ Системные вызовы │
│ (интерфейс к ядру) │
├─────────────────────────────────────┤
│ ЯДРО │
│ (kernel space) │
└─────────────────────────────────────┘

Ядро (Kernel) - сердце системы

Основные подсистемы ядра:

1. Управление процессами

  • Планировщик - распределяет процессорное время
  • Управление памятью - виртуальная память, подкачка
  • Межпроцессное взаимодействие (IPC) - сигналы, каналы, семафоры

2. Файловая подсистема

  • VFS (Virtual File System) - абстрактный слой над разными ФС
  • Буферный кэш - кэширование блоков диска в памяти
  • Драйверы файловых систем (ext4, XFS, etc.)

3. Подсистема ввода-вывода

  • Драйверы устройств - взаимодействие с железом
  • Блочные устройства (диски) и символьные (терминалы)
  • Сетевой стек - TCP/IP, сокеты

Пространства выполнения

Система разделена на два пространства:

Kernel Space (пространство ядра)

  • Полный доступ к железу
  • Выполнение системных вызовов
  • Обработка прерываний
  • Управление ресурсами

User Space (пользовательское пространство)

  • Ограниченные привилегии
  • Доступ к системе только через системные вызовы
  • Изоляция процессов друг от друга

Системные вызовы - интерфейс к ядру

Основные группы системных вызовов:

// Управление процессами
fork() // создание процесса
exec() // запуск программы
wait() // ожидание завершения процесса

// Файловые операции
open() // открытие файла
read() // чтение
write() // запись
close() // закрытие

// Управление памятью
mmap() // отображение файла в память
brk() // изменение размера кучи

Файловая система как основа

Единая иерархия:

  • Корневой каталог / - начало всего
  • Все устройства в /dev/
  • Системная информация в /proc/ и /sys/
  • Временные файлы в /tmp/

Каждый файл характеризуется:

  • inode - метаинформация (права, размер, время)
  • Путь - местоположение в иерархии
  • Тип - обычный файл, каталог, устройство, ссылка

Управление процессами

Каждый процесс имеет:

  • PID (Process ID) - уникальный идентификатор
  • Родительский процесс - все процессы образуют дерево
  • Виртуальное адресное пространство - изолированная память
  • Дескрипторы файлов - открытые файлы и устройства

Состояния процесса:

  • Running (выполняется)
  • Ready (готов к выполнению)
  • Blocked (ждет ресурс)
  • Zombie (завершился, но не убран родителем)

Ключевые особенности архитектуры

Многозадачность

  • Вытесняющая многозадачность - ядро принудительно переключает процессы
  • Планировщик распределяет время между процессами справедливо

Многопользовательность

  • Пользователи и группы - система прав доступа
  • root - суперпользователь с полными правами
  • Изоляция данных и процессов разных пользователей

Переносимость

  • POSIX-совместимость - стандартизированный интерфейс
  • Четкое разделение аппаратно-зависимого и независимого кода
  • Поддержка различных архитектур процессоров

1. Что происходит при выполнении системного вызова

Системный вызов - это контролируемый переход из пользовательского режима в режим ядра.

Пошаговый процесс

Шаг 1: Инициация вызова

// Пользовательская программа вызывает:
int fd = open("/tmp/file.txt", O_RDONLY);

Шаг 2: Переход в библиотеку

  • Библиотека libc содержит wrapper-функции
  • open() в libc подготавливает параметры и вызывает соответствующий системный вызов

Шаг 3: Программное прерывание

# На x86-64 используется инструкция syscall
mov $2, %rax # номер системного вызова open
mov $filename, %rdi # первый аргумент
mov $flags, %rsi # второй аргумент
syscall # переход в kernel mode

Шаг 4: Переключение контекста

  • Процессор переключается в привилегированный режим (ring 0)
  • Сохраняется состояние пользовательского процесса
  • Переключается на стек ядра
  • Включается обработчик системных вызовов в ядре

Шаг 5: Выполнение в ядре

// В ядре выполняется примерно такой код:
asmlinkage long sys_open(const char __user *filename, int flags, umode_t mode) {
// Проверка прав доступа
// Поиск файла в файловой системе
// Создание файлового дескриптора
// Обновление таблицы открытых файлов процесса
return fd; // возвращаем дескриптор файла
}

Шаг 6: Возврат в пользовательский режим

  • Результат помещается в регистр %rax
  • Восстанавливается пользовательский контекст
  • Переключение обратно в непривилегированный режим (ring 3)

Важные моменты

  • Изоляция: пользовательский код не может напрямую обращаться к ядру
  • Проверка прав: ядро проверяет, имеет ли процесс право на операцию
  • Атомарность: системный вызов выполняется целиком или не выполняется вообще

2. Как обеспечивается безопасность между процессами

UNIX использует многоуровневую систему изоляции и контроля доступа.

Изоляция памяти

Виртуальная память:

Процесс A          Процесс B
┌─────────────┐ ┌─────────────┐
│ 0x00001000 │ │ 0x00001000 │ <- Одинаковые адреса!
│ 0x00002000 │ │ 0x00002000 │
│ 0x00003000 │ │ 0x00003000 │
└─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────────────────────────┐
│ Физическая память │
│ 0x10001000 │ 0x20001000 │ <- Разные физические адреса
└─────────────────────────────────┘
  • Каждый процесс видит свое виртуальное адресное пространство
  • MMU (Memory Management Unit) транслирует виртуальные адреса в физические
  • Процесс не может обратиться к памяти другого процесса

Привилегии пользователей

Модель UID/GID:

# Каждый процесс выполняется от имени пользователя
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
john 1234 0.1 0.8 12345 6789 pts/0 S 14:30 0:00 bash
mary 5678 0.2 1.2 23456 9876 pts/1 S 14:35 0:01 python

Проверка доступа к файлам:

// При каждом обращении к файлу ядро проверяет:
if (current_user_id == file_owner_id) {
// Проверяем права владельца (rwx)
} else if (current_group_id == file_group_id) {
// Проверяем права группы (rwx)
} else {
// Проверяем права остальных (rwx)
}

Изоляция файловых дескрипторов

Каждый процесс имеет свою таблицу дескрипторов:

// Процесс A
fd[0] -> stdin
fd[1] -> stdout
fd[2] -> stderr
fd[3] -> /home/user/file1.txt

// Процесс B
fd[0] -> stdin
fd[1] -> stdout
fd[2] -> stderr
fd[3] -> /tmp/another_file.txt // Совершенно другой файл!

Системы контроля доступа

Традиционные UNIX-права:

$ ls -l /etc/passwd
-rw-r--r-- 1 root root 2847 Jan 15 10:30 /etc/passwd
# rwx для owner, r-x для group, r-- для others

Современные расширения:

  • SELinux: принудительный контроль доступа (MAC)
  • AppArmor: профили безопасности для приложений
  • Capabilities: детальные привилегии вместо root/user

Изоляция процессов

Пространства имен (namespaces):

# PID namespace - изоляция идентификаторов процессов
# Network namespace - изоляция сетевых интерфейсов
# Mount namespace - изоляция файловых систем
# User namespace - изоляция пользователей и групп

3. Почему "все есть файл" - важный принцип

Этот принцип создает единообразный интерфейс для работы с различными ресурсами системы.

Унификация интерфейса

Одни и те же операции для разных объектов:

// Работа с обычным файлом
int fd = open("/home/user/data.txt", O_RDONLY);
read(fd, buffer, size);
close(fd);

// Работа с устройством (например, последовательный порт)
int fd = open("/dev/ttyS0", O_RDWR);
read(fd, buffer, size); // читаем с порта
close(fd);

// Работа с сетевым соединением
int fd = socket(AF_INET, SOCK_STREAM, 0);
read(fd, buffer, size); // читаем из сокета
close(fd);

Типы "файлов" в UNIX

Обычные файлы:

$ ls -l /home/user/
-rw-r--r-- 1 user user 1024 Jan 15 10:00 document.txt

Каталоги:

drwxr-xr-x 2 user user 4096 Jan 15 10:00 Documents/

Символьные устройства (посимвольный ввод-вывод):

$ ls -l /dev/tty*
crw-rw-rw- 1 root tty 5, 0 Jan 15 10:00 /dev/tty
# c = character device

Блочные устройства (блочный ввод-вывод):

$ ls -l /dev/sd*
brw-rw---- 1 root disk 8, 0 Jan 15 10:00 /dev/sda
# b = block device

Именованные каналы (FIFO):

$ mkfifo /tmp/mypipe
$ ls -l /tmp/mypipe
prw-r--r-- 1 user user 0 Jan 15 10:00 /tmp/mypipe
# p = pipe

Символические ссылки:

lrwxrwxrwx 1 root root 7 Jan 15 10:00 /bin/sh -> /bin/bash
# l = link

Сокеты:

srwxr-xr-x 1 user user 0 Jan 15 10:00 /tmp/socket
# s = socket

Виртуальные файловые системы

Системная информация через /proc:

$ cat /proc/cpuinfo     # информация о процессоре
$ cat /proc/meminfo # информация о памяти
$ cat /proc/1234/status # информация о процессе 1234

Конфигурация ядра через /sys:

$ cat /sys/class/net/eth0/address  # MAC-адрес сетевого интерфейса
$ echo 1 > /sys/class/leds/led0/brightness # включить светодиод

Преимущества принципа

1. Простота программирования

// Одна функция для чтения из любого источника
ssize_t universal_read(int fd, void *buf, size_t count) {
return read(fd, buf, count); // работает для всех типов "файлов"
}

2. Композиция и конвейеры

# Можно соединять любые источники и приемники данных
cat /proc/cpuinfo | grep processor | wc -l
# файл -> утилита -> утилита -> утилита

3. Прозрачность и отладка

# Можно легко исследовать состояние системы
$ ls /proc/*/fd/ # посмотреть открытые файлы всех процессов
$ cat /sys/kernel/debug/tracing/trace # трассировка ядра

4. Гибкость

  • Новые типы ресурсов легко интегрируются в существующую модель
  • Утилиты работают с новыми типами "файлов" без изменений
  • Система администрирования становится более единообразной

Ограничения принципа

Не все операции подходят:

  • Некоторые устройства требуют специальных ioctl() вызовов
  • Сетевые операции (bind, listen, accept) не вписываются в read/write модель
  • Управление процессами требует отдельных системных вызовов

Тем не менее, принцип "все есть файл" остается одной из самых элегантных и мощных идей UNIX, обеспечивающей простоту, единообразие и гибкость системы.