Большинство популярных языков программирования — это языки высокого уровня. Например, Java, Python или C#. Конечно, программировать на них можно и слабо представляя, как на самом деле работают различные уровни абстракции. Но для хорошего IT-специалиста важно понимать и то, как устроена платформа, с которой он взаимодействует, как функционирует среда разработки, компилятор, отладчик. Это приводит нас к разговору о низкоуровневом программировании.
LLP (low-level programming) — это удобный способ разобраться с тем, как писать надёжные, быстрые, компактные и эффективные программы. Наши студенты изучают ассемблер и Си прежде всего ради того, чтобы лучше почувствовать, как работает ПО.
Зачем это нужно знать
IT-индустрия быстро развивается и подвержена моде на технологии и языки. Фундаментальные концепции — например, модели вычислений, виртуальная память, компиляция и сборка программ — изменяются гораздо реже. Понимание того, как базовые принципы таких концепций проявляются в одном языке программирования, поможет быстро освоить похожий язык, если это потребуется для работы. LLP в этом случае дает понять, как программа из исходного текста становится набором машинных инструкций, и как они выполняются на компьютере. В свою очередь знакомство с механизмами управления памятью и принципами работы компилятора поможет писать менее требовательный к вычислительным ресурсам код.
Уточнение: Нам достаточно часто приходится встречаться с тезисом о том, что LLP — это только программирование железа и тому подобные задачи. Это не так.
Где можно встретить LLP на практике:
- Трейдинг. Торги на бирже совершают программы с невероятно высокими требованиями к времени реакции на колебания рынка. За счёт «удалённости» языков высокого уровня от «железа», очень сложно предсказать время отклика программы на внешнее событие. Поэтому здесь задействуется LLP, чтобы обеспечить контроль над скоростью транзакций.
- Робототехника. Вычислительные ресурсы здесь ограничены, поэтому высоки требования к качеству ПО и компактности кода. Общепринятые подходы создания ПО тут не всегда работают и зачастую разработка может идти на Си или другом системном языке.
- Системное ПО. Это — ОС, компиляторы (JIT, AOT), браузеры (сегодня они стали платформой для создания сложных приложений). Его работа не всегда заметна обывателю, но создаёт инфраструктуру для прикладного ПО.
Как мы к этому готовим
Наши студенты направлений 09.03.01 «Информатика и вычислительная техника» и 09.03.04 «Программная инженерия» изучают ряд дисциплин, связанных с низкоуровневым программированием. Это — операционные системы, системное ПО (в том числе в среде Unix), разработка компиляторов, технологии виртуализации и др.
Стартовая точка — курс «Низкоуровневое программирование». Здесь студентов ждет работа с ассемблером и Си. Параллельно — они изучают процесс компиляции и выполнения программ. Курс идет в формате «от постановки задачи к готовой программе». Основная цель курса — получить представление о том, как меняется представление программы при переходах между:
- написанием кода;
- препроцессингом и компиляцией;
- связыванием (linking);
- выполнением и отладкой.
Фото hackNY.org CC BY-SA
От студентов, которые приходят на курс, мы ожидаем минимальный опыт программирования на языках высокого уровня (например, Java или C#). И рекомендуем следующие онлайн-курсы:
- «Java. Базовый курс» Алексея Владыкина
- «Введение в архитектуру ЭВМ: Элементы ОС» Кирилла Кринкина
Курс «Низкоуровневое программирование» читается преимущественно по книге, которая написана специально для наших студентов. Книга издана на английском и переведена на японский и португальский. Мы также часто работаем с Intel64 software developer manual и System V AMD64 ABI.
Что внутри курса
Если говорить простыми словами, данный курс можно детализировать следующим образом (применительно к тем темам, которые были обозначены выше):
- Написание кода. Подразумевается свободное владение ассемблером для Intel 64 (NASM, Linux) и C11. Здесь изучаются модели вычислений, даются расширенные рекомендации по стилю написания кода.
- Препроцессинг и компиляция. Изучаются препроцессоры NASM и C, затрагиваются формальные грамматики, распространённые оптимизации кода компилятором, а также механика типичной реализации конструкций языка Си на уровне ассемблера (проще говоря, демонстрация идеи «Си — высокоуровневый ассемблер»).
- Связывание (линковка). Статическое и динамическое. На практике изучается формат ELF — студенты исследуют, что именно делает линковщик и динамический загрузчик. К слову, этому был посвящён отдельный мини-курс.
- Выполнение и отладка программы. Здесь мы разбираемся с тем, как эти задачи связаны с архитектурой компьютера, с устройством виртуальной памяти и привилегированным режимом, прерываниями, механизмом системных вызовов. Помимо этого рассматриваем то, как применяется gdb (мощный отладчик, позволяющий «заглянуть под капот» любой программе).
Важные организационные нюансы
Лекции мы разбиваем совместным написанием кода и изучением скомпилированных файлов с помощью инструментов из binutils (objdump, readelf) или gdb. Во время лекции (и не только) студенты задают вопросы как устно, так и в канал курса в известном мессенджере, а преподаватели отвечают на них при первой возможности.
В качестве практических занятий студенты выполняют лабораторные работы. Порой студентам в процессе обучения хочется разработать что-то своё, лично им интересное (игру, программу для управления роботом, компилятор и т.д.).
Поэтому в качестве замены экзамену студенты могут сделать индивидуальный проект на тему, согласованную с преподавателем. После прохождения код-ревью и публикации на GitHub они записывают видео с демонстрацией проекта (на этом канале собраны проекты, реализованные с 2014 года, ссылки на GitHub — в описании).
Пример работы студента курса: аналоговый левитрон (подробный рассказ о проекте на Хабре)
Некоторые другие проекты, выполненные по итогам курса:
- клон «Танчиков» с мультиплеером по сети
- клон классической игры «Астероиды» с векторной графикой
- Forth на микроконтроллере STM32
- эмулятор аркадного автомата CHIP-8 под UEFI
- виртуальная файловая система для работы с Telegram
- Ridiculously Awesome Shell
О преподавателе
На курсе со студентами взаимодействует целая команда преподавателей, которые читают лекции, принимают лабораторные работы, консультируют студентов. Студенты старших курсов участвуют в обсуждениях и помогают нынешним слушателям курса разобраться в тонкостях низкоуровневого программирования.
Основную работу по подготовке материалов к курсу сделал Игорь Жирков, автор учебника по нему. Игорь учился в Университете ИТМО и Академическом Университете, получил степень магистра в ИТМО в 2016 году. Область его интересов — теории типов, математическая логика, языки программирования и низкоуровневое программирование. Его основной текущий проект связан с формальной верификацией рефакторингов языка С с помощью Coq и верифицированного компилятора CompCert для языка С.
Этот курс помогает научиться смотреть на задачу с адекватного уровня абстракции и, скажем условно, не пытаться «писать web-приложения на ассемблере». Мы стараемся подойти к изучению Си и ассемблера с точки зрения понимания концепций, связанных с этими «инструментами» и их окружением — операционными системами и CPU.
Кстати, здесь мы сможете найти книгу, на написание которой вдохновило преподавание этого курса (она была издана на английском, японском и португальском).
Автор: itmo