На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
  
> The bulletins of the projects
    Концептуальные основы Virtual Operating System (VS).

    Тема доступна здесь: http://pascal.sources.ru/cgi-bin/forum/YaBB.cgi?board=Projects;action=display;num=1062777820

    Изменения:
       nvm, 10/09/2003 - начальная версия
       Shaman, 10/09/2003 - добавлена заметка Модератора
       nvm, 14/09/2003 - уточнены детали работы с памятью и список функций ядра.

    Заметка Модератора:
    Данный проект очень напоминает KeyKOS и GNU Hurd (по целям и заявлениям участников обсуждения), за исключением отдельных деталей, которые имеют ИМХО очень спорные обоснования автора. Настоятельно рекомендую всем желающим участвовать в обсуждении ознакомиться с существующими решениями: KeyKOS, Hurd, QNX, UNIX, Linux.
    ЗЫ: Hurd не является UNIX системой в чистом виде. Это (как и QNX) операционные системы, поддерживающие стандарт posix.


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

    Ограничения тестовой версии.

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


    Основные понятия.

    Ключ - универсальный идентификатор «субъекта» (приложения).

    Вид:
    root\parent1\...\parentN\name, где name - идентификатор ключа, parentX - идентификаторы предков.

    Действия:
    - ключ можно создать,
    - уничтожить,
    - передать,
    - ключом можно владеть,
    - можно ассоциироваться с ключом,
    - на ключ можно ссылаться.

    Правила.

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


    Файл, процесс=задача=приложение, поток=нить.
    Указатели в системе все 6-байтные - включают сегмент.
    Handle - целое число, позволяющее ссылаться на объект приложения.

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

    Handle — целое число, позволяющее ссылаться на объект приложения. Физически handle это всегда индекс в массиве дескрипторов объектов задачи. Такие массивы хранятся в ядре. Каждой задаче и каждому типу объектов выделяется свой массив – соответственно, своя индексация.

    Секция памяти – системная абстракция, эквивалентная сегменту в i386.
    Дескриптор секции.
    struct Section
    {
         pointer start;
         pointer end;
         int segment_selector;
         enum Type {CODE, DATA, STACK, OTHER};
         Type type;
         bool read_only;
         bool is_static; // невыгружаемая
    };

    Функции ядра:
    — управление памятью и портами ВУ,
    — управление потоками,
    — поддержка (обеспечение) ключей,
    — транспортировка сообщений.

    Список функций.

    Работа с памятью.
    Выделение и освобождение памяти осуществляется только по секциям.
    handle GetMemory(const Section&,  bool allow_absence=false) — выделяет память заданного размера на заданный указатель (приложение само конфигурирует свой виртуальный образ). Доп. флаг разрешает выделение памяти с неподгруженными страницами.
    handle GetMemory(handle task_key, handle section) — вариант функции: вместо размера передается handle памяти, полученной от другого процесса.
    bool EnlargeSection(handle section, pointer new_end, bool allow_absence) – увеличить секцию.
    handle ShareMemory(handle task_key, handle section, bool read_only) — открыть секцию другому процессу.
    bool FreeMemory(handle section) — освобождает память по заданному указателю.

    Работа с ключами.
    handle CreateKey(char* name, handle parent_key) — создать ключ.
    handle FindKey(char* full_name) — найти ключ. Ключ ядра не имеет имени, но его handle в любом процессе равен 0.
    bool GrantKey(handle key, handle recipient_key) — передать права на ключ.

    bool BindKey(handle code_section, pointer entry_point, handle task_key) – связывает ключ с точкой входа обработчика сообщений.
    bool CreateTask(handle task_key) — при создании задачи ей должен быть передан ключ, который будет с ней ассоциирован. Создает только адресное пространство, но не запускает потока. Для запуска потока задачи необходимы вызовы BindKey и  Fork(Run)Message.

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

    bool PostMessage(handle key, int len, const pointer buffer[, handle sender_key]) — поставить сообщение в очередь ключу. Если ключ не имеет обработчика, идет поиск вверх по иерархии наследования. Параметр sender_key используется только для посылки сообщений потомку и означает ключ отправителя, который будет указан в сообщении.
    bool SendMessage(handle key, int len, const pointer buffer, int resp_len, pointer resp_buffer) — поставить сообщение в очередь процессу с ожиданием обработки и получить ответное сообщение (эквивалент функционального вызова).
    bool RunMessage(handle stack, handle key, int len, const pointer buffer, int resp_len, pointer resp_buffer)  — переслать сообщение с ожиданием обработки и получить ответное сообщение. При этом в принимающей задаче автоматически создается новый поток (с указанным стеком) в счет ресурсов отправителя (получается полный эквивалент функционального вызова).
    bool ForkMessage(handle stack, handle key, int len, const pointer buffer, priority)  — переслать сообщение. При этом в принимающей задаче автоматически создается новый поток в счет ресурсов отправителя, поток отправителя не приостанавливается. Функция также является единственным средством создания рабочего потока (ключ получателя в том же адресном пространстве). Параметр priority задает приоритет создаваемого потока.
    bool ReturnMessage(handle key, int len, const pointer buffer) — отправить ответ на Send/Run/ForkMessage. Если получатель является создателем данного потока, то поток завершается. Ответ на ForkMessage приходит в форме PostMessage.
    bool CreateMessageQueue(handle buffering_section) — создать очередь сообщений.
    const Message* GetMessage(int waiting_time) – прочитать сообщение в очереди.

    handle CreateSemaphore().
    bool KillSemaphore(handle).
    bool LockSemaphore(handle, int waiting_time).
    bool UnlockSemaphore(handle) – смысл функций синхронизации очевиден.

    bool TraceMessages(handle key) — включить перехват сообщений задачи. В тестовой версии не реализовывать.

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

    bool SetPrivilegeLevel(int level) – установить уровень привилегий процессора для процесса. Доступна только при владении ключом root.

    bool OwnPort(int port) — «приватизировать» порт.
    bool GrantPort(handle key, int port) — передает приложению доступ к порту.
    bool RevokePort(handle key, int port) — запрещает приложению доступ к ранее переданному порту.
    bool PortIn(int &value, int port) – чтение порта.
    bool PortOut(int value, int port) – запись в порт.

    Обработка прерываний.

    Все прерывания (как аппаратные, так и программные) непосредственно перехватываются только ядром. Однако ядро само обрабатывает ТОЛЬКО прерывания таймера (и, в перспективе, страничные сбои при подкачке). Все остальные прерывания переадресовываются приложениям через вызов RunMessage. Таким образом, суть прерывания сводится к созданию новой нити в приложении-обработчике.
    Приоритет прерывания целиком отражается (сводится к) приоритетом созданного потока.

    bool BindInterrupt(handle key, int interrupt, priority) – связать прерывание с ключом обработчика. Параметр priority задает приоритет потока, создаваемого прерыванием.
    bool FreeInterrupt(int interrupt) – «освободить» прерывание.

    Доступ к портам устройств.

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

    Техническая реализация системных вызовов.

    Обращения к системе производятся через специально выделенное прерывание (одно на все вызовы). Параметры (входные и выходные) передаются в стеке.

    Концепция пользователя.

    Понятие пользователя в системе локализовано в пределах приложения - менеджера пользователей (таких приложений может быть несколько). Пользователь представлен именем, паролем и списком доступных ключей.
    Всем пользовательским интерфейсом в системе управляет специальное приложение-драйвер (GUI), и пользователь напрямую общается только с ним.
    Это приложение «во всем слушается» пользователя, но изначально не имеет никаких прав. Для регистрации пользователь сообщает ему логин, который пересылается менеджеру, который презентует в GUI соответствующие ключи.
    Пример работы GUI.
    Обычное приложение в среднем имеет права доступа на запись только в одну папку на диске. Пусть это будет Интернет-браузер. Предположим он получил от пользователя команду сохранить документ. Прав на запись в нужную папку он не имеет. Но в любом случае он должен обратиться к GUI с заданием вывести диалог FileSave. В качестве результата будет получено не имя файла, а handle, уже открытый на запись!
    Такая технология позволяет избежать наличия у приложений лишних прав. Тот же Интернет-браузер может теперь смело запускать любые вирусы (хоть exe, хоть макро). Худшее, что те смогут сделать - стереть кэш скачанных файлов.

    Файловая система PlainFS.

    Логическая структура файла:
    struct File
    {
         key read;      ключ доступа на чтение
         key write;       ключ доступа на запись
         int attribute;      атрибут (в том числе признак стертого файла)
         int8 refs[];      ссылки на файлы (смещения до первых секторов)
         char name[];      имя файла
         char data[];      данные
    };

    Файл является единственным элементом системы, содержит ссылки на другие файлы, заменяя каталоги.
    Права на запись не дают прав на чтение. Если ключи доступа пустые, то используются ключи предка.
    Ссылки все двусторонние. Первая ссылка - на предка.

    Каждый сектор диска имеет следующие служебные поля:
    - относительный адрес начального сектора файла,
    - признаки исправности предыдущего и следующего секторов (?),
    - относительный адрес следующего сектора файла.

    Все области диска равноправны, выделенные служебные структуры (области) отсутствуют, положение корневого файла произвольно.
    Сообщение отредактировано: Shaman -
    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
    0 пользователей:


    Рейтинг@Mail.ru
    [ Script execution time: 0,0339 ]   [ 15 queries used ]   [ Generated: 28.04.24, 10:07 GMT ]