раздел 32
Системное программирование: Zig, ассемблер, Verilog
Компьютерные системы по Брайанту и О'Халларону, но на Zig вместо C и с Verilog вместо учебного HCL. Сначала минимум Zig: целые любой ширины, пять видов указателей, раскладка структур, ошибки и аллокаторы, comptime и сборка. Дальше машинный уровень x86-64 и чтение дизассемблера своего кода, процессор Y86-64 проводами на Verilog с конвейером и рисками, производительность и CPE, иерархия кэшей, компоновка, процессы и сигналы, виртуальная память и свой аллокатор, системный ввод/вывод, сети с веб-сервером TINY и конкурентность с кэширующим прокси. Программа это байты, которые процессор читает по протоколу.
Начать разделРаздел открыт для всех. Войди, чтобы отмечать пройденные уроки, вести заметки и решать задачи в редакторе. войти
Уроки раздела
- Зачем Zig системщику и путь hello от текста до процесса
- Значения, типы и поток управления
- Указатели, срезы и массивы
- Структуры, перечисления, объединения и раскладка в памяти
- Ошибки, defer и аллокаторы
- comptime, сборка, тесты и C
- Биты и целые по-зиговски
- Плавающая точка на уровне битов
- От Zig к машинному коду
- Операнды, семейство mov и стек
- Арифметика: lea, сдвиги и полное произведение
- Флаги, переходы и cmov
- Циклы и switch на машинном уровне
- Процедуры и стековые кадры
- Массивы, структуры и выравнивание на машинном уровне
- Переполнение буфера и защиты от него
- Числа с плавающей точкой и SIMD
- inline-ассемблер и голое железо
- JIT: пишем машинный код x86-64
- Y86-64: система команд и кодирование
- Ассемблер Y86-64 на Zig
- Симулятор Y86-64 на Zig
- Вентили и комбинационная логика на Verilog
- Verilog: слова, мультиплексоры и ALU
- Verilog: такт, регистры и память
- SEQ: этапы fetch и decode
- SEQ: execute, memory, write back и новый PC
- Принципы конвейера: задержка и пропускная способность
- PIPE: риски, продвижение и остановы
- PIPE на Verilog и CPI
- Границы оптимизирующего компилятора и метрика CPE
- Инварианты цикла, вызовы и обращения к памяти
- Модель современного процессора
- Развёртывание циклов и параллелизм на уровне инструкций
- Пределы: регистры, ветвления и память
- Профилирование и бенчмарки
- Технологии хранения и локальность
- Организация кэша
- Симулятор кэша: cachelab на Zig
- Код, дружественный к кэшу
- Гора памяти
- Объектные файлы, ELF и символы
- Разрешение имён и статические библиотеки
- Перемещение и загрузка
- Динамическая компоновка, PIC, GOT и PLT
- Подмена функций и компоновка на практике
- Исключения и системные вызовы
- Процессы: fork, execve, waitpid
- Сигналы
- Ядро на своей машине: таймер, вытеснение и маска прерываний
- Нелокальные переходы и ошибки в Zig
- Оболочка с управлением заданиями: shlab на Zig
- Виртуальная адресация и страницы
- Виртуальная память как менеджер и как защита
- Трансляция адреса и TLB
- Отображение в память: mmap, fork, execve и копирование при записи
- Как устроен malloc: куча, блоки и неявный список
- Свой аллокатор: malloclab на Zig
- Сборка мусора и десять ошибок с памятью
- Unix I/O и короткие счёты
- Буферизованный ввод-вывод и метаданные файлов
- Общие файлы, переадресация и пайпы
- Клиент, сервер и устройство сети
- Сокеты: эхо-клиент и эхо-сервер
- HTTP и веб-контент
- Проект: веб-сервер TINY
- Проект: песочница как у раннера курса
- Сервер на процессах и мультиплексирование ввода-вывода
- Потоки
- Разделяемые переменные и семафоры
- Пул потоков и параллельные вычисления
- Потокобезопасность, гонки и взаимоблокировки
- Проект: async в Zig и кэширующий прокси
Темы и кластеры
Zig как инструмент системщика
- Зачем Zig системщику и путь hello от текста до процессаЯзык без скрытого потока управления и скрытых аллокаций. Путь hello.zig: токены, AST, ZIR, AIR, машинный код, ELF, загрузка, процесс. Три режима сборки.есть урок
- Значения, типы и поток управленияЦелые любой ширины, явные преобразования, четыре семейства арифметики (проверяемая, wrapping, saturating, с флагом переполнения), опционалы, switch с диапазонами и labeled switch.есть урок
- Ошибки, defer и аллокаторыНаборы ошибок, try, catch и errdefer, defer как RAII без деструкторов. Интерфейс std.mem.Allocator и его реализации: page, FixedBuffer, Arena, DebugAllocator с отчётом об утечках.есть урок
- Структуры, перечисления, объединения и раскладкаstruct с методами, enum с явным тегом, union(enum), packed struct как битовое поле, extern struct как C-раскладка, @sizeOf, @bitSizeOf, @alignOf и @offsetOf.есть урок
- Указатели, срезы и массивыПять видов указателей на одну память, выравнивание в типе, @ptrCast и @alignCast, asBytes и bytesAsValue, что проверяет ReleaseSafe и чего не проверяет ReleaseFast.есть урок
- comptime, сборка, тесты и Ccomptime и дженерики как функции над типами, @typeInfo, inline for. zig test, build.zig и модули, zig cc как кросс-компилятор, C через addTranslateC, export в обе стороны. Как устроен раннер курса.есть урок
Биты и числа, минимум
- Биты и целые по-зиговскиБыстрое напоминание про представление целых, но с целыми любой ширины: 4-битные усечения и 5-битное переполнение как буквальные тесты. @bitCast, endianness через readInt/writeInt, тип сдвига Log2Int, умножение на константу и деление на степень двойки глазами компилятора.есть урок
- Плавающая точка на уровне битовIEEE-754 через мини-формат и f16/f32/f64/f80/f128 в Zig. Округление до чётного, денормали, NaN. @bitCast между u32 и f32, операции над float на уровне битов: удвоение, деление пополам, преобразования в целое и обратно.есть урок
Машинный уровень: x86-64 и ассемблер
- Арифметика: lea, сдвиги и полное произведениеlea как калькулятор адреса и как умножение, унарные и бинарные операции, сдвиги с %cl, 128-битное умножение через пару %rdx:%rax, idiv с подготовкой делимого через cqto, идиома xor для обнуления.есть урок
- Операнды, семейство mov и стекОдиннадцать форм операнда и формула Imm + R[rb] + R[ri] * s, семейство mov без формы память-в-память, правило про imm32 со знаковым расширением и movabsq, movz против movs, push и pop как пара инструкций. Байты ModRM и SIB.есть урок
- От Zig к машинному кодуКак достать листинг через -femit-asm и objdump, шестнадцать регистров во всех ширинах и их роли по System V, суффиксы b/w/l/q, отличие Debug от ReleaseFast в байтах, префикс REX по битам. Первый шаг своего дизассемблера zt disasm.есть урок
- Массивы, структуры и выравнивание на машинном уровнеАдресная арифметика и вложенные массивы, смещения полей структуры, объединения, выравнивание и padding, @offsetOf и extern struct как способ проверить раскладку. Тегированные указатели: младшие биты выровненной ячейки под тег.есть урок
- Переполнение буфера и защитыЧто перезаписывается в кадре при выходе за границу массива, атака с подменой адреса возврата на своём бинарнике в песочнице, канарейка, ASLR, nop-салазки, NX. Как режимы сборки Zig меняют картину: проверка границ в ReleaseSafe и её отсутствие в ReleaseFast.есть урок
- Процедуры и стековые кадрыcall и ret, стек времени выполнения, шесть регистров аргументов и возврат в rax, callee-saved против caller-saved, локальные на стеке и в регистрах, рекурсия как обычный кадр, выравнивание стека на 16.есть урок
- Флаги, переходы и cmovФлаги CF, ZF, SF, OF, инструкции cmp и test, set по условию, кодирование переходов относительно счётчика, if в форме goto, условная передача данных cmov и цена ошибки предсказания ветвления с измерением.есть урок
- Циклы и switch на машинном уровнеdo-while, while через jump-to-middle и guarded-do, for и continue на машинном уровне, switch через таблицу переходов и во что Zig превращает switch с диапазонами и labeled switch.есть урок
- Числа с плавающей точкой и SIMDРегистры XMM и YMM, скалярные операции addsd и movsd, преобразования cvtsi2sd и cvttsd2si, float-константа в .rodata со смещением от rip, знак и модуль через xorps и andps, сравнение ucomisd и NaN через флаг чётности. Дальше книги: @Vector и AVX2, @reduce как дерево свёрток.есть урок
- inline-ассемблер и голое железоasm volatile в Zig: ограничения, clobbers, volatile. Системный вызов write без libc, функция целиком на ассемблере в .s, собранная через zig cc и вызванная из теста, callconv(.naked) и своя _start, минимальный бинарник без std. Врезка про aarch64.есть урок
- JIT в машинный код x86-64Десять уроков читали машинный код, теперь пишем его сами. Тегированные значения в одно слово, шаблон на каждый узел дерева, вызов примитива через movabs и call, переход с обратной заплаткой, хвостовой вызов как jmp, память от mmap с правами на исполнение и ограничение W^X.есть урок
Архитектура процессора: Y86-64 на Zig и Verilog
- Ассемблер Y86-64 на ZigЛексер, директивы .pos, .align и .quad, два прохода и таблица меток, энкодер, листинг как у yas. Программы sum, rsum, absSum с переходом и с cmov, пузырьковая сортировка, switchv через таблицу переходов.есть урок
- Принципы конвейераЗадержка и пропускная способность, конвейерные регистры, неравномерные этапы, накладные расходы регистров, глубокие конвейеры, обратная связь как проблема.есть урок
- Симулятор Y86-64 на ZigСимулятор уровня инструкций: fetch, decode, execute, memory, writeback и pc как шесть функций над одной структурой состояния, коды Stat, трасса в формате, который потом печатает и Verilog. Тонкости pushq %rsp и popq %rsp.есть урок
- SEQ: этапы fetch и decodeШесть этапов SEQ, таблицы вычислений для каждого класса инструкций, этапы fetch и decode на Verilog: need_regids, need_valC, srcA и srcB, dstE и dstM.есть урок
- SEQ: execute, memory, write back и новый PCExecute с ALU и Cnd, memory с mem_addr, mem_data, mem_read и mem_write, Stat, write back, new_pc. SEQ собран целиком, программы урока 21 идут на нём, трасса сверяется с Zig-симулятором. Добавляем iaddq.есть урок
- Verilog: вентили и комбинационная логикаЛогические вентили, модуль, wire и logic, assign, тестбенч с $display и $finish, запуск iverilog и vvp, lint через verilator. HCL из книги переводится на Verilog: eq, битовый мультиплексор, 64-битный компаратор.есть урок
- Verilog: слова, мультиплексоры и ALUСлова и шины, case как мультиплексор, always_comb, ALU Y86-64 с флагами ZF, SF и OF, принадлежность множеству через список меток в одном пункте case. Минимум и медиана трёх слов.есть урок
- Verilog: такт, регистры и памятьТактирование, always_ff по фронту, регистр, регистровый файл с двумя портами чтения и двумя записи, память с байтовой адресацией, дисциплина «читаем в такте, пишем по фронту», задержка и период такта, waveform в VCD.есть урок
- Y86-64: система команд и кодированиеСостояние, видимое программисту, полный список инструкций Y86-64 и их байтовое кодирование (icode и ifun, rA и rB, valC), чем учебная ISA отличается от x86-64, коды состояния AOK, HLT, ADR, INS. Кодируем руками, ищем битый байт, проектируем iaddq.есть урок
- PIPE на Verilog и CPIPIPE на Verilog: логика продвижения, stall и bubble, комбинации рисков, тесты на программах с известным числом тактов, CPI как единица плюс штрафы, сравнение стратегий предсказания, absSum на cmov против перехода.есть урок
- PIPE: риски, продвижение и остановыSEQ+ и перенос PC update, регистры F, D, E, M и W, предсказание PC, риски по данным, продвижение из пяти точек, риск загрузки и использования, ret и три пузырька, ошибочно предсказанный переход и два пузырька, исключения в конвейере.есть урок
Производительность программ
- Границы оптимизирующего компилятора и метрика CPEЧто компилятор не имеет права делать: наложение указателей и noalias, побочные эффекты, барьеры для встраивания. CPE и честное измерение: разогрев, минимум из серий, шум. combine1 до combine3.есть урок
- Инварианты цикла, вызовы и обращения к памятиВынос инвариантов из цикла, сокращение вызовов, накопление в регистре вместо памяти (combine4) и почему компилятор сам этого не делает. Многочлен в лоб и по Горнеру. CPE трёх исполнителей zl.есть урок
- Профилирование и бенчмаркиperf stat и perf record, callgrind, flame graph, микробенчмарки и их ловушки, закон Амдала как правило, куда смотреть. Многочлен и сумма ниже задержки сложения.есть урок
- Модель современного процессораСуперскалярный внеочередной процессор: функциональные блоки, таблица задержка, выпуск и ёмкость, переименование регистров, граф потока данных, критический путь как нижняя граница CPE. psum и его критический путь.есть урок
- Пределы: регистры, ветвления и памятьВытеснение регистров при большом k, предсказание ветвлений и cmov в горячем цикле, производительность загрузки и сохранения, зависимость запись/чтение, memset словами, @Vector и AVX2 как обход ограничения блока.есть урок
- Развёртывание циклов и параллелизм на уровне инструкцийРазвёртывание k×1, несколько аккумуляторов k×k, переупорядочение операций k×1a, ограничение пропускной способности. combine5 до combine7 и таблица CPE по всем версиям.есть урок
Иерархия памяти и кэши
- Гора памятиПрограмма гора памяти на Zig: пропускная способность чтения по размеру рабочего множества и шагу, размеры L1, L2 и L3 читаются с графика, предвыборка видна на срезе по шагу, x86-64 против Apple Silicon.есть урок
- Организация кэшаИерархия как цепочка кэшей, виды промахов, параметры S, E, B и m, разбор адреса на тег, индекс и смещение, прямое отображение, ассоциативный и полностью ассоциативный кэш, LRU, политики записи, иерархия Core i7 и Apple M-серии.есть урок
- Технологии хранения и локальностьSRAM, DRAM, SSD и диск в одной таблице с числами 2026 года, время доступа и ресурс записи SSD. Временная и пространственная локальность данных и инструкций, обход с шагом 1 против шага N, чтение локальности по коду.есть урок
- Код, дружественный к кэшуШесть порядков циклов умножения матриц и три класса промахов, блочное разбиение, транспонирование с минимумом промахов, структура массивов против массива структур, cache line и false sharing. cons-ячейки zl и кэш.есть урок
- Симулятор кэша: cachelab на ZigПарсер трассы обращений в формате valgrind, симулятор S, E, B с LRU, счётчики попаданий, промахов и вытеснений, сверка с эталонными трассами, генератор трасс из своей программы. Кэш встаёт между Y86 и памятью.есть урок
Компоновка
- Объектные файлы, ELF и символыДрайвер компиляции, статическая компоновка как разрешение имён плюс перемещение, перемещаемый ELF по секциям, Elf64_Sym и типы символов, псевдосекции ABS, UNDEF и COMMON, что Zig кладёт в какую секцию. zt readelf и zt nm.есть урок
- Перемещение и загрузкаЗаписи Elf64_Rela, R_X86_64_PC32 и R_X86_64_32 с расчётом руками, модели кода, исполняемый ELF по сегментам, заголовок программы, точка входа, образ процесса, _start и что Zig делает вместо libc. zt ld, второй шаг, и call rel32 в JIT zl.есть урок
- Подмена функций и компоновка на практикеПодмена на трёх уровнях: макросы и препроцессор, --wrap, LD_PRELOAD с dlsym RTLD_NEXT; трассировщик malloc для чужой программы; binutils на практике; Zig: musl против glibc, -fstrip, --gc-sections, кросс-линковка, zig cc, -lc. zt ltrace.есть урок
- Разрешение имён и статические библиотекиСильные и слабые определения, три правила и баги от них, статические библиотеки .a и алгоритм E, U, D, порядок в командной строке, циклические зависимости. В Zig: linkage weak и сборка .a в build.zig. zt ld, первый шаг.есть урок
- Динамическая компоновка, PIC, GOT и PLTРазделяемые библиотеки и ld-linux.so, dlopen и dlsym, позиционно-независимый код, GOT для данных, PLT и ленивое связывание для функций, -z now и RELRO. Своя .so на Zig из C и из Zig, LD_DEBUG=bindings. zt ldd.есть урок
Исключения, процессы, сигналы
- Исключения и системные вызовыЧетыре класса исключений, таблица исключений, режимы процессора, исключения x86-64, Linux syscall ABI, strace, сисколл руками через asm и через std.os.linux, что добавляют обёртки std.posix. trap в Y86 и zt strace.есть урок
- Нелокальные переходы и ошибки в Zigsetjmp и longjmp как исключения времени выполнения, sigsetjmp, чем это опасно; как Zig обходится без них: ошибки как значения, errdefer, @panic, свой обработчик паники, трассы стека; сравнение с исключениями C++.есть урок
- Процессы: fork, execve, waitpidЛогический поток управления и своё адресное пространство, переключение контекста, fork и графы процессов, execve, waitpid, зомби, std.process.Child, ловушка буферизации при fork. Контексты процессов Y86 и zbox run.есть урок
- Проект: оболочка с job controlРазбор командной строки, встроенные jobs, bg, fg и quit, фоновые задания, группы процессов и терминал, SIGINT, SIGTSTP, SIGCHLD и SIGCONT, пайпы и переадресация, проверка трассами как в shlab.есть урок
- Проект: ядро для Y86Таймер как устройство, прерывание по тактам, контекст в PCB, переключение двух процессов по кругу, сисколлы write, yield и exit, маска прерываний в обработчике и что ломается без неё, iret.есть урок
- СигналыОтправка и получение сигналов, группы процессов, обработчики, блокировка через sigprocmask, сигналы не копятся в очередь, шесть правил безопасного обработчика, гонка при регистрации задания и sigsuspend. zt prof и лимит времени в zbox.есть урок
Виртуальная память и аллокаторы
- Виртуальная адресация и страницыФизические и виртуальные адреса, адресные пространства, страницы и таблица страниц, попадание и сбой страницы, размещение по требованию, виртуальная память как полностью ассоциативный кэш DRAM над диском.есть урок
- Виртуальная память как менеджер и как защитаОдинаковое адресное пространство у всех процессов, разделяемые библиотеки на общих страницах, биты прав в PTE и mprotect, /proc/self/maps на Zig, vm_area_struct в Linux. zt pmap и лимит памяти в zbox.есть урок
- Сборка мусора и ошибки работы с памятьюДостижимость, Mark&Sweep, консервативный сборщик и Boehm GC; десять классических ошибок работы с памятью и что с каждой делает Zig в трёх режимах сборки, DebugAllocator, valgrind и ASan. Mark&Sweep для zl.есть урок
- Как устроен mallocТребования к аллокатору, throughput и utilization, внутренняя и внешняя фрагментация, неявный список с заголовком, выравнивание, first, next и best fit, разбиение, рост кучи, слияние с граничными тегами.есть урок
- Отображение в память, fork и execveРазделяемые и приватные объекты, копирование при записи, fork и execve через виртуальную память, mmap и munmap, копирование и правка файла через отображение. W^X для JIT zl и MMU для Y86.есть урок
- Проект: свой аллокаторstd.mem.Allocator и его vtable, три версии: неявный список, явный список, сегрегированные списки; замеры utilization и throughput на трассах, сравнение с аллокаторами std, jemalloc и mimalloc. Пул ячеек zl.есть урок
- Трансляция адресов и TLBMMU по шагам, физически адресованный кэш, TLB как кэш PTE, многоуровневые таблицы и их экономия, сквозной пример книги целиком, четыре уровня Core i7, огромные страницы и охват TLB.есть урок
Системный ввод/вывод
- Буферизованный ввод/вывод и метаданныеСвой RIO с внутренним буфером, цена системного вызова, std.Io.Reader и Writer с явным буфером и забытый flush, ошибка смешивания буферов, stat и fstat, обход каталога, копирование блоками. Захват вывода в zbox.есть урок
- Общие файлы, переадресация и пайпыТри таблицы ядра, fork и общая позиция в файле, dup2 и переадресация, конвейер из pipe, dup2 и execve, SIGPIPE и EPIPE, O_CLOEXEC, стандартный ввод/вывод против системного, сокеты как файлы.есть урок
- Unix I/O и короткие счётыФайл как последовательность байтов, open, read, write, lseek и close, правило наименьшего свободного дескриптора, откуда берутся короткие счёты, readn и writen на Zig, std.Io.File как обёртка. Устройства Y86.есть урок
Сети и веб-сервер
- Клиент, сервер и устройство сетиМодель клиент-сервер как транзакции, из чего сделана сеть: LAN, маршрутизаторы, инкапсуляция, IP-адреса в четырёх формах, DNS, соединение как пара сокетов, getaddrinfo и getnameinfo, hostinfo на Zig.есть урок
- Сокеты: эхо-клиент и эхо-серверsocket, connect, bind, listen, accept, структуры адресов, open_clientfd и open_listenfd на Zig, эхо-клиент и эхо-сервер, std.Io.net как обёртка, SO_REUSEADDR, что видно в ss и lsof. Сеть выключена в zbox.есть урок
- HTTP и веб-контентВеб как набор соглашений: URL, статический и динамический контент, MIME, транзакция HTTP по строкам, заголовки и коды ответов, CGI через fork, execve и переменные окружения, буферизация в дочернем процессе.есть урок
- Проект: веб-сервер TINYTINY на Zig: doit, разбор строки запроса и заголовков, parse_uri, serve_static через mmap, serve_dynamic через dup2 в сокет, clienterror, проверка через curl, HEAD, POST, SIGPIPE, видео. POST /run в zbox.есть урок
- Проект: песочница как у раннера курсаФинал zbox: пространства имён через clone и unshare, pivot_root в свою корневую ФС, mount для /proc, PR_SET_NO_NEW_PRIVS, seccomp-фильтр из таблицы разрешённых сисколлов, сверху TINY с POST /run, сравнение с флагами docker run у bs-runner.есть урок
Конкурентность и прокси
- ПотокиМодель выполнения потоков, std.Thread над pthreads: spawn, join, detach, передача аргументов и гонка с индексом цикла, сервер на потоках, что общего у потоков с процессами и что нет, zt prof по потокам.есть урок
- Потокобезопасность, гонки и взаимоблокировкиЧетыре класса небезопасных функций, реентерабельность против потокобезопасности, блокировка с копированием, гонки, взаимоблокировка на графе выполнения и правило порядка захвата, ThreadSanitizer.есть урок
- Проект: async в Zig и кэширующий проксиБесцветный async Zig 0.16: Io.Threaded и Io.Evented для одного кода, io.async, Group, Queue, отмена; сравнение с тремя моделями книги. Финал proxylab: кэширующий веб-прокси с пулом, LRU-кэшем под читателями-писателями и тестами.есть урок
- Пул потоков и параллельные вычисленияСервер с заранее созданным пулом на sbuf, psum в пяти версиях от мьютекса на итерацию и атомика до общего массива и локального аккумулятора, ускорение и эффективность, сильное и слабое масштабирование, закон Амдала числом, false sharing.есть урок
- Разделяемые переменные и семафорыКакие переменные общие у потоков, badcnt и граф выполнения, семафоры P и V, мьютекс как двоичный семафор, sbuf производитель-потребитель, читатели и писатели в двух вариантах, Mutex, Semaphore, Condition и RwLock из std.Io.есть урок
- Сервер на процессах и мультиплексирование ввода-выводаТри способа обслужить много клиентов: процесс на соединение и его цена, select и poll с пулом дескрипторов, событийный сервер как конечный автомат, epoll против select на тысяче соединений, kqueue на macOS.есть урок