Создаем новую OS. Действительно новую, реально операционную, и правда – систему

в 12:11, , рубрики: Resource-Owner-Service, Анализ и проектирование систем, Блог компании Intel, операционная система, Программирование
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 1

О создании новой операционной системы в последнее время говорят немало, особенно в России. В сумме размер всех публикаций по данной теме наверняка превышает размеры исходного кода любой операционной системы. Так что остается только одна проблема – от этих разговоров никаких новых OS не появляется. Всё, что предъявляется публике (и на что тратятся бюджетные деньги), на поверку оказывается кастомизированными сборками OS семейства Linux, а значит, не содержит ничего принципиально нового. Но, если о чем-то не говорят, это не значит, что его не существует.
В этой статье – проект принципиально новой OS, созданный в нерабочее время одним из ведущих сотрудников (Principal Engineer) российского подразделения Intel.

Общие характеристики системы

Свойства предлагаемой ОС совпадают с целями ее создания. А именно:

  • минимально возможное потребление системных ресурсов
  • масштабируемость по отношению к мощности процессора, памяти, месту на диске, количеству одновременно выполняемых задач, то есть, применимость как в мощных серверах, так и в мобильных устройствах и даже таких специфических системах, как сети беспроводных сенсоров
  • унифицированное решение для гетерогенных компьютерных систем, включая сетевые, то есть, поддержка совместной работы на системах CPU и GPU различной архитектуры, с оптимальным распределением работы между ними
  • повышение надежности и безопасности (в сравнении с существующими системами)
  • легкость разработки, распространения и обновления программного обеспечения при максимальном сохранении существующих сейчас языков и инструментов разработки софта.

Основные принципы, лежащие в основе работы ОС

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

  • Пользовательский интерфейс. Он может быть любым – от самых сложных вариантов до простой текстовой консоли, например, в случае системы беспроводных сенсоров.
  • Модель работы с памятью. Виртуальная и физическая память, страницы… или линейное адресное пространство. Наличие или отсутствие свопа, защиты областей памяти..
  • Разграничение прав доступа на пользовательские и привилегированные..
  • Файловая система. Она также может быть любой из ныне существующих, либо реализовать современные идеи отказа от файлов и представления всего в виде объектов, либо вообще, как Гудвин, великий и ужасный, выглядеть по-разному для разных приложений….

А теперь о том, что будет новым.
Прежде всего, название ;). У предлагаемой ОС пока нет устоявшегося названия. Хотя обсуждаются разные варианты (можете предлагать свои в комментариях), рабочее название – ROS OS.

▍Знакомьтесь, Resource-Owner-Service (ROS) модель

Основная идея устройства новой ОС, названная автором Resource-Owner-Service (Ресурс-Владелец-Услуга) и призванная обеспечить вышеприведенные характеристики системы, красива, проста и, на первый взгляд, знакома. Но только на первый взгляд. Итак:

  • Все «компоненты» устройства, на котором работает ОС, – как железные (ЦПУ, место для хранения данных и т.п.), так и программные (например, участки кода, реализующие определенный алгоритм), – рассматриваются как ресурсы и разделяются между владельцами – программами/задачами.
  • Все взаимодействия между задачами осуществляются исключительно посредством запросов и предоставлением услуг (сервисов).

Напоминает обычную модель клиент-сервер? Да, но есть еще третий принцип, в котором и состоит основное различие с данной моделью —

  • «Овеществление» сервисов. А именно, сервис в ROS модели – это не просто функциональность или интерфейс для обмена данными, предоставляемые объектом, а новая коммуникационная и синхронизационная сущность – объект.

Назовем экземпляры этих объектов «каналами». Канал – это разделяемый объект, содержащий как код, так и данные и предназначенный для передачи данных и управления между задачами. Заметьте, что здесь намеренно отсутствует разграничение на провайдера и потребителя услуг (то есть, сервер и клиент), так как каждая задача может и предоставлять, и потреблять определенный набор сервисов. Из исключительности каналов как средства взаимодействия задач следует еще одно свойство ROS-модели: коммуникационная синхронизация. Любая передача управления между задачами по определению может осуществляться только в связке с получениемпредоставлением услуги, то есть, каналы становятся синхронизационными объектами, однозначно определяющими место и назначение синхронизации задач.

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

Дизайн ОС включает в себя два вида каналов: дуальные и мульти. Как видно из названия, у первых число присоединенных задач не может превышать 2, а у последних ограничения по количеству подключенных задач отсутствуют (определяются исключительно объемом доступной памяти и особенностями конкретной реализации ОС). Каждый канал имеет два «противоположных» конца. Задачи, присоединенные к ним, называются экспортерами и импортерами канала, соответственно. Разница между этими двумя типами задач несущественна – оба должны создать экземпляр канала, то есть, выделить память и таким образом поместить канал в свое адресное пространство. Единственная цель разделения на экспортеров и импортеров – поддержка задач с традиционной семантикой клиент-сервер, т.е. производитель и потребитель услуг.

На рисунке ниже представлена принципиальная схема ROS OS:
image

Все крайне просто. Серо-голубым цветом помечены физические компоненты системы – процессоры (CPU), память (memory). Dev (Device) – это физическое устройство хранения данных.
Ядро ОС владеет ресурсами процессоров и памяти. Остальными ресурсами системы могут владеть привилегированные задачи (P-task), предоставляющие в свою очередь сервисы задачам пользовательского уровня (U-task) посредством коммуникационных каналов (channel).
ROS OS реализует: Менеджер Памяти (ME-M), Менеджер Каналов (CH-M), Менеджер Системных Каналов (SC-M), Менеджер Задач (TA-M), и Диспетчер Задач (TA-D). Ядро идентифицирует задачи и запросы на сервисы посредством системных каналов (syschan).

Управление задачами

▍Скоординированное вытеснение (cooperative preemption)

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

Вместо этого, задачи предоставляют окружение исполнения, состоящее из четырех указателей в форме {func(chan, sys, loc)}, так что ядро может очистить стек задачи, а затем, непосредственно перед активацией, выделить задаче новый (или предоставить существующий пустой) стек, после чего позвать функцию func с данными параметрами. При этом, значения остальных, не используемых в передаче этого окружения регистров остаются неопределенными или обнуляются, что быстрее сохранениявосстановления корректного значения.
Непосредственно перед активацией задачи система очищает окружение из 4-ех указателей, чтобы исключить ошибочное преждевременное вытеснение задачи.
Заметим, что скоординированное вытеснение (при условии, что все задачи в системе ему следуют) означает, что при идеальных условиях исполнения для каждого ядра CPU будет существовать только один стек. Дополнительные стеки будут выделяться, только если задача будет вытеснена преждевременно, перед тем, как она инициализирует контекст из 4-ех указателей.

▍Модель передачи управления для потоков (Yield-To)

Дизайн ОС не предполагает явных функций ожидания (wait) или взаимного исключения для потоков. Вместо этого межпотоковая синхронизация достигается явной передачей управления другим задачам, идентифицируемым при помощи указателя на канал и номера задачи в канале. То есть, управление передается не конкретной задаче, а любому агенту, который предоставляет соответствующий сервис и проиндексирован в своем сервисном канале. При этом, за конкретную эффективную внутреннюю реализацию протокола для синхронизации отвечают сами коммуникационные агенты.
Операционная система может поддерживать множественные стратегии передачи управления задачами, такие как «уступить любой», «уступить заданной» и «разрешить исполнение заданной». Последняя стратегия подразумевает, что уступающая задача не намеревается отдавать контроль на «своем» CPU, так что новая задача должна быть активирована в параллель с текущей на другом CPU.

▍Детерминированное планирование задач

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

▍Производительность

Так как в ROS модели может существовать несколько провайдеров одного и того же сервиса (по сети или даже локально), единственным критерием выбора между различными провайдерами будет их производительность в данных конкретных условиях.
Для управления производительностью программ системный канал имеет счетчики-таймеры активного времени и времени ожидания, предоставляющие информацию о состоянии текущей задачи. Так как обычно известно, какой задаче (каналу) было передано управление, производительность может быть оценена просто по значению соответствующего таймера по возвращении в вызывающую задачу.
Сравнив производительности нескольких каналов, задача может переключиться на использование более быстрых из них, что может оказаться особенно полезным в условиях динамической адаптации к меняющимся условиям (обширные сети, физически нестабильные окружения и т.д.).

▍Упрощение структуры кода

Поскольку каналы представляют собой динамические структуры данных, разделяемые между адресными пространствами задач и подверженные возможным перемещениям в памяти ядром ОС, код каналов, да и задач в общем случае должен быть написан так, чтобы не зависеть от статичного положения данных в памяти (или располагать их относительно вызывающего кода) и позиции самого кода. Отсутствие статичных структур, независимость от позиции и контекст из 4-ех указателей позволяют обойтись без сложных заголовков программ и определяет следующую простейшую структуру программы:
Каждая программа представляется в памяти в виде двух частей: кода и данных (с опциональным разграничением между ними для обеспечения защиты доступа). Начало куска кода – это стартовая функция задачи, принимающая на вход три указателя, как описано выше.

▍Создание и структура задач

Задача может быть создана из любого фрагмента кода. Адрес начала кода будет являться адресом стартовой функции задачи – той самой, с прототипом четырех указателей – func(chan, sys, loc), где chan и loc – опциональные (так, loc – может указывать на локальные данные), а sys – указывает на созданный для задачи системный канал. Заметим, что код задачи копируется на новое место в памяти для упрощения дальнейших операций с каналами – например, если часть кода будет экспортирована в качестве канала.
Внутренняя структура задач выглядит так:
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 3

Каждая задача содержит дескриптор, описывающий ее тип (type) – пользовательский или привилегированный, а также указатели на: таблицы страниц адресного пространства (address space), локальную память (heap), стек (stack) и обработчик исключений (exception handler).
Помимо этого, дескриптор задачи включает список отображенных каналов – (task2chan), т.е. список пар: «индекс-дескриптор канала, указатель на код данного канала».
В случае, когда задачи разделяют общее адресное пространство, указатели каналов могут пропускаться с целью оптимизации использования памяти.
Первый элемент списка отображенных каналов – это всегда системный канал задачи. Он хранит контекст исполнения и другие свойства, как описано дальше.

▍Диспетчеризация задач

Дизайн ОС предусматривает два типа активации задач: (а) по системному таймеру согласно запрашиваемому расписанию и (б) по явной передаче управления из другой задачи.
Расписание активации может быть запрошено в виде периода и продолжительности активации. Эти два параметра определяют следующую диаграмму состояния задачи

Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 4
Граф изменения состояния задач.

Изначально, при активации задачи, она входит в защищенное (active-protected) состояние, то есть, она не подлежит вытеснению. После того, как задача превышает заявленную продолжительность, защита от вытеснения снимается. В случае, когда в листе готовых к исполнению задач (ready list) есть другая задача, система может вытеснить первую задачу, поместить ее дескриптор в ready list и активировать другую задачу. Вновь активированная задача возобновляет исполнение либо в активном состоянии (если она была вытеснена ранее), либо в защищенном состоянии (в случае запланированного исполнения или вообще для всех задач, в зависимости от конкретной реализации)

Важный момент: задание расписания исполнения на основе периода позволяет ядру системы гарантировать задачам реального времени определенную частоту активации, а не предельный срок ее начала. Таким образом, система может сдвигать задачи во времени, если это не нарушает частоту активации. Задание продолжительности позволяет избежать проблем, связанных с приоритетом задач, посредством определения для них предварительно запланированного (запрошенного) отношения активности/ожидания. Конкретные реализации ОС могут ограничить продолжительность максимальной длиной кванта времени, чтобы «жадные» задачи не занимали процессор бесконечно. Все вышесказанное упрощает схему планировщика и обеспечивает его быстрое функционирование.
Планировка на основе заданных периода и продолжительности активации позволяет планировщику определять неизбежные конфликты (см. рисунок ниже) и либо отклонять запрос на активацию, либо смягчать требования планировки реального времени.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 5

Помимо вытесняющей многозадачности, система поддерживает и кооперативную. Явная передача управления может осуществляться с помощью специального системного метода yield(). Вызов этого метода приведет к передаче управления задаче, идентифицируемой при помощи указателя на канал и номера задачи в нем, и исполнению данной задачи на заданном процессоре. Также, как было сказано выше, возможно передать управление не конкретной задаче, а любому агенту, который предоставляет соответствующий сервис и проиндексирован в своем сервисном канале.

Каждый конфликт планировки (включая динамический, когда задачу вытесняют по превышению продолжительности исполнения) в общем случае приводит к выделению ядром ОС дополнительных ресурсов – нового стека для активированной задачи. Поэтому для оптимального использования системных ресурсов каждая задача может сообщить ядру ОС, что она не нуждается в сохранении регистров и данных стека. В этом случае ОС при вызове yield() может освободить стек задачи и сбросить содержимое регистров. При активации такой задачи система вызовет прототип {func(chan, sys, loc)} на пустом стеке и неопределенном содержании регистров.
Для обеспечения запланированной или вытесняющей активации задачи диспетчер задач поддерживает список готовых к выполнению задач. Это – кольцевой буфер, содержащий индексы-дескрипторы (или указатели) задач и запланированное время активации, как показано на рисунке ниже. Для вытесненных задач это время будет, конечно, нулевым.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 6

Время может выражаться в абсолютных или относительных единицах (по отношению к максимальному периоду), и обновляться в соответствии с запрошенными периодами активации. Когда задача вытесняется или уступает контроль, ее дескриптор помещается в первый свободный слот кольцевого буфера. Отметим, что так как список упорядочен по времени, задача может быть вставлена в список перед другими, если крайний срок ее активации наступает раньше.
Готовые к исполнению задачи могут выбираться из листа, начиная с конца (хвоста) или с указателей на листы задач (task list pointers), содержащихся в Блоках управления процессорами, которые описаны ниже.

Накладные расходы планировки задач разделяются на статические и динамические. Первые – это постоянное время инициализации процессорного контекста задачи (напомним, что именно это время уменьшает кооперативная многозадачность), а вторые – накладные расходы самого диспетчера задач, пропорциональные количеству операций поиска в листе готовности задач (как для поиска текущей готовой к исполнению задачи, так и для вставки новой). При этом, нет необходимости поиска задачи для активации, так как она всегда будет находиться в «хвосте» списка.
Накладные расходы на вставку задач будут минимальными, если все задачи запросили периоды активации одинаковой продолжительности. Если же периоды разные, то поиск займет логарифмическое число операций, так как лист готовности включает набор следующих друг за другом, по большей мере, сортированных данных, а несортированные элементы могут быть найдены по цепочке ссылок.

▍Управление каналами

Каждому каналу в системе присваивается дескриптор, который хранит информацию о топологии канала (type), его идентификатор (guid), а также указатели: на тело канала (body pointer) в общем адресном пространстве, где размещены все каналы; и на дескрипторы задач (chan2task), которые присоединены к данному каналу. То есть, система хранит таблицу дескрипторов каналов, показанную на рисунке ниже:
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 7

Обратные ссылки каналов на задачи необходимы для идентификации задач посредством указателя на канал и внутриканального индекса (для передачи управления между задачами). В этой схеме внутриканальный индекс – это позиция дескриптора задачи (task descidx) в соответствующем листе chan2task.

▍Системные вызовы и управление контекстом

Системные каналы – это комплекс средств для идентификации задач, сохранения их контекста, получения системной информации, генерации системных вызовов и передачи управления другим задачам.
На рисунке ниже показана структура системного канала.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 8

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

Комбинация в одном канале четырех-указательного окружения исполнения и контекста исполнения может быть удобной для целей удаленной отладкимониторинга.
Информация о хронометраже каждой задачи обновляется ядром ОС в реальном времени. Наличие полей, относящихся к каждому процессору системы, позволяет параллельную активацию задач (идентификация задач происходит на основе таблицы дескриптора канала).
Конкретные реализации системы могут выделить один регистр (доступный для чтения непривилегированным задачам – например, при наличии поддержки в железе, сегментный регистр) для хранения постоянного указателя на текущий системный канал задачи. В этом случае задачам не требуется сохранять указатель на системный канал, вместо этого они могут ссылаться на него через соответствующий макрос.

Системный канал должен обязательно поддерживать следующие функции, которые, фактически, будут являться системным API:

//(1) Создание новой задачи из заданной функции func указанного размера, типа и аргументов; возвращает статус {ok, err};
int exec(func, size, type, arg0, arg1); 

//(2)  Передача упpавления задаче, присоединенной к указанному каналу под заданным индексом subidx; либо любой готовой задаче. Вариант без аргументов предназначен для задания конкретного процессора для исполнения задачи (вызову предшествует заполнение соответствующих полей системного канала); возвращает статус {ok, err}; 
int yield(chan, subidx, strategy); 
int yield(); 

//(3)  Уничтожение вызывающей данную функцию задачи;
 void exit(); 

//(4) Выделение буфера памяти заданного размера и типа. Возвращает указатель на выделенный буфер
 void* malloc(size, type); 

//(5) Освобождение буфера памяти, заданного указателем-аргументом; возвращает статус {ok, err};
int free(pointer); 

//(6)Экспорт канала заданного ID (guid) и топологии (type) по заданному адресу. Возвращает новый адрес канала.
 void* export(pointer, guid, type); 

//(7) Импорт канала заданного ID (guid) и топологии (type) по заданному адресу. Возвращает новый адрес канала.
void* import(pointer, guid, type); 

//(8) Отключение задачи от данного канала; возвращает статус {ok, err, channel_destroyed}; 
int disconnect(pointer); 
 
//(9)  Получение индекса задачи в заданном канале; искомый индекс – возвращаемое значение функции.
int self(pointer); 

То есть, весь системный API состоит менее, чем из 10 функций!!

▍Управление процессорами

Каждому процессору в системе присваивается Блок Управления Процессором, который содержит некоторые свойства текущей выполняемой задачи, как показано на рисунке ниже.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 9

Также в соответствие процессору ставятся некоторые другие управляющие таблицы – прерываний (включая межпроцессорные) и таблица системных дескрипторов.

Уровни сложности системы

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

▍Реализация управления каналами

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

Каналы могут идентифицироваться уникальными 128-битными идентификаторами либо определенными индексами, валидность которых может контролироваться локальной (масштаба системы или сети) или глобальной службой.
Еще одна возможность – присвоение каналам имен, образованных в виде иерархических путей, подобных системным файловым путям существующих ОС.

▍Реализация управления задачами

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

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

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

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

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

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

//(10) Связь вектора прерываний с его обработчиком., функция возвращает статус {ok, err}
int connect(int vector, void* handler); 

При вызове этой функции система проверяет привилегии инициатора запроса и текущий индекс задачи. Ядро ОС гарантирует при получении каждого прерывания из указанного вектора переключение контекста на привилегированную задачу – инициатора запроса. Нулевой параметр handler удаляет предварительно установленный обработчик прерываний.
Дескриптор канала в этом случае будет содержать функцию- обработчик прерываний.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 10

Примеры коммуникаций посредством каналов

▍Дуальные каналы

Простейшая модель коммуникации задач – это дуальные каналы. Они гарантируют обслуживание только двух коммуницирующих агентов, хотя каждый из них может соединяться с более чем одним каналом того же ID. То есть, и один провайдер сервиса может предоставлять его нескольким клиентам (каждому – в отдельном канале), и один потребитель получать одну и ту же услугу от разных поставщиков.

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

Дуальные каналы кода практически эквивалентны каналам с данными, единственное отличие – они предоставляют разделяемый интерфейс, который инкапсулирует операции обработки и передачи данных, что может оказаться более удобным в некоторых случаях объектно-ориентированного дизайна. В этом случае операции передачи управления могут осуществляться внутриканальным кодом по поручению текущей задачи.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 11
Здесь и на последующих рисунках U-Task означает пользовтельскую задачу.

▍Мульти-каналы

Мульти-каналы могут использоваться для обеспечения вычислительного сервиса – то есть, для разделения кода между несколькими задачами (подобно динамическим библиотекам во многих традиционных ОС).
Разделяемый код при этом необязательно должен поддерживаться своей задачей-экспортером.
Задача может первоначально экспортировать канал и завершиться, а внутриканальный код при импорте разместит локальные данные в адресном пространстве импортирующей задачи и, таким образом, будет являться как полностью интегрированным с импортером, так и изолированным от других импортеров (для случаев, когда конкретная реализация ОС поддерживает изоляцию адресных пространств).
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 12

Ниже обсуждаются более сложные случаи мульти-канальных конфигураций.

▍Пулы задач

Предназначение пулов задач – поддержка синхронизационных моделей, традиционно используемых в симметричной многопроцессорности (SMP). Каналы данных, к которым присоединены несколько задач, могут быть как контейнером для разделяемых (обрабатываемых параллельно) данных, так и служить средством синхронизации, то есть, включать в себя счетчик числа задач-пользователей канала и, опционально, их индексов в канале.
Пулы задач могут использовать возможности планировки задач ОС и обеспечивать эффективную синхронизацию параллельных задач – в противоположность наиболее примитивным схемам опроса каналов для синхронизации.

Экспортер канала может взять на себя обязанности распределения работы и, так как количество процессоров и распределяемых задач известно, экспортер может передать управление определенным задачам на определенных ЦПУ. Когда задачи-исполнители завершат обработку данных, они могут вернуть управление распределяющей работу задаче.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 13

▍Пулы запросов

Пулы запросов могут оказаться эффективными в условиях, когда создание отдельного канала для каждого клиента потребовало бы значительных затрат памяти – при разделении больших буферов памяти иили обслуживании большого числа клиентов.
Эти пулы обеспечивают интерфейсы, которые можно назвать арбитражными. Победитель арбитража может поместить запрос (передать данные), а другие инициаторы запросов могут быть переключены в неактивное состояние (прозрачным образом – с использованием внутриканального кода). Когда обслуживание победителя арбитража завершается, победитель извещается об этом, копирует свои выходные данные, и арбитражный процесс возобновляется – из остальных участников арбитража выбирается победитель, пробуждается операцией yield, и процесс полностью повторяется.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 14

▍Маркеры доступа

Маркеры доступа (Access tokens) позволяют реализовать модель разделения данных (иили получения одинакового обслуживания ) между несколькими задачами без установки каналов передачи данных. Вместо этого, единственный провайдер сервисов управляет всеми ресурсами или обеспечивает сервисы всем активным агентам в системе. Каждый агент может запросить у него маркер доступа, который и будет являться ключом к разделяемым данным. Затем агенты могут «поделиться» маркером с другими доверенными агентами (посредством специального канала обмена), в результате чего доверенные агенты смогут получить тот же самый тип обслуживания или доступа к данным.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 15

▍Параллельный доступ к устройствам

Прежде чем говорить о работе с физическими устройствами, заметим, что драйвера в ROS OS – это не особые выделенные сущности, а обычные привилегированные задачи, предоставляющие свои сервисы посредством каналов.
Во многих случаях необходимо обеспечить нескольким задачам одновременный доступ к потокам данных, производимым или потребляемым физическим устройством. На рисунке ниже показаны 2 типичных примера параллельной коммуникации с устройством.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 16

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

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

▍Удаленное использование каналов

В соответствии с дизайном ОC – независимостью работы с компонентами от их расположения, каждый агент в локальной системе может соединиться с каналом, экспортируемым удаленно. Для поддержки этого механизма в систему вводятся так называемые репликаторы каналов, которые помимо поддержки обычной функциональности экспортаимпорта каналов опрашивают свои локальные системы на предмет имеющихся каналов, проверяют наличие сетевых запросов на эти каналы, и, при их наличии, поддерживают пары (наборы) удаленно синхронизированных каналов максимально прозрачным образом для удаленных клиентов этих каналов. Иллюстрация описанному механизму – ниже.
Создаем новую OS. Действительно новую, реально операционную, и правда – систему - 17
Здесь и на последующих рисунках P-Task означает пользовтельскую задачу.

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

 //(11)запрос имеющихся каналов;  Функция возвращает длину массива каналов или ошибку.
 int query(guid[], size[], type[]); 

Здесь guid[] – массив индексов каналов, size[]предоставляет размеры каналов, а type[] содержит типытопологию запроса канала (импортированный, экспортированный или мульти).

▍Поддержка со стороны языков программирования

Существующие языки программирования потребуют небольшого расширения для поддержки ROS модели и облегчения разработки для предлагаемой ОС. В случае C++, расширение, затрагивающее семантику языка, это модификатор типа – “channel”, показывающий тип содержимого, которое должно быть объединено в форму, пригодную для межпроцессной коммуникации. Оставшиеся проблемы, связанные с канальной коммуникацией, решаются с использованием функций ROS OS API, как показано ниже.

// объявление
channel class A // новый модификатор типа
{ 
int x; 
virtual void f(); 
void g(); 
} *a, *b, *c, *d; 

//системный канал для вызова системных функций
syschan_t* sys; 
bool multi = false; // или true для мультиканалов

// выделение памяти
a = new A; // объединяет x, f, g и vft 
b = new A; // выделение пространства для импорта

// служебные функции
c = export(a, sys, multi); // экспортировать канал типа A 
if(c != a){ delete a; a = c; } // система переместила канал 
d = import(b, sys, multi); // импортировать канал типа A 
if(d != b){ delete b; b = d; } // система переместила канал
int i = self(a, sys); // получить свой индекс в канале
disconnect(a, sys); // отсоединить экспортированный канал
disconnect(b, sys); // отсоединить импортированный канал
//OS может освобождать дескрипторы каналов

То есть, сначала требуется объявить тип вашего канала (используя модификатор типа channel), а также выделить стандартным способом области памяти для экспортера и импортера канала. После чего канал, очевидно, должен быть присоединен посредством экспорта с одной стороны и импорта с другой. Для этого используются специальные функции export() и import(), в которые передаются указатели на ваш канал, системный канал и указывается вид канала (мульти или дуальный).
Проверки указателей, приведенные после вызовов этих функций, приведены здесь для сред с разделяемым адресным пространством, где существует вероятность, что ядро ОС передвинет тело канала в памяти с выделенного ранее места.
Каждые агент, подсоединенный к каналу, имеет свой внутриканальный индекс, получаемый функцией self(), который остается неизменным де тех пор, пока агент не отключится от канала (вызовом disconnect()) и не присоединится к нему снова (посредством import() и export()). Остальные связанные с каналами функции в соответствии с дизайном ОС рекомендуется определить так, чтобы они принимали указатель на ваш канал, текущий системный канал, а также указатель на локальное хранилище данных или опциональный параметр.
Ниже приведен прототип функции chanfunc, которая может являться как функцией запуска задачи с семантикой (arg0, sys, arg1), так и функцией инициализации канала (которая выделит локальное хранилище данных и вернет его указатель loc), либо вообще любой служебной внутриканальной функцией

/// sys – Указатель на системный канал
/// chan – указатель на сам канал
/// loc – указатель на локальные данные или опциональный параметр 
void* chanfunc(chan, sys, loc);

▍Генерация кода

При написании программ для ROS ОС с использованием существующих компиляторов, предназначенных для других ОС, надо соблюдать осторожность. Так, разработчик должен позаботиться о неиспользовании статических переменных и работать только с автоматически (стек) и динамически выделяемыми данными и/или пользоваться возможностями некоторых компиляторов по генерации кода, не зависящего от адреса загрузки. В идеале специальный компилятор для данной ОС должен быть способен обнаружить ссылки на статические данные и сгенерировать вычисление соответствующего адреса в памяти относительно к адресу использующей эти данные функции.
Еще одна проблема, которая не может быть решена традиционными компиляторами (кроме ассемблера) – это поддержание комбинированных каналов кода и данных. Специализированный компилятор данной ОС должен уметь объединять тела канальных функций и данные в единый монолитный объект, который может быть отображен, передвинут и обработан ОС.
Для процессорных архитектур, поддерживающих защиту исполнения данных, агрегация кода и данных должна проходить на базисе раздельных страниц, чтобы не компрометировать безопасность системы.

Выводы

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

Многие из описанных принципов дизайна ОС были первоначально воплощены ее автором еще в прошлом веке (1999 году) как часть легковесной ОС, служившей предсказуемой и полностью контролируемой средой тестирования для исполняемых файлов Windows NT. И, конечно же, с тех пор многое было добавлено и улучшено.
Как видите, создать свою ОС на базе описанных принципов возможно. Было бы желание. Если оно у вас есть, то автор идеи, несомненно заинтересованный в ее реализации, с удовольствием вам поможет – проконсультирует и поддержит.

Автор: Intel

Источник

Поделиться новостью

* - обязательные к заполнению поля