Как стать автором
Обновить

Архитектура OS Inferno — 1

Время на прочтение 4 мин
Количество просмотров 3.7K
Сразу предупреждаю, что делать полный обзор архитектуры я не собираюсь. Во-первых есть оригинальная документация где всё детально описано. А во-вторых я ещё не настолько хорошо знаю Inferno, чтобы браться за написание детального описания архитектуры. Но вот про «фишки» архитектуры я обязательно расскажу, это самое интересное.

Программировать под Inferno можно на двух языках — Limbo и sh. На sh можно делать практически всё то же самое, что и на Limbo, только… очень медленно. С точки зрения взаимодействия с архитектурой системы между ними разницы практически нет, поэтому дальше я буду описывать всё с точки зрения программирования на Limbo.

Программы и библиотеки


В Inferno нет отличия между программой (application, script) и библиотекой (.so — shared object, .dll). Все программы на Limbo являются модулями, и могут быть использованы в этом качестве любой другой программой/модулем. Т.е. одна «программа» может в любой момент подгрузить ЛЮБУЮ другую «программу» в память как обычную библиотеку и начать вызывать её функции. А закончив вызывать нужные функции она может выгрузить ставший не нужным модуль из памяти (загрузка/выгрузка происходит во время выполнения, а не при запуске программы).

Под «программами» в Inferno подразумеваются такие модули, которые совместимы с интерфейсом «Command», что означает всего лишь что в модуле есть функция с названием init которая принимает два параметра (графический контекст и список аргументов) и ничего не возвращает.

А знаете, что происходит, когда вы в командной строке sh набираете имя программы для запуска — например, ls или pwd? Происходит смешная вещь — sh просто находит на диске файл с байт-кодом модуля, имя которого вы набрали, подгружает его и вызывает его функцию init. А когда init завершается sh выгружает этот модуль из памяти и ждёт набора имени следующей команды. :) (Если честно, на практике там всё капельку сложнее, но суть именно такая.) Таким образом аналога всего семейства сисколов POSIX exec*() в Inferno вообще нет — они не нужны. :)

Интерфейсы


Это не самый ключевой момент, но раз уж о нём зашла речь… Limbo язык сильнотипизированный, проверка типов осуществляется и на этапе компиляции, и на этапе выполнения. Когда вы подгружаете какой-то модуль, вы получаете его handler в переменной, и тип этой переменной должен определять какие функции/константы есть в этом модуле, какие и какого типа у них параметры, etc. Вот описание всего этого хозяйства и называется интерфейсом модуля. (Похожая схема используется в паскале — там тоже отдельно пишется описание интерфейса и отдельно реализация модуля.)

При этом один модуль может реализовывать несколько разных интерфейсов (наборов функций/констант). И один интерфейс может иметь много разных реализаций в разных модулях (что используется в случае интерфейса «Command»).

А когда вы подгружаете модуль Limbo нужно указать и путь к файлу с реализацией (байт-кодом модуля) и название интерфейса (описания интерфейсов подгружаются на манер include .h-файлов в C). После чего Limbo может проверить совместимость указанных вами реализации и интерфейса, и если всё в порядке вернёт вам handler этого модуля имеющий тип указанного вами интерфейса.

Что касается использования памяти, то всё сделано правильно — в памяти отдельно хранится одна копия кода подгруженного модуля на все процессы Inferno, а к каждому handler-у этого модуля прилагается отдельная память для хранения стека, глобальных переменных, etc. этого модуля. Когда вы выгружаете свой handler освобождается часть памяти занятая его копией глобальных переменных/стека, а когда освобождается последний handler из памяти выгружается и сам код модуля.

Процессы и нити (threads)


В Inferno нет отличия между процессом и нитью. По сути все процессы Inferno это очень легкие нити.

Как нити Inferno отображаются на нити host OS (если Inferno запущен в hosted режиме под виндой/линухом/etc.) зависит от реализации Inferno для этой host OS. Насколько я понимаю, Inferno использует смешанную модель — часть нитей его внутренние, т.е. один процесс/нить host OS может содержать несколько нитей Inferno, а часть нитей Inferno отображаются на нити host OS один-к-одному (в частности это касается нитей выполняющих блокирующие syscall host OS — напр. чтение из сокета).

По сути работающая OS Inferno изнутри выглядит как кучка выполняющихся, равных между собой нитей, шарящих между собой всю память. И это не смотря на то, что часть нитей «как бы» относится к одному работающему приложению, часть к другому — всё это только у вас в голове, а Inferno до этой группировки нитей по приложениям дела нет.

Если вас ужаснула идея отсутствия защиты памяти одних приложений от других и вы вспомнили про DOS… то я вас успокою — эта проблема, как и многие другие в Inferno, решена на архитектурном уровне и никаких специальных усилий «для защиты» не требует. А именно — в Dis (виртуальной машине) просто нет способа для прямого доступа в память по адресу, все указатели являются по сути высокоуровневыми ссылками на фиксированные участки памяти. Таким образом, любая нить имеет доступ только к тем участкам памяти, на которые у неё есть указатели. А указатель на существующий (т.е. выделенный какой-то другой нитью) участок памяти нить может получить только параметром или через IPC между нитями. Таким образом сильная типизация Dis обеспечивает абсолютную защиту памяти в качестве «побочного эффекта». :)
Теги:
Хабы:
+24
Комментарии 28
Комментарии Комментарии 28

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн