Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Вопросы и ответы по ОС.doc
Скачиваний:
37
Добавлен:
27.08.2019
Размер:
3.35 Mб
Скачать

18. Идея ос на базе jit-vm. Недостатки, достоинства, ограничения концепции (смотреть, например, Singularity)

Just-in-time compilation (JIT) - технология увеличения производительности программных систем, использующих байт-код, путём трансляции байт-кода в машинный код непосредственно во время работы программы. Т.о достигается высокая скорость выполнения (сравнимая с компилируемыми языками) за счёт увеличения потребления памяти (для хранения результатов компиляции) и затрат времени на компиляцию. JIT базируется на двух более ранних идеях, касающихся среды исполнения: компиляции байт-кода и динамической компиляции.

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

Концепция ОС на базе JIT-VM будет рассмотрена на примере ОС Singularity.

Singularity – это ОС, разрабатываемая как основа для более надежного системного и прикладного ПО. Singularity написана на Sing#, который является расширением языка С#. Архитектура ОС построена на основе 3 ключевых абстракций: ядра, SIP и каналов. Микроядро содержит основную функциональность системы, включая управление памятью, создание и завершение процессов, работу каналов, планирование (scheduling) и ввод/вывод. Как и в случае других микроядер, большая часть функциональности системы находится в процессах вне ядра. Ядро Singularity практически полностью состоит из безопасного (safe) кода, а остальная часть системы (включая все драйверы устройств, системные процессы и приложения), исполняемая в SIP, состоит только из безопасного кода.

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

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

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

Объектные пространства не могут совместно использовать указатели. Это 1) улучшает абстракцию данных и изоляцию процесса, скрывая детали реализации и предотвращая появление висячих указателей (dangling pointers) на выгруженные процессы; 2) ослабляет ограничения при реализации, позволяя процессам использовать разные исполняющие системы и их сборщики мусора; 3) делает прозрачными учет и восстановление ресурсов, благодаря однозначно монопольному использованию памяти процессом; 4) упрощает интерфейс ядра, устраняя потребность управлять множеством типов указателей и адресных пространств.

Недостатки и ограничения архитектуры

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

  2. MMU (memory management unit) можно обойти с помощью DMA. В обычной ОС драйверы работают в режиме ядра и им доверяют, а в Singularity это проблема. Правда в новейших архитектурах процессоров доступ железа к пямяти контролируется.

  3. (от меня:) скорость должна быть меньше – иначе где-то подвох. то есть система, основанная на jit, как риалтаймовая система по любому не прокатит)

Главные достоинства Singularity:

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

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

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

  4. Поддержка языка и компилятора для создания всей системы из безопасного кода и для проверки межпроцессных коммуникаций с явным управлением ресурсами.

  5. Устранение различий между ОС и подсистемой исполнения безопасного языка, такой, как JVM или CLR.

  6. Повсеместное использование спецификаций для описания, конфигурирования и проверки компонентов.

19. Внешний интерфейс ядра (совокупность системных вызовов). Интерфейс стандартной библиотеки Си и его взаимосвязь с системными вызовами. Разделение задач между ядром и стандартной библиотеки (на примере futex).

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

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

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

Более подробно на примере Linux 2.4 и архитектуры i386:

Во время начальной загрузки системы вызывается функция arch/i386/kernel/traps.c:trap_init(), которая настраивает IDT (Interrupt Descriptor Table) так, чтобы вектор 0x80 указывал на точку входа system_call из arch/i386/kernel/entry.S.

Когда пользовательское приложение делает системный вызов, аргументы помещаются в регистры и приложение выполняет инструкцию int 0x80. В результате приложение переводится в привилегированный режим ядра и выполняется переход по адресу system_call в entry.S. Далее:

  1. Сохраняются регистры.

  2. В регистры ds и es заносится KERNEL_DS, так что теперь они ссылаются на адресное пространство ядра.

  3. Если значение eax больше чем NR_syscalls, то возвращается код ошибки ENOSYS.

  4. Если задача исполняется под трассировщиком (tsk->ptrace & PF_TRACESYS), то выполняется специальная обработка. Сделано это для поддержки программ типа strace и отладчиков.

  5. Вызывается sys_call_table+4*(syscall_number из eax). Эта таблица инициализируется в том же файле (arch/i386/kernel/entry.S) и содержит указатели на отдельные обработчики системных вызовов, имена которых, в Linux, начинаются с префикса sys_, например sys_open, sys_exit, и т.п. Эти функции снимают со стека свои входные параметры, которые помещаются туда макросом SAVE_ALL.

  6. Вход в 'system call return path'. Проверяется необходимость вызова планировщика (tsk->need_resched != 0) и имеются ли ожидающие сигналы.

Linux поддерживает до 6-ти входных аргументов в системных вызовах. Они передаются через регистры ebx, ecx, edx, esi, edi (и ebp для временного хранения). Номер системного вызова передается в регистре eax.

Интерфейс стандартной библиотеки языка C

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

Большинство системных вызовов, возвращающих целое значение, использует значение -1 при возникновении ошибки и значение большее или равное 0 при нормальном завершении. Системные вызовы, возвращающие указатели, обычно для идентификации ошибочной ситуации пользуются значением NULL. Для точного определения причины ошибки C-интерфейс предоставляет глобальную переменную errno, описанную в файле <errno.h> вместе с ее возможными значениями и их краткими определениями. Для получения символьной информации об ошибке на стандартном выводе программы для ошибок может применяться стандартная функция perror().

Разделение задач между ядром и стандартной библиотеки (на примере futex)

Ядро Linux обеспечивает работу с фьютексами (futex, 'Fast Userspace muTexes') как со строительным материалом для связей и семафоров пространства пользователя. Фьютекс идентифицируется блоком памяти, который может разделяться между разными процессами.

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

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

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