Карта операционной системы: слои от железа до вашей программы, ядро и его устройство, загрузка, прерывания, драйверы, время, права и песочницы — и чем Linux, macOS и Windows отличаются для разработчика.
Листайте стрелками ← →, свайпом или кнопками внизу. В схемах нажимайте кнопки и двигайте ползунки.
Выберите ресурс — увидите все три работы и что будет, если программа поведёт себя плохо.
Каждый слой прячет сложность нижнего и даёт верхнему стабильный интерфейс. Самый важный рубеж — между ядром и всем остальным: ниже него код работает с полными правами.
syscall.libSystem и Win32. Номера вызовов меняются между версиями, и Go там ходит в ядро через системные DLL и dylib.Нажмите на слой и переключите ОС.
Выберите архитектуру и нажмите действие.
От кнопки питания до приглашения входа — цепочка программ, каждая из которых загружает следующую и уступает ей место. Ядро стартует где-то в середине.
systemd-analyze, самые медленные сервисы — systemd-analyze blame, а то, что реально задержало загрузку, — systemd-analyze critical-chain.dmesg или journalctl -k -b.Выберите машину и идите по шагам.
Юнит сервиса содержит After=network.target. После перезагрузки сервера сервис падает: «не удалось разрешить имя db.internal». Ручной systemctl restart помогает. В чём дело?
network.target означает лишь, что запущены службы управления сетью; адреса и маршруты ещё могут не быть настроены. Ждать сеть — Wants=network-online.target вместе с After=network-online.target. Но надёжнее, чтобы сервис сам повторял подключение: сеть и база могут пропасть и после загрузки.Выберите событие и пройдите путь по шагам.
На каждое прерывание — вход в ядро, сохранение регистров, промах по кэшам. При миллионе пакетов в секунду ядро только и делало бы, что обрабатывало прерывания, а до приложения очередь не доходила бы. Это livelock.
top это столбцы hi и si, по ядрам — mpstat -P ALL 1, счётчики — /proc/interrupts и /proc/softirqs.Драйвер — код ядра, который знает регистры конкретного устройства. Наружу он отдаёт общий интерфейс: open, read, write, ioctl, mmap. Поэтому cat умеет читать и диск, и терминал, и счётчики ядра.
/dev создаёт udev, когда ядро находит устройство. Буква c или b в ls -l — символьное или блочное, пара чисел — номер драйвера и экземпляра.Выберите файл.
Всё, что программа хочет от ядра, проходит через одну дверь: номер вызова в регистр, аргументы в регистры, инструкция syscall (svc на ARM). Ядро проверяет аргументы и права, делает работу, возвращает число или код ошибки.
open() в Python, fs.open в Node, FileInputStream в Java вызывают libc, а та — ядро.strace, режимы процессора, процессы и сигналы — в презентации про процессы и потоки.
# Python: open("data.txt")
# → CPython: _io.FileIO → open() из libc
# → glibc: mov $257, %eax # номер openat
# syscall # кольцо 3 → кольцо 0
# → ядро: do_sys_openat2 → VFS → ext4
# ← rax = 3 (fd) или -ENOENT → errno
$ strace -e trace=openat python3 -c 'open("data.txt")'
openat(AT_FDCWD, "data.txt", O_RDONLY|O_CLOEXEC) = 3
# Сколько дверей: Linux x86-64 — около 370 вызовов,
# macOS — ~550 BSD-вызовов плюс Mach-ловушки,
# Windows — ~480 в ntoskrnl и ~1200 в win32k,
# и номера меняются от сборки к сборке
CLOCK_REALTIME) — секунды с 1970 года. Их переставляет NTP, администратор, гипервизор после паузы ВМ. Длительности ими мерить нельзя.CLOCK_MONOTONIC) только растут, на сне ноутбука стоят. CLOCK_BOOTTIME учитывает и сон.Запустите замер и устройте часам неприятность.
Сервис на Python пишет в метрики time.time() - start. Ночью на графике появилась задержка запроса −1,8 секунды. Что произошло?
time.time() считает секунды в UTC, часовые пояса и летнее время на неё не влияют. А вот шаг NTP после рассинхронизации — например, когда виртуальную машину сняли с паузы — двигает часы на секунды назад или вперёд. Длительности мерьте монотонными часами: time.monotonic() или perf_counter() в Python, performance.now() в JS, System.nanoTime() в Java. В Go time.Now() несёт монотонное показание, и time.Since безопасен.У процесса есть uid и список групп, у файла — владелец, группа и 12 бит режима. Ядро выбирает одну тройку бит — владельца, группы или остальных — и проверяет только её.
passwd пишет в /etc/shadow.x.Кликайте по битам режима, выбирайте пользователя и действие.
CAP_NET_BIND_SERVICE — порты ниже 1024, CAP_SYS_TIME — менять часы, CAP_SYS_ADMIN — почти всё остальное.Выберите, кто запускает процесс, и действие.
В вашем домашнем каталоге лежит файл -r--r--r-- root root report.txt. Вы не root. Получится ли rm report.txt?
w и x на каталог. rm лишь спросит подтверждение, -f убирает вопрос, а не проверку. Исключение — sticky bit (t) на каталоге, как у /tmp: там удалить чужой файл может только его владелец./proc и /sys — файловые системы без диска: файлы генерирует ядро при чтении. dmesg — кольцевой журнал ядра: OOM killer, ошибки дисков, сегфолты.strace останавливает процесс на каждом вызове и замедляет его в разы. eBPF считает прямо в ядре: ~100 нс на событие, можно на проде.Выберите вопрос и запустите однострочник bpftrace.
Рантаймы прячут различия, но не все. Код, который работает на ноутбуке, падает в CI или у коллеги на Windows как раз в этих местах.
Выберите тему.
В проекте на Node есть файл config.js, а в коде — require('./Config'). На Mac всё работает, в CI на Linux — Cannot find module './Config'. Почему?
Config.js находит config.js. Linux ищет точное совпадение. Отсюда же проблемы git с файлами, отличающимися только регистром. Ловите это заранее: линтер (import/no-unresolved с проверкой регистра), CaseSensitivePathsPlugin в webpack, сборка в Linux-контейнере.uname -a, lsmod, modinfo — какое ядро и какие модули загружены.systemd-analyze, journalctl -b, dmesg -T — загрузка и журнал ядра./proc/interrupts, mpstat — кто и на каком ядре прерывает.id, getpcaps, grep Cap /proc/PID/status — права и capabilities процесса.bpftrace и утилиты bcc: execsnoop, opensnoop, biolatency.sysctl, log show, dtruss. Windows: Process Monitor и Process Explorer.
uname -r; lsmod | head
systemd-analyze blame | head
systemd-analyze critical-chain myapp.service
journalctl -k -b -p warning
watch -n1 'grep -E "CPU|eth0|nvme" /proc/interrupts'
mpstat -P ALL 1 # %irq %soft по ядрам
ls -l /dev/null /dev/nvme0n1
cat /sys/class/net/eth0/mtu
id; stat -c '%A %U:%G %n' file
capsh --decode=$(awk '/CapEff/{print $2}' /proc/self/status)
grep Seccomp /proc/PID/status # 2 = фильтр
execsnoop-bpfcc # кто что запускает
bpftrace -e 'tracepoint:syscalls:sys_enter_openat
{ printf("%s %s\n", comm, str(args.filename)); }'
Эта презентация — обзор. Каждая подсистема ядра разобрана подробно в своей.
/proc, /sys, dmesg и eBPF.Ядро — это не программа, которая работает. Это программа, которая отвечает на события.