Для чего предназначена система программирования

Уилл Крайтон — 9 сентября 2018 года Меня раздражает фраза Мне всегда казалось. Что в нем излишне сочетаются две идеи: низкоуровневое программирование (работа с деталями реализации машины) и проектирование систем (создание и управление сложным набором взаимодействующих компонентов). Почему это так? Как долго это было правдой? И что мы могли бы получить от переосмысления идеи систем?

1970-е годы: Совершенствование сборки

Давайте вернемся к истокам современных компьютерных систем. Чтобы понять. Как развивался этот термин. Я не знаю, кто придумал эту фразу изначально. Но мои поиски показывают. Что серьезные усилия по определению “компьютерных систем” начались примерно в начале 70-х. В языках системного программирования (Bergeron et al. 1972) авторы говорят:

Системная программа-это интегрированный набор подпрограмм. Вместе образующих целое, большее. Чем сумма его частей. И превышающее некоторый порог размера и/или сложности. Типичными примерами являются системы мультипрограммирования, перевода. Моделирования. Управления информацией и разделения времени. […] Ниже приведен частичный набор свойств. Некоторые из которых находятся в несистемных системах. Но не все из них должны присутствовать в данной системе.

  1. Проблема, которую необходимо решить. Носит широкий характер и состоит из множества. Как правило. Весьма разнообразных подзадач.
  2. Системная программа. Скорее всего. Будет использоваться для поддержки других программных и прикладных программ. Но также может быть и полным пакетом приложений.
  3. Он предназначен для непрерывного “производственного” использования. А не для одноразового решения одной прикладной задачи.
  4. Вероятно, он будет постоянно развиваться в количестве и типах функций. Которые он поддерживает.
  5. Системная программа требует определенной дисциплины или структуры. Как внутри. Так и между модулями (т. е. “коммуникации”) . И обычно разрабатывается и реализуется более чем одним человеком.

Это определение вполне приемлемо—компьютерные системы являются крупномасштабными. Долго используемыми и изменяющимися во времени. Однако, хотя это определение в значительной степени носит описательный характер. Ключевая идея в статье носит предписывающий характер: отстаивание отделения языков низкого уровня от системных языков (в то время противопоставление ассемблера ФОРТРАНУ).

Цель системного языка программирования состоит в том. Чтобы обеспечить язык. Который может быть использован без излишнего беспокойства по поводу “битовой вертушки”. Но при этом будет генерировать код. Который не заметно хуже, чем тот. Который генерируется вручную. Такой язык должен сочетать лаконичность и читабельность языков высокого уровня с пространственно-временной эффективностью и способностью “добраться” до машин и операционных систем. Доступных на языке ассемблера. Время проектирования. Написания и отладки должно быть сведено к минимуму. Не накладывая ненужных накладных расходов на системные ресурсы.

В то же время исследователи из CMU опубликовали BLISS: A Language for Systems Programming (Wulf et al., 1972). Описав его как:

Мы называем BLISS “языком реализации”. Хотя и признаем. Что этот термин несколько двусмыслен, поскольку. По-видимому. Все компьютерные языки используются для реализации чего-либо. Для нас эта фраза означает язык общего назначения. Более высокого уровня. В котором основное внимание уделяется конкретному применению. А именно написанию больших производственных программных систем для конкретной машины. Языки специального назначения. Такие как компиляторы-компиляторы. Не попадают в эту категорию. И мы не обязательно предполагаем. Что эти языки должны быть машинно-независимыми. Мы подчеркиваем слово “реализация” в нашем определении и не используем такие слова. Как “проектирование” и “документацияМы не обязательно ожидаем. Что язык реализации будет подходящим средством для выражения первоначального проекта большой системы или для исключительной документации этой системы. Такие понятия. Как машинная независимость. Выражающая проектирование и реализацию в одной и той же нотации. Самодокументация и другие. Являются явно желательными целями и являются критериями. По которым мы оценивали различные языки.

Здесь авторы противопоставляют идею “языка реализации” как более высокого уровня. Чем ассемблер. Но более низкого уровня. Чем “язык проектирования”. Это противоречит определению. Приведенному в предыдущей статье. Согласно которому проектирование системы и ее реализация должны иметь отдельные языки.

Обе эти статьи являются исследовательскими артефактами или адвокатами. Последняя запись. Которую следует рассмотреть (также с 1972 года. Продуктивный год!), — это Системное программирование (Donovan 1972). Учебный текст для изучения системного программирования.

Что такое системное программирование? Вы можете представить себе компьютер как некое животное. Которое подчиняется всем командам. Говорят, что компьютеры-это в основном люди. Сделанные из металла. Или, наоборот. Люди-это компьютеры. Сделанные из плоти и крови. Однако, как только мы приблизимся к компьютерам, мы увидим. Что они в основном являются машинами. Которые следуют очень специфическим и примитивным инструкциям. На заре существования компьютеров люди общались с ними посредством включения и выключения переключатели. Обозначающие примитивные инструкции. Вскоре люди захотели давать более сложные инструкции. Например, они хотели иметь возможность сказать X = 30 * Y; учитывая, что Y = 10, что такое X? Современные компьютеры не могут понять такой язык без помощи системных программ. Системные программы (например. Компиляторы. Загрузчики. Макропроцессоры. Операционные системы) были разработаны для того. Чтобы компьютеры лучше адаптировались к потребностям своих пользователей. Кроме того, люди хотели получить больше помощи в механике подготовки своих программ.

Мне нравится. Что это определение напоминает нам. Что системы служат людям. Даже если они просто инфраструктура. Не открытая непосредственно конечному пользователю.

1990 — е годы: расцвет скриптинга

В 70-е и 80-е годы большинство исследователей обычно рассматривали системное программирование как контраст с ассемблерным. Других хороших инструментов для построения систем просто не существовало. (Я не совсем понимаю. Где во всем этом была Шепелявость? Ни один из ресурсов. Которые я читал. Не цитировал Lisp. Хотя я смутно знаю. Что машины Lisp существовали. Хотя и недолго.)

Однако в середине 90-х годов с появлением динамически типизированных скриптовых языков в языках программирования произошли серьезные изменения. Улучшая более ранние системы сценариев оболочки. Такие как Bash, языки. Такие как Perl (1987). Tcl (1988). Python (1990). Ruby (1995). PHP (1995) и Javascript (1995). Пробились в основной поток. Кульминацией стала влиятельная статья “Scripting: Higher Level Programming for the 21st Century” (Ousterhout 1998). Это сформулировало “дихотомию Остерхаута” между “системными языками программирования” и “скриптовыми языками

Скриптовые языки предназначены для различных задач. Чем языки системного программирования. И это приводит к фундаментальным различиям в языках. Языки системного программирования были разработаны для построения структур данных и алгоритмов с нуля. Начиная с самых примитивных элементов компьютера. Таких как слова памяти. Напротив, скриптовые языки предназначены для склеивания: они предполагают существование набора мощных компонентов и предназначены в первую очередь для соединения компонентов вместе. Языки системного программирования строго типизированы. Чтобы помочь управлять сложностью. В то время как языки сценариев не имеют типов. Чтобы упростить соединения между компонентами и обеспечить быструю разработку приложений. […] Несколько последних тенденций. Таких как более быстрые машины. Лучшие скриптовые языки. Растущее значение графических пользовательских интерфейсов и компонентных архитектур. А также рост Интернета. Значительно увеличили применимость скриптовых языков.

На техническом уровне. Как показано выше. Ousterhout противопоставлял скрипты и системы по осям безопасности типа и инструкций для каждого оператора. На уровне дизайна он охарактеризовал новые роли для каждого класса языков: системное программирование предназначено для создания компонентов. А сценарии-для их склеивания.

Примерно в это же время начали набирать популярность статически типизированные. Но собранные в мусор языки. Java (1995) и C# (2000) превратились в титанов. Которых мы знаем сегодня. Хотя эти два языка традиционно не считаются “системными языками программирования”. Они использовались для разработки многих крупнейших в мире программных систем. Оустерхаут даже недвусмысленно упомянул: “В мире Интернета. Который сейчас формируется. Java используется для системного программирования.”

2010-е годы: Размытие границ

В последнее десятилетие грань между скриптовыми языками и системными языками программирования начала стираться. Такие компании. Как Dropbox. Смогли построить удивительно большие и масштабируемые системы только на Python. Javascript используется для визуализации сложных пользовательских интерфейсов в реальном времени на миллиардах веб-страниц. Постепенная типизация набрала обороты в Python, Javascriptи других скриптовых языках. Позволяя переходить от “прототипного” кода к “производственному” коду путем постепенного добавления статической информации о типе.

В то же время огромные инженерные ресурсы, вложенные в JIT-компиляторы как для статических языков (например, Java HotSpot), так и для динамических языков (например, Lua LuaJIT, Javascript V8, Python PyPy), сделали их производительность конкурентоспособной с традиционными системными языками программирования (C, C++). Крупномасштабные распределенные системы. Такие как Spark, написаны на Scala. Новые языки программирования. Такие как Julia. Swift и Go. Продолжают раздвигать границы производительности на языках. Собирающих мусор.

На панели под названием На вопрос “что такое системный язык программирования в 2014 году” они ответили (отредактированная транскрипция):

  • Нико Мацакис: Написание клиентских приложений. Полярная противоположность тому. Для чего предназначен Go. В этих приложениях у вас есть высокие требования к задержке. Высокие требования к безопасности. Множество требований. Которые не возникают на стороне сервера.
  • Бьярне Страуструп: Системное программирование вышло из области. Где приходилось иметь дело с аппаратным обеспечением. А затем приложения стали более сложными. Вам нужно иметь дело со сложностью. Если у вас есть какие-либо проблемы со значительными ограничениями ресурсов. Вы находитесь в области системного программирования. Если вам нужен более мелкозернистый контроль. То вы также находитесь в области системного программирования. Именно ограничения определяют. Является ли это системным программированием. У тебя кончается память? У тебя что, мало времени?
  • Роб Пайк: Когда мы впервые объявили Go. Мы назвали его системным языком программирования. И я немного сожалею об этом. Потому что многие люди предполагали. Что это язык написания операционных систем. То, что мы должны были бы назвать языком серверного письма, это то. Что мы действительно думали о нем. Теперь я понимаю. Что у нас есть язык облачной инфраструктуры. Другое определение системного программирования — это то. Что работает в облаке.
  • Андрей Александреску: У меня есть несколько лакмусовых бумажек для проверки того. Является ли что-то системным языком программирования. Языки системного программирования должны быть в состоянии позволить вам написать в нем свой собственный распределитель памяти. Вы должны быть в состоянии подделать число в указатель. Так как именно так работает аппаратное обеспечение.

Значит, системное программирование связано с высокой производительностью? Ограниченность ресурсов? Аппаратный контроль? Облачная инфраструктура? Похоже, что в широком смысле языки в категории C, C++. Rust и D различаются с точки зрения их уровня абстракции от машины. Эти языки раскрывают детали базового оборудования. Такие как распределение/компоновка памяти и мелкозернистое управление ресурсами.

Другой способ думать об этом: когда у вас есть проблема эффективности. Сколько свободы у вас есть. Чтобы решить ее? Замечательная часть низкоуровневых языков программирования заключается в том, что. Когда вы обнаруживаете неэффективность. В ваших силах устранить узкое место путем тщательного контроля деталей машины. Векторизируйте эту инструкцию. Измените размер структуры данных. Чтобы сохранить ее в кэше. И так далее. Точно так же статические типы обеспечивают большую уверенностьнапример. “эти две вещи. Которые я пытаюсь добавить. Определенно являются целыми числами”). Низкоуровневые языки обеспечивают большую уверенность что “этот код будет выполняться на машине. Как я указал.”

Напротив, оптимизация интерпретируемых языков-это абсолютные джунгли. Невероятно трудно понять. Будет ли среда выполнения последовательно выполнять ваш код так. Как вы ожидаете. Это точно такая же проблема с авто-распараллеливающимися компиляторами—“авто-векторизация не является моделью программирования” (см. Историю ispc). Это все равно что писать интерфейс на Python, думая: “Ну. Я, конечно. Надеюсь, что тот. Кто вызывает эту функцию. Даст мне int.”

Сегодня: …так что же такое системное программирование?

Это возвращает меня к моему первоначальному недовольству. То, что многие люди называют системным программированием. Я думаю о таком же низкоуровневом программировании-раскрытии деталей машины. Но как же тогда быть с системами? Вспомните наше определение 1972 года:

  1. Проблема, которую необходимо решить. Носит широкий характер и состоит из множества. Как правило. Весьма разнообразных подзадач.
  2. Системная программа. Скорее всего. Будет использоваться для поддержки других программных и прикладных программ. Но также может быть полным пакетом приложений.
  3. Он предназначен для непрерывного “производственного” использования. А не для одноразового решения одной прикладной задачи.
  4. Скорее всего. Он будет постоянно развиваться в количестве и типах функций. Которые он поддерживает.
  5. Системная программа требует определенной дисциплины или структуры. Как внутри модулей. Так и между ними (т. е. “коммуникации”) . И обычно разрабатывается и реализуется более чем одним человеком.

Это больше похоже на проблемы разработки программного обеспечения (модульность. Повторное использование. Эволюция кода). Чем на проблемы низкого уровня производительности. Это означает. Что любой язык программирования. Который отдает приоритет решению этих проблем. Является системным языком программирования! Это все еще не означает, что каждый язык является языком системного программирования. Динамические языки программирования, возможно. Все еще далеки от системных языков. Поскольку динамические типы и идиомы типа “проси прощения. А не разрешения” не способствуют хорошему качеству кода.

Что же тогда дает нам это определение? Вот хоть бери: функциональные языки. Такие как OCaml и Haskell-это гораздо больше систем. Ориентированных на чем низкоуровневые языки. Такие как C или C++. Когда мы учим систем программирования для студентов. Мы должны включать принципы функционального программирования. Как неизменность. Влияние богатой тип систем в улучшение дизайна интерфейса и полезных функций высшего порядка. Школы должны обучать как системному программированию. Так и программированию низкого уровня.

Существует ли различие между системным программированием и хорошей программной инженерией? Не совсем, но проблема здесь в том. Что программная инженерия и низкоуровневое программирование часто преподаются изолированно. В то время как большинство занятий по программной инженерии обычно ориентированы на Java-“написание хороших интерфейсов и тестов”. Мы также должны научить студентов тому. Как проектировать системы. Которые имеют значительные ограничения ресурсов. Возможно, мы называем низкоуровневое программирование “системами”. Потому что многие из наиболее интересных программных систем являются низкоуровневыми (например. Базы данных, сети. Операционные системы и т. Д.). Поскольку низкоуровневые системы имеют много ограничений. Они требуют от своих разработчиков творческого мышления.

Другое обрамление заключается в том. Что программисты низкого уровня должны стремиться понять. Какие идеи в проектировании систем могут быть адаптированы к реальности современного оборудования. Я думаю, что сообщество Rust было чрезвычайно инновационным в этом отношении , рассматривая , как хорошие принципы проектирования программного обеспечения/функционального программирования могут быть применены к низкоуровневым проблемам (например, фьючерсы, обработка ошибокили, конечно же, безопасность памяти).

Подводя итог. Томы называем “системным программированием”, я думаю. Следует назвать “низкоуровневым программированиемПроектирование компьютерных систем как область слишком важна. Чтобы не иметь собственного названия. Четкое разделение этих двух идей обеспечивает большую концептуальную ясность в пространстве проектирования языка программирования. А также открывает дверь для обмена идеями между этими двумя пространствами: как мы можем проектировать систему вокруг машины и наоборот?

Пожалуйста, направляйте комментарии на мой почтовый ящик по адресу wcrichto@cs.stanford.edu или Хакерские новости.