Глубокая диагностика блочного слоя: blktrace и biolatency

Рейтинг: 61% · 6 голосов
Подробный курс по диагностике и производительности Linux для админов и DevOps: с чего начать, методология USE, логи (journalctl, dmesg), процессы, CPU, память и OOM, диск и IO (iostat), сеть (ss, tcpdump), системные вызовы и strace, ltrace, perf, флеймграфы, ftrace, eBPF и bpftrace, lsof, мониторинг. Разбор каждого инструмента с чтением вывода.
Ответить
Аватара пользователя
Dmitry_SRE
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Глубокая диагностика блочного слоя: blktrace и biolatency

Сообщение Dmitry_SRE »

Оглавление курса (47)
  1. С чего начать диагностику Linux: методология вместо паники
  2. Первые 60 секунд: экспресс-диагностика нагруженного сервера
  3. Методологии диагностики: USE, RED и здравый смысл
  4. Логи systemd через journalctl: где искать причину
  5. Где лежат логи Linux: /var/log, dmesg и rsyslog
  6. Источники правды: load average, /proc и /sys
  7. Процессы Linux: ps, pstree и состояния процессов
  8. Интерактивный мониторинг: top и htop
  9. Метрики процессов во времени: pidstat
  10. Приоритеты и ограничения: nice, ionice, cgroups
  11. Сигналы и зависшие процессы: kill, и что делать с D-state
  12. Загрузка CPU: user, system, iowait, steal и контекст-свитчи
  13. Диагностика CPU по ядрам: vmstat и mpstat
  14. Профилирование CPU: perf top и поиск пожирателя
  15. Частоты, троттлинг и NUMA: turbostat и numastat
  16. Память Linux: RSS, VSZ, page cache и миф о нехватке памяти
  17. Сколько памяти занято: free, /proc/meminfo и vmstat
  18. Поиск утечек и пожирателей памяти: smem, pmap, smaps
  19. Swap и подкачка: swapon, swappiness, когда своп - это боль
  20. OOM killer: кто и за что убил процесс
  21. Память ядра и slab: slabtop и куда уходит RAM
  22. Подсистема ввода-вывода: путь запроса от приложения до диска
  23. Диагностика диска: iostat и чтение await, %util
  24. Кто грузит диск: iotop и атрибуция io процессам
  25. Файловые системы: df, du, иноды и куда делось место
  26. Глубокая диагностика блочного слоя: blktrace и biolatency (вы здесь)
  27. Кеш страниц и грязные данные: dirty pages, fsync, drop_caches
  28. Сетевой стек Linux: путь пакета и где возникают задержки
  29. Сокеты и соединения: ss и netstat
  30. Захват трафика: tcpdump для диагностики сети
  31. Задержки и потери в сети: ping, mtr, диагностика латентности
  32. Сеть как источник проблем: nftables, conntrack, дропы
  33. Что такое системный вызов и зачем его трассировать
  34. strace: трассировка системных вызовов на практике
  35. strace в бою: почему программа висит, падает или тормозит
  36. ltrace: трассировка вызовов библиотек
  37. Накладные расходы трассировки и безопасные альтернативы
  38. perf: универсальный профайлер Linux
  39. Флеймграфы: визуализация профиля производительности
  40. Трассировка ядра: ftrace и trace-cmd
  41. eBPF: революция в наблюдаемости Linux
  42. Инструменты eBPF на практике: BCC и bpftrace
  43. lsof: открытые файлы, дескрипторы, кто держит файл и порт
  44. Разбор кейса: приложение тормозит - пошаговая диагностика
  45. Непрерывный мониторинг: sar, node_exporter, Grafana, Zabbix
  46. Диагностика в контейнерах: cgroups, лимиты, Docker и Kubernetes
  47. Карта инструментов и сквозной разбор инцидента производительности
Бывает так: iostat показывает, что диск загружен процентов на тридцать, await вроде в норме, а приложение раз в пять минут намертво подвисает на чтении. Средние цифры врут - они размазывают редкий всплеск латентности по всей секунде. Когда iostat и vmstat уже не отвечают на вопрос "где конкретно теряется время на одном запросе", пора спускаться на уровень ниже - в блочный слой Linux. В этом уроке разберём два подхода: классический blktrace и современный biolatency на eBPF. Первый показывает жизнь каждого запроса по стадиям, второй - быстро рисует распределение задержек почти без накладных расходов. По состоянию на 2026 год именно eBPF-инструменты стали инструментом первого выбора на проде, а blktrace остался для разового глубокого вскрытия. Разберём оба и научимся выбирать.

Что такое блочный слой Linux и почему iostat мало

Когда программа делает read() или write() к файлу, запрос проходит длинный путь: файловая система -> page cache -> block layer -> драйвер -> само устройство. Блочный слой linux (block layer) - это прослойка ядра, которая принимает запросы ввода-вывода, складывает их в очередь, при необходимости объединяет соседние (merge), сортирует планировщиком и отдаёт драйверу. С ядра 5.0 в строю только multi-queue-планировщики: none (он же по умолчанию для NVMe), mq-deadline и bfq; старый single-queue cfq/deadline удалён. Посмотреть текущий можно так:

Код: Выделить всё

cat /sys/block/sda/queue/scheduler
# [mq-deadline] none bfq   - в скобках активный
Именно в этом слое запрос может застрять в очереди, не дождавшись свободной структуры request или пропускной способности устройства.

iostat смотрит на этот слой сверху и выдаёт агрегаты: средний await (с util-linux/sysstat последних лет это r_await и w_await отдельно для чтения и записи), утилизацию, IOPS. Это как средняя температура по больнице. А нам для расследования редких всплесков нужна детализация по каждому запросу: сколько он простоял в очереди ядра, сколько ехал до устройства, сколько устройство его реально обслуживало. Вот тут и нужен block layer trace.

Ключевая идея: задержку io в Linux можно разложить на стадии. Запрос проходит через события (action), которые ядро штампует временными метками:
  • Q (Queue) - запрос только что попал в блочный слой;
  • G (Get request) - под него выделена структура request;
  • I (Insert) - вставлен в очередь планировщика;
  • D (Dispatch) - отправлен драйверу и устройству;
  • C (Complete) - устройство отчиталось, что закончило.
Бывают и другие буквы: M (merge, запрос слили с соседним), P/U (plug/unplug - ядро придерживает и потом разом отпускает пачку запросов), X (split, большой запрос разбили). Из основных меток получаются три важнейших интервала. Q2D - время от постановки до отправки на устройство (сколько провисел в ядре, очереди и планировщике). D2C - время от отправки до завершения (это и есть задержка самого устройства плюс драйвер). И Q2C - полное время запроса, оно равно Q2D + D2C. Простое правило: если большой Q2D - тормозит ядро или планировщик (очередь забита, нехватка request-ов, плохой merge); если большой D2C - тормозит железо, контроллер или насыщена шина.

Изображение

Практика: blktrace, blkparse и btt по шагам

Сначала классика. blktrace собирает сырые события через debugfs, blkparse превращает их в читаемый вид, btt считает статистику по стадиям. Все три лежат в одном пакете blktrace (Debian/Ubuntu - apt install blktrace, RHEL/Fedora - dnf install blktrace; в Astra Linux и RED OS пакет тоже называется blktrace). Нужен смонтированный debugfs (обычно уже есть в /sys/kernel/debug).

Запускаем трассировку устройства (не раздела, а целого диска, например /dev/sda) на несколько секунд под нагрузкой:

Код: Выделить всё

sudo blktrace -d /dev/sda -w 10 -o sda
# -w 10  - писать 10 секунд
# -o sda - префикс файлов: появятся sda.blktrace.0, sda.blktrace.1 ...
Есть и шорткат btrace (= blktrace + blkparse в один поток на экран): sudo btrace /dev/sda. Теперь посмотрим события в человекочитаемом виде:

Код: Выделить всё

blkparse -i sda -o sda.parsed | head
  8,0    1     1   0.000000000  3421  Q   R 12345678 + 8 [mysqld]
  8,0    1     2   0.000001500  3421  G   R 12345678 + 8 [mysqld]
  8,0    1     3   0.000004200  3421  D   R 12345678 + 8 [mysqld]
  8,0    1     4   0.000231000     0  C   R 12345678 + 8 [0]
Как читать строку по колонкам слева направо: 8,0 - major,minor устройства; затем номер CPU; порядковый номер записи; временная метка в секундах от старта трассировки; PID; буква действия (Q/G/D/C - те самые стадии); R или W (чтение/запись); стартовый сектор; + 8 - длина в секторах (8 секторов по 512 байт = 4 КБ); в скобках имя процесса. Видишь? Один и тот же запрос (сектор 12345678) прошёл Q -> G -> D почти мгновенно, а от D до C прошло 0.000231 - 0.0000042, то есть около 227 микросекунд. Это и есть D2C, время устройства. Для NVMe это многовато (там норма десятки мкс), для обычного SATA SSD - вполне ок.

Руками вычитать метки из тысяч строк нереально, поэтому статистику считает btt (берёт бинарь, который сначала собирают через blkparse -d):

Код: Выделить всё

blkparse -i sda -d sda.bin >/dev/null
btt -i sda.bin
==================== All Devices ====================
            ALL           MIN           AVG           MAX           N
--------------- ------------- ------------- ------------- -----------
Q2Q             0.000000512   0.000041   0.004821      24117
Q2D             0.000000821   0.000009   0.001204      24118
D2C             0.000031002   0.000198   0.038122      24118
Q2C             0.000033140   0.000208   0.038901      24118
Здесь время в секундах. Смотрим колонку MAX - вот они, выбросы. Средний D2C 198 мкс - нормально, но MAX 38 миллисекунд (0.038122) - это и есть тот самый всплеск, который губит latency. Раз он сидит в D2C, а Q2D мал - ядро ни при чём, тормозит само устройство (диск задумался, сборка мусора/GC на SSD, проблема контроллера, перегретый NVMe в троттлинге). Если бы раздулся Q2D - копали бы планировщик и глубину очереди (nr_requests). Q2Q - интервал между приходом запросов, по нему видно равномерность нагрузки. У btt есть полезные ключи: -l file выгрузит пер-IO задержки D2C, -z file - пер-IO Q2D, а -A и блок "DEV" - разбивку по устройствам. Именно сюда (в D2C-выгрузку) и стоит смотреть, когда ищешь конкретные медленные запросы.

Современная альтернатива: biolatency и biosnoop на eBPF (актуально на 2026)

blktrace пишет на диск гигабайты сырья и сам создаёт нагрузку. На проде под пиком это иногда неприемлемо. Тут выручает biolatency - инструмент на eBPF, который считает гистограмму прямо в ядре в BPF-карте и почти не грузит систему. Накладные расходы пренебрежимы при разумном IOPS (ориентир < 10k IOPS на устройство); при очень высоком IOPS overhead стоит замерить заранее.

Важный нюанс 2026 года про экосистему. Исторически эти инструменты ставились из пакета bpfcc-tools (Debian/Ubuntu) или bcc-tools (RHEL/Fedora) - это Python-обёртки поверх bcc, и команда называется с суффиксом, например biolatency-bpfcc. Сейчас на проде предпочитают libbpf-tools (CO-RE/BTF): те же biolatency и biosnoop, но скомпилированные в маленький C-бинарь, который "compile once, run everywhere" - не тащит за собой Python, LLVM и kernel-headers и не пересобирает BPF на каждом хосте при запуске (память на порядок меньше, старт мгновенный). Если в дистрибутиве есть пакет libbpf-tools - бери его. Для интерактивного ковыряния удобен bpftrace: те же инструменты идут как biolatency.bt и biosnoop.bt. Для всего этого нужно ядро с eBPF (практически любое современное, 5.x и выше; для CO-RE - ядро с BTF, то есть CONFIG_DEBUG_INFO_BTF=y, что есть в свежих Ubuntu, RHEL 9+, Astra и RED OS на новых ядрах). Запускается от root.

biolatency показывает не средние, а полное распределение io латентности linux в виде гистограммы степеней двойки:

Код: Выделить всё

sudo biolatency 10 1
Tracing block device I/O... Hit Ctrl-C to end.

     usecs               : count     distribution
       128 -> 255        : 3210     |****************************************|
       256 -> 511        : 1840     |**********************                  |
       512 -> 1023       : 210      |**                                      |
      1024 -> 2047       : 12       |                                        |
      8192 -> 16383      : 4        |                                        |
Аргументы "10 1" - интервал 10 секунд, 1 раз. Читается так: первые две колонки - диапазон задержки (запись "128 -> 255" значит "от 128 до 255 мкс"); count - сколько запросов попало в этот диапазон; справа ASCII-столбик, нормированный на самый частый диапазон. Главное, что видно сразу: основная масса io укладывается в 128-511 мкс - здоровое ядро гистограммы. А вот те 4 запроса в диапазоне 8-16 мс - редкие выбросы, ради которых всё и затевалось. На средних в iostat они растворились бы бесследно. Если на гистограмме два горба (один быстрый, другой медленный) - это классическая бимодальность: например, попадания в кэш устройства против промахов.

Важная тонкость, которую часто путают: по умолчанию biolatency меряет латентность от выдачи запроса устройству до завершения, то есть фактически это D2C (время устройства), а не полный путь запроса. Чтобы учесть и время в очереди ОС (получится Q2C-подобная картина), добавь флаг -Q. Другие полезные флаги: -m - гистограмма в миллисекундах вместо мкс; -D - отдельная гистограмма на каждое устройство; -F - разбивка по флагам io (отдельно чтение, запись, sync, metadata - очень помогает понять, кто медленный: чтения или флаши журнала).

Когда нашёл, что выбросы есть, но не знаешь, КТО их создаёт, берёшь biosnoop - он логирует каждый запрос отдельной строкой:

Код: Выделить всё

sudo biosnoop
TIME(s)     COMM           PID    DISK    T  SECTOR     BYTES  LAT(ms)
0.000000    mysqld         3421   sda     R  12345678   4096      0.23
0.413200    jbd2/sda1-8    289    sda     W  98765432   8192     14.81
0.413500    backup.sh      7711   sda     R  55512300   131072    9.40
По колонкам: TIME - время от старта в секундах; COMM и PID - процесс и его pid (кто инициировал io); DISK - устройство; T - тип (R чтение, W запись); SECTOR - сектор; BYTES - размер запроса; LAT(ms) - задержка в миллисекундах. Видно сразу: процесс backup.sh ломится большими чтениями по 128 КБ с задержкой под 9 мс и забивает диск, из-за чего страдает mysqld. Вот тебе виновник всплеска по имени и pid - то, чего blktrace без доп. возни не даёт так наглядно. У biosnoop тоже есть флаги: -Q добавит колонку QUE(ms) - сколько запрос провёл в очереди ОС (то самое Q2D!) отдельно от времени устройства; это позволяет одной командой разделить вину между ядром и железом.

Типичные грабли и заблуждения
  • Трассируют раздел (/dev/sda1) вместо всего устройства (/dev/sda). blktrace вешается на устройство целиком - указывай диск, иначе часть событий мимо.
  • Забывают, что blktrace сам создаёт io, записывая трейс. Пиши трейс на ДРУГОЙ диск или направь в tmpfs/в сеть (blktrace -d ... -o - | blkparse -i -), не на тот диск, что исследуешь.
  • Путают единицы. В выводе btt - СЕКУНДЫ, в biolatency по умолчанию - МИКРОсекунды, в biosnoop - МИЛЛИсекунды. 0.038 секунды это 38 мс, а не 38 мкс. Перепутаешь - сделаешь неверный вывод.
  • Думают, что гистограмма biolatency - это полный путь запроса. По умолчанию это только время устройства (D2C). Хочешь учесть очередь ОС - флаг -Q.
  • Смотрят только на AVG и пропускают MAX и хвост гистограммы. Расследование всплесков - это всегда про хвост распределения (p99/p999), а не про среднее.
  • Ждут от biosnoop точного виновника. Поля COMM/PID для асинхронного io обманчивы: флаш страниц делает поток ядра (kworker), коммит журнала ext4 - jbd2, fsync чужой записи может прилететь от kswapd. Это подсказка, а не приговор - сверяйся с типом io и шаблоном доступа.
  • Запускают Python-bcc (biolatency-bpfcc) на проде и удивляются паузе на старте и аппетиту к памяти. На 2026 для прода правильнее libbpf-tools (CO-RE): тот же результат без LLVM и заголовков ядра.
Мини-лаба: повтори руками прямо сейчас
  • Поставь пакеты: blktrace и (libbpf-tools либо bpfcc-tools/bcc-tools). Проверь имя команды: biolatency или biolatency-bpfcc.
  • В одном терминале создай нагрузку на тестовый диск:

    Код: Выделить всё

    fio --name=t --filename=/tmp/testfile --rw=randread --bs=4k --size=512M --runtime=20 --time_based
  • Во втором сними гистограмму:

    Код: Выделить всё

    sudo biolatency -D 5 3
    - посмотри, в какие диапазоны попадает основная масса и есть ли хвост; затем повтори с -Q и сравни, насколько выросли цифры за счёт очереди.
  • Сними пер-запросный лог:

    Код: Выделить всё

    sudo biosnoop -Q
    на пару секунд, найди строки с самым большим LAT, посмотри QUE против LAT и какой процесс их породил.
  • Для контраста сними классику:

    Код: Выделить всё

    sudo blktrace -d /dev/sdX -w 5 -o lab
    , затем

    Код: Выделить всё

    blkparse -i lab -d lab.bin >/dev/null && btt -i lab.bin
    . Сравни Q2D и D2C в колонке MAX - реши, кто виноват: ядро или устройство.
Контрольные вопросы
  • Чем D2C отличается от Q2D и о чём говорит большое значение каждого из них?
  • В каком масштабе времени выводит цифры btt, а в каком biolatency по умолчанию? Сколько микросекунд в строке "8192 -> 16383"?
  • Что именно меряет biolatency без флагов и какой флаг добавляет время очереди ОС?
  • Почему на проде в 2026 предпочитают libbpf-tools вместо Python-bcc и чем eBPF безопаснее blktrace?
  • Каким инструментом и по какому полю ты найдёшь процесс, создающий медленные io, и почему этому полю нельзя верить вслепую?
Что запомнить

Когда iostat показывает "вроде норм", а подвисания есть - спускайся в блочный слой. Раскладывай задержку запроса на стадии Q -> D -> C: Q2D это ядро, очередь и планировщик, D2C это устройство и драйвер. Для разового глубокого разбора бери blktrace + blkparse + btt (секунды, статистика по стадиям, выгрузка пер-IO через -l/-z). Для прода и поиска редких выбросов почти без overhead - biolatency (гистограмма, по умолчанию мкс и время устройства, -Q добавляет очередь) и biosnoop (пер-запросный лог, мс, плюс имя и pid виновника и колонка очереди по -Q). На 2026 ставь их версию libbpf-tools (CO-RE/BTF), а не тяжёлый Python-bcc. И главное правило расследования латентности: среднее врёт - смотри на хвост распределения.
👍3 ❤️1 🔥 😄 🤔3
Аватара пользователя
Stephenj
Сообщения: 1
Зарегистрирован: 27 май 2026, 13:49

Re: Глубокая диагностика блочного слоя: blktrace и biolatency

Сообщение Stephenj »

Блин, вот про путаницу в единицах прям в точку - я неделю назад читал btt и думал что у меня 38 МИКРОсекунд максимум, радовался. А там миллисекунды оказались. И отдельное спасибо за -Q у biolatency, я реально думал что гистограмма это весь путь запроса.
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
matsui55
Сообщения: 1
Зарегистрирован: 29 май 2026, 21:19

Re: Глубокая диагностика блочного слоя: blktrace и biolatency

Сообщение matsui55 »

Подскажите, а на старом ядре 4.x libbpf-tools с CO-RE заведётся или там BTF нет? У нас часть парка на RHEL 8, biolatency-bpfcc работает, но хочется лёгкую версию без питона на прод. blktrace везде ок, но overhead на пике пугает.
👍 ❤️1 🔥 😄 🤔2
Ответить
← Предыдущая глава
Файловые системы: df, du, иноды и куда делось место
Следующая глава →
Кеш страниц и грязные данные: dirty pages, fsync, drop_caches

Все главы курса «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Поделиться темой: ✈ Telegram VK
Похожие запросы: ss как посмотреть открытые сокеты и соединенияstrace почему программа висит и тормозитЛоги и диагностика macOSкак посмотреть процессы в linux и убить зависшийчто такое load average в linux и какое значение нормальноеТраблшутинг и обслуживание Mac

Вернуться в «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя