• Колибри для Фантома

      Краткое содержание: разработка модуля совместимости с ОС Колибри внутри модуля совместимости с ОС Юникс внутри ОС Фантомь)

      Внутри ОС Фантом есть маленький простенький Юникс. POSIX подсистема. В принципе необязательная для работы самого Фантома и довольно неполная — Unix Quake под ней собрать удалось, а, например, апач не соберётся почти наверняка. Тем не менее — она есть.

      Чтобы продолжить, надо понимать, что такое ОС Колибри. Колибри — обрусевший западный проект микро-операционки на ассемблере. Собственно, это описание довольно полно. Над Колибри работают фанаты программирования на ассемблере x86, она, соответственно, непереносима и, к сожалению, очень плохо спроектирована. Очень — это катастрофически. Для понимания масштаба бедствия — нет никакого общего механизма определения успешности или ошибочности системного вызова. Для некоторых определить несупех просто невозможно, некоторые вызовы возвращают свой личный набор кодов ошибок, некоторые — какой-то другой.

      Почему же, тем не менее, любопытно реализовать слой совместимости с этой ОС? Тому несколько причин:

      • Она очень компактна. Забегая вперёд — первую программу для Колибри в Фантоме удалось запустить через четыре часа работы.
      • Этот мини-проект стал драйвером развития некоторых нативных подсистем Фантома,
        в частности — оконной.
      • Главное — всё состояние процесса Колибри, известное ядру, укладывается в небольшую структуру. Многие (почти все!) вызовы — stateless, то есть не опираются о какое-либо знание,
        хранимое в ядре. Это идеальный кандидат на реализацию персистентных (переживающих перезапуск ОС) бинарных (не написанных на байткод-языке) процессов в Фантоме.
      Читать дальше →
    • Фантом: большая сборка мусора

        Эта статья — продолжение, начало здесь. Для тех, кто не кликнул на ссылку, краткая вводная:

        Мы обсуждаем сборку мусора в операционной системе Фантом, то есть в среде виртуальной (байткод-) машины, работающей в персистентной оперативной памяти. Размер персистентной памяти — порядка размера диска, то есть единицы терабайт на сегодня и, потенциально, десятки и сотни терабайт завтра.

        Поскольку речь идёт о виртуальной памяти, то существенная часть объектов в любом случае находится не в оперативной памяти, независимо от того, какой алгоритм и вообще подход мы избрали. То есть — стоимость доступа к объекту велика. Это, в общем случае, дисковая операция.

        Кроме того, следует ожидать, что в сетевой среде совокупности таких виртуальных машин будут обмениваться прокси-объектами, то есть будет существовать граф объектов, растянутый на много машин в сети и, конечно, во всём этом кошмаре потенциально потребуется уметь собирать мусор не только на одной машине, но и по сети.

        Принятая мной идея схемы сборки мусора в такой среде выглядит как совокупность двух сборщиков.
        Читать дальше →
      • Драйверы умного или виртуального железа

          Первая статья про драйверы была уж совсем вводной, и мне подумалось, что её нельзя не дополнить рассказом про то, как устроены драйверы более современных устройств.

          Для начала введём определение bus master: устройство, способное быть не только ведомым, но и ведущим на шине компьютера. То есть — не только отвечать на транзакции ввода-вывода, инициированные процессором, но и самостоятельно их инициировать — по собственной инициативе «ходить» в память.

          История таких устройств уходит корнями в понятие DMA: ещё во времена прародителя микропроцессоров, микропроцессора 8080 (КР5080ИК80), появилось понимание, что процессор хорошо бы разгрузить от рутинной операции перетаскивания байтиков между устройствами в-в и памятью.
          Читать дальше →
        • UDP/TCP File System, Trivial Remote File System

            Сегодня выходной, так что напишу коротко про мелочи, до которых, как правило, руки не доходят.

            TCP FS



            Есть ещё одна вещь, которой нет в современном Юниксе и которую я хочу иметь в unix box фантома. Она проста как мычание, и почему её никто не сделал — непостижимо:

            #cat /tcp/host/port > local_file
            


            Правда, я хочу использовать иной синтаксис имени файла, URL style — tcp://host:port, но это уже детали. Естественно, наравне с TCP просится UDP, и там вообще проблем нет.

            Заголовок спойлера
            Вообще unix-подсистема Фантома «ест» как традиционные Юниксовые имена, /usr/include/stdio.h, так и URL-и, tcp://ya.ru:80.


            Для TCP есть очевидная проблема — нужен ли нам listen или connect, но её можно решить через указание в имени «файла» определённого суффикса.

            Сказать на эту тему настолько больше нечего, что перейдём без остановки к следующей.

            TRFS — тривиальная дистанционная файловая система.


            Читать дальше →
          • Lazy threads: опциональный параллелизм

              Статья-гипотеза. Описанное нигде не было реализовано, хотя, в принципе, ничто не мешает запилить такое в Фантоме.

              Эта идея пришла мне в голову очень давно и даже где-то была мной описана. Триггер к тому, чтобы её описать сегодня — обсуждение сетевых драйверов Линукса в комментариях к Анатомии драйвера.

              Сформулирую проблему, описанную там, как я её понимаю: сетевой драйвер Линукса работает в отдельной нити, которая читает принятые пакеты из устройства и синхронно их обрабатывает. Прогоняет через роутинг, файрволл и, если пакет не нам, отправляет его в исходящий интерфейс.

              Понятно, что некоторые пакеты обслуживаются быстро, а иные могут потребовать много времени. В такой ситуации хотелось бы иметь механизм, который динамически порождает обслуживающие нити по мере необходимости, и механизм достаточно дешёвый в ситуации, когда лишние нити не нужны.

              То есть хотелось бы такого вызова функции, который при необходимости можно конвертировать в старт нити. Но по цене вызова функции, если нить реально не оказалась нужна.

              Мне эта идея пришла когда я рассматривал совершенно фантастические модели для Фантом, включая акторную модель с запуском нити вообще на любой вызов функции/метода. Саму модель я отбросил, а вот идея lazy threads осталась и до сих пор кажется интересной.

              Как это.
              Читать дальше →
            • Анатомия драйвера

                Опять вернёмся в традиционную область разработки операционных систем (и приложений для микроконтроллеров) — написание драйверов.

                Я попробую выделить некоторые общие правила и каноны в этой области. Как всегда — на примере Фантома.

                Драйвер — функциональная компонента ОС, ответственная за отношения с определённым подмножеством аппаратуры компьютера.

                С лёгкой руки того же Юникса драйвера делятся на блочные и байт-ориентированные. В былые времена классическими примерами были драйвер диска (операции — записать и прочитать сектор диска) и драйвер дисплея (прочитать и записать символ).

                В современной реальности, конечно, всё сложнее. Драйвер — типичный инстанс-объект класса, и классов этих до фига и больше. В принципе, интерфейс драйверов пытаются как-то ужать в прокрустово ложе модели read/write, но это самообман. У драйвера сетевой карты есть метод «прочитать MAC-адрес карты» (который, конечно, можно реализовать через properties), а у драйвера USB — целая пачка USB-специфичных операций. Ещё веселее у графических драйверов — какой-нибудь bitblt( startx, starty, destx, desty, xsize, ysize, operation ) — обычное дело.

                Цикл жизни драйвера, в целом, может быть описан так:

                • Инициализация: драйвер получает ресурсы (но не доступ к своей аппаратуре)
                • Поиск аппаратуры: драйвер получает от ядра или находит сам свои аппаратные ресурсы
                • Активация — драйвер начинает работу
                • Появление/пропадание устройств, если это уместно. См. тот же USB.
                • Засыпание/просыпание аппаратуры, если это уместно. В контроллерах часто неиспользуемая аппаратура выключается для экономии.
                • Деактивация драйвера — обслуживание запросов прекращается
                • Выгрузка драйвера — освобождаются все ресурсы ядра, драйвер не существует.


                (Вообще я написал в прошлом году черновик открытой спецификации интерфейса драйвера — см. репозиторий и документ.)

                Мне известны три модели построения драйвера:

                • Поллинг
                • Прерывания
                • Нити (threads)

                Читать дальше →
              • Персистентная ОС: ничто не блокируется

                  Это — статья-вопрос. У меня нет идеального ответа на то, что здесь будет описано. Какой-то есть, но насколько он удачен — неочевидно.

                  Статья касается одной из концептуальных проблем ОС Фантом, ну или любой другой системы, в которой есть персистентная и «волатильная» составляющие.

                  Для понимания сути проблемы стоит прочесть одну из предыдущих статей — про персистентную оперативную память.

                  Краткая постановка проблемы: В силу того, что прикладная программа в ОС Фантом персистентна (не перезапускается при перезагрузке), а ядро — нет (перезапускается при перезагрузке и может быть изменено между запусками), в такой системе нельзя делать блокирующие системные вызовы. Обычным способом.
                  Читать дальше →
                • Атрибуты устройств, или ioctl must die

                    В процессе работы над ОС Фантом, которая вообще не Юникс никаким местом, мне, тем не менее, захотелось сделать в нём Unix-compatible подсистему. Не то, чтобы прямо POSIX, но что-то достаточно близкое. Отчасти из любопытства, отчасти для удобства, отчасти как ещё один migration path. (Ну и вообще было интересно, насколько трудно написать простенький Юникс «из головы».) В качестве цели номер 1 была поставлена задача запустить quake 1 for Unix, которая и была достигнута.

                    В процессе, естественно, появились open/close/r/w/ioctl, и появилось ощущение, что последний неприлично, постыдно устарел. В качестве упражнения для размятия мозга я реализовал (в дополнение к обычному ioctl) некоторый альтернативный API, который бы позволил управлять свойствами устройств более гибким и удобным с точки зрения пользователя способом. Этот API, конечно, имеет свои очевидны минусы, и, в целом, эта статья — RFC, aka request For Comments.

                    Итак, API на уровне пользователя:

                    // returns name of property with sequential number nProperty, or error
                    errno_t listproperties( int fd, int nProperty, char *buf, int buflen );
                    
                    errno_t getproperty( int fd, const char *pName, char *buf, int buflen );
                    errno_t setproperty( int fd, const char *pName, const char *pValue );
                    


                    Правила:

                    1. Никаких дефайнов с номерами, только имена.
                    2. Никаких бинарных данных, только строки

                    Читать дальше →
                  • От шедулера к планировщику

                      См. две другие статьи этой группы — Делаем многозадачность и Преемптивность: как отнять процессор.

                      Сразу просьба к строгим читателям. Если вы не поняли какой-либо термин из применённых — спросите, я подскажу, что я имел в виду. А если вам нравится другое написание или перевод этого термина — укажите его в комментарии. Я применяю те, которые нравятся мне.

                      Итак, в прошлых статьях описан механизм реализации многозадачности за вычетом планировщика, он же шедулер, он же скедулер, он же Васька меченый, сорри, заговариваюсь я с этими терминами…

                      Как я уже говорил, шедулер — это просто функция, которая отвечает на вопрос: какую нить и на сколько времени поставить на процессор.

                      Кстати, в SMP системе шедулер ничем не отличается от однопроцессорного. Вообще, чтобы проще понимать структуру взаимодействия сущностей на одном и нескольких процессорах, проще всего представить себе следующую модель: для каждого процессора есть нить «простоя» (которая работает, если вообще больше некому и просто останавливае процессор до прерывания), которая постоянно пытается «отдать» процессор (которым она как бы владеет) другим нитям, выбирая нить с помощью шедулера.

                      Говоря о шедулере нельзя не сказать о приоритетах.

                      Приоритет — свойство нити (или процесса) влияющее на конкуренцию этой нити с другими нитями за процессор.

                      Приоритет обычно описывается парой <класс приоритета, значение приоритета внутри класса>.
                      Читать дальше →
                    • Преемптивность: как отнять процессор

                        Эта статья не имеет смысла без предыдущей, в которой описывались основные механизмы переключения контекстов в многозадачной ОС.

                        Здесь я расскажу, как кооперативная многозадачность превращается во враждебную преемптивную.

                        Суть этого превращения проста. В машине есть таймер, таймер генерирует прерывания, прерывания приостанавливают код нити и отдают процессор в руки механизма многозадачности. Оный уже вполне кооперативно переключает процессор на новую нить, как и описано в предыдущей статье.

                        Но, как обычно, есть нюансы. См. код для интела.

                        Сам «отъём» процессора делается как в рамках обычного хардверного прерывания, обычно — по таймеру, так и в рамках «софтверного» прерывания — которое, собственно, такое же прерывание, но вызванное специальной инструкцией процессора. Такой способ переключения контекста нужен, если мы (например, в рамках примитива синхронизации) явно останавливаем нить и не хотим ждать, пока прилетит таймерное прерывание.
                        Читать дальше →