Операционные системы смарт-карт

 

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

Некоторые смарт-карты рассчитаны на применение языка Java. Это значит, что ПЗУ смарт-карты содержит интерпретатор Java Virtual Machine (JVM — виртуальной машины Java). На карту загружаются Java-аплеты (небольшие программы), которые выполняются JVM-интерпретатором. Некоторые из этих карт способны справлять­ся сразу с несколькими Java-апплетами, что влечет за собой работу в мультипро­граммном режиме и необходимость установки очередности выполнения программ. При одновременном выполнении двух и более апплетов приобретают актуальность вопросы управления ресурсами и защиты, которые должны быть решены с помощью имеющейся на карте операционной системы (как правило, весьма примитивной).

 

 

Структура операционной системы

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

Монолитные системы

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

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

Тем не менее даже такие монолитные системы могут иметь некоторую струк­туру. Службы (системные вызовы), предоставляемые операционной системой, за­прашиваются путем помещения параметров в четко определенное место (например, в стек), а затем выполняется инструкция trap. Эта инструкция переключает машину из пользовательского режима в режим ядра и передает управление операционной системе (шаг 6 на рис. 1.17). Затем операционная система извлекает параметры и определяет, какой системный вызов должен быть выполнен. После этого она перемещается по индексу в таблице, которая в строке k содержит указатель на про­цедуру, выполняющую системный вызов k (шаг 7 на рис. 1.17).

 

 
 

 

 


Такая организация предполагает следующую базовую структуру операционной системы:

1. Основная программа, которая вызывает требуемую служебную процедуру.

2. Набор служебных процедур, выполняющих системные вызовы.

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

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


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

Многоуровневые системы

Обобщением подхода, показанного на рис. 1.21, является организация операцион­ной системы в виде иерархии уровней, каждый из которых является надстройкой над нижележащим уровнем. Первой системой, построенной таким образом, была система THE, созданная в Technische Hogeschool Eindhoven в Голландии Э. Дейк- строй (Е. W. Dijkstra) и его студентами в 1968 году. Система THE была простой па­кетной системой для голландского компьютера Electrologica Х8, имевшего память 32 К 27-разрядных слов. Как показано в табл. 1.3, у системы было шесть уровней. Уровень 0 занимался распределением ресурса процессора (процессорного времени), переключением между процессами при возникновении прерываний или истечении времени таймера. Над уровнем 0 система состояла из последовательных процес­сов, каждый из которых мог быть запрограммирован без учета того, что несколько процессов были запущены на одном процессоре. Иными словами, уровень 0 обе­спечивал основу многозадачности центрального процессора.

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

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

 
 


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

Следует отметить, что система уровней в конструкции ТНЕ играла лишь вспо­могательную роль, поскольку все части системы в конечном счете компоновались вместе в единую исполняемую программу, а в MULTICS кольцеобразный механизм главным образом существовал в процессе выполнения и реализовывался за счет аппаратного обеспечения.

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

Микроядра

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

Различные исследователи определяли количество ошибок на 1000 строк кода (например, Basilli and Perricone, 1984; Ostrand and Weyuker, 2002). Плот­ность ошибок зависит от размера модуля, возраста модуля и других факторов, но приблизительная цифра для солидных промышленных систем — 10 ошибок на 1000 строк кода. Следовательно, монолитная операционная система, состоящая из 5 000 000 строк кода, скорее всего, содержит около 50 000 ошибок ядра. Разумеется, не все из них имеют фатальный характер, некоторые ошибки могут представлять собой просто выдачу неправильного сообщения об ошибке в той ситуации, которая складывается крайне редко. Тем не менее операционные системы содержат столь­ко ошибок, что производители компьютеров снабдили свою продукцию кнопкой перезапуска (которая зачастую находится на передней панели), чего не делают производители телевизоров, стереосистем и автомобилей, несмотря на большой объем программного обеспечения, имеющийся в этих устройствах.

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

Было разработано и получило распространение множество различных микро­ядер (Accetta et al, 1986; Haertig et al, 1997; Heiser et al., 2006; Herder et al, 2006; Hildebrand, 1992; Kirsch et al., 2005; Liedtke, 1993, 1995, 1996; Pike et al., 1992; Zu- beri et al., 1999). Особенно часто они используются в приложениях, работающих в реальном масштабе времени в промышленных устройствах, авионике и военной технике, которые выполняют особо важные задачи и должны отвечать очень вы­соким требованиям надежности. Часть общеизвестных микроядер представляют Integrity, К42, L4, PikeOS, QNX, Symbian и MINIX 3.

Кратко рассмотрим микроядро MINIX 3, в котором максимально использована идея модульности и основная часть операционной системы разбита на ряд неза­висимых процессов, работающих в режиме пользователя. MINIX 3 — это POSIX- совместимая система с открытым исходным кодом, находящаяся в свободном доступе по адресу www.minix3.org (Herder et al., 2006а; Herder et al., 2006b).

Микроядро MINIX 3 занимает всего лишь около 3200 строк кода на языке С и 800 строк кода на ассемблере, который использован для самых низкоуровневых функций, в частности для перехвата прерываний и для переключения процессов. Код на языке С занимается управлением и распределением процессов, управляет межпроцессным взаимодействием (путем обмена сообщениями между процессами) и предлагает набор примерно из 35 вызовов ядра, позволяя работать остальной части операционной системы. Эти вызовы выполняют функции подключения обработчиков к прерываниям, перемещения данных между адресными простран­ствами и установки новых схем распределения памяти для только что созданных процессов. Структура процесса МINIХ 3 показана на рис. 1.22, где обработчики вызовов ядра обозначены как Sys. В ядре также размещен драйвер часов, потому что планировщик работает с ними в тесном взаимодействии. Все остальные драйверы устройств работают как отдельные пользовательские процессы.

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

 

 


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

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

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

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

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

Клиент-серверная модель

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

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

Очевидным развитием этой идеи будет запуск клиентов и серверов на разных компьютерах, соединенных локальной или глобальной сетью, как показано на рис. 1.23. Поскольку клиенты связываются с серверами путем отправки сообщений, им не обязательно знать, будут ли эти сообщения обработаны локально, на их соб­ственных машинах, или же они будут отправлены по сети на серверы, расположен­ные на удаленных машинах. Что касается интересов клиента, следует отметить, что в обоих случаях происходит одно и то же: отправляются запросы и возвращаются ответы. Таким образом, клиент-серверная модель является абстракцией, которая может быть использована как для отдельно взятой машины, так и для машин, объединенных в сеть.

Становится все больше и больше систем, привлекающих пользователей, си­дящих за домашними компьютерами, в качестве клиентов, а большие машины, работающие где-нибудь в другом месте, — в качестве серверов. Фактически по этой схеме работает большая часть Интернета. Персональные компьютеры отправляют запросы на получение веб-страницы на сервер, и им возвращается эта веб-страница. Это типичная картина использования клиент-серверной модели при работе в сети.

Виртуальные машины

Первые выпуски OS/360 были системами исключительно пакетной обработки. Но многие пользователи машин IBM/360 хотели получить возможность инте­рактивной работы с использованием терминала, поэтому различные группы раз­работчиков как в самой корпорации IBM, так и за ее пределами решили написать для этой машины системы с разделением времени. Позже была выпущена офици­альная система разделения времени — TSS/360, и когда она наконец-то дошла до потребителей, то была настолько громоздкой и медлительной, что под нее было переоборудовано всего лишь несколько вычислительных центров. В конечном счете от этого проекта отказались, после того как на него уже было потрачено 50 млн долларов (Graham, 1970).

VM/370

Группа из научного центра IBM Scientific Center в Кембридже, Массачусетс, раз­работала совершенно другую систему, которую IBM в конечном итоге приняла как законченный продукт. Эта система, первоначально называвшаяся CP/CMS, а позже переименованная в VM/370 (Seawright and MacKinnon, 1979), была осно­вана на следующем проницательном наблюдении: система с разделением времени обеспечивает 1) многозадачность и 2) расширенную машину с более удобным ин­терфейсом, чем у простого оборудования. Сущность VM/370 заключается в полном разделении этих двух функций.

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

Поскольку каждая виртуальная машина идентична настоящему оборудованию, на каждой из них может работать любая операционная система, которая может быть запущена непосредственно на самом оборудовании. На разных виртуальных маши­нах могут быть запущены разные операционные системы, как это часто и происходит на самом деле. Изначально на системах VM/370 одни пользователи запускали в сво­их виртуальных машинах OS/360 или одну из других больших операционных систем пакетной обработки или обработки транзакций, в то время как другие запускали одно­пользовательскую интерактивную системуCMS (Conversational Monitor System —система диалоговой обработки ) для пользователей системы разделения времени.

 
 


Когда программа под управлением операционной системы CMS выполняет системный вызов, он перехватывается в системное прерывание операционной системы на своей собственной виртуальной машине, а не на VM/370, как это было бы при ее запуске на реальной, а не на виртуальной машине. Затем CMS вы­дает обычные команды ввода-вывода для чтения своего виртуального диска или другие команды, которые могут ей понадобиться для выполнения этого вызова. Эти команды ввода-вывода перехватываются VM/370, которая выполняет их в рам­ках моделирования реального оборудования. При полном разделении функций многозадачности и предоставления машины с расширенной архитектурой каждая из составляющих может быть намного проще, гибче и удобнее для обслуживания.

В своем современном перерождении z/VM обычно используется для запуска нескольких полноценных операционных систем, а не упрощенных, однопользова­тельских систем вроде CMS. Например, на машинах zSeries можно запустить одну или несколько виртуальных машин Linux, а наряду с ними запустить обычные операционные системы IBM.