В централизованной однопроцессорной системе, как правило, важно только относительное время и не важный точность времен. В распределенной системе, где каждый процессор имеет собственные времена со своей точностью хода, ситуация резко меняется: программы, которые используют время (например, программы, подобный команде make в UNIX, что используют время создания файлов, или программы, для которых важно время прибытия сообщений и т.п.) становятся зависимыми от того, временами какого компьютера они пользуются.
Целиком другой подход к достижению взаимного исключения в распределенных системах иллюстрируется рисунком 3.7. Все процессы системы образуют логическое кольцо, то есть каждый процесс знает номер своей позиции в кольце, а также номер ближайшего к нему такого процесса. Когда кольцо инициализируется, процесса 0 передается так называемый токен. Токен циркулирует по кольцу. Он переходит от процесса n к процессу n+1 путем передача сообщения по типу " точка-точка". Когда процесс получает токен от своего соседи, он анализирует, не нужно или ему самому войти в критическую секцию.
Системы, которые составляются с нескольких процессов, часто легче программировать, используя так называемые критические секции. Когда процесса нужно читать или модифицировать некоторые что разделяются структуры данных, он прежде всего входит в критическую секцию для того, чтобы обеспечить себе исключительное право использования этих данных, при этом он уверен, что никакой процесс не будет иметь доступа к этому ресурсу одновременно с ним. Это называется взаимным исключением. В однопроцессорных системах критические секции защищаются семафорами, мониторами и другими аналогичными конструкциями.
Чтобы понять работу RPC, рассмотрим сначала выполнение вызова локальной процедуры в обычной машине, которая работает автономно. Пусть это, например, будет системный вызов
count=read (fd,buf,nbytes); где fd - целое число, buf - массив символов, nbytes - целое число.
Чтобы осуществить вызов, что вызывает процедура заталкивает параметры у стек в обратном порядке (рисунок 3.1). После того, как вызов read выполнен, он помещает что поворачивается значения в регистр, перемещает адрес возвращения и возвращает управление процедуре, которая вызывает, что выбирает параметры со стека, возвращая его в исходное состояние.
Примитивы, которые были описаны, есть небуферизуемыми примитивами. Это означает, что вызов ПОЛУЧИТЬ сообщает ядру машины, на которой он выполняется, адрес буфера, в который следует поместить сообщение, которые находится для него.
Эта схема работает прекрасно при условии, что получатель выполняет вызов ПОЛУЧИТЬ раньше, чем отправитель выполняет вызов ПОСЛАТЬ. Вызов ПОЛУЧИТЬ сообщает ядру машины, на которой выполняется, по какому адресу должно поступить ожидаемое сообщение, и в какую область памяти необходимо его поместить.
Рассмотрим вопрос о том, как клиент задает местоположение серверу. Одним из методов решения этой проблемы есть непосредственное использование сетевого адреса серверу в клиентской программе. Недостаток такого подхода - его чрезвычайная негибкость: при перемещении серверу, или при увеличении числа серверов, или при изменению интерфейса во всех этих и многих других случаях необходимо перекомпилировать все программы, которые использовали жесткую задачу адреса серверу. Для того, чтобы избежать всех этих проблем, в некоторых распределенных системах используется так называемое динамическое связывание.
Весь зависимый от устройства код помещается в драйвер устройства. Каждый драйвер управляет устройствами одного типа или, может быть, одного класса.
В операционной системе только драйвер устройства знает о конкретных особенностях какого-то устройства. Например, только драйвер диска имеет дело с дорожками, секторами, цилиндрами, временами установления головки и других факторов, которые обеспечивают правильную работу диска.
Драйвер устройства принимает запрос от устройств программной прослойки и решает, как его выполнить.
Взаимодействие программных компонентов при выполнении отдаленного вызова процедуры иллюстрируется рисунком 3.2. После того, как клиентский стаб был вызванный программой-клиентом, его первой задачей является заполнения буфера что отправляется сообщениям. В некоторых системах клиентский стаб имеет единый буфер фиксированной длины, которая заполняется каждый раз из самого начала при поступлении каждого нового запроса. В других системах буфер сообщения представляет собой пул буферов для отдельных полей сообщения, причем некоторые из этих буферов уже заполнены.
Физическая организация файла описывает правила расположения файла на устройстве внешней памяти, в частности на диске. Файл составляется из физических записей - блоков. Блок - наименьшая единица данных, которой внешнее устройство обменивается с оперативной памятью. Непрерывное размещение - простейший вариант физической организации (рисунок 2.34,а), при какому файла дается последовательность блоков диска, которые образуют единый сплошной участок дисковой памяти. Для задачи адреса файла в этом случае достаточно заметить только номер начального блока.
Устройства ввода вывода делятся на два типа: блок-ориентированные устройства и байт-ориентированные устройства. Блок-ориентированные устройства берегут информацию в блоках фиксированного размера, каждый из который имеет свой собственный адрес. Проще всего блок-ориентированное устройство - диск. Байт-ориентированные устройства не адресуемы и не позволяют делать операцию поиска, они генерируют или потребляют последовательность байтов. Примерами являются терминалы, строчные принтеры, сетевые адаптеры.
Файлы идентифицируются именами. Пользователи дают файлам символьные имена, при этом учитываются ограничения ОС как на используемые символы, так и на длину имени. До недавнего времени эти границы были очень узкими. Так в популярной файловой системе FAT длина имен ограничивается известной схемой 8.3 (8 символов - собственное имя, 3 символа - расширение имени), а в ОС UNIX System V имя не может содержать более 14 символов. Однако пользователю намного удобнее работать с длинными именами, поскольку они позволяют даты файла действительно мнемоническое название, по которому даже через достаточно большой промежуток времени можно будет припомнить, что содержит этот файл.
В некоторых файловых системах запить к внешним устройствам, в которых адресация осуществляется блоками (диски, ленты), перехватываются промежуточной программной прослойкой-подсистемой буферизации. Подсистема буферизации представляет собой буферный пул, который располагается в оперативной памяти, и комплекс программ, которые управляют этим пулом. Каждый буфер пула имеет размер, равный одному блоку. При поступлении запроса на чтение некоторого блока подсистема буферизации просматривает свой буферный пул и, если находит необходимый блок, то копирует его в буфер процесса, который спрашивает.
Программист имеет дело с логической организацией файла, представляя файл в виде определенным чином организованных логических записей. Логическая запись - это наименьший элемент данных, которым может оперировать программист при обмене с внешним устройством. Даже если физический обмен с устройством осуществляется большими единицами, операционная система обеспечивает программисту доступ к отдельной логической записи. На рисунке 2.33 показанные несколько схем логической организации файла. Записи могут быть фиксированной длины или переменной длины.
Раньше припускалось, что когда отправитель посылает сообщение, адресат его обязательно получает. Но реально сообщения могут теряться. Предположим, что используются примитивы, которые блокируют. Когда отправитель посылает сообщение, то он прекращает свою работу до тех пор, пока сообщение не будет послано. Однако нет никаких гарантий, которые после того, как он восстановит свою работу, сообщение будет доставлено адресату.
Для решения этой проблемы существует три подхода. Первый заключается в том, что система не берет на себя никаких обязательств по поводу доставки сообщений.
Все средства синхронизации, которые были рассмотрены раньше, относятся к нижнему уровню, например, семафоры. Они требуют от программиста детального знания алгоритмов взаимного исключения, управление критическими секциями, умение предотвращать клинчи (взаимные блокирования), а также владение средствами восстановления после краха. Однако существуют средства синхронизации более высокого уровня, которые освобождают программиста от необходимости вникать во все эти подробности и позволяют ему сконцентрировать свое внимание на логике алгоритмов и организации параллельных вычислений.
Большая часть программного обеспечения ввода вывода есть независимой от устройств. Точная граница между драйверами и независимыми от устройств программами определяется системой, потому что некоторые функции, которые могли бы быть реализованы независимым средством, в действительности выполненные в виде драйверов для повышения эффективности или по другим причинах.
Типичными функциями для независимой от устройств прослойки есть:
обеспечение общего интерфейса к драйверам устройств,
именование устройств, защита устройств,
обеспечение независимого размера блока,
буферизация,
распределение памяти на блок-ориентированных устройствах,
распределение и освобождения выделенных устройств,
сообщение об ошибках.
Прерывания должны быть скрыты как можно глубже в недрах операционной системы, чтобы как можно меньшая часть ОС имела с ними дело. Наилучшее средство составляется в разрешении процесса, который инициировал операцию ввода вывода, блокировать себя к завершению операции и наступление прерывания. Процесс может блокировать себя, используя, например, вызов DOWN для семафора, или вызов WAIT для переменной условия, или вызов RECEIVE для ожидания сообщения. При наступлении прерывания процедура обработки прерывания выполняет разблокирование процесса, который инициировал операцию ввода вывода, используя вызовы UP, SIGNAL или посылая процесса сообщения.
Функционирование любой файловой системы можно представить многоуровневой моделью (рисунок 2.36), у которой каждый уровень предоставляет некоторый интерфейс (набор функций) уровню , который вышележит, а самый, в свою очередь, для выполнения своей работы использует интерфейс (оборачивается с набором запросов) уровня , который нижележит.
Рис. 2.36. Общая модель файловой системы
Задачей символьного уровня являются определения по символьному имени файла его уникального имени.
Основная идея организации программного обеспечения ввода вывода составляется в разбивке его на несколько уровней, причем нижние уровне обеспечивают экранизацию особенностей аппаратуры от верхних, а те, в свою очередь, обеспечивают удобный интерфейс для пользователей.
Ключевым принципом является независимость от устройств. Вид программы не должен зависеть от того, читает ли она данные из гибкого диска или из жесткого диска.
Очень близкой к идее независимости от устройств есть идея одинакового именования, то есть для именования устройств должны быть принятые единые правила.
В сравнении с доступом к памяти, традиционный доступ к файлам выглядит запутанным и неудобным. По этой причине некоторые ОС, начиная с MULTICS, обеспечивают отображение файлов в адресное пространство выполняемого процесса. Это выражается в появлении двух новых системных вызовов: MAP (отобразить) и UNMAP (отменить отображение). Первый вызов передает операционной системе в качестве параметров имени файла и виртуальный адрес, и операционная система отображает указанный файл в виртуальное адресное пространство по указанному адресу.
В реальных системах возможность попадания в кэш составляет приблизительно 0,9. Высокое значение возможности пребывания данных в кэш-памяти связано с наличием в данных объективных свойств: пространственной и временной локальности.
Пространственная локальность. Если состоялось обращение по некоторому адресу, то с высокой степенью возможности в ближайшее время состоится обращения к соседним адресам.
Временная локальность. Если состоялось обращение по некоторому адресу, то такое обращение по этому же адресу с широкой озможность состоится в ближайшее время.
Определить права доступа к файла - значит определить для каждого пользователя набор операций, что он может применить к данному файла. В разных файловых системах может быть определенный свой список что дифференцируются операций доступа. Этот список может включать такие операции:
создание файла,
уничтожение файла,
открытие файла,
закрытие файла,
чтение файла,
запись в файл,
дополнение файла,
поиск в файле,
получение атрибутов файла,
установление новых значений атрибутов,
переименование,
выполнение файла,
чтение каталога,
и другие операции с файлами и каталогами.
Примитивы бывают что блокируют и не блокируют, иногда они называются соответственно синхронными и асинхронными. При использовании примитива, который блокирует, процесс, который выдал запрос на его выполнение, прекращается к полному завершению примитива. Например, вызов примитива ПОЛУЧИТЬ прекращает процесс, который вызывает, к получению сообщения.
При использовании примитива, который не блокирует, управления поворачивается процесса, который вызывает, немедленно, еще до того, как необходимая работа будет выполнена.
Понятие "нить"
В традиционных ОС понятие нити тождественно понятию процесса. В действительности желательно иметь несколько нитей управления, которые разделяют единое адресное пространство, но выполняющихся квазипараллельно.
Предположим, например, что файл-сервер блокируется, ожидание выполнения операции с диском. Если сервер имеет несколько нитей управления, вторая нить может выполняться, пока первая нить находится в состоянии ожидания. Это повышает пропускную способность и производительность.
Когда процесс хочет войти в критическую секцию, он формирует сообщение, которые содержит имя нужной ему критической секции, номер процесса и текущее значение времени. Потом он посылает это сообщение всем другим процессам. Предполагается, что передача сообщение надежное, то есть получение каждого сообщения сопровождается подтверждением. Когда процесс получает сообщение такого рода, его действия зависят от того, в каком состоянии относительно указанной в сообщении критической секции он находится.
При вызовах кодов, которые владеют разными уровнями привилегий, возникает проблема передачи параметров между разными стеками, потому что для надежной защиты задачи разного уровня привилегий имеют разные сегменты стеков. Селекторы этих сегментов хранятся в контексте задача - сегменте TSS (Task State Segment). Если визивається подпрограмма, которая имеет другой уровень привилегий, то из текущего стека у стек уровня доступа что вызывается сегмента копируется столько 32- разрядных слов, сколько указано в поле счетчика слов дескриптора шлюза.
При включенной системе управления страницами работает как описанный выше сегментный механизм, так и механизм управления страницами, однако при этом содержание работы сегментного механизма меняется. В этом случае виртуальное адресное пространство задачи имеет размер в 4 Гбайта, в котором размещаются все сегменты (рисунок 2.22). По старику селектор задачи определяет номер виртуального сегмента, а сдвиг в команде задача - сдвиг внутри этого сегмента. Потому что теперь все сегменты разделяют одно адресное пространство, то возможно их наложение, но процессор не контролирует такие ситуации, оставляя эту проблему операционной системе.
В случае, когда селектор указывает на таблицу LDT, виртуальный адрес превратится в физический аналогичным воображением, но для доступа к самой таблице LDT добавляет еще один этап, потому что в процессоре регистр LDTR указывает на размещение таблицы LDT не прямо, а косвенно. Самый регистр LDTR имеет размер 16 битый и содержит селектор дескриптора таблицы GDT, что описывает расположение этой таблицы в физической памяти. Поэтому при доступе к элементу физической памяти через таблицу LDT происходит двукратное преобразование виртуального адреса в физический, причем оба раза по описанной выше схеме.
В идеале RPC должен функционировать правильно и в случае отказов. Рассмотрим такие классы отказов:
Клиент не может определить местоположение сервера, например, в случае отказы нужного серверу, или через того, что программа клиента была скомпилирована давно и использовала старую версию интерфейса серверу. В этом случае в ответ на запрос клиента поступает сообщение, которое содержит код ошибки.
Утерян запрос от клиента до серверу. Самое простое решение - через определенное время повторить запрос.
Разработчики новых операционных систем ринутся обеспечить пользователя возможностью работать сразу с несколькими файловыми системами. В новом понимании файловая система составляется с многих составляющих, в число которых входят и файловые системы в традиционном понимании.
Новая файловая система имеет многоуровневую структуру (рисунок 2.39), на верхнему уровне которой располагается так называемый переключатель файловых систем (в Windows 95, например, такой переключатель называется устанавливаемым диспетчером файловой системы - installable filesystem manager, IFS).
Второй общий подход к реализации механизма транзакций называется списком намерений. Этот метод заключается в том, что модифицируются самые файлы, а не их копии, но перед изменением любого блока ведется запись в специальный файл - газета регистрации, где отмечается, которая транзакция делает изменения, который файл и блок меняется и которые старое и новое значения изменяемого блока. Только после успешной записи у газету регистрации делаются изменения в исходном файле. Если транзакция фиксируется, то и об этом делается запись в газету регистрации, но старые значения измененных данных хранятся.
Для того, чтобы послать сообщение, необходимо заметить адресу получателя. В очень простой сети адреса может задаваться в виде константы, но в более сложных сетях нужен и более утонченное средство адресации.
Одним из вариантов адресации на верхнему уровне есть использования физических адресов сетевых адаптеров. Если в компьютере, который получает, выполняется только один процесс, то ядро будет знать, что делать с сообщением, которое поступило - передать его этому процессу. Однако, если на машине выполняется несколько процессов, то ядру не известно, которому из них предназначенное сообщение, поэтому использование сетевого адреса адаптеру в качестве адреса получателя приводит к очень серьезному ограничению - на каждой машине должен выполняться только один процесс.
s#0