Перейти к основному содержимому

Подсистема ввода-вывода в ОС

Разработчику Архитектору Инженеру

Ввод-вывод - диск, сеть и устройства

Процессор считает миллиарды операций в секунду, а диск отвечает за миллисекунды, сеть — за RTT в миллисекундах и выше. ОС существует во многом затем, чтобы скрыть эту медлительность: не блокировать всю машину на ожидании диска, переиспользовать каналы, ставить запросы в очередь.

Без понимания I/O непонятны:

  • почему read() «висит»;
  • зачем буферизация и кэш страниц;
  • как SSD и NVMe менили планировщики;
  • связь прерываний с планированием CPU.

См. также: ядро — устройства, определение ОС, периферия в «Как работает компьютер».


Цель подсистемы I/O

ЗадачаКак решается
Единый интерфейсread/write/ioctl, сокеты, mmap
ЗащитаПриложение не пишет в порты диска напрямую
МультиплексированиеМного процессов — один контроллер
ЭффективностьDMA, очереди, асинхронный I/O
Обработка ошибокПовтор, таймаут, сообщение приложению

Слои — от приложения до устройства

┌─────────────────────────────────────┐
│ Приложение (cat, PostgreSQL, браузер) │
└─────────────────┬───────────────────┘
│ syscall: read, write, poll, io_uring
┌─────────────────▼───────────────────┐
│ Ядро: VFS (файлы), socket layer, │
│ block layer (диски), char devices │
└─────────────────┬───────────────────┘
│ вызовы драйвера
┌─────────────────▼───────────────────┐
│ Драйвер устройства (модуль ядра) │
└─────────────────┬───────────────────┘
│ MMIO, порты, DMA-дескрипторы
┌─────────────────▼───────────────────┐
│ Контроллер (AHCI, NVMe, NIC, USB) │
└─────────────────┬───────────────────┘

Физическое устройство

Драйвер — переводчик: «запиши блок LBA 42» → команды конкретному чипу. В Unix устройство часто видно как файл /dev/sda, /dev/tty, /dev/null — идея «всё есть файл» из 3.md.


Типы устройств

Символьные (character devices)

Поток байтов без фиксированных блоков: клавиатура, терминал, /dev/random, иногда принтер.

  • Доступ последовательный;
  • часто буфер в ядре невелик;
  • read может блокироваться до нажатия клавиши.

Блочные (block devices)

Данные адресуются блоками фиксированного размера (512 B, 4 KiB): HDD, SSD, разделы.

  • Ядро может кэшировать блоки в page cache;
  • файловая система строится поверх блочного слоя;
  • см. файловую систему Windows.

Сетевые

Логически сокет — другой API (send/recv), но та же идея: syscall → стек ядра → драйвер NIC.


Программный, прерыванийный I/O и DMA

Programmed I/O (PIO)

CPU сам крутит цикл: «есть данные?» → прочитать байт → повторить. Просто, но дорого для больших объёмов — процессор занят.

I/O через прерывания

Устройство сигнализирует контроллеру прерываний (IRQ): «пакет готов», «сектор записан». CPU прерывает текущий процесс, ядро вызывает обработчик прерывания (ISR) — короткий, быстрый; тяжёлую работу откладывает в bottom half / workqueue / поток ядра.

Связь с процессами: пока ваш процесс ждёт диск, он в состоянии Waiting; планировщик отдаёт CPU другим — см. 5114.

DMA (Direct Memory Access)

Контроллер DMA переносит данные между устройством и RAM без участия CPU в каждом байте. CPU настраивает дескриптор (адрес, длина), уходит заниматься другим; по завершении — прерывание.

Современные NVMe и сетевые карты активно используют DMA — иначе гигабитная сеть съела бы весь CPU.

Аналогия
PIO — вы сами носите коробки в грузовик. DMA — вы наняли грузчиков (контроллер), а сами пьёте кофе; когда грузчики закончили — звонят (прерывание).


Буферизация и кэш

Буфер в ядре

Двойная буферизация, кольцевые очереди — сглаживают разницу скоростей «производитель / потребитель».

Page cache (кэш страниц)

Чтение файла часто не идёт на диск: данные уже в RAM как страницы кэша. Запись может попасть в dirty pages и сброситься на диск позже (fsync, sync).

Это связывает I/O с виртуальной памятью и замещением страниц.

Spooling (спулинг)

Очередь заданий на медленное устройство (принтер): приложение «отдало» файл в spool, освободило себя; демон печати работает с принтером по одному. Разрушает условие deadlock «прямой захват принтера» — см. 5119.


Синхронный и асинхронный I/O

РежимПоведениеПример
Синхронныйread() блокирует поток до данныхПростой код
НеблокирующийO_NONBLOCK, poll/select/epollСерверы, event loop
Асинхронный (AIO)io_uring (Linux), IOCP (Windows)Высокая нагрузка на диск/сеть

Важно: «асинхронный» не значит «магически быстрее» — значит поток не ждёт в syscall; завершение приходит callback’ом или повторным read.


Планировщик ввода-вывода (диск)

Когда много процессов бьют в один диск, порядок запросов влияет на latency и throughput.

АлгоритмИдея
FCFSПо очереди
SSTFСначала ближайший сектор (головка диска) — starvation
SCAN / C-SCAN«Лифт» по цилиндрам
CFQ (старый Linux)Справедливость между процессами
mq-deadline, bfq, noneСовременные SSD/NVMe — другая физика

На SSD выигрывает случайный доступ; планировщик чаще упрощён (none на NVMe), упор на очередь глубины контроллера.

В справочнике Linux: /sys/block/<dev>/queue/scheduler — см. 51.md.


ioctl и управление устройством

Не всё укладывается в read/write. ioctl — расширенные команды: eject CD, настройка сетевого интерфейса, громкость, режим терминала.

Терминал: stty, raw/cooked mode — классический пример символьного устройства.


Ошибки и надёжность

  • EIO, ENOSPC, ETIMEDOUT — errno в POSIX.
  • Повтор при временных сбоях (сеть).
  • Журналирование ФС — целостность после обрыва питания — 411.
  • RAID, резервирование — уровень ниже ОС, но видно как одно устройство.

Пример — что происходит при cat file.txt

  1. Shell вызывает read() → syscall в ядро.
  2. VFS находит inode, page cache.
  3. Если страницы нет — page fault, чтение с диска через block layer.
  4. Драйвер ставит DMA-запрос; процесс блокируется (Waiting).
  5. Диск закончил → IRQ → драйвер будит процесс → данные в user buffer.
  6. Повтор до EOF; write() на stdout — тот же путь к терминалу/пайпу.

Связь с другими темами

  • Процесс ждёт I/O → не держит CPU → планирование.
  • Дисковый swap → подкачка.
  • Драйвер в kernel mode → сбой опасен в монолитном ядре → 3.md.
  • Блокировки на файлах → 5119.

Что запомнить

  1. I/O — слои и абстракции, не «магия драйвера».
  2. Прерывания + DMA освобождают CPU.
  3. Кэш делает повторное чтение файла быстрым.
  4. Планировщик диска всё ещё важен под смешанной нагрузкой; на NVMe — иная эра.
  5. Для серверов — epoll / io_uring / IOCP, не только синхронный read.

Чек-лист · Память · Замещение страниц


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).