Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Московский Государственный Университет
Факультет вычислительной математики и кибернетики
Конспект по курсу лекций Операционные системы
Операционные системы
Мы рассмотрим классовый подход в организации Операционных систем (ОС).
Основные блоки: 1)введение (историческое развитие вычислительных систем (ВС), определяемое появлением и развитием программного обеспечения и взаимодействием с аппаратными средствами компьютера; обзор свойств и характеристик ОС
2)методы и принципы организации ОС (процессы и взаимодействие процессов, файловая система, планирование, управление внешними устройствами, управление оперативной памятью, сетевое взаимодействие)
Введение экскурсия в этот предмет.
Понятие ОС ключевое понятие, связанное с ЭВМ система интерфейсов для обеспечения удобства общения пользователя с компьютером, взаимодействия пользователя с аппаратными средствами компьютера.
У всех ОС разное назначение. История появления и развития ОС связано с развитием ОС.
Развитие вычислительной техники и программного обеспечения
Вычислительную технику традиционно или исторически разделяли на так называемые поколения. Поколение это группа компьютеров, которые объединены по совпадению определенного набора признаков. Обычно это архитектура, элементная база, области применения и т.д.
Первое поколение компьютеров появилось в связи с массовым вычислением задач связанных с обороной (ядерное оружие и т.п.). Для ввода/вывода и в качестве запоминающего устройства использовались перфоленты. Строки вручную вводились в оперативную память и затем выполнялись.
Проблемы В случае возникновения ситуаций типа деления на нуль компьютер останавливался. Изменять программу также было очень тяжело, так как машинные коды завязаны на адресацию, следовательно, для того, чтобы редактировать, приходилось сдвигать всю программу. Чтобы не сдвигать, делали безусловный переход на конец программы, затем возвращались обратно.
Что появилось:
• однопользовательский, персональный режим
• зарождение класса сервисных, управляющих программ
• зарождение языков программирования
Появились такие упрощения жизни программистов как язык Assembler (появились мнемонические обозначения), программный транслятор из Assembler в машинные коды, появилась возможность вносить комментарии. Благодаря появлению мнемонических обозначений, корректировать программу стало проще.
Если компьютеры 1-го поколения были связаны восновном с вооружением, войной, то компьютеры 2-го поколения уже стали более распространенными и начали применяться в более привычных для нас сферах работы: управление предприятиями, сбор информации и т.д. Строились они на новой элементной базе на полупроводниковых приборах это диоды и транзисторы. Конец 50-х 2-я половина 60-х годов.. Размер компьютеров второго поколения на порядки уменьшился по сравнением с компьютерами первого поколения, уменьшилась энергопотребление, уменьшились габариты, увеличилась скорость.
Стало возможно создавать более сложные по архитектуре системы. Уменьшились размеры, следовательно, проводники стали короче, следовательно, время работы уменьшилось и тепла стало выделяться меньше.
Пакетная обработка заданий Для работы компьютера формировался пакет программ, уход от персонального компьютера.
Проблема: Внешнее устройство было медленное, поэтому самая дорогая часть компьютера центральный процессор(ЦП)- основное время простаивал. (пока ленту на нужное место промотаешь…) На ранних этапах рядом с компьютером сидела девушка и на кнопку нажимала, чтобы компьютер запустить…
Мультипрограммирование В памяти находились несколько программ пользователя(ей).Если одна программа не может выполняться(например, в связи с работой с внешним устройством), то можно запускать другую. Хорошо бы запускать компьютер только тогда, когда все готово, чтобы от девушки не зависеть…
Языки управления заданиями Нужен был язык, который позволял бы до начала работы программы сформировать требования, которые необходимы для ее выполнения.
Чтобы предотвратить зацикливание нужно знать:
1)Максимальное время счета программы
2) Объем оперативной памяти, которая будет использована максимально
3) Сколько памяти на магнитной ленте потребуется
4) Стоит ли магнитная лента …
Проблема: Работа с внешними устройствами упростилась, то есть появились стандартные управляющие программы, но у каждого внешнего устройства своя управляющая программа.
файловые системы Появились файловые системы. Пользователю представилась возможность именовать данные и сохранять их. Можно не знать, где конкретно они лежат.
виртуальные устройства Проблема: Развивались внешние устройства. У каждого свои особенности управления, а их очень много, поэтому неудобно. Появились виртуальные устройства обобщение над всеми маленькими.
Их появление вызвало массовое внедрение вычислительной техники в повседневную жизнь. Массовое появление фирм, каждая из которых производила свою модель компьютеров.
Проблема: В результате идентичные устройства от разных производителей не взаимозаменялись.
Решение:
1)У третьего поколения все устройства стали унифицироваться.
2) Устройства стали использовать идентичные расходные материалы. Все стало унифицированное (машинная лента, катушка)
Все стало стыковаться за счет унификации аппаратных интерфейсов
создание семейств компьютеров Раньше программное обеспечение жило столько, сколько компьютер. Программы погибали вместе с компьютерами. Появились задачи, требующие компьютер с вполне определенной архитектурой. Например, для управления больницей компьютер с большой внешней памятью. Компьютеры стали программно-приемственными снизу вверх. Семейства различались по цене и возможностям. Стала возможной модернизация компьютеров.
Большее развитие получили операционные системы. Появились первые сильно развитые операционные системы, у которых архитектура и основные компоненты были унифицированы. Одной из первых операционных систем значимых, этапных для всего развития мирового программного обеспечения - было появление операционной системы UNIX. В операционных системах появились простые средства разработки драйверов, появились стандартные интерфейсы организации драйверов.
Основной аппаратной характеристикой компьютеров 4-го поколения является использование интегральных схем большой и сверхбольшой интеграции. Т.е. элементная база используют устройства в корпусе которого может быть реализован целый функциональный узел процессор и т.д. Появились абсолютно новые сферы применения и компьютер все более и более стал терять свойства устройства для выполнения программ и для работы с программистом. Развитие элементной базы оно определило с одной стороны потенциальную возможность сфер применения компьютерной техники, с другой стороны потребность создания максимально дружественных интерфейсов между пользователем и вычислительной системой (очень значимо). Появление дружественных интерфейсов позволило возродить понятие персональный компьютер. Были созданы все условия, как аппаратные так и программные, для того, чтобы компьютер стал персональным устройством. Т.е. компьютеры 4-го и далее поколений это персональные компьютеры, которые могут применяться везде и всюду, отсюда массовое распространение компьютеров. Миниатюризации вычислительной техники позволила совершить существенное развитие применения компьютеров, как встраиваемых устройств, используемых для управления теми или иными технологическими и производственными процессами. Развитие компьютеров 4-го поколения и далее обусловила толчок к развитию сетевых технологий. Развитию сетевых технологий прошло по пути развитию компьютеров. Изначально создавались корпоративные компьютерные сети. Развитие вычислительной техники, появление больших объемов информации потребовало создание средств, которые бы унифицировали с одной стороны, с другой стороны максимально упростили создание компьютерных сетей. Здесь можно говорить о первом решении, которое было формально предложано международным институтом стандартизации (ISO) это модель открытой системы ISO OSI. Модель ISO OSI немного обобщала уже опыт развития семейства протоколов, которые были сформированы в результате разработки проекта AADR, которое получило название TCP IP. Сетевые технологии это разработка обще принятых программных и аппаратных интерфейсов, которые позволяют подключать компьютер через разные интерфейсы к тем или иным сетям. Одной из проблем связанным с компьютерами 4-го поколения и последующих является проблема, связанная с обеспечением безопасности хранения и передачи данных. Информация стала товаром и предметом собственности. Следовательно, возникает проблема, связанная с обеспечением безопасности в части минимизации возможности несанкционированного доступа к этой информации. На сегодняшний день существует целая отрасль, занимающаяся этими проблемами.
Основы архитектуры вычислительной системы
Вычислительная система - совокупность аппаратных и программных средств, функционирующих в единой системе и предназначенных для решения задач определенного класса.
Структура вычислительной системы
Взаимодействие уровней осуществляется с помощью межуровневых интерфейсов.
Средства программные доступные на уровнях управления ресурсами ВС:
•Система команд компьютера
• Программный интерфейс драйверов устройств как физических, так и виртуальных.
Аппаратный уровень вычислительной системы
Аппаратный уровень ВС. С позиции верхних уровней это физические ресурсы и система команд ЭВМ. Каждому физическому ресурсу соответствует определенный аппаратный компонент компьютера и его характеристики.
Характеристики:
1)Правила программного использования (для ЦП система команд, для внешних устройств команды управления ими)
2) производительность и/или емкость (ЦП тактовая частота, разрядность обработки машинного слова; для внешнего запоминающего устройства объем помещающейся информации и скорость доступа)
3) Степень занятости или используемости (ЦП время, которое было затрачено, ОЗУ объем используемой памяти; для линий связи их загруженность)
Все эти характеристики определены для данного конкретного устройства в конкретном случае. Одно и тоже устройство может иметь различные характеристики, в зависимость от использования.
Средства программирования, доступные на аппаратном уровне:
• система команд компьютера;
• аппаратные интерфейсы программного взаимодействия с физическими ресурсами.
Управление физическими ресурсами ВС
Данный уровень является 1-м первым уровнем системного программного обеспечения вычислительной системы и его назначение в систематизации и стандартизации правил программного использования физических ресурсов. На этом уровне обеспечивается создание программ управления физическими ресурсами. Для обеспечения управления физическими ресурсами, используются программы, которые называются драйверами физического ресурса (устройства).
Драйвер физического устройства программа, основанная на использовании команд управления конкретного физического устройства и предназначенная для организации работы с данным устройством.
Драйвер А предоставляет возможность работы с блочной организацией данных на магнитной ленте. Это означает, что вся информация, которая записывается на магнитную ленту, представлена в виде последовательности блоков данных фиксированного размера, каждый из блоков данных пронумерован от начала этой ленты. Соответственно для чтения данных с ленты мы должны пропустить все блоки, который предшествуют необходимому нам блоку и после этого идет обмен.
2-я модель это модель, в которой магнитная лента организована в виде устройства, в котором используется запись определенной длины. В этом случае каждая запись ограничивается маркером начала и маркером конца. В общем случае длина записи может быть неограниченной. Для чтения информации при этой модели организации драйвера мы должны пропустить соответствующее количество записей до записи с необходимым номером.
Уровень управления физическими ресурсами программная составляющая вычислительной системы, обеспечивающая предоставление для каждого конкретного физического ресурса интерфейса для использования драйвер физического ресурса (устройства).
Драйвер физического устройства упрощает для пользователя интерфейс работы с устройством.
Драйвер физического устройства скрывает от пользователя детальные элементы управления конкретным физическим устройством. Драйвер физического устройства ориентирован на конкретные свойства устройства.
На данном уровне иерархии вычислительной системы обеспечивается корректное функционирование и использование физических ресурсов/устройств.
На этом уровне пользователю доступны: системы команд, аппаратные устройства, доступ к физическим ресурсам через соответствующие драйверы.
Проблемы:
1)Для работы на этом уровне надо быть знакомым с интерфейсом соответствующего устройства
2) Программа должна модифицироваться для работы с устройствами другого типа
Управление логическими/виртуальными ресурсами
Логическое/виртуальное устройство (ресурс) устройство/ресурс, некоторые эксплутационные характеристики которого (возможно все) реализованы программным образом.
Драйвер логического/виртуального ресурса - программа, обеспечивающая существование и использование соответствующего ресурса
Разветвленная иерархия виртуальных и физических устройств:
Драйверы делятся на 3 категории
1.Драйверы физических устройств
2. драйверы логических или виртуальных устройств, обобщающие и унифицирующие возможность доступа для устройств
одного класс.
3. Драйверы логических виртуальных устройств не существующих в аппаратном виде
.Рассмотрим пример ниже. Группа А - это драйвера физических устройств, мы видим здесь драйвера конкретных дисковых устройств, здесь драйвер жесткого диска фирмы IBM , здесь еще какой-нибудь; драйвер оперативной памяти и т.д. Группа В это драйвера виртуальных ресурсов, которые обобщают свойства конкретных устройств, в частности драйвер виртуального диска, это означает что в системе есть единый упрощенный интерфейс, который доступен для программиста и программ и который обеспечивает работу с виртуальным диском. Что в данный момент ассоциируется с виртуальным диском это уже проблема операционной системы, с ним может быть ассоциирован драйвер конкретного физического устройства (драйвер физического диска, драйвер оперативной памяти). Группа С файловая система;
Т.о. все что есть в ВС на программном уровне, мы можем переопределить в терминах ресурсов и драйверов.
Уровень управления физическими и виртуальными ресурсами составляют ОС.
Ресурсы вычислительной системы - совокупность всех физических и виртуальных ресурсов.
Одна из характеристик ресурсов вычислительной системы их конечность, следовательно возникает конкуренция за обладание ресурсом между его программными потребителями.
Операционная система - это комплекс программ, обеспечивающий управление ресурсами вычислительной системы.
Средства программирования, доступные на уровнях управления ресурсами ВС:
• система команд компьютера;
• программные интерфейсы драйверов устройств (как физических, так и виртуальных)
На уровне ОС пользователю доступны:
Системы программирования
Система программирование это комплекс программ, обеспечивающий поддержание жизненного цикла программы в вычислительной системе
Уровень системы программирования обеспечивает поддержание этапов жизни программы: проектирование, кодирование, тестирование, отладка, изготовление программного продукта.
Этапы ,связанные с разработкой и внедрением программы, называются жизненным циклом.
Проектирование
Обычно он включает исследование задачи, исследование характеристик объектной среды (как объектная среда будет связана с нашей системой).
Объектная среда это та ВС, в рамках которой продукт будет функционировать.
Построение модели функционирования автоматизированного объекта.
Определение характеристик инструментальной среды.
Инструментальная среда это ВС, которая будет использована для разработки программ. В каких-то случаях объектная среда и инструментальная среда могут совпадать. Выбор инструментальной среды - это выбор как аппаратной платформы для разработки так и тех программных инструментов, которые будут использованы при построении программного продукта.
Выбор алгоритмов и инструментальных средств. Проектирование решения, что будет использовано.
Априорная оценка ожидаемых результатов. Суть: предварительная оценка характеристик проектируемого решения(продукта). До начала его практической реализации.
Наличие априорной оценки позволяет:
а) достичь, в принципе, поставленной задачи;
б) повысить надежность рекурционной характеристики того решения, которое создается.
Понятно, что данная последовательность этапов проектирования достаточно укрупненная и не претендует на единственность, но по сути она такова. И соответственно на жизненном пути программы 1-й шаг это проектирование. По сути, без проектирования говорить о последующих этапах бессмысленно.
Результатом этапа проектирования является спецификация на создаваемое программное решение. Эти спецификации могут быть формальные и не формальные. Это зависит от использования тех или иных средств систем программирования и вообще тех или иных систем программирования. Соответственно 1-й этап проектирования это спецификация.
Кодирование
Спецификация бывает формальная и неформальная.
Средства для разработки программных продуктов
1) средство автоматизации контроля использования межмодульных интерфейсов, которые обеспечивают контроль за правильностью использования в программе классификаций регламентирующих межмодульные связи: количество параметров, типы параметров права доступа к параметрам и т.д.;
2) средство автоматизации получения объектных исполняемых модулей программы, обеспечивающее автоматический контроль за соответствием исходных модулей объектным и исполняемым модулям, т.е. предусматривается возможность последующего редактирования исходных модулей;
3) системы поддержки версий, т.е. системы, которые позволяют фиксировать состояние проекта в виде некоторых версий, а также протоколировать все изменения исходных модулей и при необходимости осуществлять возврат к предыдущим версиям.
Тестирование и отладка
Тестирование это проверка спецификаций функционирования программы на некоторых наборах входных данных. И после того можно говорить, что программа работает верно на том или ином наборе тестов. В связи с этим в тестировании есть проблема формирования тестового набора (покрытия), которая решается в зависимости от конкретной ситуации. Отладка процесс поиска, анализа и исправления зафиксированных при тестировании и эксплуатации ошибок.
Ввод программной системы в эксплуатацию внедрение) и сопровождение
Включает следующий набор требований:
1 - подготовка документации, иногда автоматической или автоматизированной;
2 - возможность сбора так называемых логов по функционированию программы или параметров функционирования программы для того, чтобы можно было анализировать как характеристики эксплуатации так возникающие внештатные ситуации и т.д . и т.п. .
Современные технологии разработки программного обеспечения
Каскадная модель
С точки зрения технологий программирования на сегодняшний день существует достаточно много различных моделей одна из них это каскадная модель. Ее суть заключается в том, что этапы проектирования, кодирования, тестирования и отладки связаны в некоторую единую однонаправленную последовательность действий и предполагается линейное движение по этим этапам. Т.е. постановщик задачи общается с заказчиком, получает задачу, формулирует постановку задачи, архитекторы занимаются проектированием. После того так проектирование закончено, уже к вопросам архитектуры и проектирования не возвращаются, т.е. идет уже утвержденное решение архитектурное, которое начинает реализовываться на этапе кодирования. Понятно, что эта модель она немножечко утрированная, потому что такая линейная последовательность без возврата встречается очень редко, хотя из-за того, что кодирование, тестирования и отладка очень часто связаны с некоторыми итерационными вещами. Так или иначе, мы можем в процессе тестирования обнаружить достаточно существенную ошибку, и возникает необходимость на кодирование и т.д
Каскадная итерационная модель
. Некоторым развитием каскадной модели является каскадно-итерационная модель. Суть ее заключается в том, что возможны возвраты на предыдущие этапы. В этом случае, очевидно, она более реальна, но она достаточно хаотическая. Это означает, что заказчик, предоставляя исполнителю задачу или проект, он в общем случае не имеет более или менее детерминированной информации о сроках реализации проекта, потому что количество внутренних итераций оно может быть произвольно, а получение результата, по которому можно оценивать успех или не успех выполнения проекта, так же во времени совершенно не детерминировано, все зависит от количества этих самых итераций. Вещь хорошая, но для заказчика такая не удобна, а для исполнителя такая модель очень удобна, если заказчик оплачивает каждый час работы. Ну и обе эти модели стадают трагедией связанной с тем, что, к сожалению, практика разработки программного проекта показывает на то, что все полностью учесть однозначно на этапе проектирования практически невозможно для более или менее сложной задачи. Т.е. так или иначе мы изначально доходим до этапа реализации, зная, что спроектировать раз и навсегда скорее всего не получится, хотя бы по интерфейсным частям, а иногда и по функциональным частям.
Спиральная модель организации жизненного цикла программных систем
Соответственно следующая модель, которая на сегодняшний день является достаточно популярной это спиральная модель организации жизненного цикла. Суть ее заключается в следующем: проект с точки зрения реализации разбивается на последовательность промежуточных результатов или прототипов, соответственно для получения каждого из прототипов, мы проходим этап проектирования прототипа, кодирования, тестирования, отладки. После получения прототипа предполагается этап детализации проекта. В результате которого мы обозначаем следующий прототип или уточняем спецификации и т.д. и вот эта спираль идет до тех пор пока один из прототипов не становится конечной программой, соответственно количество итераций может быть либо детерминированным (это зависит уже от организации взаимодействия заказчика и исполнителя), либо в пределах разумного не ограниченным.
Система программирования это комплекс программ, обеспечивающий технологию автоматизации :
• проектирования,
• кодирования,
• тестирования,
• отладки и сопровождения программного обеспечения.
Прототип - программа, частично реализующая функциональность и внешний интерфейс разрабатываемой системы.
Немного истории:
Начало 50-х годов ХХ века. Система программирования или система автоматизации программирования включала в себя ассемблер (или автокод) и загрузчик, появление библиотек стандартных программ и макрогенераторов.
Середина 50-х начало 60-х годов ХХ века. Появление и распространение языков программирования высокого уровня (Фортран, Алгол-60, Кобол и др.). Формирование концепций модульного программирования.
Середина 60-х годов начало 90-х ХХ века. Развитие интерактивных и персональных систем, появление и развитие языков объектно-ориентированного программирования.
90-е ХХ века настоящее время. Появление промышленных средств автоматизации проектирования программного обеспечения, CASE-средств (Computer-Aided Software/System Engineering), унифицированного языка моделирования UML.
Появляются промышленные средства автоматического проектирования програмного обеспечения. CASE средства.
Средства программирования, доступные на уровне системы программирования - программные средства и компоненты СП, обеспечивающие поддержание жизненного цикла программы
Средства программирования, доступные на уровне системы программирования - программные средства и компоненты СП, обеспечивающие поддержание жизненного цикла программы
Прикладные системы
Первый этап развития прикладных систем
Прикладная система программная система, ориентированная на решение или автоматизацию решения задач из конкретной предметной области.
Основной проблемой, возникающей как в ЭВМ в отдельности, так и в вычислительной системе в целом является несоответствие производительности основных компонентов друг другу. Так скорость обработки информации ЦП существенно превосходит скорость доступа к ОЗУ. В свою очередь скорость доступа к внешним устройствам существенно ниже этих показателей для ЦП и ОЗУ и т.д. Так как эти компоненты работают в системе, то на первый взгляд итоговая производительность такой системы будет определяться наименее “скоростным” компонентом (то есть заведомо основное влияние на системную производительность будет оказывать скорость доступа к ОЗУ, так как обращения в ОЗУ при работе ЭВМ происходят постоянно).
Второй этап развитие систем программирования и появление средств создания и использования библиотек программ
Третий этап
характеризуется появлением пакетов прикладных программ ,имеющих развитые и стандартизированные интерфейсы, возможность совместного использования различных пакетов.
Прикладные системы
Примеры:
Основные тенденции в развитии современных прикладных систем
• Стандартизация моделей автоматизируемых
бизнес-процессов
• B2B (business to business)
• B2C (business to customer)
• ERP (Enterprise Resource Planning)
• CRM (Customer Relationship Management)
• Открытость системы
• API - Application Programming Interface
Выводы
Базовые определения и понятия:
• Вычислительная система
• Физические ресурсы (устройства)
• Драйвер физического устройства
• Логические или виртуальные ресурсы (устройства)
• Драйвер логического/виртуального ресурса
• Ресурсы вычислительной системы
• Операционная система
• Жизненный цикл программы в вычислительной системе
• Система программирования
• Прикладная система
Основы компьютерной архитектуры
«Компьютер фон Неймана»:
• Джон фон Нейман (John Von Neumann)
• EDVAC (Electronic Discrete Variable Computer - Электронный Компьютер Дискретных Переменных)
• Предварительный доклад о компьютере EDVAC (A First Draft Report on the EDVAC).
• Джон Мочли (John Mauchly) и Джон Преспер Эккерт (John Presper Eckert).
• ENIAC (Electronic Numerical Integrator And Computer).
Структура, основные компоненты:
Структура компьютера фон Неймана
"принципы построения компьютера фон Неймана":
Принцип двоичного кодирования
(придуман в США в 30 е годы, все поступающие данные кодируются в виде двоичного сигнала) Суть заключается в том, что все поступающие и обрабатываемые компьютером данные кодируются при помощи двоичных сигналов. Т.е. как данные, которые должны обрабатываться, так и управляющая информация (команды), кодируются одинаково в двоичной системе.
Принцип программного управления
Программа состоит из команд, в которых закодирована операция и координаты или сами операнды, над которыми должна выполниться данная операция. Выполнение компьютером программы это автоматическое выполнение определенной последовательности, составляющих программу. Последовательность выполняемых компьютером команд определяется последовательностью и составом команд и данных, составляющих программу.
Принцип хранимой программы
Для хранения команд и данных программы используется единое устройство памяти, которое представляется в виде вектора слов, все слова имеют последовательную адресацию, команды и данные представляются и хранятся единым образом. Интерпретация информации, размещенной в памяти, происходит в момент выполнения команды, связанной с данной ячейкой памяти, т.е. мы последовательно подошли к какому-то машинному слову и выбрали его как машинную команду, то в этом случае содержимое этого машинного слова интерпретируется как машинная команда, если в такой-то команде операнд ссылается на это же самое машинное слова, то в этом случае содержимое этого машинного слова интерпретируется как данные, которые будут обрабатываться, т.е. это означает, что одну и ту же информацию, записанную в память можно представлять и как команды и как данные.
Предполагается, что компьютеры фон Неймана имеют следующие компоненты:
Предполагается, что ЦП состоит из устройства управления (УУ) и арифметико-логического устройства (АЛУ). УУ обеспечивает последовательный выбор команд, которые необходимо выполнить программе, их контроль, дешифрацию и в зависимости от типа команд последующую обработку. Последующая обработка может быть двух типов: либо команда выполняется в УУ (это, например, команда передачи управления), либо команда передается в АЛУ (в том случае если эта команда арифметического типа).
Рассмотрим каждое из компонентов модельной машины.
Оперативное запоминающее устройство
Компьютер представляет собой разнородные, разноцелевые, разноскоростные аппаратные устройства.
Функция ОЗУ - хранение программы, выполняющейся в компьютере
ОЗУ - устройство, предназначенное для хранения оперативной информации.
В ОЗУ размещается исполняемая в данный момент программа и используемые ею данные.
ОЗУ состоит из ячеек памяти, содержащей поле машинного слова и поле служебной информации.
В ОЗУ все ячейки памяти имеют уникальные имена, имя - адрес ячейки памяти.
Доступ к содержимому машинного слова осуществляется посредством использования адреса.
Машинное слово поле программно изменяемой информации.
в машинном слове могут располагаться
1) машинные команды (или части машинных команд)
2) данные, с которыми может оперировать программа.
Использование содержимого поля служебной информации необходимо для целостности и корректности использования машинного слова.
Служебная информация (иногда ТЭГ) поле ячейки памяти, в котором схемами контроля процессора и ОЗУ автоматически размещается информация, необходимая для осуществления контроля за целостностью и корректностью использования данных, размещаемых в машинном слове.
В поле служебной информации могут размещаться :
1)разряды контроля четности машинного слова, при чтении контроль соответствия;
2)разряды контроля «данные-команда»;
3)машинный тип данных осуществление контроля за соответствием машинной команды и типа ее операндов.
Использование содержимого поля служебной информации
1. Контроль за целостностью данных.
Элементарная схема контроля основывается на том, что тэг состоит из одного разряда, который формируется автоматически при записи информации в машинное слово. Этот разряд осуществляет контроль четности, либо контроль нечетности в зависимости от условия. Суть его в следующем: при выполнении команды записи некоторых данных в машинное слово схема управления ОП осуществляет подсчет двоичных единиц, записываемых в коде машинного слова, и в этом случае автоматически формируется контрольный разряд. Для нашего случая идет работа с контролем четности, т.е. если количество единичек в машинном слове нечетное, то в контрольном разряде появляется единичка, которая формирует четное количество единиц в ячейках, если четное, то в контрольном разряде выставляется ноль. Вот записали это слово, и все правильно сформировалось. При записи либо при чтении возможет сбой памяти и потеря содержимого разрядов. Соответственно при чтении осуществляется контроль содержимого ячейки в соответствии с содержимым машинного слова и разряда тэга. Если при контроле получается код, который не соответствует контролю четности или нечетности, то фиксируется ошибка памяти. Понятно, что эта схема совсем примитивная, ненадежная, потому что потеря или добавление четного количества единичек этой схемой контроля не фиксируется. Это пример. Дальше можно использовать более сложные модели организации контроля четности, но в этом случае уже используется не один разряд, а большее количество. Есть схемы, которые использую поле тега и содержимое машинного слова, могут частично исправлять ошибки, но это уже различия.
2. Контроль доступа к командам/данными.
Контроль осуществляется при помощи тегов. Если команда захочет рассмотреть данные в качестве команды то будет прерывание. Происходит проверка на семантическую правильность.
3. Контроль доступа к машинным типам данных
Тип данных определенный формат данных, с конкретным набором операций, известных для этого формата. Т.е. если выполняется команда «плавающий арифметикой» (арифметика с плавающей точкой),то она выполнится нормально в том и только в том случае, если операнды помечены как данные в формате с плавающей точкой, иначе произойдет ошибка. Соответственно при записи можно назначать, что эта ячейка есть плавающая точка определенный режим.
Важной характеристикой ОП является производительность ОП.
Производительность оперативной памяти - скорость доступа процессора к данным, размещенным в ОЗУ:
Cкорость доступа к данным ОЗУ существенно ниже скорости обработки информации в ЦП.
Фигурируют два компонента, которые определяют эту характеристику производительности:
•время доступа (access time- taccess) - время между запросом на чтение слова из оперативной памяти и получением содержимого этого слова.
•длительность цикла памяти (cycle time - tcycle) - минимальное время между началом текущего и последующего обращения к памяти.
(tcycle>taccess)
Необходимо, чтобы итоговая скорость выполнения команды процессором как можно меньше зависела от скорости доступа к коду команды и к используемым в ней операндам из памяти.
На самом деле одна из целей нашего курса проследить взаимосвязи влияния, которое оказывает производительность одного компонента на другой компонент и те обще системные решения, которые появляются в ВС в целом и в аппаратуре в частности для сглаживания дисбаланса в производительности. Эта одна из основных проблем, которые есть в архитектуре компьютера и в организации операционной системы. В связи с этим одно из аппаратных решений, которое применяется в архитектуре компьютера и предназначено для сглаживания дисбаланса между производительностью компьютера и производительностью ОП, есть расслоение памяти.
Расслоение памяти
Расслоение ОЗУ один из аппаратных путей решения проблемы дисбаланса в скорости доступа к данным, размещенным в ОЗУ и производительностью ЦП. Суть его в следующем: предполагается, что вся память аппаратно разделена на последовательность областей, каждая из которых называется банк памяти и ОП физически представляется как объединение k банков памяти. Соответственно для управления ОП существует аппаратура, которая обеспечивает функцию управления, это контроллер ОП. Он может состоять из двух уровней это контроллер доступа к памяти в целом, который имеет k своих внутренних каналов, которые могут работать параллельно, и каждый из каналов связан с контролем соответствующего банка памяти. Ячейки памяти расположены между соседними банками. Это означает, что в нулевом банке находится нулевая ячейка, в первом банке первая ячейка и т.д. , в k-1 м банке k-1 я ячейка, k я ячейка находится в нулевом банке, k+1 в первом и т.д. Все адреса размазаны по этим банкам. Всегда гарантированно, что соседние адреса, если их не более k находятся в разных банках. Это и есть аппаратная организация. Соответственно, если имеется ОП без расслоения памяти и с расслоением, то мы имеем следующую картину:
Последовательное чтение рядом стоящих ячеек памяти в одном случае время будет сравнимо с количеством этим ячеек умноженное на время цикла памяти, а в другом случае кол-во ячеек, умноженное на время доступа. И за счет того, что время цикла всегда больше времени доступа, мы имеем выигрыш. Соответственно при расслоении памяти используется следующий принцип: обычно количество банков памяти есть степень двойки это означает, что если взять адрес, который используется в машине, и вырезать из этого адреса какое-то количество разрядов, то получается номер банка. Т.о. получая исполнительный адрес, данные сразу же попадают на нужный банк. Суть в том, что мы можем при запросе последовательных адресов параллельно запускать доступ к разным банкам, т.е. оптимизировать доступ к рядом стоящим ячейкам памяти..
Другие пути решения данной проблемы возможность предварительной буферизации при чтении команд/данных и оптимизация при записи в ОЗУ больших объемов данных.
Центральный процессор
Процессор или центральный процессор (ЦП) компьютера обеспечивает последовательное выполнение машинных команд, составляющих программу, размещенную в оперативной памяти. Осуществляется выбор машинного слова, содержащего очередную машинную команду, дешифрация команды, контроль корректности данных, определение исполнительных адресов операндов, получение значения операндов и исполнение машинной команды.
Регистровая память совокупность устройств памяти ЦП ,предназначенных для временного хранения операндов, информации, результатов операций.
Устройство управления (control unit) координирует выполнение команд программы процессором.
Арифметико-логическое устройство (arithmetic/logic unit) обеспечивает выполнение команд, предусматривающих арифметическую или логическую обработку операндов.
Регистровая память
Регистры общего назначения (РОН) используются в машинных командах для организации индексирования и определения исполнительных адресов операндов, а также для хранения значений наиболее часто используемых операндов, в этом случае сокращается число реальных обращений в ОЗУ и повышается системная производительность ЭВМ.
Качественный и количественный состав специализированных регистров ЦП зависит от архитектуры ЭВМ. Выше представлены некоторые из возможных типов регистров, обычно входящие в состав специализированных регистров.
Регистр адреса (РА) - содержит адрес команды, которая исполняется в данный момент времени. По содержимому РА ЦП осуществляет выборку текущей команды, по завершении ее исполнения регистр адреса изменяет свое значение тем самым указывает на следующую команду, которую необходимо выполнить.
Регистр результата (РР) - содержит код, характеризующий результат выполнения последней арифметико-логической команды. Содержимое РР может характеризовать результат операции. Для арифметических команд это может быть «=0», «>0», «<0», переполнение. Содержимое РР используется для организации ветвлений в программах, а также для программного контроля результатов.
Слово состояние процессора (ССП или PSW) - регистр, содержащий текущие «настройки» работы процессора и его состояние. Содержание и наличие этого регистра зависит от архитектуры ЭВМ. Например, в ССП может включаться информация о режимах обработки прерываний, режимах выполнения арифметических команд и т. п. Частично, содержимое ССП может устанавливаться специальными командами процессора.
Регистры внешних устройств (РВУ) - специализированные регистры, служащие для организации взаимодействия ЦП с внешними устройствами. Через РВУ осуществляется обмен данными с ВУ и передача управляющей информации (команды управления ВУ и получения кодов результат обработки запросов к ВУ).
Регистр указатель стека - используется для ЭВМ, имеющих аппаратную реализацию стека, в данном регистре размещается адрес вершины стека. Содержимое изменяется автоматически при выполнении «стековых» команд ЦП.
Устройство управления и арифметико-логическое устройство
Устройство управления (control unit) координирует выполнение команд программы процессором.
Арифметико-логическое устройство (arithmetic/logic unit) обеспечивает выполнение команд, предусматривающих арифметическую или логическую обработку операндов.
Эти устройства являются «мозгом» процессора. Как именно функционирование этих устройств обеспечивает выполнение программ? Для ответа на этот вопрос рассмотрим упрощенную схему выполнения программ, иногда называемую рабочим циклом процессора.
Пусть у нас имеется специальный регистр - счетчик команд. В начальный момент времени в счетчике команд образуется адрес первой команды программы. Договоримся о том, что любая команда размещается в одном машинном слове и адреса соседних машинных слов различаются на 1. Рассмотрим последовательность действий в УУ процессора при выполнении программы.
По содержимому счетчика команд на выполнение из памяти выбирается очередная команда. Далее формируется адрес следующей команды, которая должна выполнится, т.е. счетчик команд увеличивается на 1. После этого в УУ происходит анализ кода операции, которая находится в выбранной команде, если это арифметико-логическая команда, то происходит вычисление исполнительных адресов операндов и выбор соответствующих их значений, и после этого идет обращение в АЛУ, в пределах которого выполняется эта команда. После этого цикл возвращается на этап выбора очередной команды по содержимому счетчика команд. Если после анализа кода операции выяснилось, что это не есть арифметико-логическая команда, а некоторая команда отличающаяся, (т.е. если, например, это команда передачи управления), то происходит анализ условия перехода. Это означает автоматический выбор специального регистра и по его значению принимается решение о переходе, если условие не выполнено, то мы переходим к выбору следующей команды, иначе происходит вычисление исполнительного адреса и счетчику команд присваивается этот адрес перехода и опять осуществляется переход на этап выборки команды по значению счетчика команд.
Рабочий цикл процессора
Рабочий цикл процессора последовательность действий, происходящая в процессоре во время выполнения программы.
Кэш-память (L1)
Вернемся к проблеме дисбаланса скорости доступа к ОЗУ и скорости обработки информации ЦП.
Первое решение использовать программные средства. Программист может разместить наиболее часто используемые операнды в РОН, тем самым сокращается количество «медленных» обращений в ОЗУ. Результат решения во многом зависит от качества программирования.
Второе решение использование в архитектуре ЭВМ специальных регистровых буферов или КЭШ памяти (Аппаратное решение). Традиционно, в развитых ЭВМ используется аппаратная буферизация доступа к операндам команд, а также к самим командам. Регистровые буфера или КЭШ память предназначены для разрешения проблемы несоответствия скоростей работы ОЗУ и ЦП, на аппаратном уровне, т.е. эта форма оптимизации в системе организована аппаратно и работает всегда, вне зависимости от исполняемой программы.
Кэш память (cache memory) первого уровня (L1)
Автоматически минимизирует число обращений к ОП за командами и операндами команд. Обмен данными между КЭШем и оперативной памятью осуществляется блоками фиксированного размера .
Адресный тег блока содержит служебную информацию о блоке (соответствие области ОЗУ, свободен/занят блок, ......).
Нахождения данных в КЭШе - попаданием (hit). Если искомых данных нет в КЭШе, то фиксируется промах (cach miss).
При возникновении промаха происходит обновление содержимого КЭШа - вытеснение.
Появляется вопрос кого “убирать”? Существуют различные дифференцированные критерии решения этой проблемы.
Стратегии вытеснения:
•случайная;
Это когда в системе есть в системе датчик случайных чисел (аппаратный) и он будет генерировать номер того блока, который должен быть освобожден.
•вытеснение наименее популярного (LRU - Least-Recently Used).
Наиболее распространенный и простой критерий - вытеснять из таблицы самую “старую” строчку. А признаком старения может быть, например, количество обращений к буферу, при котором нет обращения к этой строчке.
Вытеснение КЭШа данных:
•сквозное кэширование (write-through caching)
эта стратегия предусматривает при появлении команды записи менять содержимое соответствующего операнда в блоке КЭШа и сбрасывать этот блок в оперативную память, т.е. в каждый момент времени состояние КЭШа и оперативной памяти идентично. Понятно, что здесь есть некий накладной расход, поскольку происходит частый сброс в ОП при записи информации в КЭШ. Но, поскольку, команд чтения по статистике больше, чем команд записи то эффект есть.
•кэширование с обратной связью (write-back cache) - тег модификации (dirty bit )
Суть: при появлении команд записи меняется содержимое машинного слова только в КЭШе. Соответственно во времени нарастает несоответствие содержимого блока в КЭШе и в ОП. При появляется проблемы вытеснения работает следующая схема. В тэге КЭШа присутствует тэг модификации, в котором устанавливается признак того, что в данном блоке уже произошла модификация, при первой записи слова в этот блок, соответственно, если возникает проблема вытеснения и, по какой-то стратегии из обозначенных, выбран блок КЭШа, который нужно вытеснить, то анализируется тэг модификации, если блок не модифицировался, то он просто освобождается, если он модифицировался, то происходит сброс содержимого блока в соответствующие адреса ОП.
При использовании кэш памяти (cache memory):
• сокращается количество обращений к ОЗУ;
• существенно увеличивается скорость доступа к памяти в случае использования ОЗУ с «расслоением», т.к. обмены блоков с памятью будут проходить, практически параллельно.
Аппарат прерываний
Прерывание - событие в компьютере, при возникновении которого в процессоре происходит предопределенная последовательность действий. Прерывания возникают в нестандартных ситуациях (например, в регистре команд декодируется операция с неизвестным кодом).мы заранее оговариваем какого рода нестандартные ситуации могут произойти. Прерывание это программно аппаратное средство.
Прерывания:
• внутренние - инициируются схемами контроля работы процессора
• внешние - события, возникающие в компьютере в результате взаимодействия центрального процессора с внешними устройствами.
При прерывании: запускается обработчик прерываний (после обработки программа может быть продолжена, поэтому сохранение параметров, заранее оговаривается перечень регистров, которые автоматически сохраняются, например, счетчик команд, регистр результатов и т.д.), освобождаются регистры общего назначения, которые будут использоваться программой обработчиком, на время обработки все остальные прерывания блокируются (помещаются в очередь), передается управление на точку операционной системы(место в памяти), где находится код обработчика именно этого прерывания, перечень свободных ресурсов для обработчика зависит от реализации ОС.
Этап аппаратной обработки прерываний
. На аппаратной стадии обработки прерывания происходит следующее: завершается выполне6ние текущей команды, в том случае если прерывание не связано с выполнением текущей команды (предположим, пришло внешнее прерывание), происходит блокировка прерываний. Обработка прерываний предполагает приостановку текущей программы, и при этом после обработки прерываний возможно продолжение текущей программы, т.е. как правило, программа после возникновения прерывания продолжает свое исполнение. При возникновении прерывания в системы должны быть предусмотрены средства, которые, с одной стороны, позволили бы вызвать программу, которая будет обрабатывать возникшее прерывание. С другой - сохранили бы возможность продолжения программы после обработки прерывания, так как существуют регистры, которые при обработке прерывания нужно либо не трогать, либо как-то сохранить. В модельной аппаратуре компьютера имеются средства сохранения актуального состояния процессора, которые сохраняют все необходимые для загруженной программы регистры в специальном регистровом буфере. Чтобы не возникла ситуация, когда информация скидывается в этот буфер, но не успевает обработаться, а в это время появилось еще одно прерывание и скидывается поверх еще то включается режим блокировки прерываний. В режиме блокировки прерываний происходит следующее: либо возникающие в системе прерывания игнорируются, либо их обработка откладываются до тех пор, пока не будет обработано предыдущее прерывание. После блокировки происходит аппаратный сброс части регистров в буфер и начинается программный этап обработки прерываний.
Программный этап обработки прерываний
Происходит идентификация типа прерывания:
Идентификация прерывания может осуществляться разными моделями.
Модель организации прерываний с использованием «регистра прерываний»
Каждый разряд этого регистра отвечает за появление того или иного прерывания или группы прерываний. Т.е. каким-то регистрам может соответствовать прерывание определенного типа, а в каком-то регистре возможна индикация о том, что есть еще один периферийный регистр прерываний, в котором появилось прерывание. Когда ОС получает управление, то специальными командами, которые доступны только ОС, может прочесть регистр прерываний и определить причину прерывания, а после этого, в зависимости от причины, передать управление на ту или иную программу обработки прерывания.
Модель организации прерываний с использованием «вектора прерываний»
. Здесь аппаратно предполагается, что в определенном фрагменте ОП размещается вектор прерываний. Это таблица, каждая строка которой соответствует определенному прерыванию, соответственно код строки есть адрес программы-обработчика соответствующего прерывания, также в этой строке могут находиться дополнительная информация, например, о том в какой режим нужно включить процессор при переходе, приоритет операций и т.д. и т.п. Соответственно при возникновении прерывания аппаратно, управление передается не на одну точку, а уже на точку, которая соответствует конкретному прерыванию, т.е. уже сразу идет попадание на обработчик прерываний.
Модель организации прерываний с использованием регистра «слово состояние процессора»
Код прерывания аппаратно помещается в регистр «слово состояние процессора», после этого программа-обработчик прерывания выбрав этот код принимает решение о дальнейшей последовательности действий, которые необходимо осуществить для обработки прерывания, стоящего под этим кодом.
Внешние устройства
Внешние устройства на сегодняшний день во многом определяют эксплуатационные характеристики компьютера. Система внешних устройств компьютера достаточно широкая. Но есть некоторый типовой набор внешних устройств. Традиционно внешние устройства можно разделить на следующие классы:
Внешние запоминающие устройства устройства, предназначенные для хранения данных и программ
Устройства ввода и отображения информации осуществляют ввод из вне некоторой информации и отображение ее в виде некоторых результирующих данных. Традиционно это печатающие устройства, устройство дисплея и устройство ввода изображения и текста.
Устройства приема и передачи данных используются для получения данных с других компьютеров, «из вне». (модем, факс…)
Внешние запоминающие устройства (ВЗУ).
Обмен данными:
• записями фиксированного размера блоками
• записями произвольного размера
Доступ к данным:
• операции чтения и записи (жесткий диск, CDRW).
• только операции чтения (CDROM, DVDROM, …).
ВЗУ является устройством последовательного доступа если для чтения i-й записи необходимо прочесть («просмотреть») предыдущие i-1 запись. Устройства последовательного доступа являются простейшими ВЗУ. Они обычно используются для архивирования данных. Скорость обработки запросов чтения/записи самая низкая (большой объем механических действий, таких как перемотка лент вперед-назад при выполнении обмена).
Устройство прямого доступа характеризуется возможностью чтения любой записи без предварительного просмотра каких-либо других записей, размещенных на данном устройстве. ВЗУ прямого доступа классифицируются по производительности
Последовательного доступа:
•Магнитная лента
Прямого доступа:
• Магнитные диски
• Магнитный барабан
• Магнито - электронные ВЗУ прямого доступа
3.6.1.1 Устройство последовательного доступа
Магнитная лента
Примером устройства последовательного типа является магнитная лента (МЛ).
Чтобы добраться до определенной записи, нужно пройти все предыдущие.
Обычно длина физической записи МЛ произвольная, она определяется специальными маркерами начала и конца записи.
3.6.1.2 Устройства прямого доступа
Наименее скоростные устройства прямого доступа. Блок может перемещаться от края к центру. Каждое устройство характеризуется фиксированным числом цилиндров. Дорожки относящиеся к одному цилиндру также пронумерованы. Дорожки образуют концентрические окружности. Все дорожки разделены на сектора. Начала одноименных секторов лежат в одной плоскости.
Для задания координат определенного сектора в управляющее устройства необходимо передать:
- номер цилиндра, где расположен сектор
- номер дорожки на которой находится сектор
- номер сектора
- считывание информации производится внутри условного цилиндра.
При выполнении обмена совершаются следующие действия:
*перемещение считывающей/головки на нужный цилиндр;
*ожидание выхода головки на начало нужного сектора диска (ожидание механического поворота диска на начало сектора);
*непосредственный обмен (в темпе движения диска);
Магнитный барабан
Высокоскоростное ВЗУ.
Предназначен больших вычислительных комплексов. Представляет из себя большой цилиндр длиной до метра, в диаметре 30 40 см. Поверхность покрыта особым веществом, над поверхностью штанга с головками над треками. Скорость доступа достаточно большая. Механическая составляющая только вращение барабана. Магнитный барабан используется операционными системами высокопроизводительных ЭВМ для хранения оперативных данных (данных, время доступа к которым должно быть минимальным).
При выполнении обмена совершаются следующие действия:
- электронное включение считывающей/записывающей головки, соответствующей нужному треку;
- ожидание размещения головки над началом нужного сектора (ожидание механического поворота барабана на начало сектора);
- непосредственный обмен в темпе движения барабана.
Магнито-электронные ВЗУ прямого доступа
Память на магнитных доменах, ВЗУ, построенные на элементной базе ОЗУ и т.п.
Из-за электрических свойств домены разгоняются по трекам. Обмен информацией между головкой и барабаном это включение головки и ожидание, когда домен «прибежит» на головку.
В обмене отсутствует «механическая» составляющая, поэтому это наиболее быстродействующие ВЗУ.
Используется американцами на шатлах.
Организация потоков данных при обмене с внешними устройствами
. Если посмотреть на взаимодействие ЦП, ОП и внешнего устройства, то это взаимодействие логически разделяется на два компонента:
Поток управляющей информации. Т.е. каким-то образом программно задается информация о том, что необходимо прочесть или записать или переместить данные из одного места в другое.
Поток данных. Т.е. непосредственно ответ на управляющее воздействие и связанное с этим ответом перемещение данных от внешнего устройства в ОП или в ЦП.
Организация потока данных между устройствами может осуществляться 2-мя способами. Организация потока данных между ОП и внешним устройством через ЦП. Это означает, что программе нужно считать или записать какую-то порцию данных процессор выполняет соответствующую последовательность команд. При этом выполняются либо специальные команды ввода- вывода либо процессор специальным образом размещает информацию на специальных регистрах. После это если есть команда чтения информации, то с устройства поступают данные опять-таки на специальные регистры, ЦП ловит (т.е. здесь могут появляться прерывания) эти данные и размещает их в нужную часть ОП, т.е. туда, куда эти данные должны быть размещены. Т.е. это схема обмена через ЦП.
Использование устройств прямого доступа к памяти(direct memory access DMA). это означает, что поток данных, т.е. поток управления идет через ЦП, т.е. ЦП выдает управляющую информацию и отслеживает окончательное выполнение, а поток данных идет не через процессор, а через некоторый контроллер, который называется DMA-контроллер. Т.о. обмен может осуществляться без участия ЦП. Т.е. при подаче информации на ЦП о том, что необходимо произвести обмен, DMA-контроллер производит соответствующие необходимые действия и начинается обмен. Когда обмен прошел, то происходит прерывание и ЦП получает информацию о том, что обмен прошел или не прошел.
Модели синхронизации при обмене с внешними устройствами
Существует две принципиально различные стратегии выполнения обмена с внешними устройствами: синхронная и асинхронная работа с ВУ.
Синхронная организация обмена
Процессор подает запрос внешнему устройству и ожидает завершения выполнения запроса.
Системы с синхронной организацией работы ВУ неэффективны с точки зрения использования времени работы центрального процессора. Процессор часто «ожидает» выполнения запроса. Наиболее подходит для однопрограммных специализированных вычислительных систем.
Асинхронная организация обмена
При обработке запроса к ВУ происходит разделение выполнения на три части:
1) передача ЦП запроса на выполнение работ. После этого процессор может выполнять другие команды.
2) параллельно работе ЦП происходит выполнение запроса к ВУ (т.е. в это время процессор может выполнять другие машинные команды).
3) выполнение работы ЦП прерывается и ему передается информация о завершении выполнения запроса. ЦП может также приостановить работу в случае обращения в область ОЗУ, находящуюся в обмене.
Асинхронная организация работы с ВУ более эффективна, но требует наличия развитого аппарата прерываний.
2.Синхронное управление внешними устройствами с использованием контроллеров
внешних устройств. Суть такова : появились между ЦП и внешнем устройством -устройства, которые называются контроллер внешнего устройства. Контроллер внешнего устройства служит промежуточным звеном для общения ЦП и внешнего устройства. С точки зрения ЦП контроллер внешнего устройства может иметь интерфейсную часть (команды) более высоко уровневую, чем для 1-го случая (могут быть, команды уже подачи информации позиционировать на соответствующий сектор). Но при этом на ЦП ложится значительная работа по обработке ошибочных и ситуаций и прочих внештатных ситуаций, которые выполняются при обмене. Уровень и объем непроизводительной работы ЦП зависит от конкретного контроллера (т.е. насколько интеллектуальный контроллер).
3.Асинхронное управление внешними устройствами с использованием контроллеров внешних устройств.
Альтернатива синхронному управлению. Т.е. при появлении асинхронного управления появляются прерывания, которые позволяют откладывать и запараллеливать разные действия, а проблема загрузки ЦП непроизводительной работой она остается как есть. Все равно ЦП, несмотря на асинхронную работу, занимается плюс непроизводительной работой.
4.Использование контроллера прямого доступа к памяти (DMA) при обмене.
. Понятно, что здесь речь идет о асинхронном обмене и есть существенная оптимизация за счет того, что поток данных минует ЦП и много проблем как бы уходят со стороны ЦП. Тем не менее остаются проблемы связанные с управлением и отработкой данных обмена.
5.Управление внешними устройствами с использованием процессора или канала ввода/вывода.
В системе кроме ЦП присутствует специализированный процессор (специализированная машина), который обеспечивает управление внешними устройствами, который обеспечивает оптимизацию работы с внешними устройствами. Т.е. эта машина может иметь свою локальную оперативную память, и в этой локальной оперативной памяти может быть организовано кэширование доступа к внешнему устройству для того, чтобы минимизировать непосредственные обращения к внешнему устройству. Системы ввода/вывода предоставляют для процессора высокоуровневый интерфейс общения, который обеспечивает минимизацию загрузки ЦП объектной организацией ввода/вывода. Это означает, что проблемы обнаружения ошибок, попытки локализации ошибок и т.д. все это уже происходит внутри процессора канала ввода/вывода и ЦП это не затрагивает.
Данная иерархия строится с позиций близости к ЦП, стоимости памяти и системной составляющей. Т. е. есть ЦП и элементами памяти в ЦП являются регистры общего назначения и КЭШ 1-го уровня. Следующий уровень это уровень устройства, которое называется КЭШ 2-го уровня, которое находится между ЦП и ОЗУ, т.е. обычно это устройство, которое быстрее ОП, но может быть медленнее и дешевле КЭШа 1-го уровня, а также может обладать немножко большими размерами чем КЭШ 1-го уровня, соответственного схема работы с КЭШем 2-го уровня аналогично схеме работы с КЭШем 1-го уровня. по иерархии уровень после уровня ОЗУ это уровень внешнего запоминающего устройство с внутренней КЭШ-буферизацией. Т.е. это устройства, аппаратное управление которых имеет КЭШ буферизации, т.е. это уже менее эффективно, чем ОП, но достаточно эффективно, потому что опять-таки за счет внутреннего кэширования (при той же схеме кэширования, которая имеет место в схеме ЦП - ОЗУ ), сокращается реальное количество обращений к устройству и тем самым получается существенное повышение производительности работы устройства. Следующий уровень - внешнее запоминающее устройство прямого доступа без КЭШ-буферизации. Это устройства существенно менее эффективные, но также предназначенные для оперативного доступа к данным, т.е. это устройства, которые обычно используются в программе для организации внешнего хранения и доступа за данными, соответственно по производительности они могут быть разными, но для каких-то ситуаций категории этих двух устройств не принципиальны. Последним уровнем этой иерархии является уровень внешнего запоминающего устройства долговременного хранения данных. Т.е. это устройства, предназначенные для архивирования и долговременного хранения информации, к этим устройствам могут относиться и как устройства прямого доступа, и устройства последовательного доступа. Суть иерархии: на вершине находятся самые высоко скоростные, которые, в свою очередь являются также и самыми дорогими устройствами, но спускаясь вниз, мы получаем устройства менее дорогие, но обладающие более худшими показателями по скорости доступа, за счет всей системы предусматриваются достаточно большие элементы сглаживания дисбаланса в производительности каждого из типов этих устройств.
Изначально был однопрограммный режим работы. ЦП простаивал во время обмена информацией с магнитной лентой, диском…Появились новые задачи, новые устройства, однопрограммный режим перестал устраивать.
За счет аппаратно-программных решений, ВС поимела возможность обрабатывать несколько программ одновременно: какие-то выполняют обмен, какие-то ждут своей очереди, какие-то запущены на счет.
Появился мультипрограммный режим - режим при котором возможна организация переключения выполнения с одной программы на другую.
Мультипрограммный режим - режим при котором возможна организация переключения выполнения с одной программы на другую.
Аппаратные средства компьютера, необходимые для поддержания мультипрограммного режима
Аппарат защиты памяти. Если выполняется одна программа, то надо, чтобы она не смогла испортить память, занятую другой. Надо выделить каждой программе по «куску» памяти, установить права доступа до каждого куска и, если программа залезает не в свой кусок, выдавать прерывание.
А) пример защиты с помощью анализа, (применялся на заре программирования)
Программа непрерывная область памяти. Выделялось два регистра: в одном адрес начала программы, в другом конец. Каждый раз смотрим, не залезли ли в «чужую» область памяти.
Б) защита по ключу
Каждой странице физической памяти ставится в соответствии регистр с номером равным номеру страницы. Каждой задаче присваивается номер. После выделения каждой задаче физических страниц в каждый регистр заносится номер решаемой задачи, играющий роль ключа. Существует особый регистр значение которого равно номеру программы которая сейчас считается. При каждом обращении к памяти проверяется совпадает ли ключ на процессоре с ключом страницы памяти.
Специальный режим операционной системы : все множество машинных команд разбивается на 2 группы. Первая группа команды, которые могут исполняться всегда (пользовательские команды). Вторая группа команды, которые могут исполняться только в том случае, если ЦП работает в режиме ОС. Если ЦП работает в режиме пользователя, то попытка выполнения специализированной команды вызовет прерывание "Запрещенная команда”.
прерывание по таймеру
Как минимум в машине должно быть прерывание по таймеру, что позволит избежать “зависания“ всей системы при зацикливании одной из программ.
1. Вложенные обращения к подпрограммам
В вызываемую подпрограмму нужно передать параметры. Возникает проблема сохранения регистров. Требуются лишние затраты времени.
2. Накладные расходы при смене обрабатываемой программы:
• необходимость включения режима блокировки прерываний;
• программное сохранение / восстановление содержимого регистров при обработке прерываний;
3.Перемещаемость программы по ОЗУ
Сложно перемещать программу по оперативной памяти так, чтоб не было привязки программы к определенному диапазону адресов.
Программу с математическими адресами надо положить на физическую память. Или например, программа была прервана, следовательно после прерывания, нужно вернуть программу в память, 99% что она попадет на новое место. Возникает необходимость настроить программу на новое место.
4. Фрагментация памяти
Система работает в мультипрограммном режиме. Программы должны храниться в памяти. Программы находятся в разных частях памяти. Когда приходит новая программа, возникает вопрос, куда ее поместить. Если нет свободного куска памяти нужного размера, но есть много маленьких свободных фрагментиков, в суммарный объем которых возможно записать новую программу, а ни в один в отдельности нельзя (фрагментация памяти), то как это сделать?
Фрагментация памяти приводит к деградации системы.
Один из способов решения это компрессия (но при этом возможны потери памяти). Существуют и другие решения.
Один из способов решения проблемы вложенных процедур регистровые окна.
В компьютере имеется k физических регистров. Система команд машины предоставляет l регистров общего назначения, l различных регистровых окна. Каждый из l регистров отображается на k физических регистров. В каждый момент времени программа работает с одним регистровым окном.
Каждое регистровое окно делится на части:
а) область регистров, использующихся для получения и передачи параметров из\в внешние подпрограммы.
Б) область локальных регистров подпрограмм
В)область временных регистров
Возможна кольцевая схема организации регистровых окон.
При вызове подпрограммы происходит переключение текущего регистрового окно на следующее регистровое окно, при этом возможно пересечение 3-тей части текущего окна с первой частью последующего окна. Этим достигается, во-первых, практически автоматическая передача и прием параметров, во-вторых, всегда создается новый комплект локальных регистров, которые присутствуют в программе.
Рассмотрим, что происходит при непосредственной работе:
Обращаемся к 1-й программе, ей выделяется 0-е регистровое окно. Дальше пошли в глубину на 2-й уровень, выделилось 1-е регистровое окно и т.д. до тех пор, пока не дошли до последнего. Что будет, когда этот круг обойдем? Начинается откачка этих окон в ОП. Эта схема гарантирует эффективную работу программ с вложенностью не более фиксированного, если вложенность больше, то возникают проблемы, но все равно начинается работа с КЭШем и мы все равно не опускаемся на уровень общения с ОП. Соответственно система может иметь специальный регистр-указатель текущего окна и указатель сохраненного окна.
При обращении в функцию происходят следующие действия:
Увеличиваем указатель текущего окна на единичку по модулю N.
Сравниваем, получилось ли: (указатель на новое содержимое текущего окна) = (указатель на сохраненное окно), если получилось, то мы дошли до ситуации, в которой пытаемся обратиться за окном, которое уже занято, т.е. пошли по второму кругу этого цикла. Происходит прерывание. Мы откачиваем в память текущее окно, после этого меняем указатель на сохраненное окно и используем освобожденное текущее окно, так новое. Если не равно, то идем на «Использование окна CWP, вызов функции».
При выходе из функции мы начинаем действовать в следующим образом:
Рассмотрим пример:
Main программа, которая изначально имеет некоторое состояние 0. Т.е. у нас 2 регистровых окна. 0-е регистровое окно принадлежит программе Main, 1-е свободное. Если количество операций выхода из функции будет больше, чем количество обращений, то возникнут проблемы. Рассмотрим, что происходит при (ложном) обращении к таким функциям.
Есть такой процессор Itanium это 64-х разрядный процессор. В нем используется модель, в которой считается, что регистровое окно, доступное программе в каждый момент, состоит из 128 регистров общего назначения. Соответственно 32 регистра это статические регистры, которые остаются всегда неизменными, оставшиеся регистры (96) динамические (виртуальные), т.е. которые отображаются на кольцевой список физических регистров. Размер этого кольцевого списка может варьироваться от … и до… . «Динамическое» окно можно заказать окно произвольного размера (от регистра GR32 до регистра GR32+N (N=0,..,95).
Происходит оптимизация работы с физическими регистрами
Использование системного стека может частично решать проблему минимизации накладных расходов при смене обрабатываемой программы и/или обработке прерываний. Частично стек реализуется на регистрах, таким образом существенно ускоряется работа. Что лучше использовать: стек или регистры? Однозначного ответа нет.
Давайте рассмотрим схему подготовки исполняемого кода. Мы имеем исходный текст, который обрабатываем компилятором. В результате получается объектный модуль. Из достаточного количества объектных модулей с помощью редактора внешних связей сформировали исполнительный модуль. И потом когда-то этот исполнительный модуль мы запустим на выполнение. Т.е. исполнительный модуль это есть уже программа в коде машины, которая использует определенную адресацию. Эта адресация называется программная или логическая или виртуальная адресация т.е. те адреса, которые используются в программе, они программные, логические или виртуальные адреса. При выполнении программы имеется проблема установления соответствия между виртуальными адресами и реальными адресами ОП (физическими адресами). Аппарат это аппаратные средства компьютера, которые обеспечивают преобразование виртуальных адресов, используемых в программе в физические адреса ОП, в которых размещена данная программа при выполнении. Соответственно за время развития вычислительной техники была целая совокупность моделей организации виртуальной памяти. Т.е. моделей аппаратной реализации вот этого установления соответствия, такого чтобы программа, которая уже откомпилирована и собрана, в относительно произвольный момент времени могла спокойно выполняться на компьютере без преобразования.
1-м способом организации виртуальной памяти можно считать организацию базирования.
Аппарат виртуальной памяти аппаратные средства компьютера, обеспечивающие преобразование (установление соответствия) программных адресов, используемых в программе адресам физической памяти в которой размещена программа при выполнении.
Базирование адресов реализация одной из моделей аппарата виртуальной памяти. При базировании выделяется регистр, в котором будет храниться адрес, начиная с которого размещается программа. Проблема: программы должны располагаться в одном блоке.
В <Rбазы> загружается адрес начала программы в памяти. Исполняемые адреса, используемые в модуле будут автоматически преобразовываться в адреса физического размещения данных путем их сложения с регистром Rбаз..
Базирование адресов решение проблемы перемещаемости программы по ОЗУ.
Благодаря базированию адресов виртуальное адресное пространство программы отображается в физическую память взаимнооднозначно.
Аппарат базирования не решает проблемы фрагментации.
Пусть одна система команд ЭВМ позволяет адресовать и использовать m страниц размером 2k каждая.
Физическое адресное пространство, в общем случае может иметь произвольное число физических страниц. Структура физического и виртуального адресов будут различаться размером поля номер страницы.
В ЦП ЭВМ имеется аппаратная таблица страниц.
Исполнительный физический адрес будет отличаться от исполнительного виртуального адреса за счет поля ”номер страницы”.
Содержимое таблицы определяет соответствие виртуальной памяти физической для выполняющейся в данный момент программы/процесса. Соответствие определяется следующим образом: i-я строка таблицы соответствует i-й виртуальной странице.
При замене процесса таблицу надо менять.
Виртуальное адресное пространство множество виртуальных страниц, доступных для использования в программе. Количество виртуальных страниц определяется размером поля «номер виртуальной страницы» в адресе.
Физическое адресное пространство оперативная память, подключенная к данному компьютеру. Физическая память может иметь произвольный размер (число физических страниц может быть меньше, больше или равно числу виртуальных страниц).
Достоинства и недостатки аппарата виртуальной страничной памяти
«+» Частичное решение проблемы фрагментации ОЗУ
«+» Организация защиты памяти, а также свопирования страниц
«-» Необходимость наличия в ЦП аппаратной таблицы значительных размеров
Существуют регистры процессора, где каждой виртуальной странице ставится в соответствие реальная физическая. В процессе выполнения программы при каждом обращении в память по какому-то виртуальному адресу по регистру приписки заменяется номер виртуальной страницы на соответствующую физическую, по адресу которой и будет обращение в память. Может произойти прерывание, происходит замена одного процесса на другой. Страницы будут откачены на внешнюю память (свопинг) и будут ждать нового обращения. После обработки прерываний происходит проверка закачены ли страницы обратно.
А теперь вернемся немного назад и рассмотрим основы взаимодействия в сети. Время одпроцессорных компьютеров потихоньку уходит. То есть те компьютеры, которые мы относим к категории однопроцессорных, на самом деле являются многопроцессорными. Помимо ЦП имеется много других элементов, которые выполняют те или иные специальные функции.(обработка видео, графическая и т.д.) Идеально однопроцессорная система сходит на нет. Для работы с современными прикладными задачами требуется либо наличие нескольких компьютеров, либо многопроцессорная архитектура.
Причины:
1) Мы живем в информационном обществе интернет как сфера общения между людьми. Необходимо получать информацию извне.
2)Появления спектра задач, для решения которых не возможно применять подходы, которые применялись для однопроцессорных систем, или их сложность не позволяет решать их на одном процессоре, следовательно требуется параллельная архитектура
Существует много различных подходов классификации. Но не существует адекватной по состоянию на сегодняшний день классификации.
Поговорим о классификациях Флинна.
Есть поток управляющей информации собственно команд (инструкций), и поток данных. Считаем потоки данных и команд независимыми (условно). Рассмотрим все возможные комбинации:
ОКОД (SISD single instruction (одиночный поток команд), single data stream, (одиночный поток данных)) Традиционные компьютеры, которые мы называем однопроцессорными. То есть для каждой команды одиночные порции операндов, которые будут обрабатываться. . Пример классическая машина по Фон - Нейману.
ОКМД(SIMD single instruction(одиночный поток команд), multiple data stream(множественный поток данных)) Для каждой команды порция данных (векторная или матричная обработка данных)
Можно разделить на две группы:
массивно параллельные процессоры (существует набор процессоров, работающих одинаково с данными, например на всех выполняется сложение)
векторные процессоры (работают с данными как с вектором)
МКОД (MISD multiple instruction(множественный поток команд), single data stream(одиночный поток данных)) это вырожденная категория, считается, что ее нет. Т Т.о. эта категория пока не охваченная, и не понятно, как ее можно охватить, хотя есть интерпретации и этой категории, например, к ней иногда относят всякого рода параллельные специализированные графические системы, которые занимаются, предположим, распознаванием, то есть когда над одной порцией данных одновременно используются разные команды.
МКМД (MIMD - multiple instruction(множественный поток команд), multiple data stream(множественный поток данных)) Многомашинная осоциация.Некоторое количество процессорных элементов, каждый из которых обрабатывает свои данные.
MIMD - >=2 процессоров, имеющих свои устройства управления, каждый из которых может выполнить свою программу.
Системы с общей оперативной памятью Для всех процессорных элементов имеется общая оперативная память. Исполняемая программа берется из единого пространства, куда имеет доступ все процессоры. Любое слово памяти читается одновременно несколькими процессорами, следовательно необходима синхронизация чтения и записи.
С ростом числа процесоров рост производительности замедляется. И начиная с некоторого количества увеличивать число процессоров нет смысла.
UMA Каждый из процессорных элементов, входящих В систему имеет равные возможности и скорость доступа в ОП.
SMP Настроена на параллелизм. Имеют ограничения от аппаратной и от программной реализации начиная с некоторого количества элементов возникают проблемы с синхронизацией.
NUMA Каждый процессорный элемент имеет локальную память (с быстрым к ней доступом) и нелокальную (долгий по времени доступ).Обеспечивается досткп ко всей ОП, но куда-то быстрее, куда-то медленнее. Формат данных не унифицирован.
Системы с распределенной оперативной памятью есть >= 2 процессорных элемента, каждый из которых имеет свою локальную оперативную память, к которой не имеет доступ другие процессоры.
COW Наиболее популярна на сегодняшний день многомашинная система, которая объединяет специальной быстрой сетью и предназначена для решения на этом комплексе тех или иных прикладных задач.
MPP Промышленное развитие кластеров.Используются спец. Средства коммуникации, более дорогиие и более специализированные.
Гетерогенные системы объединяющие кластеры разных мощностей. Преимущества кластеров: 1) относительная дешевизна; 2) способность к расширению, увеличению мощностей.
Основная проблема высокопроизводительнах ЭВМ- отвод тепла. Сначала воздух, потом вода, потом энертные газы.
Терминальный комплекс это многомашинная ассоциация предназначенная для организации массового доступа удаленных и локальных пользователей к ресурсам некоторой вычислительной системы
Терминальный комплекс может включать в свой состав:
1) основную вычислительную систему систему, массовый доступ к ресурсам которой обеспечивается терминальным комплексом;
2)
2) локальные мультиплексоры аппаратные комплексы, предназначенные для осуществление связи и взаимодействия вычислительной системы с несколькими устройствами через один канал ввода/вывода, в общем случае возможна схема M x N, где M число обслуживаемых мультиплексором устройств, N число используемых для организации работы каналов ввода/вывода (M > N);
3)
3) локальные терминалы оконечные устройства, используемые для взаимодействия пользователей с вычислительной системой (это могут быть алфавитно-цифровые терминалы, графические терминалы, устройства печати, вычислительные машины, эмулирующие работу терминалов и т.п.) и, подключаемые к вычислительной системе непосредственно через каналы ввода/вывода или через локальные мультиплексоры;
4)
4) модемы устройства, предназначенные для организации взаимодействия вычислительной системы с удаленными терминалами с использованием телефонной сети. В функцию модема входит преобразование информации из дискретного, цифрового представления, используемого в вычислительной технике в аналоговое представление, используемое в телефонии и обратно (в общем случае модем это устройство, предназначенное для взаимного преобразования данных из различных форм представления, например, могут быть оптические модемы, преобразующие данные из цифрового формата в оптический, предназначенный для передачи по оптоволоконным линиям связи). Со стороны вычислительной системы модем подключается либо через канал ввода/вывода, либо через мультиплексор.
5)
5) удаленные терминалы терминалы, имеющие доступ к вычислительной системе с использованием телефонных линий связи и модемов.
6)
6) удаленные мультиплексоры мультиплексоры, подключенные к вычислительной системе с использованием телефонных линий связи и модемов.
Линии связи / каналы
Телефонная сеть состоит из набора телефонных станций, объединенных друг с другом линиями связи. Связь абонентов телефонной в том числе и связь удаленных терминалов с вычислительной системой осуществляется с использованием коммутируемого канала, либо по выделенным каналам.
Две разновидности каналов:
1)Коммутируемые поднимая трубку домашнего телефона мы даем на АТС информацию, что хотим соединиться с абонентом. Сигнал передается на ближайшую станцию на пути к вызываемому абоненту. Количество абонентов АТС < количества одновременно обрабатываемых линий.(между АТС также) Существуют 2 проблемы : время, затрачиваемое на коммутацию; возможность отказа от соединения (в новый год, когда все хотят позвонить). При нескольких звонках к одному и тому же абоненту, раз от раза маршруты коммутации (т.е. набор проводов, по которым идет сообщение) отличаются друг от друга, за счет того, что каждый раз выбираются свободные каналы в телефонных станциях по пути соединения. После завершения сеанса связи между абонентами коммутируемый канал освобождается.
2) Выделенный - обеспечивает связь между 2 абонентами на постоянной основе. Уменьшает пропускную способность остальной сети. За то этот канал с гарантированным качеством соединений.
Линии связи, связывают удаленный терминал с:
- терминальным комплексом канал точка-точка.
эта линия может быть либо выделенной (мы договариваемся с телефонными станциями и фиксируем коммутацию), либо коммутируемой.
- группой удаленных терминалов многоточечный канал.
на входе находится удаленный мультиплексор. Многоточечные каналы также могут быть либо выделенными, либо коммутируемыми.
С точки зрения передачи потоков информации:
1.Симплексные каналы - каналы, по которым передача информации ведется в одном направлении (например, телевизионный канал обеспечивает передачу информации только в одном направлении от передающей антенны к принимающей).
2.Дуплексные каналы - каналы, которые обеспечивают одновременную передачу информации в двух направлениях (например, телефонный разговор, мы одновременно можем и говорить и слушать).
3.Полудуплексные каналы - каналы, которые обеспечивают передачу информации в двух направлениях, но в каждый момент времени только в одну сторону (подобно рации).
В некотором смысле вычислительная сеть, или сеть ЭВМ, или компьютерная сеть это есть развитие и обобщение идей, которые были изначально заложены в терминальных комплексах. С точки зрения архитектуры, можно говорить, что архитектура очень похожа, если заменить терминальные устройства на компьютеры. В общем случае вычислительная сеть представляет собой программно-аппаратный комплекс, обладающий след характеристиками:
Компьютерная сеть объединение компьютеров (или вычислительных систем), взаимодействующих через коммуникационную среду.
Коммуникационная среда каналы и средства передачи данных
Рассмотрим в качестве примера некоторое отображение компьютерной сети. Компьютерная сеть будет состоять из абонентских машин, которые обеспечивают обмен содержательной информацией работы с пользователями, и абонентские машины могут осуществлять взаимодействие друг с другом через коммуникационную среду или коммуникационную сеть. Коммуникационная среда или коммуникационная сеть может включать в свой состав коммуникационные или вспомогательные компьютеры и каналы связи между абонентскими машинами или коммуникационными машинами. Абонентские или основные компьютеры еще иногда называют хосты. Соответственно коммуникационные или вспомогательные компьютеры могут иметь свое наименование в зависимости от той функции, которую они выполняют это может быть шлюз, маршрутизаторы и т.д. и т.п. Взаимодействие в сети осуществляется между одной абонентской машиной и другой или между группами абонентских машин, по аналогии с терминальным комплексом коммуникационная сеть или коммуникационная среда обеспечивает определение соединения между этими двумя машинами, стратегию определения этого соединения и обеспечивают передачу информации в сети.
Итак, абонентские машины могут организовывать взаимодействие друг с другом. Сеанс последовательного взаимодействия это сеанс связи. Взаимодействие можно представить в виде отправки и получения сообщения.
Компьютеры сети исторически подразделяются на три категории:
Сеть коммутации каналов Это сеть, которая обеспечивает установку канала связи на время всего сеанса связи между абонентскими машинами.
«+» 1. если канал скоммутировался, то нет накладных расходов. Передача идет со скоростью самого медленного
элемента.
«-» 1.Сеанс связи произвольной длительности, следовательно из коммуникационной среды забирается канал на
недетерминированный промежуток времени, следовательно может произойти деградация сети.
2. Занятие канала без гарантированной продуктивности (лектор молчит, лекция дет)
Сеть коммутации сообщений Сеанс связи представляется в виде последовательности сообщений. Сообщение это порция данных произвольного размера. Сообщение отправляется в сеть по некоторой информации о маршруте. Если свободных каналов нет, то сообщение сохраняется на коммутирующей машине.
Проблемы:
1.В этих сообщениях должна быть информация о порядке сообщений, т.к. иначе 2ое сообщение может прийти первым,
если пойдет другим путем.
2. Размер сообщения может быть произвольным, следовательно недетерминированное время занятия канала.
Коммутирующие компьютеры должны обладать средствами аккумуляции произвольного количества сообщений.
«+» 1. Мы убрали промежутки молчания между сообщениями. Каналы в это время не заняты.
2. канал не устанавливается непрерывно, следовательно экономим канальный ресурс.
Сеть коммутации пакетов Все сообщения разделяются на блоки данных некоторого фиксированного размера. После этого сеть работает также, как сеть коммутации сообщений, но с пакетами. Каждая машина пытается от пакета избавится (принцип горячей картошки).
«+» Детерминированность пакета, т.е. Можно просчитать все характеристики коммуникационной среды.
Реальные сети всегда строятся на комбинации этих трех принципов.
Организация сетевого взаимодействия
Модель организации взаимодействия в сети ISO/OSI
Необходима аппаратная стандартизация. Предложена модель семиуровневого взаимодействия в сетях.
Основные проблемы: 1. Стандартизация программного обеспечения, устройств и т.д. С развитием сетей та проблема увеличивалась. Сети создавались как корпоративные, локальные. Каждое решение было уникальным. (каналы связи, формат передаваемой информации, программный интерфейс), следовательно перенос сетевой программы с одного компьютера на другой был невозможен, либо сильно затруднен. Т.к. мир существует на объединении и разделении предприятий, это было очень неудобно. Возникла необходимость стандартизации.
OSI системы открытых интерфейсов.
1..7 все возможные уровни взаимодействия компьютеров в сети
Каждый уровень использует логически целостный набор действий и форматов данных, предназначенных для передачи информации между взаимодействующими в сети ВС. регламентации программных или аппаратных средств.
В каждом уровне модель ISO/OSI предполагает наличие некоторого количества протоколов, каждый из которых может осуществлять взаимодействие с одноименным протоколом на другой взаимодействующей машине (возможно виртуальной).
1.Физический уровень На этом уровне однозначно определяется физическая сфера передачи данных и форматы передаваемых сигналов. На этом уровне решаются вопросы взаимосвязи в терминах сигналов. Этот уровень однозначно определяется физической средой, используемой для передачи данных и отвечает за организацию физической связи между устройствами и передачи данных в сети.
2. Канальный уровень Обеспечивает управление доступом к физической среде передачи данных, в частности обеспечение синхронизации передачи данных. Формализуются правила передачи данных.Решаются задачи обнаружения и синхронизации ошибок.
3.Сетевой уровень Решается вопрос управления связью между взаимодействующими компьютерами. Решается задача
маршрутизации. и адресацией в сети.
4. Транспортный уровень (уровень логического канала) Решаются проблемы управления и передачи данных локализация и обработка ошибок, сервис передачи данных.
5.Сеансовый уровень Управление сеансами связи. Синхронизация отправки и приема данных.Управление подтверждением полномочий. Обработка внештатных ситуаций. прерывания/продолжения работы в тех или иных внештатных ситуациях, управление подтверждением полномочий (паролей).
6.Представительский уровень Разрешается проблема унификации кодировок. Уровень представления данных. На этом уровне находятся протоколы, реализующие единые соглашения перевода из внутреннего представления данных конкретной машины в сетевое и обратно.
7. Прикладной уровень Осуществляет стандартизацию взаимодействия с прикладными системами.
Основные понятия
Протокол формальное описание сообщений и правил, по которым сетевые устройства (вычислительные системы) осуществляют обмен информацией.
или
Правила взаимодействия одноименных уровней.
Интерфейс правила взаимодействия вышестоящего уровня с нижестоящим.
Служба или сервис набор операций, предоставляемых нижестоящим уровнем вышестоящему.
Стек протоколов перечень разноуровневых протоколов, реализованных в системе
Логическое взаимодействие сетевых устройств по i-ому протоколу
Для организации взаимодействия при передаче сообщений от одного уровня к соседнему, существуют стандартизованные соглашения, которые называются интерфейсами.
Таким образом, данные от одной прикладной программы до другой прикладной программы в сети проходят путь от уровня протоколов прикладных программ до физического уровня на ВС, отправляющей данные, и далее на ВС, принимающей данные, они проходят этот путь обратном порядке.
Соответствие модели ISO/OSI модели семейства протоколов TCP/IP
Уровень доступа к сети. Стандартизация доступа к сети. Состоит из подпрограмм доступа к физической сети. Модель TCP/IP не разделяет два уровня модели OSI канальный и физический, а рассматривает их как единое целое.
Межсетевой уровень
Работает с дейтаграммами, адресами, выполняет маршрутизацию и «прикрывает» транспортный уровень от общения с физической сетью. Однако, в отличие от сетевого уровня модели OSI, этот уровень не устанавливает соединений с другими машинами.
Протоколы уровня доступа к сети используют при передаче и приеме данных пакеты, называемые фреймами. На межсетевом уровне используются дейтаграммы
3. Транспортный уровень. Обеспечивает доставку данных от компьютера к компьютеру, обеспечивает средства для поддержки логических соединений между прикладными программами. В отличие от транспортного уровня модели OSI, в функции транспортного уровня TCP/IP не всегда входят контроль за ошибками и их коррекция. TCP/IP предоставляет два разных сервиса передачи данных на этом уровне.
Уровень транспортных протоколов семейства представляется двумя протоколами TCP и UDP. Протокол TCP оперирует сегментами. UDP пакетами. На уровне прикладных программ, системы построенные на использовании протокола TCP используют поток данных, а системы использующие UDP - сообщения.
4. Уровень прикладных программ Состоит из прикладных программ и процессов, использующих сеть и доступных пользователю. В отличие от модели OSI, прикладные программы сами стандартизуют представление данных
Свойства протоколов семейства TCP/IP
•Открытые стандарты протоколов, которые поддерживаются почти всеми операционными средами и вычислительными платформами независимо от аппаратного обеспечения сети аппаратных данных.
• независимость от аппаратного обеспечения сети передачи данных
• обладает уникальным системным именованием сетевых устройств, что позволяет любому устройству единым образом адресоваться в этой сети.
• Стандартизованные протоколы прикладных программ
Взаимодействие между уровнями протоколов TCP/IP
Вокруг посылаемой информации много служебной
Приведем пример. Допустим, нужно отправить сообщение. Оно вначале делится, затем может объединяться, в зависимости от протоколов, добавляются заголовочные оконечные части, которые несут какую-то служебную информацию, до тех пор, пока либо пакет не дойдет до получателя, либо не будет обнаружено, что он потерялся. Аналогия обычное письмо.
Уровень доступа к сети. Протоколы на этом уровне обеспечивают систему средствами для передачи данных другим устройствам в сети. Они определяют, как использовать сеть для передачи дейтаграмм IP. В отличие от протоколов более высоких уровней, протоколы этого уровня должны знать детали физической сети (структуру пакетов, систему адресации и т.д.), чтобы правильно оформить передаваемые данные.
Межсетевой уровень. Протокол IP
•Функции протокола IP
lформирование дейтаграмм
lподдержание системы адресации
lобмен данными между транспортным уровнем и уровнем доступа к сети
lорганизация маршрутизации дейтаграмм
lразбиение и обратная сборка дейтаграмм
IP является протоколом без логического установления соединения. Это значит, что он не обменивается контрольной информацией для установки соединения, перед началом передачи данных. IP оставляет другим протоколам право устанавливать соединения этим занимается либо протокол TCP, либо сами прикладные программы.
Протокол IP не обеспечивает обнаружение и исправление ошибок
Одним из основных свойств протокола IP является система адресации, которая обеспечивает уникальное именование любого сетевого устройства.
Устройство будем считать сетевым, если с ним ассоциирован некоторый стек протоколов)
Система адресации протокола IP
IP адрес представляется последовательностью четырех байтов. В адресе кодируется уникальный номер сети, а также номер компьютера (сетевого устройства в сети).
. Для представление содержимого IP адреса используется последовательность цифр:
N1.N2.N3.N4 ,
где Ni десятичное представление содержимого i го байта адреса.
Типы адресов
A номер сети <=126, уникальные сети, которые исторически принадлежат крупным мировым корпорациям.
С самые распространенные.
Некоторые из IP адресов являются зарезервированными, т.е. их интерпретация отличается от стандартной.
Поле номера сети |
Поле номера машины/устройства |
Комментарий |
Все нули |
Все нули |
Адрес данного устройства |
Номер сети |
Все нули |
Ссылка на сеть в целом. |
Все нули |
Номер устройства |
Устройство в данной сети |
Все единицы |
Все единицы |
Все устройства данной сети |
Номер сети |
Все единицы |
Все устройства заданной сети |
127 |
Код |
Используется для отладки и тестирования сетевых приложений (зацикленный адрес - loopback address). При отправке данных по этому адресу, стек протоколов возвращает переданные данные процессу-отправителю. Т.е. происходит эмуляция работы сети, без реального сетевого взаимодействия (взаимодействия между различными стеками протоколов). |
Протоколы TCP/IP были созданы для передачи данных через ARPANET, которая является сетью с коммутацией пакетов.
Пакет это блок данных, который передаётся вместе с информацией, необходимой для его корректной доставки. Каждый пакет перемещается по сети независимо от остальных.
Дейтаграмма это пакет протокола IP. Контрольная информация занимает первые пять или шесть 32-битных слов дейтаграммы. Это её заголовок (header). По умолчанию, его длина равна пяти словам, шестое является дополнительным. Для указания точной длины заголовка в нём есть специальное поле длина заголовка (IHL, Internal Header Length).
Шлюз устройство, передающее пакеты между различными сетями
Маршрутизация процесс выбора шлюза или маршрутизатора
Маршрутизация дейтаграмм:
Шлюз компьютер, который имеет >=2 сетевых адаптеров (каждый имеет свой IP адрес)
Компьютерные системы могут передавать данные только внутри той сети, к которой они подключены. Поэтому передача дейтаграмм из одной сети в другую идёт через шлюзы от одного к другому. Внутри хоста данные проходят пути от уровня прикладных программ до уровня доступа к сети (и обратно). Дейтаграммы, которые переправляет шлюз, поднимаются только до межсетевого уровня. На этом уровне протокол IP, узнавая адрес получателя данных (на протяжении всего пути следования этот адрес не меняется меняются промежуточные машины), принимает решение отправить дейтаграмму в одну из сетей, к которым подключен.
На рисунке выше показано, как используются шлюзы для ретрансляции пакетов.
Протокол контроля передачи (TCP, Transmission Control Protocol) - обеспечивает надежную доставку данных с обнаружением и исправлением ошибок и с установлением логического соединения.
Протокол пользовательских дейтаграмм (UDP, User Datagram Protocol) - отправляет пакеты с данными, «не заботясь» об их доставке.
TCP Надежная передача данных. При отправке TCP пакета идет подтверждение о получении. Подтверждение должно прийти за некоторое детерминированное время. Если не пришло, то считается, что пакет потерялся. Обеспечивается порядок приема и передачи сообщений.
UDP Не требует подтверждения о доставки пакета.
TCP лучше, но за это мы платим содержательной скоростью и нагрузкой на сеть.
UDP быстрее, т.к. меньше мусора пересылается.
Выводы
UDP лучше для локальной сети, а TCP для межсетевого взаимодействия.
На самой вершине архитектуры семейства протоколов TCP находится уровень прикладных программ.
Все процессы этого уровня пользуются протоколами транспортного уровня для обмена данными по сети.
Протоколы, опирающиеся на TCP
TELNET (Network Terminal Protocol), Протокол сетевого терминала, разработан для удаленного доступа к компьютерам сети (remote login).
FTP (File Transfer Protocol), Протокол передачи файлов, используется для интерактивной передачи файлов между компьютерами сети.
SMTP (Simple Mail Transfer Protocol), Простой протокол передачи почты. Основной протокол для обмена почтой, использующийся в Internet.
Протоколы, опирающиеся на UDP
DNS (Domain Name Service), Служба имен доменов, или просто Служба именования. С помощью этого протокола устанавливается взаимно однозначное соответствие между IP-адресами сетевых устройств и их именами.
RIP (Routing Information Protocol), Протокол информации о маршрутизации. Маршрутизация данных (поиск путей их передачи от хоста-отправителя к хосту-получателю) в Internet является одной из важнейших функций семейства протоколов
TCP/IP. RIP используется сетевыми устройствами для обмена информацией о маршрутизации.
NFS (Network File System), сетевая файловая система. С помощью этого протокола компьютеры могут совместно использовать файлы, разбросанные по сети.
Сетевая ОС
Мы имеем физическую сеть в которой подключенные компьютеры взаимодействуют с помощью протоколов, сетевая ОС предоставляет пользователям распределенные прикладные приложения.
Распределенная ОС
Состоит из ядра, локализованного в рамках одного компьютера, и остальных функций распределенных по компьютерам сети.
Проблема распределения файловой системы
Операционная система это комплекс программ, обеспечивающий контроль за существованием, распределением и использованием ресурсов ВС.
Любая ОС оперирует некоторым набором базовых сущностей (понятий) на основе которых строится логика функционирования системы. Например, подобными базовыми понятиями могут быть задача, задание, процесс, набор данных, файл, объект.
Программа только текст (может быть откомпелированный)
Процесс это совокупность машинных команд и данных, исполняющаяся в рамках ВС и обладающая правами на владение некоторым набором ресурсов.
(Существует много определений процесса. Например: элементарная программная единица, которой осуществляет управление ОС.)
Важное свойство процесса ресурсы, которыми процесс владеет. Существуют ресурсы, которые монопольно принадлежат данному процессу, и разделяемые ресурсы, которые принадлежат одновременно 2 и более процессам.
Существует проблема организации работы процессов с разделяемыми ресурсами. (Аналог - коммунальная квартира). Для этого существуют специальные средства ОС, которые обеспечивают корректное использование ресурсов.
Возможно два варианта выделения ресурсов процессу:
1.предварительная декларация использования тех или иных ресурсов (до начала выполнения процесса системе
предаставляется перечень ресурсов, которые будут использованы процессом);
Более строгая и более экономичная.
2.Динамическое пополнение списка принадлежащих процессу ресурсов по ходу выполнения процесса при
непосредственном обращении к ресурсу . Более гибкая. Но для нее нужна стартовая память.
Реальная схема зависит от конкретной ОС. На практике возможно использование комбинации этих вариантов.
Любая ОС должна удовлетворять набору свойств:
1.Надежность. Система должна обеспечивать надежное функционирование своих компонентов. Количество ошибок,
которые могут встретиться в системе должно стремиться к нулю.
2. Обеспечение защиты. Защита от внешних воздействий от вирусов по сети и т.п. Должна быть возможность
ограничить доступ к информации некоторым пользователям.
3. Эффективность.
а) использование различных энергосберегающих ресурсов
б) удовлетворять определенным критериям качества функционирования ОС
в) обеспечение качества предоставляемых услуг для данного количества пользователей.
4. Предсказуемость. Система должна работать предсказуемо в нештатных ситуациях, чтобы не возникали
неразрешимые последствия.
Резидентная постоянно находящаяся в памяти
Ядро (kernel) резидентная часть ОС, работающая в режиме супервизора. («обычно» работает в режиме физической адресации).
API набор функций, предоставляемых системному программисту, разрабатывающему прикладные программы, и ориентированные на организацию взаимодействия результирующей программы и вычислительной системы.
В ядре размещаются программы обработки прерываний и драйверы наиболее «ответственных» устройств. Это могут быть и физические, и виртуальные устройства. Например, в ядре могут располагаться драйверы файловой системы.
Динамически подгружаемые драйверы физических и виртуальных устройств- это драйверы, добавление которых в систему возможно «на ходу» без перекомпоновки программ ОС. Они могут являться резидентными и нерезидентными, а также могут работать как в режиме супервизора, так и в пользовательском режиме.
Системный вызов средство ОС, обеспечивающее возможность процессов обращаться к ОС за теми или иными функциями. Пример: создание/завершение процесса, создание канала взаимодействия между процессами и т.д.
При системном вызове основной исполняемый код часть кода ядра. Вызов подпрограммы исполняемый код не в ядре.Обычно системный вызов заменяется на специальное прерывание и потом управление передается ОС.
Монолитное ядро ядро, которое включает в себя все возможности операционной системы, запускаются как единый процесс. Все функциональные компоненты ядра, имеют доступ ко всем внутренним файлам системы. При внесении изменений в монолитную систему все программы перекомпановываются и система перезагружается. Подсистема управления процессами: распределение памяти, планирование, взаимодействие процессов. Файловая подсистема: доступ к файлам.
Можно разделить систему на два уровня: пользовательский уровень, на котором есть штатное средство взаимодействия пользователя с системой для Unixa. Соответственно внутри реализовано монолитное ядро. Т.е. ядро ОС Unix это достаточно большой компонент, который собирается из разных частей (компонентов) и он включает в себя разные подсистемы: обеспечение взаимодействия процессами, управление памятью, планирование, файловую подсистему, драйверы разных устройств и т.д. и т.п. И все это является как одно единое целое, и какие-то изменения в этом монолитном большом ядре бывают достаточно трудоемки, как минимум для этого надо выполнять операцию пересборки ядра.
Микроядерная архитектура
Существует стационарное микроядро, которое обеспечивает минимальные функции ОС.
Функции микроядра: работа с адресным пространством, взаимодействие процессов, планирование.
Достоинства микроядерной архитектуры:
Единообразные интерфейсы, расширяемость, гибкость, переносимость, надежность, поддержка распределенных систем.
Недостаток много накладных расходов, связанных с «перемещением»запроса до адресата.
Логические функции ОС
управление процессами
распределение времени ЦП, распределение ресурсов между процессами, создание и удаление процессов, межпроцессные связи, синхронизация процессов
управление ОП
ОС может реализовать различные стратегии управления ОП. Либо стратегия выделения фиксированного количества ОП под процесс, либо динамическое выделения памяти. Должен быть, соответствующий аппарат распределения памяти. Также должен быть аппарат защиты памяти.
В функции ОС также входит: размещение прикладных процессов, реализация виртуальной памяти, изоляция процессов, автоматическое размещение программ по памяти, средства для организации долгосрочного хранения информации.
планирование
Распределение квантов времени на работу с ЦП, внешними устройствами и т.п. между процессами при многопроцессном использовании
управление устройствами и ФС
Унификация способов доступа к информации, контроль доступа, управление редактированием файлов.
Будем считать, что мы рассматриваем некоторую модельную ОС, в которой процесс, после его формирования поступает на буфер ввода процессов буфер ввода процессов -область на внешней памяти, где аккумулируются все процессы, которые еще не начали выполняться. Происходит формирование необходимых процессу структур данных.
Затем процесс поступает в Буфер обрабатываемых процессов - область памяти, где хранятся данные процессов, которые начали обрабатываться в мультипрограммном режиме.
Обобщенный жизненный цикл процесса можно представить в виде некоторого ориентированного графа, , называемого графом состояний, у которого есть узлы состояния и ребра, по которым процесс может переходить из одного состояния в другое, называемого графом состояний..
0.После формирования процесс поступает в очередь на начало обработки ЦП (попадает в БВП).
1.В БВП выбирается наиболее приоритетный процесс для начала обработки ЦП (попадает в БОП).
2.Процесс прекращает обработку ЦП по причине ожидания операции в/в, поступает в очередь завершения операции обмена (БОП).
3.Процесс прекращает обработку ЦП, но в любой момент может быть продолжен (например, истек квант времени ЦП, выделенный процессу). Поступает в очередь процессов, ожидающих продолжения выполнения центральным процессором (БОП).
4.Наиболее приоритетный процесс продолжает выполнение ЦП (БОП).
5.Операция обмена завершена и процесс поступает в очередь ожидания продолжения выполнения ЦП (БОП).
6.Переход из очереди готовых к продолжению процессов в очередь процессов, ожидающих завершения обмена (например, ОС откачала содержимое адресного пространства процесса из ОЗУ во внешнюю память) (БОП).
7.Завершение процесса, освобождение системных ресурсов. Корректное завершение работы процесса, разгрузка информационных буферов, освобождение ресурсов (например, реальный вывод информации на устройство печати).
Пакетная ОС
Пакет программ совокупность программ, для выполнения каждого из которых требуется некоторое время работы процессора. Этот тип был на первых компьютерах. Пакет программ стопка перфокарт.
Стратегия переключения с одного процесса на другую, если
а)выполняемый процесс завершен
б)возникло прерывание по обмену в выполняемом программе
в)зафиксировался факт зацикливания.
Квант времени ЦП некоторый фиксированный ОС промежуток времени работы ЦП
ЦП предоставляется процессу на один квант времени. Меняя размер кванта можно получить различные характеристики ОС. Большой квант времени удобен для отладки.
Если квант времени устремить к нулю, то у пользователя создается впечатление, что он работает один на этой ОС. Это происходит потому, что критерий эффективности с точки зрения человека через сколько компьютер реагирует на действия человека.
Переключение выполнения процессов происходит только в одном из случаев:
- Исчерпался выделенный квант времени
- Выполнение процесса завершено
- Возникло прерывание
- Был фиксирован факт зацикливания процесса
Системы реального времени
являются специализированными системами в которых все функции планирования ориентированы на обработку некоторых событий за время, не превосходящее некоторого предельного значение
Критерий качества обработка любого события за некоторый гарантированный промежуток времени (бортовой компьютер, автопилот…)
Реально (за исключением систем реального времени, которые могут быть разные по областям применения, важности серьезности и т.д.) используются комбинации пакетных и систем разделения времени друг в друге и с различными стратегиями
Непосредственное управление Внешними устройствами ЦП. В основном требуется переместить данные из ВУ в ОЗУ (и наоборот). ЦП по своей инициативе почти никогда не обращается к ОЗУ.
Историческая модель основана на том, что управление осуществлялось с помощью ЦП.
Когда говорится о том, что организовано управление внешним устройством, то подразумевается, что реализуется два потока информации:
Поток управляющей информации обеспечивает управление ВУ, поток данных начинает двигаться от ВУ к ОЗУ в результате выполнения 1ого потока. Оба потока обрабатывает ЦП, что «отвлекает» его от других задач пользователя.
Синхронное управление внешними устройствами с использованием контроллеров внешних устройств.
В результате развития аппаратной части компьютера появляются контролеры внешнего устройства. Он упростил жизнь ЦП. Все равно поток команд идет через ЦП, контролер взял некоторые функции:
1.Обнаружение ошибок
2. Обеспечение более высокоуровнего интерфейса по управлению ВУ
3. Позволяет использовать команды типа «вывести головку на нужный сектор», «…на нужный цилиндр»
4. Появилось разделение функций синхронизации. ЦП подавал сигнал и ждал.
В результате развития аппаратных прерываний появилась возможность использовать ассинхронный режим работы.
Асинхронное управление внешними устройствами с использованием контроллеров внешних устройств.
Появление контролеров прямого доступа позволяет вывести поток данных, который появляется при обмене с ВУ из ЦП. Это имеет смысл для блокориентированных устройств, подразумевающих большой поток информации. Поток управляющей информации остается в ведении ЦП.
Управление внешними устройствами с использованием процессора или канала ввода/вывода.
Наличее процессоров ввода-вывода позволяет обеспечить высокоуровневый интерфейс для ЦП при управлении внешними устройствами. ЦП предоставляются различные макрокоманды. (например «записать на диск … начиная с …места»)
Цели, которые стоят перед программным обеспечением:
1. унификация программных интерфейсов доступа к внешним устройствам (унификация именования, абстрагирование от свойств
конкретных устройств);
2. обеспечение конкретной модели синхронизации при выполнении обмена (синхронный, асинхронный обмен);
3. обработка возникающих ошибок (индикация ошибки, локализация ошибки, попытка исправления ситуации);
корректно обработать эту ситуацию, минимизировать негативные последствия.
4. буферизация обмена в системе очень многоуровневая, применяется на всех этапах:
- развитые канала ввода-вывода могут иметь встроенный КЭШ, который управляется внутри этих каналов. Эта функция
остается на уровне ОС, этот КЭШ ОС полностью программноориентирован.
5. обеспечение стратегии доступа к устройству (распределенный доступ, монопольный доступ);
6. планирование выполнения операций обмена возникает, когда возникает конкуренция за доступ к ресурсу.
T время обмена;
С время выполнения программы между обменами
t общее время выполнения программы
Схемы буферизации ввода-вывода
а) Без буферизации
Если обмен проходит без буферизации, то совокупное время выполнения программы будет складываться из времени обмена и времени выполнения программы между обменами.
б) Одинарная буферизация
При использовании одиночной буферизации подавляется заказ на обмен с ОП, и процесс может в этом случае не ожидать. Целесообразно использовать, когда идет интенсивный поток заказов на обмен.
в) Двойная буферизация
Модель использования двойной буферизации следующая: в один буфер помещаются данные по обмену, в другой ОС готовит данные за предыдущий обмен.
г) Циклическая буферизация
Какую схему выбрать зависит от интенсивности буферизации и особенности действий
Возможна ситуация, когда поток заказов на обмен > пропускной способности сстемы в некоторые моменты.
Тогда есть несколько вариантов действий:
1.Принимаем решения о порядке обработки запросов
2. начинаем учитывать приоритеты
3. осуществляем случайный выбор.
Проблема: Обмены могут быть зависимы друг от друга. В таком случае некоторые варианты не подходят.
Пусть наш диск может сразу переходитьс iой дорожки на jую без начального позиционирования.
Рассмотрим модельную ситуацию:
головка HDD позиционирована на дорожке 15
Очередь запросов к дорожкам: 4, 40, 11, 35, 7, 14
Варианты решения
1. простейшая модель случайная выборка из очереди
2.
Общее время выполнения 135ед.
Среднее время выполнения 21.5 ед.
3.SSTF
Приоритет имеет обмен, для которого потребуется наименьшее время. «Жадный» алгоритм на каждом шаге пытается получить максимальный эффект. Общая нагрузка на систему с точки зрения обмена сокращается в 3 раза. Возможно «залипание» головки в том случае, если обмен идет интенсивно с одними и теми же дорожками. Некоторые процессы будут отделены.
4.LIFO
Смысл попытка развязать последовательность обмена, связанную с новыми источниками.
Приоритетный алгоритм (RPI) это алгоритм, когда последовательность обменов (очередь) имеет характеристику приоритетов. При использовании приоритетных алгоритмов может возникать проблема голодания или дискриминации. Проблема дискриминации возникает при непрерывном поступлении более приоритетных запросов на обмен, в это время как менее приоритетные запросы простаивают.
Находясь в начальной позиции сначала двигаемся в одну сторону до конца, затем в другую до конца.
Для " набора запросов
перемещений £ 2 х число_дорожек
Выходим на минимальную (максимальную дорожку, а затем движемся в одну сторону. Пройдем не более двух маршрутов.
N-step-SCAN
Разделение очереди на подочереди длины £ N запросов каждая (из соображений FIFO). Последовательная обработка очередей. Обрабатываемая очередь не обновляется. Обновление очередей, отличных от обрабатываемой.
Этот алгоритм срывает головку с залипания.
Распространенный пример: 2 очереди, одна обрабатывается, другая собирает вновь поступающие запросы.
Существуют проблемы с организацией больших потоков данных.
В общем случае для дисковых систем имеют место как минимум две проблемы:
Все это обусловило появление так называемых RAID систем. Вначале RAID переводили как избыточный массив недорогих дисков. Со временем понятие RAID системы изменилось и на сегодняшний день оно переводится как избыточный массив независимых дисков.
Итак, RAID система представляет собой набор независимых дисков, которые рассматриваются ОС как единое дисковое устройство, где данные представляются в виде последовательности записей, которые называются полосы. /*Полосы цилиндрически распределены по дисковому устройству. */
Рассмотрим модели организации многодисковых систем, которые относятся к классу RAID.
Семь уровней RAID систем.
RAID 0 (без избыточности)
Не является настоящим RAID уровнем, поскольку не использует избыточность для повышения эффективности.
Пользовательские и системные данные распределяются по всем дискам массива. Это лучше, чем использовать один большой диск, так как появляется вероятность того, что два
различных блока памяти, к которым поступили два различных запроса ввода\вывода, размещены на различных дисках, вследствие чего эти два запроса могут обрабатываться параллельно.
Все пользовательские и системные данные рассматриваются как хранящиеся на одном логическом диске. Диск делится на полосы, которые могут быть физическими блоками, селекторами или другими единицами хранения. Полосы циклически размещаются на последовательных дисках массива. В n-дисковом массиве первые n полос распологаются как первые полосы каждого из n дисков; вторые n- как вторые полосы каждого из n дисков и т.д.
«+» Если один запрос ввода\вывода обращается к множеству логически последовательных полос, то параллельно может быть обработано до n полос. Уменьшается время обработки.
RAID 1 (зеркалирование Предполагает наличие массивов устройств. 1ая группа циклическое распределение устройств по уровням 2ая группа-копия первой. Запись идет параллельно и независимо)
«+»
RAID первого уровня это достаточно дорогостоящая конструкция, потому что получается двойное резервирование, но тем не менее эта система наиболее просто организована.
RAID 2 избыточность с кодами Хэмминга (Hamming, исправляет одинарные и выявляет двойные ошибки) Также используется разделение на полосы. Полосы оказываются очень малыми; нередко они соответствуют одному байту или слову. Обмен с синхронизацией головок чтения записи. Часть дисковых устройств предназначены для хранения содержательной части информации. Существует несколько дисковых устройств, в которых реализованы коды Хемминга.
При считывании осуществляется одновременный доступ ко всем дискам. Данные запроса и код коррекции ошибок передаются контролеру массива. При наличии однобитовой ошибки контролер способен быстро ее откорректировать, так что доступ для чтения в этой схеме не замедляется.
При записи происходит одновременное обращение ко всем дискам массива.
. Имеют место 2 проблемы:
1.Соответственно избыточность меньше, чем у RAID 1, но все равно она присутствует.
2. Есть зависимые обмены, т.е. обмены, которые организованы на специализированных движениях головок. И соответственно информация сильно распределена по RAID массиву. Т.е. последовательная информация за счет маленького размера полосок распределена. Т.е. одновременно происходит обращение ко всей цепочке. Т.е. нет независимых обменов в каждом дисковом устройстве.
RAID 3 (четность с чередующимися битами) 4 диска содержательные для размещения логических данных. 5ый контрольная избыточная информация.
Суть: Если представить, что модель RAID состоит из 5 дисков. В этих 5 дисках 4 диска содержательные, т.е. для размещения логического диска с соответствующими полосками. 5-й диск это контрольная избыточная информация. Содержимое пятого диска выражается по формулам через содержимое первых 4.То есть определенный разряд 5-го диска представляется как «исключающее или» для соответствующих ему содержательных разрядов. В случае гибели какого-нибудь из устройств утверждается, что информацию на этом устройстве можно восстановить по второй, приведенной ниже, формуле. Т.е. имеет место избыточность, которая с одной стороны дает синхронизированный параллельный доступ, а с другой имеется функция, которая восстанавливает информацию в случае гибели устройства.
Пример: 4 диска данных, один четности:
Потеря данных на первом диске
X4(i)=X3(i)XOR X2(i)XOR X1(i)XOR X0(i)
X1(i)=X4(i)XOR X3(i)XOR X2(i)XOR X0(i)
RAID 4
Он не синхронизированный, т.е. в этом плане он аппаратно организован проще, чем предыдущие. Схема примерно та же самая: имеется 4 устройства для логического диска, на которых располагаются полосы, и 5-е устройство, в котором находятся контрольные суммы. Контрольная сумма вычисляется по той же самой формуле, что и в RAID 3. И здесь есть проблема работы в случае независимого обмена.
Пример: 4 диска данных, один четности:
При независимом обмене происходит обновление следующим образом: предположим, что обновление произошло на первом диске.
X4(i)=X3(i)XOR X2(i)XOR X1(i)XOR X0(i)
все разряды на 4-м будут обновлены по следующей формуле:
X4new(i)=X4(i)XOR X1(i)XOR X1new(i)
Восстановление информации проходит по предыдущей схеме (это схема обновления, потому что обмены могут быть независимыми, т.е. обмен может происходить только по одной полоске, но для этого необходимо скорректировать содержимое контрольной полоски и использовать ее для восстановления).
RAID 5 (распределенная четность циклическое распределение «четности»)
RAID 5 - это использование циклического распределения контрольного диска.
Суть: в RAID 3 и RAID 4 есть некоторая диспропорция в распределении потока обмена, т.е. сильно нагружено последнее устройство (это плохо тем, что рано или поздно это устройство выйдет из строя первым), на котором находится контрольная сумма. Т.о. контрольный диск циклически распределен по всем устройствам, т.е. вся работа равномерно распределяется.
RAID 6 (двойная избыточность циклическое распределение четности с использованием двух схем контроля: N+2 дисков)
RAID 6 это двойная избыточность. Делается еще одно дополнительное устройство для хранения избыточной информации.
Какие-то из RAID массивов можно реализовать чисто программно. Какие-то из них можно реализовать только аппаратно. Какие-то из них можно реализовать в зависимости от решения. Это все относится к проблеме управления внешними устройствами: качеством и свойством работы внешних устройств в системе.
Уровни RAID (сравнение)
Категория |
Уровень |
Описание |
Скорость обработки запросов |
Скорость передачи данных |
Типичное применение |
Расщепление |
0 |
Без избыточности |
Большие полосы: отлично |
Малые полосы: отлично |
Приложения с некритическими данными, требующие высокой производительности |
Зеркалирование |
1 |
Зеркалирование |
Хорошо/ удовлетворительно |
удовлетворительно/ удовлетворительно |
Системные диски, важные файлы |
Параллельный доступ |
2 |
Избыточность с кодами Хэмминга |
Плохо |
Отлично |
|
3 |
Четность с чередующимися битами |
Плохо |
Отлично |
Приложения с большими запросами ввода/вывода (графич. редакторы, САПР) |
|
Независимый доступ |
4 |
Четность с чередующимися блоками |
Отлично/ удовлетворительно |
Удовлетворительно/ плохо |
|
5 |
Распределенная четность с чередующимися блоками |
Отлично/ удовлетворительно |
Удовлетворительно/ плохо |
Высокая скорость запросов, интенсивное чтение, поиск данных |
|
6 |
Двойная распределенная четность с чередующимися блоками |
Отлично/плохо |
Удовлетворительно/ плохо |
Приложения, требующие исключительно высокой надежности |
OC Unix: Работа с внешними устройствами
Особенность UNIX- все устройства обслуживаются в системе виде файлов.
С точки зрения внутренней организации системы, как и в подавляющем большинстве других операционных систем, работа с внешними устройствами осуществляется посредством использования иерархии драйверов, которые позволяют организовывать взаимодействие ядра ОС с конкретными устройствами. В системе Unix существует единый интерфейс организации взаимодействия с внешними устройствами, для этих целей используются специальные файлы устройств, размещенные в каталоге /dev. Файл устройства позволяет ассоциировать некоторое имя (имя файла устройства) с драйвером того или иного устройства. Следует отметить, что здесь мы несколько замещаем понятие устройства понятием драйвер устройства, так как несмотря на то, что мы используем термин специальные файлы устройств, на практике, мы используем ассоциированный с данным специальным файлом драйвер устройства, и таких драйверов у одного устройства может быть произвольное число. Возможно, более удачным было бы использовать специальный файл-драйвер устройства.
В системе существуют два типа специальных файлов устройств:
- файлы байториентированных устройств (драйверы обеспечивают возможность побайтного обмена данными и, обычно, не используют централизованной внутрисистемной кэш-буферизации );
- файлы блокориентированных устройств (обмен с данными устройствами осуществляется фиксированными блоками данных, обмен осуществляется с использованием специального внутрисистемного буферного кэша).
Следует отметить, файловая система может быть создана только на блокориентированных устройствах.
В общем случае тип файла определяется свойствами конкретного устройства и организацией драйвера. Конкретное физическое устройство может иметь, как байториентированные драйверы драйверы, так и блокориентированные. Например, если рассмотреть физическое устройство Оперативная память , для него можно реализовать, как байториентированный интерфейс обмена (и соответствующий байториентированный драйвер), так и блокориентированный.
Содержимое файлов устройств размещается исключительно в соответствующем индексном дескрипторе, структура которого для фалов данного типа, отличается от структуры индексных дескрипторов других типов файлов.
Итак индексный дескриптор файла устройства содержит:
- тип файла устройства байториентированный или блокориентированный;
- «старший номер» (major number) устройства - номер драйвера в соответствующей таблице драйверов устройств;
- «младший номер» (minor number) устройства служебная информация, передающаяся драйверу устройства.
Система поддерживает две таблицы драйверов устройств.
bdevsw таблица драйверов блокориентированных устройств.
cdevsw - таблица байториентированных устройств. Выбор конкретной таблицы определяется типом файла устройства. Соответственно, поле старший номер определяет строку таблицы с которой ассоциирован драйвер устройства. Драйверу устройства может быть передана дополнительная информация через поле младший номер это может быть, например, номер конкретного однотипного устройства или некоторая информация, определяющая дополнительные функции драйвера
Каждая запись этих таблиц содержит так называемый коммутатор устройства структуру, в которой размещены указатели на соответствующие точки входа (функции) драйвера. Таким образом, в системе определяется базовый уровень взаимодействия с драйвером устройства (конкретный состав точек входа определяется конкретной версией системы). В случае, если конкретный драйвер устройства не поддерживает работу с той или иной точкой входа, на ее место устанавливается специальная ссылка-заглушка на точку ядра.
В качестве примера, рассмотрим типовой набор точек входа в драйвер (b - префикс точки входа, характеризующий конкретный драйвер):
- bopen() открытие устройства, обеспечивается инициализация устройства и внутренних структур данных драйвера;
- bclose() закрытие драйвера устройства, например в том случае, если ни один из процессов не работает с драйвером;
- bread() чтение данных;
- bwrite() запись данных;
- bioctl() управление устройством, задание режимов работы драйвера, определение набора внутренних операций/команд драйвера;
- bintr() обработка прерывания, вызывается ядром при возникновении прерывания в устройстве с которым ассоциирован драйвер;
- bstrategy() управление стратегией организации блокориентированного обмена (некоторые функции оптимизации организации обмена, обработка специальных ситуаций, связанных с функционированием конкретного устройства и т.п.).
Так в некоторых реализациях системы возможно отсутствие точек входа чтения и записи для блокориентированнх устройств. В этом случае блокориентированный обмен реализуются путем передачи управления на точу bstrategy().
В системе возможно обращение к функциям драйвера в следующих ситуациях:
1. старт системы, определение ядром состава доступных устройств.
2. обработка запроса ввода/вывода (запрос может быть инициирован, любыми процессами, в том числе и ядром);
3. обработка прерывания, связанного с данным устройством, в этом случае ядро вызывает специальную функцию драйвера;
4. выполнение специальных команд управления (например, остановка устройства, приведение устройства в некоторое начальное состояние и т.п.).
Существует два, традиционных способа включения драйверов новых устройств в систему:
· путем «жесткого», статического встраивания драйвера в код ядра, требующего перекомпиляцию исходных текстов ядра или пересборку объектных модулей ядра.
· за счет динамического включения драйвера в систему.
Динамическое включение драйверов в систему предполагает выполнение следующей последовательности действий:
- загрузка и динамическое связывание драйвера с кодом ядра (выполняется специальным загрузчиком);
- инициализация драйвера и соответствующего ему устройства (создание специальных структур данных драйвера,
формирование данных коммутатора устройства, связывание обработчика прерываний ядра с данным драйвером).
Для обеспечения динамического включения/выключения драйверов предоставляется набор системных вызовов, обеспечивающий установку и удаление драйверов в систему.
На практике, наиболее часто мы имеем дело с обменами, связанными с доступом к содержимому обыкновенных файлов. Рассмотрим обобщенную схему организации обмена данными с файлами, т.е. внутреннюю организацию программ и данных, обеспечивающих доступ к содержимому файловой системы (файловая система может быть создана исключительно на блокориентированных устройствах).
Рассмотрим ряд информационных структур и таблиц, используемых системой для организации интерфейса работы с файлами.
Для организации интерфейса работы с файлами ОС использует информационные структуры и таблицы двух типов:
- ассоциированные с процессом;
- ассоциированные с ядром операционной системой.
Таблица индексных дескрипторов открытых файлов.
Для каждого открытого в рамках системы файла формируется запись в таблице ТИДОФ, содержащая:
· копия индексного дескриптора (ИД) открытого файла;
· кратность - счетчик открытых в системе файлов, связанных с данным ИД.
Вся работа с содержимым открытых файлов происходит посредством использования копии ИД, размещенной в таблице ТИДОФ. Данная таблица размещается в памяти ядра ОС. Если один и тот же файл открыт неоднократно, то запись в ТИДОФ создается одна, но каждое дополнительное открытие этого файла увеличивает счетчик на единицу
Таблица файлов.
Таблица файлов содержит сведения о всех файловых дескрипторах открытых в системе файлов. Каждая запись ТФ соответствует открытому в системе файлу или точнее используемому файловому дескриптору (ФД). Каждая запись ТФ содержит указатели чтения/записи из/в файл. Рассмотрим правила установления соответствия между открытыми в процессах файлами и записями ТФ. При каждом новом обращении к функции открытия файла в таблице процессов образуется новая запись, таким образом если неоднократно в одном или нескольких процессах открывается один и тот же файл, то в каждом случае будет определяться свой независимый от других файловый дескриптор, в том числе со своим указателем чтения/записи. Если файловый дескриптор в процессе образуется за счет наследования, то в этом случае новые записи в ТФ не образуются, а происходит увеличение счетчика «наследственности» в записи, соответствующей файлу, открытому в прародителе. Таблица размещается в памяти ОС.
Таблица открытых файлов.
С каждым процессом связана таблица открытых файлов (ТОФ). Номер записи в данной таблице есть номер ФД, который может использоваться в процессе. Каждая строка этой таблицы имеет ссылку на соответствующую строку ТФ. Первые три строки этой таблицы используются для файловых дескрипторов стандартных устройств/файлов ввода вывода.
Для иллюстрации работы с данными таблицами рассмотрим следующий пример.
Пусть в системе сформирован процесс №1, в нем открыт файл с именем name (для простоты будем считать, то это единственное открытие файла с данным именем в данный момент времени), в таблице ТОФ№1 этого процесса будет образована соответствующая запись, которая будет ссылаться на запись в ТФ, которая, в свою очередь, ссылается на таблицу ТИДОФ. Счетчик наследственности ТФ и счетчик кратности ТИДОФ будут равны единице.
Далее, формируется процесс №2, который в свою очередь открывает файл с именем name, в результате чего в ТФ будет образована новая запись, которая будет ссылаться на запись ТИДОФ, соответствующую индексному дескриптору файла name, счетчик кратности этой записи увеличится на единицу.
Процесс №1 выполняет системный вызов fork() в результате чего образуется процесс №3 с открытым (унаследованным) файлом name. В таблице ТОФ№3 будет размещена копия таблицы ТОФ№2, счетчик наследственности соответствующей записи ТФ и счетчик кратности в записи ТИДОФ увеличатся на единицу.
Буферизация при блокориентированном обмене
Особенностью работы с блокориентироваными устройствами является возможность организации буферизации при обмене. Суть заключается в следующем. В RAM организуется пул буферов, где каждый буфер имеет размер в один блок. Каждый из этих блоков может быть ассоциирован с драйвером одного из физических блок-ориентированных устройств.
«+» оптимизация и минимизация обмена с реальными устройствами.
«-» Существенная критичность к несанкционированному выключению машины
«-»проблемы разорванности во времени операции записи (поработал, ушел, а данные еще не записались. )
Рассмотрим, как выполняется последовательность действий при исполнении заказа на чтение блока. Будем считать, что поступил заказ на чтение N-ого блока из устройства с номером M.
1. Среди буферов буферного пула осуществляется поиск заданного блока, т.е. если обнаружен буфер, содержащий N-ый блок М-ого устройства, то фиксируем номер этого буфера. В этом случае, обращение к реальному физическому устройству не происходит, а операция чтения информации является представлением информации из найденного буфера. Переходим на шаг 4.
2. Если поиск заданного буфера неудачен, то в буферном пуле осуществляется поиск буфера для чтения и размещения данного блока. Если есть свободный буфер (реально, эта ситуация возможна только при старте системы), то фиксируем его номер и переходим к шагу 3. Если свободного буфера не нашли, то мы выбираем буфер, к которому не было обращений самое долгое время. В случае если в буфере имеется установленный признак произведенной записи информации в буфер, то происходит реальная запись размещенного в буфере блока на физической устройство. Затем фиксируем его номер и также переходим к пункту 3.
3. Осуществляется чтение N-ого блока устройства М в найденный буфер.
4. Происходит обнуление счетчика времени в данном буфере и увеличение на единицу счетчиков в других буферах.
5.Передаем в качестве результата чтения содержимое данного буфера.
Вы видите, что здесь есть оптимизация, связанная с минимизацией реальных обращений к физическому устройству. Это достаточно полезно при работе системы. Запись блоков осуществляется по аналогичной схеме. Таким образом, организована буферизация при низкоуровневом вводе/выводе. Преимущества очевидны. Недостатком является то, что система в этом случае является критичной к несанкционированным отключениям питания, т. е. ситуация, когда буфера системы не выгружены, а происходит нештатное прекращение выполнения программ операционной системы, что может привести к потере информации.
Второй недостаток заключается в том, что за счет буферизации разорваны во времени факт обращения к системе за обменом и реальный обмен. Этот недостаток проявляется в случае, если при реальном физическом обмене происходит сбой. Т. е. необходимо, предположим, записать блок, он записывается в буфер, и получен ответ от системы, что обмен закончился успешно, но когда система реально запишет этот блок на ВЗУ, неизвестно. При этом может возникнуть нештатная ситуация, связанная с тем, что запись может не пройти, предположим, из-за дефектов носителя. Получается ситуация, при которой обращение к системе за функцией обмена для процесса прошло успешно (процесс получил ответ, что все записано), а, на самом деле, обмен не прошел.
Таким образом, эта система рассчитана на надежную аппаратуру и на корректные профессиональные условия эксплуатации.
Для борьбы с вероятностью потери информации при появлении нештатных ситуаций, система достаточно «умна», и действует верно.
А именно, в системе имеется некоторый параметр, который может оперативно меняться, который определяет периоды времени, через которые осуществляется сброс системных данных.
Второе - имеется команда, которая может быть доступна пользователю, - команда SYNC. По этой команде осуществляется сброс данных на диск.
И третье - система обладает некоторой избыточностью, позволяющей в случае потери информации, произвести набор действий, которые информацию восстановят или спорные блоки, которые не удалось идентифицировать по принадлежности к файлу, будут записаны в определенное место файловой системы. В этом месте их можно попытаться проанализировать и восстановить вручную, либо что-то потерять.
Наш университет одним из первых в стране начал эксплуатировать операционную систему UNIX, и сейчас уже можно сказать, что проблем ненадежности системы, с точки зрения фатальной потери информации, не было.
Управление оперативной памятью
Основные задачи:
1.Контроль состояния каждой единицы памяти (свободна/распределена). Система должна обладать информацией о том, какая единица памяти свободна, какая занята, кем и почему. Соответственно эта функция совместно обеспечивается как аппаратурой компьютера, так и программным обеспечением ОС. ОС создает для этих целей специальные таблицы.
2.Стратегия распределения памяти. Надо выбрать правила, по которым принимать решения : когда кому и сколько памяти должно быть выделено.
3.Выделение памяти. Принятие решения о выделении конкретного объема памяти для потребителя.
4.Стратегия освобождения памяти (процесс освобождает, ОС “забирает” окончательно или временно). Одна из самых важных функций. Выбор стратегии, на основании которой система принимает решения о том, что память надо отобрать на время (при появлении более приоритетного процесса) или навсегда.
Поговорим о стратегиях и методах управления. Рассмотрим целый сектор различных методов. Некоторые старые стратегии управления памятью используются и сейчас, например в мобильных телефонах.
Стратегии и методы управления:
Одиночное непрерывное распределение.
Распределение разделами.
Распределение перемещаемыми разделами.
Страничное распределение.
Сегментное распределение.
Сегменто-страничное распределение.
План рассмотрения стратегий управления:
Основные концепции.
Необходимые аппаратные средства. (необходимое аппаратное обеспечение)
Основные алгоритмы.
Достоинства, недостатки.
Одиночное непрерывное распределение
ОП делится на 2 области. В одной находится ОС, другая предназначена для задач пользователя. (предполагается однопроцессная система.)
Необходимые аппаратные средства:
Регистр границ + режим ОС / режим пользователя. (В регистре границ находится граница между ОС и пользовательской частью ОП)
Если ЦП в режиме пользователя попытается обратиться в область ОС, то возникает прерывание.
В режиме ОС мы можем обращаться в любую точку ОП, если мы находимся в пользовательском режиме, то запрошенный адрес сравнивается с содержимым регистра границ, и , если он окажется меньше, т.е. Мы хотим обратиться в часть ОП, занятую под ОС, то выдается прерывание.
Алгоритм процесс заканчивается, мы меняем на следующий.
Достоинства: простота.
Недостатки:
Они следуют из организации.
1.Часть памяти просто не используется. (Внешняя фрагментация)
2.Процессом/заданием память занимается все время выполнения. Внутренняя фрагментация заключается в том, что вся область памяти, которую процесс занимает, занимается процессом на всё время его выполнения. Это означает, что достаточно большие области памяти, которые заняты процессом, не используются., т.к. обычно управление достаточно локализовано. Т.е. неэффективность работы с памятью.
3.Ограничение на размеры процесса. Т.е. загрузить в эту систему процесс, превосходящий область памяти, мы не можем.
Распределение неперемещаемыми разделами
Суть: Есть ОС и оставшаяся физическая память. Оставшуюся физическую память делим на конкретное количество разделов. В каждом может быть свое задание и свой процесс. Внутри каждого раздела все аналогично рассмотренному выше примеру.
Необходимые аппаратные средства:
Необходимо наличие 2 регистров границ, т.к. необходимо обеспечить корректность как по отношению к другим пользовательским разделам, так и по отношению к ОС.
Недостатки:
а. перегрузка регистра границ при каждой смене контекста;
б. сложности при использовании каналов/процессоров ввода/вывода. Если процесс попытается читать не из своей области, то это тяжело отловить
1.Ключи защиты (PSW). Каждый раздел имеет свой ключ защиты, который проверяется при всех операциях чтения\записи . Это решает проблему б).
Алгоритмы: Модель статического определения разделов
. Сортировка входной очереди процессов по отдельным очередям к разделам. Вся очередь процессов разбивается на к очередей, с каждой из которых связан свой раздел. Процесс размещается в разделе минимального размера, достаточного для размещения данного процесса. В случае отсутствия процессов в каких-то под очередях неэффективность использования памяти.
Недостаток: Может возникнуть ситуация, когда очередь больших процессов пуста, а в очереди маленьких процессов очень много процессов. А перегрузить мы не сможем.
Алгоритмы: Модель статического определения разделов
Б. Одна входная очередь процессов.
1. Освобождение раздела поиск (в начале очереди) первого процесса, который может разместиться в разделе.
Проблема: большие разделы маленькие процессы. Это несправедливо по отношению к большим процессам.
2. Освобождение раздела поиск процесса максимального размера, не превосходящего размер раздела.
Проблема: дискриминация “маленьких” процессов.
3. Оптимизация варианта 2. Каждый процесс имеет счетчик дискриминации. Если значение счетчика процесса ³ K, то обход его в очереди невозможен.
Достоинства:
Простое средство организации мультипрограммирования.
Простые средства аппаратной поддержки.
Простые алгоритмы.
Недостатки:
Внешняя Фрагментация.
Ограничение размерами физической памяти как внутри одного раздела, так и в целом
Весь процесс размещается в памяти возможно неэффективное использование и внутренняя фрагментация.
Распределение перемещаемыми разделами
Система имеет фиксированное количество разделов. Через некоторое время ее использования начинается внешняя фрагментация.
Решение: перемещение разделов и освобождение одного большого куска. Но это требует очень больших затрат.
Необходимые аппаратные средства:
1.Регистры границ + регистр базы
2.Ключи + регистр базы
Алгоритмы: Аналогично предыдущему
Достоинства:
Потенциальная ликвидация внешней фрагментации
Недостатки:
Внутренняя фрагментация
Ограничение размером физической памяти
Затраты на перекомпоновку. Операция освобождения одного большого куска ОП очень тяжела.
Страничное распределение
Посредством аппаратных и программных решений, например, таблицы страниц, возможно отображать физические страницы. Содержимое таблицы определяет соответствие виртуальной памяти физической для выполняющейся в данный момент программы/процесса. Соответствие определяется следующим образом: i-я строка таблицы соответствует i-й виртуальной странице.
При замене процесса таблицу надо менять.
Таблица страниц отображение номеров виртуальных страниц на номера физических.
Проблемы:
1. Размер таблицы страниц (количество 4кб страниц при 32-х разрядной адресации 1000000. Таблица должна иметь миллион строк, а таблицу надо перегружать каждый раз при смене контекстов Любой процесс имеет собственную таблицу страниц).
2. Скорость отображения. Эта проблема, фактически следует из проблемы 1.
Возможные аппаратные средства:
1.Полностью аппаратная таблица страниц, которая будет находится в виде сверхоперативной памяти. Все преобразования будут
проходить очень быстро (Проблемы :стоимость, полная перегрузка при смене контекстов, +: скорость преобразования).
2.Регистр начала таблицы страниц в памяти. Будет многократное увеличение количества обращений к памяти. (простота, управление смены контекстов, медленное преобразование). Альтернативное решение - организация таблицы страниц на ОП. В этом случае нам нужен аппаратный регистр начала таблицы, и переключение с контекста на контекст будет осуществляться очень хорошо и быстро, просто я буду менять содержимое регистра начала таблицы. При этом мы получим многократное увеличение количества обращений в память. И как минимум мы потеряем 100% эффективность. Понятно что, часть проблем будут минимизированы за счет работы КЭШ, но все равно это будет неэффективно. Но зато это просто и дешево.
3.Гибридные решения. Т.е. Те, которые имеют и программную и аппаратную составляющую.
Решение проблем, связанных с размерами таблицы страниц иерархическая организация таблицы страниц.
Предположим, что таблицы страниц индексируются по номерам соответствующих виртуальных страниц. Содержимое каждой записи информация о соответствующей виртуальной странице.
Поля:
α присутствие/отсутствие. Если этот признак установлен, то это означает, что в поле «номер физической станицы» находится та самая физическая страница, к которой мы обращаемся. Если отсутствует, то возможны 2 варианта: либо эта страничка запрещена для данного процесса, либо она разрешена, но сама страница в это время откачена во внешнюю память. Но в любом случае, если есть элемент отсутствия, то при обращении к этой строчке происходит прерывание.
β поле защиты (чтение, чтение/запись, выполнение). Когда процессор доходит до таблицы страниц, он уже знает, с какой целью он получает этот адрес. Либо этот адрес есть операнд, куда мы хотим записать, либо этот адрес есть операнд, из которого мы хотим считать информацию, либо этот адрес есть операнд команды, которую я хочу выбрать и выполнить (goto адрес). Соответственно это поле обеспечивает защиту. Т.е. в зависимости от того, с какой целью процессор обращается к этой строчке, и содержимого этой строчки (а содержимое могут быть коды, которые разрешают чтение, или чтение/запись, или выполнение, или запрещают их так же как в ФС), то при нарушении происходит прерывание.
γ признак изменения (модификации). Если мы в эту страничку писали, то этот признак будет установлен. Этот признак устанавливается обычно аппаратно автоматически. Снимается он либо аппаратно, либо программно ОС.
δ обращение (чтение, запись, выполнение). Когда мы обратились либо за чтением, либо за записью и т.д.
ε признак блокировки кэширования. Я заказал обмен: прочесть информацию с внешнего устройства на какую-то страницу, в конечном итоге физическую станицу. А на самом деле Ν страниц у меня находится в КЭШе. Как разрешить эту коллизию? Внешнее устройство кинет информацию в физическую память, а на самом деле я работаю с КЭШем, а потом из КЭШа я это переобновлю и все потеряется. Для того, чтобы можно было синхронизовать эту вещь, используется блокировка кэширования. Здесь, кроме управления оперативной памятью в контексте того, о чем говорим, мы еще добавляем некоторую информацию и в темы, связанные с управлением вводом/выводом и в темы, связанные с кэшированием.
Для разрешения всех коллизий, связанных со скоростью, размерами и прочим, используются гибридные решения. И, в частности, одно из решений основывается на TLB буферах.
TLB (Translation Lookaside Buffer) Буфер быстрого преобразования адресов. В процессоре есть буфер (не большой), который используется в качестве КЭШ таблицы страниц.
Структура буфера: Каждая запись содержит 2 поля - №виртуальной страницы и № физической страницы. TLB буфер буфер оперативной памяти. Поиск идет параллельно: за одну операцию просматривается наличие всей таблицы.
Мы мимеем виртуальный адрес, в котором традиционно есть поле: «виртуальная страница» и есть поле «смещение». Процессор выбирает поле «виртуальная страница» и обращается к TLB буферу. Если мы фиксируем факт попадания, то в этом случае автоматически происходит замена поля виртуальной страницы на содержимое поля физической страницы так мы получили физический адрес со всеми вытекающими параметрами, которые могут находиться в TLB. Если мы фиксируем промах, то в этом случае происходит прерывание, управление передается ОС. И ОС уже программно находит необходимую строчку и обновляет TLB буфер и соответственно дообрабатывает команду преобразования виртуального в физический.
Иерархическая организация таблицы страниц
Проблема размер таблицы страниц.
Объем виртуальной памяти современногокомпьютера - 232,…264
Пример:
Vстр. = 212 (4Kb)
Количество виртуальных страниц 220 (много)
Решение использование многоуровневых таблиц страниц (2х, 3х, 4х)
Современные системы используют многоуровневую организацию таблицы страниц.
Система разделяет VP на 2 подполя: VP1 - индекс по внешней таблице страниц, а VP2 смещение по странице, на которую указывает VP1. >4 уровней иерархии считается не целесообразно.
Многоуровневая организация
Суть многоуровневости достаточно простая: если мы имеем виртуальный адрес следующей структуры (на слайде): смещение 4кб и 20-ти разрядный адрес, то система разделяет поле виртуальной странички на два подполя. 1-е подполе это индекс по внешней таблице страниц, через этот индекс мы попадаем на страничку, в которой находится продолжение описания этой таблицы; 2-е поле это смещение по этой странице. Т.е. мы имеем внешнюю таблицу, по VP1 мы индексируемся и соответственно по содержимому этой таблицы попадаем на некоторую страницу, в которой находится часть таблицы страниц 2-го уровня. И по VP2 мы проходим смещение по этой странице и в соответствующем элементе получаем номер физической страницы. Этих уровней может быть 2, 3, 4. Больше 4-х считается нецелесообразным. Для 64-х разрядных машин таких уровней если их реализоввывать должно быть не менее 7, что совсем нецелесообразно.
Использование хэштаблиц
ХЭШ функции изначально использовались при организации таблицы имен.
ХЭШ функция берет номер виртуальной страницы и по этому номеру виртуальной страницы имеется некоторая функция, которая определяет номер записи хэш-таблицы. С этой записью связан список виртуальных страниц с их физическими страницами, которые имеют одинаковое значение хэш-функции. Это означает, что при преобразовании мы берем виртуальную страницу и фактически автоматически попадаем на этот самый список. Дальше по этому списку мы можем дойти до искомой страницы и получаем физическую страницу. Если в списке нет, то это означает, что и странички такой нет.
Инвертированные таблицы страниц
Используется в более развитых системах, системах аппаратно поддерживающих pid обрабатываемого процесса.
Каждая строка таблицы соответствует конкретной физической странице.
Проблема поиск по таблице
Замещение страниц
Проблема загрузки «новой» страницы в память, если свободных мест в памяти нет. Необходимо выбрать страницу для удаления из памяти (с учетом ее модификации пр.)
Алгоритм NRU (Not Recently Used не использовавшийся в последнее время)
Используются биты статуса страницы. R обращение, М модификация. Устанавливаются аппаратно при обращении или модификации.
Алгоритм
1.При запуске процесса M и R для всех страниц процесса обнуляются
2.По таймеру происходит обнуление всех битов R
3.При возникновении страничного прерывания ОС делит все страниц на классы:
•Класс 0: R=0; M=0; - не читался и не изменялся.
•Класс 1: R=0; M=1;
•Класс 2: R=1; M=0;
•Класс 3: R=1; M=1;
4.Случайная выборка страницы для удаления в непустом классе с минимальным номером
Стратегия: лучше выгрузить измененную страницу, к которой не было обращений как минимум в течение 1 «тика» таймера, чем часто используемую страницу
ОС фиксирует время размещения страницы. Наиболее старую страницу удаляем, но это может быть неправильно, т.к. старая может часто использоваться, а новая - редко. Поэтому используется модификация этотого алгоритма. R бит обращения.
1.Выбирается самая «старая страница». Если R=0, то она заменяется
2.Если R=1, то R обнуляется, обновляется время загрузки страницы в память (т.е. переносится в конец очереди). На п.1
Алгоритм FIFO
«Первым прибыл первым удален» - простейший вариант FIFO. Для каждой страничке, которая была помещена в память, ОС фиксирует время ее размещения. Соответственно после этого наиболее старую страницу ОС удаляет. Это не очень справедливо (проблемы «справедливости»). Потому что в этом случае старая страница может активно использоваться и быть удалена. Поэтому реально используются модификации алгоритма FIFO.
Модификация алгоритма (алгоритм вторая попытка):
1.Выбирается самая «старая страница». Если R=0, то она заменяется
2.Если R=1 (к ней обращения идут), то R обнуляется, обновляется время загрузки страницы в память (т.е. считается, что она была загружена в момент обнуления признака чтения, т.е. фактически она переносится в конец очереди). На п.1 (начинаем смотреть следующую).
Алгоритм «Часы»
Алгоритм аналогичен предыдущему, только все страницы связаны в кольцевой список. Существует указатель (стрелка) на текущую страницу.
Алгоритм LRU (Least Recently Used «менее недавно» - наиболее давно используемая страница)
Пусть в памяти N страниц. Составляется битовая матрица NxN (изначально все биты обнулены). При каждом обращении к iой странице происходит присваивание 1 всем битам iой строки и обнуление всех битов iго столбца. Строка с наименьшим 2ным числом соответствует искомой странице.
Алгоритм NFU (Not Frequently Used редко использовавшаяся страница)
Развитие предыдущего алгоритма.
Для каждой физической страницы заводится программный счетчик, который изначально обнулен. По таймеру к счетчикам прибавляется признак доступа.
В момент принятия решения выбирается страница с минимальным значением счетчика.
Это все решается программно.
Недостаток если процесс поработал и «сидит без дела», то удалить его не удастся, а он не работает.
Модификация:
1.Значение счетчика сдвигается на 1 разряд вправо.
2.Значение R добавляется в крайний левый разряд счетчика.
Достоинства страничной памяти:
Недостатки:
Сегментная организация памяти
Основные концепции:
•Виртуальное адресное пространство представляется в виде совокупности сегментов
•Каждый сегмент имеет свою виртуальную адресацию (от 0 до N-1)
•Виртуальный адрес: <номер_сегмента, смещение>
Необходимые аппаратные средства для организации сегментной памяти достаточно концептуально просты. Это таблица сегментов, по которой при вычислении физического адреса из виртуального мы можем индексироваться по номеру сегмента. Соответственно каждая запись таблицы сегментов содержит размер сегмента и адрес начала сегмента.
«+» простота реализации
«+» размер таблицы сегментов может быть много меньше размера таблицы страниц
«-» наличие внешней фрагментации
«-»сегмент рассматривается как единое целое
Преобразование происходит достаточно просто: мы индексируемся по таблице, получаем запись, после этого сравниваем смещение с размером сегмента:
если смещение выходит за пределы размера происходит прерывание,
иначе мы значению базы прибавляем смещение и получаем физический адрес.
Упрощенная модель Intel.
Виртуальный адрес содержит 2 поля: селектор и смещение. Селектор содержит информацию о номере сегмента, о локализации.
Поле Локализация это таблицы локальных дескрипторов (сегменты доступные для данного процесса) LDT (Local Descriptor Table) и Таблица глобальных дескрипторов (разделяемые между процессами сегменты) GDT (Global Descriptor Table).
По LDT и GDT и виртуальному адресу мы вычисляем линейный адрес. Линейный адрес представляется в виде двухуровневой страничной организации. По этим параметрам мы вычисляем физический адрес.
Сегменто - страничная организация памяти
Виртуальный адрес содержит 2 поля: селектор и смещение. Селектор содержит информацию о номере сегмента, о локализации (есть 2 таблицы сегментов, который используются это сегменты, которые доступны только для данного процесса, или сегменты, которые могут использоваться в разных процессах).
При использовании алгоритма сегментно\страничной организации к плюсам страничной прибавляются плюсы сегментной.
Файловые системы
Файловая система (ФС) - часть операционной системы, представляющая собой совокупность организованных наборов данных, хранящихся на внешних запоминающих устройствах, и программных средств, гарантирующих именованный доступ к этим данным и их защиту
Возможности предоставляемые ФС определяют эксплутационные качества ФС. От оптимальности организации зависит область применимости ФС.
ФС компонент ОС обеспечивающий именованный доступ к данным. Данные называются файлами, их имена - именами файлов.
Ранее работа с данными осуществлялась через координаты их на внешних носителях. Это было неудобно, т.к. надо было помнить о местонахождении данных, перемещение программы с одного носителя на другой тоже вызывало трудности. Если возникала потребность менять входные данные в программе, то это тоже было не легко.
ФС совершила революцию. Появилась возможность ассоциировать с совокупностью данных некоторое имя и осуществлять доступ к данным через указатель имени.
ОС брала на себя функции размещения данных, ассоциированных с именем, сохранение информации, соответствующей данному имени.
Существует множество разновидностей структурной организации файлов. Наиболее популярные:
1. Файл, как последовательность байтов (обмен от 1 до фиксированного числа байтов) Т.е. файл это набор данных, практически не имеющих никакой структуры. Соответственно вопрос выделения логической структуры это уже проблема пользователя. Пользователь записывает данные, как последовательность байтов, считывает их и сам уже интерпретирует. Как ни странно, на сегодняшний день это одна из самых распространенных моделей структурной организации файлов. Таким образом организуются файловые системы Unix, Windows, т.е. файл там может быть представлен как просто последовательность байтов
2. Файл, как последовательность записей переменной длины (обмен в терминах записи, информация в виде последовательности записей, поле данных + символ конца записи, последовательный доступ) В этом случае каждая запись, кроме содержательной информации, должна была иметь некоторую специальную информацию. эта специальная информация могла быть либо полем, которому указывалась длина записи, либо специальная информация могла представляться в виде специального кода - маркера конца или начала записи. При такой организации внутренней фрагментации практически не было, за исключением тех потерь, которые приходились на разметку файла по записи, т.е. либо указатели длины, либо маркеры начала и конца. В этом плане эффективность организации хранения была относительно хорошей. С другой стороны такая организация исключала прямой доступ к записи. Т.е. для того, чтобы добраться до i-ой записи нужно было промотать все предыдущие: либо пересчитать маркеры начала и конца, либо пробежаться по списку через указатели длины. Файлы такой организации имели сложность с точки зрения редактирования, т.е. изменение длины существующей записи с большой вероятностью приводило к проблеме. Поскольку увеличение записи это вообще затруднительная операция, а уменьшение тоже есть некоторая проблема. Т.е. есть какая-то внутренняя проблема, которая приводила к неэффективности редактирования такого рода файлов. Записи постоянной длины организованы были так, что в пределах размера записи никаких проблем не возникало. Проблемы возникали только в том случае, если происходило либо удаление записи, либо вставка новой записи.
3. Файл, как последовательность записей постоянной длины (обмен в терминах записей постоянной длины) Исторически этот вариант структурной организации появился из-за использования такого носителя информации, как перфокарты. Т.е. было удобно делать файл, который был прямым аналогом колоды перфокарт. Соответственно это означает, что читать из файла или писать данные в этот файл система позволяла порциями размером в 80 байт. Понятно, что такая организации файла достаточно эффективна по скорости доступа, т.е. был прямой доступ к любой записи, потому что координаты записи внутри вычислялись всегда очень просто: (номер записи)*(размер записи). С другой стороны внутренняя фрагментация. Один байт используется в записи и вся запись размером в 80 байтов становится занятой.
4. Иерархическая организация файла (дерево) (поиск, сортировка и т.д. осуществляется по ключам). Суть: структура файла представима в виде дерева. В каждом узле этого дерева находится информация о записи. Информация о записи это два содержательных поля: поле ключа и поле данных. Соответственно дерево организовано таким образом, что в нем оптимизирован доступ к записям по указанию ключа, т.е. записи отсортированы по одинаковым ключам, и разные ключи отсортированы по возрастанию ключей. Поле данных может быть произвольного размера. Место расположения записи может быть в общем случае произвольно, т.е. ФС может разместить запись, где захочет, по своим каким-то критериям. имеются накладные расходы, связанные с древовидной организацией - с организацией ключей. Обычно, это достаточно специализированные ФС, которые используются или могут использоваться в высокопроизводительных, либо специальных ВС.
Дерево, в узлах записи
(возможно переменной длины)
.
lимя
lправа доступа
lперсонификация (создатель, владелец)
lтип файла
lразмер записи
lразмер файла
lуказатель чтения / записи
lвремя создания
lвремя последней модификации
lвремя последнего обращения
lпредельный размер файла
l.....
Полный состав атрибутов файла и способ их представления определяется конкретной файловой системой.
Основные правила работы с файлами
Операционная система и файловая система обеспечивают регистрацию возможности того или иного процесса работать с содержимым файлов. «Сеанс работы» с содержимым файла:
Начало «открытие» файла (регистрация в системе возможности работы процесса с содержимым файла)
Открытие создание внутрисистемной структуры данных, кот. описывает состояние этого файла, проверяет права доступа, объявляет операционной системе тот факт, что с данным файлом будет работать тот или иной процесс. При открытии файла система формирует внутренние наборы данных, необходимые для работы с содержимым файла.
Работа с содержимым файла, с атрибутами файла
Завершение «закрытие» файла информация системе о завершении работы процесса с «открытым» файлом
Закрытие файла. Закрытие файла - информация операционной системе о том, что работа с файлом завершена.
Операция закрытия файла имеет 2 вида:
закрыть и сохранить текущее содержимое файла;
уничтожить файл.
open открытие / создание файла
«r» - на чтение
«w» - на запись
… и т.д.
close закрытие
read / write читать, писать (относительно положения указателя чтения / запись, read/write по дескриптору а не по имени)
delete удалить файл из файловой системы (напрямую или дескриптор)
seek позиционирование указателя чтение/запись
rename переименование файла
read / write _attributes чтение, модификация
атрибутов файла.
Файловый дескриптор системная структура данных, содержащая информацию о актуальном состоянии «открытого» файла.
Файловый дескриптор содержит актуальную информацию о открытом файле. Через ФД можно получить информацию о значении указателей чтения\записи.
В некоторых ФС каталог отдельное внутреннее образование, в UNIX каталог файл специального типа. Если это файл, то для него можно использовать программные интерфейсы для работы с файлами.
Каталог компонент файловой системы, содержащий информацию о содержащихся в файловой системе файлах.Специальные файлы каталоги.
Модель одноуровневой файловой системы. Традиционно-простая организация каталога одноуровневая модель ФС В ФС существует один каталог, в котором находятся все файлы находящиеся в системе.
Проблемы:
1.Коллизия имен. Каждое имя должно быть единственно.
2. нагрузка на работу с системой, если много файлов.
3. неудобно структурировать.
Модель двухуровневой файловой системы.
Модель, которая появилась в реальных системах на начальных этапах после одноуровневой.
В системе существует объединение каталогов пользователей, для каждого пользователя реализована одноуровневая модель.
Проблемы 1 и 2 исчезают, а 3 остается.
Иерархические файловые системы
За основу логической организации такой файловой системы берется дерево. В корне дерева находится, так называемый, корень файловой системы - каталог нулевого уровня. В этом каталоге могут находиться либо файлы пользователей, либо каталоги первого уровня. Каталоги первого и следующих уровней организуются по аналогичному принципу. Файлы пользователя в этом дереве представляются листьями. Пустой каталог также может быть листом. Таким образом образуется древовидная структура файловой системы, где в узлах находятся каталоги, а листьями являются либо файлы, либо пустые каталоги.
Остановимся на правилах именования в иерархической файловой системе. В данном случае используется механизм, основанный на понятии имени файла (name) и полного имени файла (path_name). Полное имя файла это путь от корневого каталога до листа (такой путь всегда будет уникальным). Существует также относительное именование, т.е. когда нет необходимости указания полного пути при работе с файлами. Это происходит в случае, когда программа вызывает файл и подразумевается, что он находится в том же каталоге, что и программа. В данном случае появляется понятие текущего каталога, т. е. каталога, на работу с которым настроена файловая система в данный момент времени. В рамках одного каталога имена файлов одного уровня должны быть разными.
Системное устройство может иметь следующую структуру. Начало - основной программный загрузчик (нулевой блок системного устройства). Аппаратный загрузчик сначала обращается в основной программный загрузчик. А основной программный загрузчик уже будет обращаться к соответствующему загрузчику ОС. Далее на системном блоке находится так называемая таблица разделов. Раздел это есть виртуальный диск. Одно пространство дискового устройства можно разделить на некоторые порции в общем случае непересекающиеся, которые называются разделами, и каждый раздел представляется в системе как отдельное дисковое устройство. Соответственно структура раздела с ФС обычно следующая: в начале идет блок загрузчика ОС. Загрузчик ОС уже знает с какой ОС он будет работать, он знает где находится информация в разделе, которая необходима для загрузки ОС и соответственно выбирает ее при запуске. Далее за блоком загрузчика ОС обычно находится последовательность блоков или блок, который называется суперблок.
Суперблок блок ФС, в котором находится информация о настройках ФС и актуальном состоянии ФС (информация о свободных блоках, данных, которые содержат каталоги.)
Блок порция данных, фиксированного размера, в рамках которого идет обмен данными с устройством.
1ый уровень виртуальности - блок устройства HDD
2ой уровень виртуальности блок ФС (блок виртуального диска) использует виртуальный размер блока. Размер можно варьировать.
3ий уровень виртуальности блок файла
Непрерывные файлы
Файл может размещаться только в последовательных блоках ФС. Соответственно такая модель она абсолютно проста для реализации, она эффективна по доступу. Т.е. практически не нужно много дополнительной информации, которая будет описывать где и что храниться, только нужно выйти на начало файла. И практически нет необходимости для чтения любой порции данных из файла читать какую-то дополнительную системную информацию, т.е. накладные расходы по обмену (в этом случае) практически нулевые.
Достоинства:
Простота реализации
Высокая производительность
Недостатки:
Фрагментация свободного пространства (прямая и косвенная)
Увеличение размера существующего файла невозможно или каждый раз проводится операция компрессии
Файлы, имеющие организацию связанного списка.
Все блоки файла организованны в единый список. Это означает, что в нулевом блоке файла имеется ссылка на 1-й блок, в первом блоке файла имеется ссылка на второй блок и т.д. до последнего? в последнем блоке файла соответственно ссылка = ΝULL. Это означает, что фактически решается проблема внешней фрагментации файла, т.е. файл в этом случае может произвольным образом расширяться
Достоинства: Отсутствие фрагментации свободного пространства
( за исключением блочной блочной фрагментации )
Простота реализации
Эффективный последовательный доступ
Недостатки: Сложность (не эффективность) организации прямого доступа
Фрагментация файла по диску
Наличие ссылки в блоке файла (ситуации чтения 2-х блоков при необходимости чтения данных объемом один блок).
Таблица размещения файловой системы Существует таблица, в которой количество строк соответствует количеству блоков, в кот. i-ая строка соотв. Iому блоку файловой системы.
Информация о файлах размещается так: начальный блок файла NAME, ФС использует для оперативной работы либо всю систему, либо ее часть.
Достоинства:
1.возможность использования всего блока для хранения данных файла
2.оптимизация прямого доступа (при полном или частичном размещении таблицы в ОЗУ)
Недостатки:
для оптимальной работы ФС желательно размещение всей таблицы в ОЗУ. (проблема размера, например для 60 Gb раздела и блоков размером 1Kb потребуется 60 000 000*4b = 240 Mb).
Индексные узлы (дескрипторы)
ФС организует кластеризованное, компактное хранение информации о размещении блоков файлов в специальной структуре, которая называется индексные узлы или индексные дескрипторы. В этой структуре находится информация о размещении блоков файлов по ФС. Т.е. соответственно есть таблица, в которой размещаются индексные узлы файла. При открытии файла осуществляется поиск по этой таблице соответствующего индексного узла, и после этого в памяти аккумулируется только индексный узел открытого файла, а не вся таблица.
Достоинства:
нет необходимости в размещении в ОЗУ информации всей FAT о все файлах системы, в памяти размещаются атрибуты, связанные только с открытыми файлами.
Недостатки:
размер файла и размер индексного узла (в общем случае прийти к размерам таблицы размещения). Решение:
ограничение размера файла
иерархическая организация индексных узлов
Каталог, кот. Представлен в виде таблицы:
1.Первая модель простейший каталог. Каталог представляется в виде таблицы, которая содержит имя файла и его атрибуты. Соответственно каждая запись фиксированного размера. Проблема в том, что в этом случае каталог получается достаточно большой по объему, т.к. последовательность атрибутов может быть достаточно большой, в частности, в атрибуты может включаться и информация о размещении файла, о проблемах возникающих в таких случаях только что уже говорилось
«-» каталог очень большой по объему.
2. Каталог содержит имя файла поле фиксированного размера, и ссылку уже на системные структуры данных или системную структуру данных, в которых находятся атрибуты соответствующих файлов. В этом случае получается более менее гибкую организацию по части размера атрибутов (они здесь могут быть достаточно произвольной длины.
«+» более гибкая организация по размеру атрибутов.
Оба вида основаны на фиксированном размере записи каталога, есть ограничения на длину имени. Проблема длинных имен: короткие неудобны размещаем суффиксы имени в атрибутах.
Содержимому любого файла соответствует единственное имя файла.
Примеры:
Содержимому файла может соответствовать два и более имен файла.
“Жесткая” связь
Для одного и того же содержимого файла может существовать>= 2 имен файла. Имя файла выносим из атрибутов. Есть содержимое файла, атрибуты и любое количество имен.
“Жесткая” связь Есть содержимое файла, есть атрибут файла, и одним из полей атрибутов является количество имен у этого файла, и есть произвольное количество имен, которые как-то распределены по каталогам ФС. В этом случае каждое из этих имен равнозначно, т.е. имеет место некоторая симметричная организация: каждое из имен синонимов равноправно, т.е. нет никакого старшинства в не зависимости от порядка образования этих файлов.
“Символическая” связь есть файл с именем Νame2, этому имени соответствуют атрибуты и соответствует содержимое, и есть специальный файл Νame1, который ссылается на имя Νame2. В этом случае имеет место ассиметричное именование файлов. Имя Νame2 позволяет организовывать более широкую работу с файлом, можно удалять, если файл будет удален, соответственно содержимое пропадет. Имя Νame1 имеет свои правила интепретации, поскольку они идут через ссылку на имя Νame2,т.е. можно осуществлять доступ к содержимому, но если удалить файл Νame1, то содержимое Νame2 останется неизменным.
Проблема размер блока файловой системы.
«Большой блок»:
- эффективность обмена
- существенная внутренняя фрагментация
( не эффективное использование пространства ВП)
«Маленький блок»:
- эффективное использование пространства ВП
- фрагментация данных файла по диску
Решение подбирать размер блока для каждой конкретной задачи.
Связный список свободных блоков
Пример:
Размер блока 1 Кб
1 блок = 256 х 4 б
255 номеров свободных блоков
1 ссылка на следующий блок
Для HDD 16 Gb список свободных блоков состоит из 16794 блоков.
Создается специальный файл для хранения списка свободных блоков. Для оперативной работы системы в ОЗУ размещается часть содержимого свободных блоков.
Для организации списка свободных блоков требуются ресурсы.
При использовании связного списка свободных блоков в ОЗУ размещается первый блок списка.
Использование битового массива
Состояние любого блока определяется содержимым бита с номером каждого блока.
Если блок свободен, бит равен 1, занят 0.
Пример:
Для HDD 16 Gb потребуется 2048 блоков для хранения
битового массива
Все пространство для хранения битовая карта массив битов. Номер бита от начала массива соответствует номеру блока. Если блок свободен, бит равен 1, занят 0.
Квотирование пространства файловой системы
Одним из ресурсов ВС является лимитированное пространство ФС, выделенное для пользователя.
Количество имен файла ограничено сверху размером раздела. Пространство, занимаемое файлами лимитировано.
Для каждого из этих ресурсов определяется 2 квоты : на блоки и на файлы.
Существуют жесткие и гибкие лимиты .
С гибким лимитом блоков связывается два параметра это непосредственно значение, которое лимитирует использование ресурсов это первый параметр. Второй параметр это так называемый счетчик предупреждений. Работа осуществляется следующим образом. Пользователь заходит в свой номер, система определяет статус использования соответствующего лимита. Если реально используемый лимит, предположим это лимит на блоки файлов, превосходит гибкий лимит блоков, то пользователю при входе в систему дается предупреждение, что он израсходовал свой лимит, и счетчик предупреждения уменьшается на 1. Пользователю дается возможность заходить к себе в номер и работать до тех пор пока счетчик предупреждений не станет равен 0. Так только он станет равен 0 работа пользователя будет блокирована.
Жесткое лимитирование этот тот лимит, который перейти нельзя никогда. Если при входе пользователя в систему обнаружится, что он превзошел жесткий лимит, то его работа блокируется сразу все зависимости от других ситуаций. Гибкий и жесткий лимит это есть средства, в принципе, возможно использование различных
Если пользователь попытался превзойти жесткий лимит, то он блокируется сразу.
Надежность файловой системы
Критерий надежность файловой системы достаточно просто формулируется. Т.е. нужно обеспечить работу таким образом, чтобы при возникновении внештатной ситуации объем потерянной информации был минимальной. Внештатная ситуация может быть самой разнообразной сбой в любом из узлов компьютера, в том числе на носителе, в котором находится файловая система, физическое уничтожение информации (в том числе и случайное) и т.д. Первым и наиболее исторически традиционным путем решения этой проблемы является резервное копирование или резервное архивирование файла.
=>
Нужно, чтобы при возникновении внештатной ситуации потеря информации стремилась к нулю.
Резервное копирование должно проходить в ограниченный промежуток времени.
Различные модели резервного копирования
1.Копируются не все файлы файловой системы (избирательность архивирования по типам файлов); Не копируются *.obj
и системные (те, которые можно переустановить).
2. Создается мастеркопия архива по расписанию, или в произвольные моменты времени. Копируются все файлы, которые
были изменены с момента создания последней мастеркопии. Копии получаются очень большие.
3. Использование компрессии при архивировании (риск потери всего архива из-за ошибки в чтении/записи сжатых данных);
4. Проблема архивирования «на ходу» (во время копирования происходят изменения файлов, создание, удаление каталогов
и т.д.). Возможность потери информации из-за внештатной ситуации (например отключение от питания)
5. Распределенное хранение резервных копий. Лучше держать много копий в разных местах хоть где-нибудь, да
останется.
Стратегии архивирования
Физическая архивация
1.«один в один»;
При этом можно копировать свободные блоки, что не надо. Решение этой проблемы п.2
2. интеллектуальная физическия архивация (копируются только использованные блоки файловой системы);
3. проблема обработки дефектных блоков. Чем больше носителей, тем больше дефектных блоков.
Логическая архивация копирование файлов (а не блоков), модифицированных после заданной даты.
Проверка целостности файловой системы
Проблема при аппаратных или программных сбоях возможна потеря информации:
• потеря модифицированных данных в «обычных» файлах;
• потеря системной информации (содержимое каталогов, списков системных блоков, индексные узлы и т.д.)
Необходим контроль целостности или непротиворечивости файловой системы.
Модельная стратегия контроля
1.Формируются две таблицы:
- таблица занятых блоков;
- таблица свободных блоков;
(размеры таблиц соответствуют размеру файловой системы число записей равно числу блоков ФС)
Изначально все записи таблиц обнуляются.
2. Анализируется список свободных блоков. Для каждого номера свободного блока увеличивается на 1 соответствующая ему запись в таблице свободных.
3. Анализируются все индексные узлы. Для каждого блока, встретившегося в индексном узле, увеличивается его счетчик на 1 в таблице занятых блоков.
4. Анализ содержимого таблиц и коррекция ситуаций.
Варианты анализа таблиц
1.Таблица занятых блоков и таблица свободных блоков дополняют друг друга до всех единиц, тогда все в порядке,
целостность системы соблюдена.
2. Пропавший блок не числится ни среди свободных, ни среди занятых. Можно оставить как есть и ждать претензий со стороны пользователя, но система замусоривается. Считаем свободным.
3. таблица занятых блоков корректна, а какой-то из свободных блоков дважды или более раз посчитан свободным, т.е. список свободных блоков (таблица) не корректен. В этом случае нужно запустить процесс пересоздания списка свободных блоков. Т.е. нужно запустить процесс, который проанализирует все индексные дескрипторы и соответственно сформирует список свободных блоков.
4.Дубликат занятого блока. Блок повстречался в 2х индексных дискрипторах. Локализуем проблему на уровне файлов.
Действие:
1.Name1 ---> копируется Name12
2.Name2 ---> копируется Name22
3.Удаляются Name1, Name2
4.Запускается переопределение списка свободных блоков
5.Обратное переименование файлов и фиксация факта их возможной проблемности.
ОС Unix: файловая система
Появление ФС UNIX совершило революцию в файловых системах по нескольким направлениям в СПО, в организации ОС.
Например:
1.UNIX был первой ОС, разработанной с помощью языка высокого уровня.
2. Элегантная и развитая система управления процессами.
3. Древовидная организация ФС. (Древовидная в общем случае, но есть средства нарушающие древовидность.) Такая организация не абсолютное первенство UNIX, это было заимствовано из Maltix, однако UNIX - первая файловая система с древовидной организацией, которая получила широкое распространение.
4. Использование концепции файлов. Все представляется в виде файлов. Все работает через унифицированный интерфейс.
Файловая система операционной системы UNIX является примером многопользовательской иерархической файловой системой с трехуровневой организацией прав доступа к содержимому файлов.
Файл Unix это специальным образом именованный набор данных, размещенный в файловой системе.
•обычный файл (regular file) традиционный тип файла, содержащий данные пользователя. Интерпретация содержимого файла производится программой, обрабатывающей файл.
OC Unix трактует понятие файла шире традиционного. В частности, в системе в качестве файла рассматриваются:
•каталог (directory) специальный файл, обеспечивающий иерархическую организацию файловой системы. С каталогом ассоциируются все файлы, которые принадлежат данному каталогу.
•специальный файл устройств (special device file) cистема позволяет ассоциировать внешние устройства с драйверами и предоставляет доступ к внешним устройствам, согласно общим интерфейсам работы с файлами.
•именованный канал (named pipe) специальная разновидность файлов, позволяющая организовывать передачу данных между взаимодействующими процессами;
•ссылка (link) позволяет создавать дополнительные ссылки к содержимому файла из различных точек файловой системы; Они могут нарушать древовидность организации ФС.
•сокет (socket) средство взаимодействия процессов в пределах сети ЭВМ.
ОС UNIX поддерживает широкий диапазон типов файлов. Каталог в ОСУ тоже файл.
Права доступа
Категории пользователей:
1. пользователь (владелец)
2. группа (всепользователи, которые принадлежат группе владельца за исключением самого владельца)
3. все пользователи системы (все пользователи системы, за исключением группы владельца и самого владельца.)
Права
1. на чтение
2. на запись
3. на исполнение (0исполняемым файлом может быть только файл полученный в результате сборки и ли командный файл.
Интерпретация этих прав зависит от типа файла.
Так для обычных файлов это традиционные права на чтение, запись данных файла и исполнение содержимого файла в качестве процесса.
Интерпретация прав доступа для других типов файлов может различаться.
Например, для файлов каталогов это:
· право на чтение каталога получение списка имен файлов;
· право на исполнение каталога получение дополнительной информации о файлах (т.е. тогда, когда требуется информация, большая чем имя файла), право на использование каталога в качестве текущего, возможность использования имени каталога внутри имени файла;
· право на запись возможность создания, переименования и удаления файла в каталоге.
Все UNIX-системы имеют соглашения о логической структуре каталогов, расположенных в корне файловой системы. Это упрощает работу операционной системы, ее обслуживание и переносимость. Эти соглашения используются при работе почтовой системы, системы печати и т.д.
Содержимое основных каталогов:
Корневой каталог / является основой любой файловой системы ОС UNIX. Все остальные файлы и каталоги располагаются в рамках структуры, порожденной корневым каталогом, независимо от их физического положения на диске.
/unix - файл загрузки ядра ОС.
/bin - файлы, реализующие общедоступные команды системы.
/etc - в этом каталоге находятся файлы, определяющие настройки системы (в частности, файл passwd), а также команды, необходимые для управления содержимым подобных специальных файлов.
/tmp - каталог для хранения временных системных файлов. При перезагрузке системы не гарантируется сохранение его содержимого. Обычно этот каталог открыт на запись для всех пользователей системы.
/mnt - каталог, к которому осуществляется монтирование дополнительных физических файловых систем для получения единого дерева логической файловой системы. Заметим, что это лишь соглашение, в общем случае можно примонтировать к любому каталогу.
/dev - каталог содержит специальные файлы устройств, с которыми ассоциированы драйверы устройств. Каждый из файлов имеет ссылку на соответствующий драйвер и указание типа устройства (блок- или байт-ориентированные). Этот каталог может содержать несколько подкаталогов, группирующих специальные файлы по типам. Таким образом, имеется возможность легко добавлять и удалять новые устройства в систему.
/lib - здесь находятся библиотечные файлы языка Си и других языков программирования.
/usr - размещается вся информация, связанная с обеспечением работы пользователей. Здесь также имеется подкаталог, содержащий часть библиотечных файлов (/usr/lib), подкаталог /usr/users (или /usr/home), который становится текущим при входе пользователя в систему, подкаталог, где находятся дополнительные команды (/usr/bin), подкаталог, содержащий файлы заголовков (/usr/include), в котором, в свою очередь, подкаталог, содержащий include-файлы, характеризующие работу системы (например, signal.h - интерпретация сигналов).
Структура ФС
Файловая система Unix может занимать раздел диска (partition). Количество разделов на каждом диске, их размеры определяются при предварительной подготовке устройства (разметка). Unix рассматривает разделы как отдельные, независимые устройства.
Суперблок файловой системы содержит оперативную информацию о текущем состоянии файловой системы, а также данные о параметрах настройки, в частности:
•размер логического блока (512б, 1024б, 2048б);
•размер файловой системы в логических блоках (включая суперблок);
•максимальное количество индексных дескрипторов (определяет размер области индексных дескрипторов);
•число свободных блоков;
•число свободных индексных дескрипторов;
•специальные флаги;
•массив номеров свободных блоков;
•массив номеров свободных индексных дескрипторов;
•и др.
В ОП постоянно находится актуальная копия суперблока.
Область (пространство) индексных дескрипторов.
Индексный дескриптор это специальная структура данных файловой системы, которая ставится во взаимно однозначное соответствие с каждым файлом.
Размер пространства индексных дескрипторов определяется параметром генерации файловой системы по количеству индексных дескрипторов, которые указаны в суперблоке.
Содержит:
1.Тип файла
2. права доступа к файлу
3. число имен каталогов ФС, ассоциированных с данным индексным дискриптором.
4. идентификатор владельца
5. размер файлда в байтах
6. время послдней модификации
7.Массив номеров блоков файлов
Блоки файлов.
Это пространство на системном устройстве, в котором размещается вся информация, хранящаяся в файлах и о файлах, которая не поместилась в предыдущие блоки файловой системы.
Работа с массивами номеров свободных блоков
В суперблоке файловой системы размещается массив номеров свободных блоков, этот массив является началом полного списка содержащего номера всех свободных блоков файловой системы.
Все свободные блоки ФС организованы в однонаправленный список, структурная организация которого следующая: 1-й элемент этого списка это есть массив из Ν ссылок, которые размещаются в суперблоке. Ν зависит от конкретной ОС, пусть это будет 100. 0-й элемент этого массива есть номер блока из пространства блоков ФС, в котором находится продолжение этого списка. Соответственно 0-й элемент этого блока есть ссылка на следующий массив из Ν ссылок и т.д. ФС оперативно работает с этим массивом. Если в нем есть свободные места, то при освобождении блоков, они записываются на свободные места, если требуются новые блоки, то они выбираются из этого массива. Если массив исчерпывается, то информация берется из следующего блока. Если массив полностью заполнен, т.е. освобождается много блоков, то выбирается следующий свободный блок и этот массив скидывается на этот блок. Это достаточно важная информация, которая в каждый момент отражает состояние ФС.
Оперативный доступ к списку осуществляется посредством использования массива в суперблоке.
Работа с массивом свободных ИД
Массив номеров свободных индексных дескрипторов содержит оперативный набор номеров свободных индексных дескрипторов. Размер массива - Nиндекс.
При освобождении индексного дескриптора, если есть свободное место в массиве, то номер освободившегося индексного дескриптора записывается в соответствующий элемент массива. Если свободного места в массиве нет, то этот номер «забывается».
При запросе нового индексного дескриптора осуществляется поиск в массиве, если массив не пустой, то все в порядке, если массив пустой происходит операция обновления его содержимого (происходит просмотр области индексных дескрипторов и занесение в массив обнаруженных свободных). Т.е. массив свободных индексных дескрипторов это своеобразный буфер.
Индексные дескрипторы
Индексный дескриптор (ИД) описатель файла, содержит все необходимые для работы с файлом служебные атрибуты.
Через ИД осуществляется доступ к содержимому файлов. Любое имя файла в системе ассоциировано с единственным ИД, но это соответствие неоднозначно. Т.е. ИД может соответствовать произвольное количество имен.
Структура индексного дескриптора:
•тип файла, права, атрибуты выполнения (если = 0, то ИД свободен);
•число имен, которые ассоциированы с данным ИД;
•идентификаторы владельца-пользователя, владельца-группы;
•размер файла в байтах;
•время последнего доступа к файлу;
•время последней модификации содержимого файла;
•время последней модификации ИД (за исключением времени доступа и времени модификации файла)
•массив номеров блоков файла.
Адресация блоков файла
Для простоты изложения будем считать, что размер блока равен 512 байт.
Размещение данных файла задается списком его блоков.
Это снимает проблемы непрерывных файловых систем, т.е. систем, где блоки файла располагаются последовательно. Таким образом реально блоки файла могут быть разбросаны по диску, но логически они образуют цепочку, содержащую весь набор данных.
Ключом, задающим подобное расположение служит массив номеров блоков файла, содержащий список из 13 номеров блоков на диске, хранящихся в ИД.
Первые десять указывают на десять блоков некоторого файла.
Если файл занимает более 10 блоков, то 11 элемент указывает на косвенный блок, содержащий до 128 адресов дополнительных блоков файла (это еще 70656 байт).
Большие файлы используют 12-ый элемент, который указывает на блок, содержащий 128 указателей на блоки, каждый из которых содержит по 128 адресов блоков файла.
Еще в больших файлах аналогично используется 13 элемент.
Трехкратная косвенная адресация позволяет создавать файлы длиной (10+128+128*128+128*128*128)*512 байт.
Таким образом,
если файл меньше 512 байт, то необходимо одно обращение к диску,
если длина файла находится в пределах 512-70565 байт, то - два и так далее.
Приведенный способ адресации позволяет иметь прямой и быстрый доступ к файлам. Эта возможность также усиливается кэшированием диска, позволяющим хранить в памяти наиболее используемые блоки.
При открытии файла соответствующий ИД считывается в память и системе становятся доступны все номера блоков данного файла.
Для одного и того же файла, открываемого несколько раз, в памяти находится только один ИД.
Система фиксирует число открытий данного файла и, когда этот счетчик обнуляется, резидентный образ ИД переписывается на диск. Если при этом изменений в файле не было и не модифицировался ИД, то запись не выполняется.
Указанные особенности существенно влияют на эффективность файловой системы.
Файл каталог
. Файл каталог для ФС System V представляет собой таблицу, каждая запись которой состоит из 16 байтов. Первые 2 байта это номер индексного дескриптора. Последующие 14 байтов это поле для имени файла. Соответственно имеется предопределенные записи в этих полях это первые две строчки. 1-я строчка это ссылка на самого себя, т.е. в этой строчке находится имя «.» (точка) и номер индексного этого файла каталога. Следующая запись это ссылка на родительский каталог, соответственно в нем имеется номер индексного дескриптора и имя «..» (две точки).Содержимое файла таблица. 1-е поле это номер индексного дескриптора (ИД), которому соответствует имя Name из второго поля.
Размеры полей в общем случае могут быть различные.
Например размер поля ИД 2 байта (ограничение числа ИД в файловой системе 65535), размер поля Name 14 байт (соответственно ограничение на длину имени).
В Unix две первые строки любого каталога имеют фиксированное содержание: имя «•» - ссылка на самого себя, имя «••» - ссылка на родительский каталог.
Видно, что при такой реализации имя файла “отделено” от других его атрибутов. Это позволяет, в частности, один и тот же файл внести в несколько каталогов. При этом, как отмечалось выше, данный файл может иметь разные имена в разных каталогах, но ссылаться они будут на один и тот же ИД, который является ключом для доступа к данным файла. При обсуждении понятия ИД говорилось, что каждая новая ссылка к ИД отмечается в специальном поле. Рассмотрим пример, иллюстрирующий связь файлов с каталогами.
Установление связей
Древовидность файловой системы Unix нарушается возможностью установления ссылок на одни и те же индексные дескрипторы из различных каталогов. Это может быть достигнуто за счет использования средств установления дополнительных связей.
Существует две разновидности этой операции.
Жесткая связь -. с одним и тем же индексным дескриптором будет ассоциироваться два или более имени, размещенных в произвольных точках ФС. При этом каждое из этих имен равноценно.
Для этого используется команда: ln …dir1/name1 …dir2/name2 (дли индексного дескриптора, с которым ассоциировано имя name1 добавляется еще одно имя name2).
Все имена, ассоциированные таким образом с индексным дескриптором равноправны.
При этом увеличивается значение поля индексного дескриптора число имен, которые ассоциированы с данным ИД.
Нельзя устанавливать жесткую связь для файлов-каталогов.
Установление символической связи - косвенная адресация на существующее имя файла.
В ФС можно создать специальный файл ссылку, содержимое которого размещается в индексном дескрипторе этого файла. Этим содержимым является текстовая строка, указывающая полное имя того файла, с которым нужно ассоциировать новое имя (имя файла-ссылки). Т.е. если name 1 и name 2 - это абсолютно равноправные файлы, то name 3 это текстовая (символьная ссылка). Для name 3 создается свой индексный дескриптор и через него организуется ссылка на файл name 1, при этом уже в индексном дескрипторе файла name 1 никакой информации о дополнительных ссылках на этот файл нет. Т.е. здесь уже некоторая асимметричная модель множественного именования содержимого файла. Если будет нужно удалить файл name 1, то система позволит это сделать, потому что нигде в информации, связанной с этим файлом, не указывается, что на него есть текстовая ссылка. И соответственно, когда после удаления этого файла произойдет обращение по ссылке /dir1/name1, то уже возникнут какие-то проблемы. Следует помнить, что не на любой файл можно установить ссылку.
Для этих целей используется команда: ln s …dir1/name1 …dir2/name3 в результате образуется специальный файл - ссылка.
Достоинства ФС модели версии System V
1)Оптимизация в работе со списками номеров свободных индексных дескрипторов и блоков.
2)Организация косвенной адресации блоков файлов, позволяющая использовать эффективный доступ к значительному количеству блоков файла.
Недостатки ФС модели версии System V
1)Концентрация важной информации в суперблоке - ключевая информация сконцентрирована в суперблоке файловой системы, физическая потеря содержимого суперблока может приводит к значительным проблемам, касающимся целостности файловой системы.
2)Проблема надежности (много ссылочных структур, возможна потеря данных при сбоях).
3)Фрагментация файла по диску т.е. при достаточно больших размерах файла его блоки могут произвольным образом размещаться на физическом МД? Что может приводить к выполнению значительного числа механических операций перемещения головок устройства при чтении/записи данных файла.
4)Организация каталога накладывает ограничения на возможную длину имени файла (14 символов).
В Unix 4.2 BSD разработана модель организации файловой системы, которая получила название Fast File System - FFS (быстрая файловая система).
Основной идеей данной модели файловой системы является кластеризация дискового пространства файловой системы, c целью минимизации времени чтения/записи файла, а также уменьшения объёма не иcпользуемого пространства внутри выделенных блоков.
Суть кластеризации заключается в следующем. Дисковое пространство, также, как и в модели s5fs имеет суперблок в котором размещена ключевая информация файловой системы (структура суперблоков s5fs и ffs, в общем случае, логически идентична), далее, дисковое пространство разделено на области одинакового размера, называемые группами цилиндров. Далее, стратегия функционирования файловой системы такова, что она старается разместить содержимое файлов (блоки файлов) в пределах одной группы цилиндров, при этом стараясь располагать файлы в той же группе цилиндров, что и каталог в котором они расположены.
Группа цилиндров:
- копия суперблока
- информация о свободных блоках (битовый массив) и о свободных индексных дескрипторах
- массив индексных дескрипторов (ИД)
- блоки файлов
Стратегии размещения
1)Новый каталог помещается в группу цилиндров, число свободных индексных дескрипторов в которой больше среднего значения во всей файловой системе в данный момент времени, а также имеющей минимальное число дескрипторов каталогов в себе;
2)для обеспечения равномерности использования блоков данных файл разбивается на несколько частей, при этом первая часть файла располагается в той же группе цилиндров, что и его дескриптор, при размещении последующих частей используется группа цилиндров, в которой число свободных блоков превышает среднее значение. Длина первой части выбирается таким образом, чтобы она адресовалось непосредственно индексным дескриптором (т.е. не «косвенно»), остальные части разбиваются фиксированным образом, например по 1 мегабайту;
3)последовательные блоки файлов размещаются исходя из оптимизации физического доступа (см. ниже)
Dt технологический промежуток времени, который затрачивает система на передачу и прием устройством МД команды на чтение очередного блока. За это время диск проворачивается и головки обмена «пропускают» начало очередного блока, поэтому если мы будем читать следующий блок, то головка будет вынуждена ожидать полного поворота диска на начало блока. В связи с этим эффективнее читать не последовательные блоки (в этом случае нужно ожидать полного поворота диска), а блоки, размещенные на диске через один, два... (смещение определяется поворотом диска за время Dt).
Внутренняя организация блоков
Блоки |
0 |
1 |
… |
N |
|||||||||
Фрагменты |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
… |
|||||
Маска |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
Обмен происходит блоками. Блоки могут быть достаточно большого размера (до 64 Кб). В системе может быть принято разбиение блока на равные фрагменты (на 2, 4, 8). То есть все пространство разделяется на «маленькие блоки» - фрагменты. Фрагменты группируются по 2, 4 или 8 в блоки (т.е. если фрагмент содержит 512 байт, то блок может быть размера 1024, 2048, 4096).
При этом блоком в этой системе может называться только «выровненный» до размера кратности набор фрагментов. Т.е. при кратности 4 (см. рисунок выше), фрагменты 0 3 входят в один блок, а фрагменты 1 4 нет.
Для хранения информации о свободных фрагментах используется битовая маска: каждому фрагменту на диске соответствует ровно 1 бит в этой маске (этот механизм упрощает алгоритм поиска свободных фрагментов и уменьшает «фрагментацию» свободного пространства).
Формат индексного дескриптора аналогичен, используемому в s5fs - в нём в качестве элементов по-прежнему используются блоки, а не фрагменты, но при размещении информации в файлах используется следующее простое правило: все блоки указанные в индексном дескрипторе, кроме последнего, должны использоваться только целиком; блок может использоваться для нескольких файлов только при хранении их последних байт, не занимающих всех фрагментов полного блока (cм. рисунок ниже). Т.о. для хранения информации об использовании последнего блока недостаточно только размера файла, хранимого в дескрипторе System 5, необходимо также хранить информацию, об используемых фрагментах в этом блоке.
Выделение пространства для файла происходит только в момент когда процесс выполняет системный вызов write. Операционная система при этом руководствуется следующим алгоритмом:
1. Если в уже выделенном файлу блоке есть достаточно места, то новые данные помещаются в это свободное пространство.
2. Если последний блок файла использует все фрагменты (т.е. это полный блок) и свободного в нём места не достаточно для записи новых данных, то частью новых данных заполняется всё свободное место. Если остаток данных превышает по размеру один полный блок, то новый выделяется полный блок и записываются данные в этот полный блок. Процесс повторяется до тех пор, пока остаток не окажется меньше чем полный блок. В этом случае ищется блок с необходимыми по размеру фрагментами или выделятся новый полный блок. Остаток данных записывается в этот блок.
3. Файл содержит один или более фрагмент (они естественным образом содержатся в одном блоке) и последний фрагмент недостаточен для записи новых данных. Если размер новых данных в сумме с размером данных, хранимых в неполном блоке, превышает размер полного блока, то выделяется новый полный блок. Содержимое старого неполного блока копируется в начало выделенного блока и остаток заполняется новыми данными. Процесс далее повторяется, как указано в пункте 2 выше. В противном случае (если размер новых данных в сумме с размером данных, хранимых в неполном блоке, не превышает размер полного блока) ищется блок с необходимыми по размеру фрагментами или выделятся новый полный блок. Остаток данных записывается в этот блок.
Структура каталога FFS
Поддержка длинных имен файлов.
Любая запись содержит:
· номер индексного дескриптора;
· длина записи в каталоге;
· длина имени файла;
· имя файла (дополненное до кратности слова).
Структура каталога немного изменяется. К двум содержательным полям добавляется номер индексного дескриптора, размер записи, т.е. записи каталога, тип файла, длина имени и имени разрешается быть длиной до 256 символов. Соответственно может возникнуть некоторое недопонимание, т.к. есть параметр размер записи и длина имени. Суть использования того и другого параметра заключается в том, что при удалении информации (какого-то имени) из каталога свободное пространство присоединяется к предыдущей записи и получается, что размер больше той содержательной информации, которая имеется. Соответственно может появиться внутренняя фрагментация.
Управление процессами
Определение процесса. Основные понятия
Процесс - совокупность машинных команд и данных,которая исполняется в рамках вычислительной системы и обладает правами на владение некоторым набором ресурсов.
Ресурсы могут принадлежать только одному процессу, либо ресурсы могут разделяться между процессами разделяемые ресурсы
Выделение ресурсов процессу:
-предварительная декларация до начала выполнения
- динамическое пополнение списка принадлежащих
- процессу ресурсов
Количество допустимых процессов в системе ресурс ВС.
Рассмотрим типовые этапы обработки процесса в системе, совокупность этих этапов будем назвать жизненным циклом процесса в системе.
Жизненный цикл процесса содержит этапы:
•образование (порождение) процесса;
•обработка (выполнение) процесса;
•ожидание (по тем или иным причинам) постановки на выполнение;
•завершение процесса.
Рассмотрим модельную ОС.
Пусть имеется специальный буфер ввода процессов (БВП) пространство, в котором размещаются и хранятся сформированные процессы от момента их образования, до момента начала выполнения. На данном этапе происходит формирование всех необходимых структур данных, соответствующих процессу. В частности, на данном этапе ОС формирует информацию о предварительно заказанных данным процессом ресурсах. Основная задача БВП «подпитка» системы новыми процессами, готовыми к исполнению.
После начала выполнения процесса он попадает в буфер обрабатываемых процессов (БОП). В данном буфере размещаются все процессы, находящиеся в системе в мультипрограммной обработке.
Жизненный цикл процесса.
0. Поступление процесса в очередь на начало обработки ЦП
(процесс попадает в БВП).
1. Начало обработки процесса на ЦП (из БВП в БОП).
2. Приостановка обработки процесса на ЦП по той или инойпричине (попадает в БОП). Переход в состояние ожидания дальнейшего выполнения.
3. Возобновление выполнения процесса на ЦП.
4. Завершение выполнения процесса, освобождение системных ресурсов.
Модель пакетной однопроцессной системы
0 Поступление процесса в очередь на начало обработки ЦП
(процесс попадает в БВП).
1. Начало обработки процесса на ЦП (из БВП в БОП).
2. Завершение выполнения процесса, освобождение системных ресурсов.
Модель пакетной мультипроцессной системы
0. Поступление процесса в очередь на начало обработки ЦП (процесс попадает в БВП)
1. Начало обработки процесса на ЦП (из БВП в ВОП)
2. Процесс прекращает обработку ЦП по причине ожидания операции в/в, поступает в очередь завершения операции обмена (БОП).
3. Операция обмена завершена и процесс поступает в очередь ожидания продолжения выполнения ЦП (БОП).
4. Выбирается процесс для выполнения на ЦП.
6. Завершение выполнения процесса, освобождение системных ресурсов.
5. Процесс прекращает обработку ЦП, но в любой момент может
быть продолжен(истек квант времени ЦП, выделенный
процессу). Поступает в очередь процессов, ожидающих
продолжения выполнения центральным процессором (БОП).
Заблокированный процесс может быть перекачен (своппирован) на внешний носитель, а на освободившееся место может быть подкачен процесс со внешнего носителя, который был откачен ранее, либо взят новый.
Типы процессов.
В мультипрограммной среде управление такими процессами тесно связано с управлением и защитой памяти, поэтому переключение процессора с выполнения одного процесса на выполнение другого является достаточно дорогой операцией.
«Полновесные процессы» - это процессы, выполняющиеся внутри защищенных участков памяти операционной системы, то есть имеющие собственные виртуальные адресные пространства для статических и динамических данных. ОС берет на себя функцию разделения ресурсов между конкурирующими процессами.
В дальнейшем, используя термин процесс будем подразумевать полновесный процесс.
Легковесные процессы, называемые еще как нити или сопрограммы, не имеют собственных защищенных областей памяти. Они работают в мультипрограммном режиме одновременно с активировавшей их задачей и используют ее виртуальное адресное пространство, в котором им при создании выделяется участок памяти под динамические данные (стек), то есть они могут обладать собственными локальными данными. Нить описывается как обычная функция, которая может использовать статические данные программы. Для одних операционных систем можно сказать, что нити являются некоторым аналогом процесса, а в других нити представляют собой части процессов.
Однонитевая организация процесса «один процесс одна нить»:
одному процессу соответствует одна нить. Управление планированием там происходит по одной ниточке
Многонитевая организация процесса:
много нитевая модель организации процессов, т.е. когда есть процесс, процессу принадлежат ресурсы, в частности ОП. И внутри процесса реализована относительно произвольное количество нитей и в этом случае нити друг с другом могут взаимодействовать через соответствующие интерфейсы (интерфейсы взаимодействия параллельных процессов) и соответственно планирование выполнения это может являться одной из функций ОС, т.о. ОС может планировать как выполнение процессов, так и выполнение нитей внутри процессов. Однако существуют решения, в которых много нитевая модель организуется так, когда процесс сам устанавливает стратегию управления нитями, в этом случае в функции ОС входит порождение нитей внутри процесса и обеспечение взаимодействия нитей (взаимодействие может быть не по модели вызова подпрограммы, а через передачу каких-то воздействий, событий, сигналов).
Понятие процесса может включать в себя понятие исполняемой нити, т. е. однонитевую организацию «один процесс одна нить». В данном случае понятие процесса жестко связано с понятием отдельной и недоступной для других процессов виртуальной памяти.
С другой стороны, в процессе может несколько нитей, т.е. процесс может представлять собой многонитевую организацию.
Ос планирует выполнение нитей внутри процесса.
В функции ОС входят обеспечение взаимодействия нитей.
Понятие «процесс» включает в себя следующее:
• исполняемый код;
•собственное адресное пространство, которое представляет собой совокупность виртуальных адресов, которые может использовать процесс;
• ресурсы системы, которые назначены процессу ОС;
• хотя бы одну выполняемую нить.
Контекст процесса.
Текущее состояние любого процесса из БОП изменяется во времени в зависимости от самого процесса и состояния ОС. С каждым из процессов из БОП система ассоциирует совокупность данных, характеризующих актуальное состояние процесса контекст процесса. (в общем случае контекст процесса содержит информацию о текущем состоянии процесса, включая информацию о режимах работы процессора, содержимом регистровой памяти, используемой процессом, системной информации ОС, ассоциированной с данным процессом).
Процессы, находящиеся в одном из состояний ожидания в своих контекстах содержат всю информацию, необходимую для продолжения выполнения - состояние процесса в момент прерывания (копии регистров, режимы ОП, настройки аппарата виртуальной памяти и т. д.). Соответственно при смене выполняемого процесса ОС осуществляет «перенастройку» внутренних ресурсов ЦП, происходит смена контекстов выполняемых процессов.
На этапе выполнения процесса ОС обеспечивает возможность корректного взаимодействия процессов от передачи сигнальных воздействий от процесса к процессу до организации корректной работы с разделяемыми ресурсами.
Контекст процесса может состоять из:
•пользовательской составляющей состояние программы, как совокупности машинных команд и данных, размещенных в ОЗУ;
•системной составляющей
информация идентификационного характера
(PID процесса, PID «родителя»…)
информация о содержимом регистров
( РОН, индексные регистры, флаги...
информация, необходимая для управления процессом
(состояние процесса,
В любой системе, оперирующей понятием процесс, существует системно-ориентированное определение процесса (определение, учитывающее конкретные особенности данной ОС).
Таблица ограничена => количество процессов в таблице ограниченно => количество процессов ограничено.
«Первое» определение процесса в ОС Unix
Процесс в ОС Unix объект, зарегистрированный в таблице процессов Unix.
Каждый процесс характеризуется уникальным именем идентификатором процесса (PID). PID целое число от 0 до некоторого предельного значения, определяющего максимальное число процессов (ресурс данной ОС), существующих в системе одновременно.
Если это максимальное число превышено, то начать обрабатывать еще один процесс невозможно.
Будем использовать термины 0й процесс, 1й процесс, 125й процесс, это означает, что речь идет о процессах с PID = 0, 1, 125. 0й процесс в системе ассоциируется с работой ядра Unix. С точки зрения организации данных PID номер строки в таблице, в которой размещена запись о процессе.
Контекст процесса
Содержимое записи таблицы процессов позволяет получить контекст процесса (часть данных контекста размещается непосредственно в записи таблицы процессов, на оставшуюся часть контекста имеются прямые или косвенные ссылки, также размещенные в записи таблицы процессов).
С точки зрения логической структуры контекст процесса Unix состоит из:
•пользовательской составляющей или тела процесса (иногда используется пользовательский контекст)
•аппаратной составляющей , в которой отражается актуальное состояние компьютера при выполнении процесса. В зависимости от состояния процесса, аппаратный компонент либо представляется содержимым соответствующих регистров настройки ЦП, либо, если процесс отложен, то аппаратный компонент копируется ОС в свои таблицы (в системную составляющую)
•системной составляющей ОС Unix (иногда системный контекст)
Иногда два последних компонента объединяют, в этом случае используется термин общесистемная составляющая контекста.
Тело процесса состоит из сегмента кода и сегмента данных.
Пользовательская составляющая (тело процесса)
Сегмент кода
•Машинные команды
•Неизменяемые константы
Сегмент данных
Статические данные
• Статические переменные
Разделяемая память
• Фактические параметры в функциях
• Автоматические переменные
• Динамическая память
Стек
Сегмент кода содержит машинные команды и неизменяемые константы соответствующей процессу программы. Стек используется для перемещения фактических параметров.
Сегмент данных содержит данные, динамически изменяемые в ходе выполнения кода процесса. Сегмент данных содержит область статических переменных, область разделяемой с другими процессами памяти, а также область стека (обычно эта область служит основой для организации автоматических переменных, передачи параметров в функции, организацию динамической памяти).
Реальные и эффективные идентификаторы пользователя и группы. Как правило при формировании процесса эти идентификаторы совпадают и равны реальному идентификатору пользователя и реальному идентификатору группы, т.е. они определяются персонификацией пользователя, сформировавшего данный процесс. При этом права процесса по доступу к файловой системе определяются правами сформировавшего процесс пользователя и его группы. Этого бывает недостаточно. Примером может служить ситуация, когда пользователь желает запустить некоторый процесс, изменяющий содержимое файлов, которые не принадлежать этому пользователю (например, изменение пароля на доступ пользователя в систему). Для разрешения данной ситуации имеется возможность установить специальный признак в исполняемом файле, наличие которого позволяет установить в процессе, сформированном при запуске данного файла в качестве эффективных идентификаторов, идентификатор владельца и группы владельца этого файла.
Разделение сегмента кода
экономия памяти в случае одновременного выполнения идентичных процессов
Некоторые современные ОС имеют возможность разделения единого сегмента кода между разными процессами. У процессов общий сегмент кода, но разные сегменты данных. Тем самым достигается экономия памяти в случаях одновременного выполнения идентичных процессов.
Например, при функционировании терминального класса одновременно могут быть сформированы несколько копий текстового редактора. В этом случае сегмент кода у всех процессов, соответствующих редакторам, будет единый, а сегменты данных будут у каждого процесса свои.
Следует отметить, что при использовании динамически загружаемых библиотек возможно разделение сегмента кода на неизменную часть, которая может разделяться между процессами и часть, соответствующую изменяемому в динамике коду подгружаемых программ.
Аппаратная составляющая
• Все регистры и аппаратные таблицы ЦП, используемые активным или исполняемым процессом
• счетчик команд
• регистр состояния процессора
• аппарат виртуальной памяти
• регистры общего назначения
и т. д.
Обращаем внимание, что аппаратная составляющая имеет смысл только для процессов, находящихся в состоянии выполнения. Для процессов, находящихся в других состояниях содержимое составляющей не определено.
Системная составляющая.
содержимое регистров и режимов работы процессора, настройки аппарата защиты памяти, виртуальной памяти, принадлежащие процессу ресурсы (как физические, так и виртуальные).
В системной составляющей контекста процесса содержатся различные атрибуты процесса, такие как:
- идентификатор родительского процесса;
- текущее состояние процесса (выполняется, отложен и т.д.);
- приоритет процесса;
- реальный идентификатор пользователя-владельца (идентификатор пользователя, сформировавшего процесс);
- эффективный идентификатор пользователя-владельца (идентификатор пользователя, по которому определяются права доступа процесса к файловой системе);
- реальный идентификатор группы, к которой принадлежит владелец (идентификатор группы к которой принадлежит пользователь, сформировавший процесс);
- эффективный идентификатор группы, к которой принадлежит владелец (идентификатор группы «эффективного» пользователя, по которому определяются права доступа процесса к файловой системе);
- список областей памяти;
- таблица открытых файлов процесса (токже является ресурсом данной системы);
- информация о том, какая реакция установлена на тот или иной сигнал (аппарат сигналов позволяет передавать воздействия от ядра системы процессу и от процесса к процессу);
- информация о сигналах, ожидающих доставки в данный процесс;
- сохраненные значения аппаратной составляющей (когда выполнение процесса приостановлено).
Некоторые пояснения.
Реальные и эффективные идентификаторы пользователя и группы. Как правило при формировании процесса эти идентификаторы совпадают и равны реальному идентификатору пользователя и реальному идентификатору группы, т.е. они определяются персонификацией пользователя, сформировавшего данный процесс. При этом права процесса по доступу к файловой системе определяются правами сформировавшего процесс пользователя и его группы. Этого бывает недостаточно. Примером может служить ситуация, когда пользователь желает запустить некоторый процесс, изменяющий содержимое файлов, которые не принадлежать этому пользователю (например, изменение пароля на доступ пользователя в систему). Для разрешения данной ситуации имеется возможность установить специальный признак в исполняемом файле, наличие которого позволяет установить в процессе, сформированном при запуске данного файла в качестве эффективных идентификаторов, идентификатор владельца и группы владельца этого файла.
Второе определение процесса Unix.
Системный вызов специальная функция, позволяющая процессу обращаться к ядру ОС за выполнением тех или иных действий.
Это может быть запрос на выполнение операций обмена, управления процессами, получения системной информации и т.п. При выполнении системного вызова в процессе происходит инициация специального прерывания «обращение к системе». В разных системах детали реализации системного вызова могут отличаться друг от друга и название прерывания «обращение к системе» здесь выбрано достаточно условно. В любом случае использование системного вызова влечет за собой накладные расходы, связанные со сменой контекста выполняющегося в данный момент процесса.
Если системный вызов не выполняется или выполняется нештатно, то он возвращает 1 в коде ответа и в переменной errno будет находится код причины отказа (для диагностирования результатов выполнения системного вызова в процессе используется переменная errno, объявленная в файле errno.h).
Процесс в ОС Unix это объект, порожденный системным вызовом fork().
Данный системный вызов является единственным стандартным средством порождения процессов в системе Unix.
Ниже рассмотрим возможности данного системного вызова подробнее.
Создание нового процесса
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
Для порождения новых процессов в UNIX существует единая схема, с помощью которой создаются все процессы, существующие в работающем экземпляре ОС UNIX, за исключением первых двух процессов (0-го и 1-го)
Для создания нового процесса в операционной системе UNIX используется системный вызов fork().
При удачном завершении возвращается:
- сыновьему процессу значение 0
- родительскому процессу PID порожденного процесса
При неудачном завершении возвращается 1, код ошибки устанавливается в переменной errno
В результате вызова fork() в таблицу процессов заносится новая запись, и порожденный процесс получает свой уникальный идентификатор. Для нового процесса создается контекст, большая часть содержимого которого идентична контексту родительского процесса, в частности, тело порожденного процесса содержит копии сегментов кода и данных его родителя.
Сыновний процесс наследует от родительского процесса:
· окружение - при формировании процесса ему передается некоторый набор параметров-переменных, используя которые, процесс может взаимодействовать с операционным окружением (интерпретатором команд и т.д.);
· файлы, открытые в процессе-отце, за исключением тех, которым было запрещено передаваться процессам-потомкам с помощью задания специального параметра при открытии. (Речь идет о том, что в системе при открытии файла с файлом ассоциируется некоторый атрибут, который определяет правила передачи этого открытого файла сыновним процессам. По умолчанию открытые в «отце» файлы можно передавать «потомкам», но можно изменить значение этого параметра и блокировать передачу открытых в процессе-отце файлов.);
· способы обработки сигналов;
· разрешение переустановки эффективного идентификатора пользователя;
· разделяемые ресурсы процесса-отца;
· текущий рабочий каталог и домашний каталоги
· и т.д.
Не наследуются от родительского процесса:
- Идентификатор процесса (PID)
- Идентификатор родительского процесса (PPID)
- Сигналы, ждущие доставки в родительский процесс
- Время посылки ожидающего сигнала, установленное системным вызовом alarm()
- Блокировки файлов, установленные родительским процессом
По завершении системного вызова fork() каждый из процессов родительский и порожденный получив управление, продолжат выполнение с одной и той же инструкции одной и той же программы, а именно с той точки, где происходит возврат из системного вызова fork(). Вызов fork() в случае удачного завершения возвращает сыновнему процессу значение 0, а родительскому PID порожденного процесса. Это принципиально важно для различения сыновнего и родительского процессов, так как сегменты кода у них идентичны. Таким образом, у программиста имеется возможность разделить путь выполнения инструкций в этих процессах. В случае неудачного завершения, т.е. если сыновний процесс не был порожден, системный вызов fork() возвращает 1, код ошибки устанавливается в переменной errno.
Пример
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
printf("PID=%d; PPID=%d \n",getpid(), getppid());
/*печать PID текущего процесса и PID процесса-предка */
fork();
/*создание нового процесса, с этого момента два процесса функционируют параллельно и независимо*/
printf("PID=%d; PPID=%d \n",getpid(), getppid());
/*оба процесса печатают PID текущего процесса и PID процесса-предка*/
return 0;
}
Напечатается две строки, какая первой неизвестно. Какой идентификатор родительского процесса распечатает вновь созданный процесс, если процесс-предок завершит свою работу раньше? (см. ответ дальше по тексту)
Пример
Программа создает два процесса процесс-предок распечатывает заглавные буквы, а процесс-потомок строчные.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char ch, first, last;
int pid;
if((pid=fork())>0)
{
/*процесс-предок*/
first =A;
last =Z;
}
else
{
/*процесс-потомок*/
first =a;
last =z;
}
for (ch = first; ch <= last; ch++)
{
write(1,&ch,1);
}
return 0;
}
Оба процесса распечатывают буквы одним и тем же оператором for. Оба процесса имеют возможность получить управление, таким образом любой из них может начать исполнение первым.
Семейство системных вызовов exec()
Ниже представлены прототипы функций семейства exec():
#include <unistd.h>
int execl(const char *path, char *arg0,…);
int execlp(const char *file, char *arg0,…);
int execle(const char *path, char *arg0,…, const char **env);
int execv(const char *path, const char **arg);
int execvp(const char *file, const char **arg);
int execve(const char *path, const char **arg, const char **env);
Семейство системных вызовов exec() заменяет тело вызывающего процесса, после чего данный процесс начинает выполнять другую программу. Управление передается на точку ее входа. Возврат к первоначальной программе происходит только в случае ошибки при обращении к exec() , т.е. если фактической замены тела процесса не произошло.
Первый параметр во всех вызовах задает имя файла программы, подлежащей исполнению. Этот файл должен быть исполняемым файлом и пользователь-владелец процесса должен иметь право на исполнение данного файла. Для функций с суффиксом «p» в названии имя файла может быть кратким, при этом при поиске нужного файла будет использоваться переменная окружения PATH. Далее передаются аргументы командной строки для вновь запускаемой программы, которые отобразятся в ее массив argv в виде списка аргументов переменной длины для функций с суффиксом «l» либо в виде вектора строк для функций с суффиксом «v». В любом случае, в списке аргументов должно присутствовать как минимум 2 аргумента: имя программы, которое отобразится в элемент argv[0], и значение NULL, завершающее список.
В функциях с суффиксом «e» имеется также дополнительный аргумент, описывающий переменные окружения для вновь запускаемой программы это массив строк вида name=value, завершенный значением NULL.
Возвращается: при удачном завершении 0, в случае ошибки -1
Заметим, что выполнение “нового” тела происходит в рамках уже существующего процесса, т.е. после вызова exec() сохраняется идентификатор процесса, и идентификатор родительского процесса, таблица дескрипторов файлов, приоритет, и большая часть других атрибутов процесса. Фактически происходит замена сегмента кода и сегмента данных.
Изменяются следующие атрибуты процесса:
- режимы обработки сигналов: для сигналов, которые перехватывались, после замены тела процесса будет установлена обработка по умолчанию, т.к. в новой программе могут отсутствовать указанные функции-обработчики сигналов;
- эффективные идентификаторы владельца и группы могут измениться, если для новой выполняемой программы установлен s-бит
- перед началом выполнения новой программы могут быть закрыты некоторые файлы, ранее открытые в процессе. Это касается тех файлов, для которых при помощи системного вызова fcntl() был установлен флаг close-on-exec. Соответствующие файловые дескрипторы будут помечены как свободные.
Сохраняются:
•Идентификатор процесса
•Идентификатор родительского процесса
•Таблица дескрипторов файлов
•Приоритет и большинство атрибутов
Пример
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{ …
/*тело программы*/
…
execl(“/bin/ls”,”ls”,”-l”,(char*)0);
/* или execlp(“ls”,”ls”, ”-l”,(char*)0);*/
printf(“это напечатается в случае неудачного обращения к предыдущей функции, к примеру, если не был найден файл ls \n”);
…
}
Пример. Вызов программы компиляции
#include <unistd.h>
int main(int argc, char **argv)
{
char *pv[]={“cc”,“-o”,“ter”,“ter.c”,(char*)0};
…
/*тело программы*/
…
execv (“/bin/cc”,pv);
…
}
Использование схемы fork-exec
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int pid;
if ((pid=fork())!=0){
if(pid>0)
{/* процесс-предок */}
else
{/* ошибка */}
}
else
{/* процесс-потомок */}
}
Программа порождает три процесса, каждый из которых запускает программу echo посредством системного вызова exec(). Данный пример демонстрирует важность проверки успешного завершения системного вызова exec() , в противном случае возможно исполнение нескольких копий исходной программы. В нашем случае если все вызовы exec() проработают неуспешно, то копий программ будет восемь. Если все вызовы exec() будут успешными, то после последнего вызова fork() будет существовать четыре копии процесса. В каком порядке они пойдут на выполнение предсказать трудно.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if(fork()==0)
{
execl(“/bin/echo”, ”echo”, ”это”, ”сообщение один”, NULL);
printf(“ошибка\n”);
}
if(fork()==0)
{
execl(“/bin/echo”, ”echo”, ”это”, ”сообщение два”, NULL);
printf(“ошибка\n”);
}
if(fork()==0)
{
execl(“/bin/echo”, ”echo”, ”это”, ”сообщение три”, NULL);
printf(“ошибка\n ”);
}
printf(“процесс-предок закончился\n”);
return 0;
}
Результат работы может быть следующим.
процесс-предок закончился
это сообщение три
это сообщение два
это сообщение один
Завершение процесса
Причинами завершения процесса могут быть:
- системный вызов _exit() (может быть с параметром целым числом кодом завершения процесса и без)
void _exit(int exitcode);
Этот вызов никогда не завершается неудачно, поэтому для него не предусмотрено возвращающего значения. С помощью параметра status процесс может передать породившему его процессу информацию о статусе своего завершения. Принято, хотя и не является обязательным правилом, чтобы процесс возвращал нулевое значение при нормальном завершении, и ненулевое в случае какой-либо ошибки или нештатной ситуации.
- оператора return, входящего в состав функции main()
В любом из этих случаев происходит следующее:
•Освобождается сегмента кода и сегмента данных процесса
•Закрываются все открытые дескрипторы файлов
•Если у процесса имеются потомки, их предком назначается процесс с идентификатором 1
•Освобождается большая часть контекста процесса однако сохраняется запись в таблице процессов и та часть контекста, в которой хранится статус завершения процесса и статистика его выполнения
•Процессу-предку посылается сигнал SIGCHLD
Состояние, в которое при этом переходит завершаемый процесс, в литературе часто называют состоянием “зомби”.
Процесс-предок имеет возможность получить информацию о завершении своего потомка. Для этого служит системный вызов wait():
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
При обращении к этому вызову выполнение родительского процесса приостанавливается до тех пор, пока один из его потомков не завершится либо не будет остановлен. Если у процесса имеется несколько потомков, процесс будет ожидать завершения любого из них (т.е., если процесс хочет получить информацию о завершении каждого из своих потомков, он должен несколько раз обратиться к вызову wait()).
Возвращаемым значением wait() будет идентификатор завершенного процесса, а через параметр status будет возвращена информация о причине завершения процесса (путем вызова _exit() либо прерван сигналом) и коде возврата. Если процесс не интересуется это информацией, он может передать в качестве аргумента вызову wait() NULL-указатель.
Если к моменту вызова wait() один из потомков данного процесса уже завершился, перейдя в состояние зомби, то выполнение родительского процесса не блокируется, и wait() сразу же возвращает информацию об этом завершенном процессе. Если же к моменту вызова wait() у процесса нет потомков, системный вызов сразу же вернет 1. Также возможен аналогичный возврат из этого вызова, если его выполнение будет прервано поступившим сигналом.
После того, как информация о статусе завершения процесса-зомби будет доставлена его предку посредством вызова wait(), все оставшиеся структуры, связанные с данным процессом-зомби, освобождаются, и запись о нем удаляется из таблицы процессов. Таким образом, переход в состояние зомби необходим именно для того, чтобы процесс-предок мог получить информацию о судьбе своего завершившегося потомка, независимо от того, вызвал он wait() до или после его завершения.
Что происходит с процессом-потомком, если его предок вообще не обращался к wait() и/или завершился раньше потомка? Как уже говорилось, при завершении процесса отцом для всех его потомков становится процесс с идентификатором 1. Он и осуществляет системный вызов wait(), тем самым освобождая все структуры, связанные с потомками-зомби.
Часто используется сочетание функций fork()-wait(), если процесс-сын предназначен для выполнения некоторой программы, вызываемой посредством функции exec(). Фактически этим предоставляется процессу- родителю возможность контролировать окончание выполнения процессов-потомков.
Пример программы, последовательно запускающей программы, имена которых указаны при вызове.
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
for (i=1; i<argc; i++)
{
int status;
if(fork()>0)
{
/*процесс-предок ожидает сообщения от процесса-потомка о завершении */
wait(&status);
printf(“process-father\n”);
continue;
}
execlp(argv[i], argv[i], 0);
return -1;
/*попадем сюда при неуспехе exec()*/
}
return 0;
}
Пусть существуют три исполняемых файла print1, print2, print3, каждый из которых только печатает текст first, second, third соответственно, а код вышеприведенного примера находится в исполняемом файле с именем file. Тогда результатом работы команды file print1 print2 print3 будет
first
process-father
second
process-father
third
process-father
Пример. Использование системного вызова wait().
В данном примере процесс-предок порождает два процесса, каждый из которых запускает команду echo. Далее процесс-предок ждет завершения своих потомков, после чего продолжает выполнение.
int main(int argc, char **argv)
{
if ((fork()) == 0) /*первый процесс-потомок*/
{ execl(“/bin/echo”,”echo”,”this is”,”string 1”,0);
exit(); }
if ((fork()) == 0) /*второй процесс-потомок*/
{ execl(“/bin/echo”,”echo”,”this is”,”string 2”,0);
exit(); }
/*процесс-предок*/
printf(“process-father is waiting for children\n”);
while(wait() != -1);
printf(“all children terminated\n”);
exit();
}
В данном случае wait() вызывается в цикле три раза первые два ожидают завершения процессов-потомков, последний вызов вернет неуспех, ибо ждать более некого.
Жизненный цикл процессов
1. Процесс только что создан посредством вызова fork().
2. Процесс находится в очереди готовых на выполнение процессов.
3. Процесс выполняется в режиме задачи, т.е. когда реализуется алгоритм, заложенный в программу. Выход из этого состояния может произойти через системный вызов, прерывание или завершение процесса.
4. Процесс может выполняться в режиме ядра ОС, т.е. когда по требованию процесса через системный вызов выполняются определенные инструкции ядра ОС или произошло другое прерывание.
5. Процесс в ходе выполнения не имеет возможность получить требуемый ресурс и переходит в состояние блокирования.
6. Процесс осуществил вызов exit() или получил сигнал на завершение. Ядро освобождает ресурсы, связанные с процессом, кроме кода возврата и статистики выполнения. Далее процесс переходит в состоянии зомби, т.е. Не работает, а завершает работу,а затем уничтожается.
Формирование процессов 0 и 1
Выше упоминалось о нестандартном формировании некоторых процессов в Unix. Речь шла о процессе начальной загрузки системы и нестандартном формировании двух специфических процессов с PID 0 и 1.
Начальная загрузка
Рассмотрим подробнее, что происходит в момент начальной загрузки OC UNIX.
Начальная загрузка это загрузка ядра системы в основную память и ее запуск.
Нулевой блок каждой файловой системы предназначен для записи короткой программы, выполняющей начальную загрузку.
Начальная загрузка выполняется в несколько этапов.
1.Аппаратный загрузчик читает нулевой блок системного устройства и передает точку входа.
2.После чтения этой программы она выполняется, т.е. ищется и считывается в память файл /unix, расположенный в корневом каталоге и который содержит код ядра системы. Осуществляется запуск яжра операционной системы.
3.Запускается на исполнение этот файл.
Инициализация системы
В самом начале ядром выполняются определенные действия по инициализации системы, а именно:
1)устанавливаются системные часы (для генерации прерываний),
2)формируется диспетчер памяти,
3)формируются значения некоторых структур данных (наборы буферов блоков, буфера индексных дескрипторов) и ряд других.
4)По окончании этих действий происходит инициализация процесса с номером "0".
По понятным причинам для этого невозможно использовать методы порождения процессов, изложенные выше, т.е. с использованием функций fork() и exec().
При инициализации этого процесса резервируется память под его контекст и формируется нулевая запись в таблице процессов.
Основными отличиями нулевого процесса являются следующие моменты
1.Данный процесс не имеет кодового сегмента это просто структура данных, используемая ядром и процессом его называют потому, что он каталогизирован в таблице процессов.
2. Он существует в течении всего времени работы системы (чисто системный процесс) и считается, что он активен, когда работает ядро ОС.
Далее ядро копирует "0" процесс и создает "1" процесс.
Сначала процесс "1" представляет собой полную копию процесса "0" , т.е. у него нет области кода. Полее происходит увеличение его размера. Во вновь созданную кодовую область копируется программа, реализующая системный вызов exec() , необходимый для выполнения программы /etc/init.
На этом завершается подготовка первых двух процессов.
Первый из них представляет собой структуру данных, при помощи которой ядро организует мультипрограммный режим и управление процессами.
Второй это уже подобие реального процесса.
Далее ОС переходит к выполнению программ диспетчера.
Диспетчер наделен обычными функциями и на первом этапе он запускает exec() , который заменит команды процесса "1" кодом, содержащимся в файле /etc/init. Получившийся процесс, называемый init, призван настраивать структуры процессов системы.
Далее он подключает интерпретатор команд к системной консоли. Так возникает однопользовательский режим, так как консоль регистрируется с корневыми привилегиями и доступ по каким-либо другим линиям связи невозможен.
На этом завершается подготовка первых двух процессов.
При выходе из однопользовательского режима init создает многопользовательскую среду.
С этой целью init организует процесс getty для каждого активного канала связи, т.е. каждого терминала. Это программа ожидает входа кого-либо по каналу связи.
init организует процесс getty для каждого активного канала связи, т.е. каждого терминала. Это программа ожидает входа кого-либо по каналу связи.
Далее, используя системный вызов exec(), getty передает управление программе login, проверяющей пароль.
Во время работы ОС процесс init ожидает завершения одного из порожденных им процессов, после чего он активизируется и создает новую программу getty для соответствующего терминала.
Таким образом процесс init поддерживает многопользовательскую структуру во время функционирования системы.
. Планирование
Важной проблемой, на решение которой ориентированы многие компоненты современных ОС является проблема планирования предоставления тех или иных услуг или функций операционной системой. Традиционно, в состав задач планирования ОС могут входить следующие:
-Планирование очереди процессов на начало обработки
-Планирование распределения времени ЦП между процессами
-Планирование свопинга («откачка»части процессов из ОП на диск)
-Планирование обработки прерываний (на какое прерывание можно сейчас не реагировать, на какое надо реагировать всегда)
-Планирование очереди запросов на обмен
В целом, комплексное решение задач планирования в ОС определяет основные эксплуатационные качества каждой конкретной системы.
Неправильное планирование приводит к деградации системы.
На данном этапе определяется уровень многопроцессности системы.
Дисциплина обслуживания очереди :
- простейшая FIFO (было на старых машинах. Сложилось исторически)
- по приоритету ()
- с учетом предполагаемого времени выполнения процесса, объема операций ввода/вывода и так далее.
(если задача большая и в\в не понадобится 3 часа, то можно запустить что-нибудь другое, чему в\в нужен)
В общем случае очередь процессов в БВП может предоставляться как объединение подочередей, где каждая подочередь включает в себя определенные классы процессов (например, такая классификация может строится на объеме запрашиваемых ресурсов и/или типе процесса). При этом возможно определение приоритета каждой из очередей (сначала рассматриваются непустые очереди с наименьшим приоритетом).
Планирование распределения времени ЦП между процессами
Квант времени непрерывный период процессорного времени.
Приоритет процесса числовое значение, показывающее степень привилегированности процесса при использовании ресурсов ВС (в частности, времени ЦП).
Для грамотного планирования надо решить две задачи:
определить величину кванта
определить стратегию обслуживания очереди готовых к выполнению процессов
Может существовать несколько очередей на обработку на ЦП. Первыми берутся процессы из первой очереди. Вторая очередь подпитывает первую, третья вторую, и т.д.. Если первая очередь пуста берется из второй, вторая пуста из третей и т. д. Планировщик определяет в какую очередь скинуть процесс.
Рассмотрим, как решается проблема с определения кванта времени.
Если величина кванта не ограничена невытесняющая стратегия планирования времени ЦП (применяется в пакетных системах). Никто принудительно не скидывает процесс с ЦП. Разработчики берут на себя функции диспетчера. Например, программа что-то долго считает => сама периодически снимает себя с ЦП, что могли выполнится задачи требующие меньшее количество времени для выполнения и не могущие долго ждать.
Вытесняющая стратегия - величина кванта ограничена.
Может существовать несколько очередей на обработку на ЦП. Первыми берутся процессы из первой очереди. Вторая очередь подпитывает первую, третья вторую, и т.д.. Если первая очередь пуста берется из второй, вторая пуста из третей и т. д. Планировщик определяет в какую очередь скинуть процесс.
Рассмотрим, как решается проблема с определения кванта времени.
•Время ожидания кванта процессом ~ q(n-1)
•Параметры: длина очереди и величина кванта.
•Дисциплина обслуживания очереди, например, FIFO.
•Переключение процессов операция, требующая времени.
Проблема: как определить длину кванта. Слишком маленький не хватит времени на переключение, большой - некоторые успеют выполниться полностью.
Величина кванта может меняться со временем
• Вначале «большой» квант q=A,на следующем шаге q=A-t, q=A-2t,…, до q=B (B<A). Преимущество для коротких задач.
• Вначале q=B, далее q=B+t,…, до q=A. Уменьшение накладных расходов на переключение задач, когда несколько задач выполняют длительные вычисления.
Если процесс интенсивно пользуется операциями ввода/вывода, то он может использовать выделенный квант не до конца. В качестве компенсации ему могут предоставляться привилегии при дальнейшем обслуживании.
Квантование с предпочтением процессам, интенсивно обращающихся к вводу/выводу
Дисциплина обслуживания очередей следующая: сначала выбирается процесс из очереди процессов, закончивших ввод/вывод.
Делаются 2 очереди готовых процессов: одна из процессов, обращающихся часто к устройствам ввода\вывода. Вторая для тех, кто основную часть времени считается на процессоре.
Рассмотренные алгоритмы, основанные на квантовании, не используют никакой предварительной информации о процессах.
Рассмотренные примеры алгоритмов относятся к классу вытесняющих.
Вытесняющая стратегия используется в системах разделения времени.
Алгоритмы, основанные на приоритетах
Приоритет может быть статическим , например для процесса ядра, и динамическим для пользовательских процессов. Динамический приоритет формируется в процессе счета как функция от времени нахождения процесса в различных очередях и др.
Вычисление приоритета основывается на статических и динамических характеристиках. Изменение приоритета может происходить по инициативе процесса, пользователя, ОС. Правила назначения приоритета процессов определяют эффективность работы системы.
Планирование по наивысшему приоритету (highest priority first - HPF).
При появлении в очереди готовых процессов процесса с более высоким приоритетом, чем у текущего наступает момент смены процесса. Смена ппроцесса происходит либо в тот же момент, когда приоритет произвольного процесса стал больше чем приоритет считающегося, либо после того, когда закончится квант времени считающегося.
Возможно два варианта:
- относительный приоритет (ожидание исчерпания кванта у текущего процесса)
Смена ппроцесса происходит либо в тот же момент, когда приоритет произвольного процесса стал больше чем
приоритет считающегося. Хорошо для пакетных систем.
- абсолютный приоритет (немедленная смена текущего процесса)
Смена ппроцесса происходит после того, когда закончится квант времени считающегося. Хорошо для тех систем,
где необходима быстрвая реакция на что-ибо
Задача выбора/постановки процесса с наивысшим приоритетом зависит от организации очереди (упорядочена/неупорядочена).
Возможно наличие очередей с одинаковым приоритетом.
Пример использования стратегии HPF.
Выбор самого короткого задания (shortest job first - SJF).
Время выполнения характеристика, на которой основан приоритет. Приоритет обратно пропорционален ожидаемому времени обработки.
Этот вариант
удобен для “коротких” процессов.
Процесс при входе в систему получает некий приоритет, который возрастает с коэффициентом A во время ожидания в очереди готовых процессов, и с коэффициентом B во время выполнения.
Из выбора A и В - разные правила планирования:
- Если 0<A<=B обслуживание очереди по дисциплине FIFO
- Если 0>B>=A обслуживание очереди по дисциплине LIFO
Нелинейные функции изменения приоритета
Например, приоритет убывает по линейному закону с течением времени. Когда достигается некое максимальное время, приоритет скачком возрастает до некоторой большой величины. Это благоприятствует коротким процессам, и при этом соблюдается условие, что ни одному процессу не придется ждать обслуживания слишком долго.
В частности, метод SJF можно модифицировать, добавляя приоритет длинным процессам после некоторого времени ожидания.
.Каждому процессу выделяется квант времени величина которого зависит от приоритета данного процесса. Процесс продвигается со скоростью пропорциональной его приоритету
Простой круговорот (RR round robin) не использует никакой статистической или динамической информации о приоритетах.
При круговороте со смещением каждому процессу соответствует своя длина кванта, пропорциональная его приоритету.
«Эгоистический» круговорот. Если параметры A и B : 0<=B<A.
Процесс, войдя в систему ждет пока его приоритет не достигнет приоритета работающих процессов, а далее выполняется в круговороте.
Приоритет выполняемых процессов увеличивается с коэффициентом B<A, следовательно, ожидающие процессы их догонят.
При B=0 «эгоистический» круговорот практически сводится к простому.
Очереди с обратной связью (feedback FB).
Используется N очередей. Новый процесс ставится в первую очередь, после получения кванта он переносится во вторую и так далее. Процессор обслуживает непустую очередь с наименьшим номером.
В FB поступивший процесс неявно получает наивысший приоритет и выполняется подряд в течении нескольких квантов до прихода следующего, но не более чем успел проработать предыдущий.
«-» Работа с несколькими очередями издержки.
«+» Удобны для коротких заданий: не требуется предварительная информация о времени выполнения процессов.
На практике концепции квантования и приоритетов часто используются совместно.
К примеру, в основе концепция квантования, а определение кванта и/или дисциплина обслуживания очередей базируется на приоритетах.
Системы реального времени являются специализированными системами в которых все функции планирования ориентированы на обработку некоторых событий за время, не превосходящее некоторого предельного значение.
Надо также учитывать последствия, к которым может привезт несоблюдение временных рамок.
Системы реального времени бывают “Жесткие” и ”мягкие ”.
В первом случае время завершения выполнения каждого из процессов должно быть гарантировано для всех сценариев функционирования системы.
Это может быть обеспечено за счет :
- полного тестирования всевозможных сценариев
- построения статического расписания
- выбора математически просчитанного алгоритма динамического планирования (моделирование ситуации, которая может произойти в системе и делаем из этого выводы.)
Периодические запросы все моменты запроса периодического процесса можно определить заранее.
Пусть {Ti} набор периодических процессов с периодами pi , предельными сроками выполнения di и требованиями ко времени выполнения ci.
Расписание удается построить не всегда.
Для проверки возможного составления расписания анализируется расписание на отрезке времени равному наименьшему общему множителю периодов этих процессов.
С целью определения возможности построения расписания используются различные критерии.
Необходимое условие наличия расписания:
Сумма коэффициентов использования m=S ci / pi <= k, где k - количество доступных процессоров.
Классический алгоритм для жестких систем реального времени с одним процессором
Используются периодические запросы на выполнение процессов,
срок выполнения каждого процесса равен его периоду pi, все процессы независимы максимальное время выполнения каждого процесса сi известно и постоянно, игнорируется время переключения контекста, вводится ограничение на суммарный коэффициент загрузки процессора S ci / pi, при существовании n задач не превосходит n(21/n-1). Эта величина при n равна ln 2, то есть 0.7
Используются вытеснения и статические приоритеты.
Суть алгоритма:
Процессы получают статические приоритеты в соответствии с величиной их периодов выполнения, при этом самый высший приоритет получает самая короткая задача.
Соблюдение приведенных ограничений гарантирует выполнение временных ограничений для всех процессов во всевозможных ситуациях.
Алгоритмы с динамическим изменением приоритетов.
Параметр deadline конечный срок выполнения.
Выбор процесса на выполнение по правилу:
выбирается процесс, у которого текущее значение разницы между конечным сроком выполнения и временем, необходимым для его непрерывного выполнения, является наименьшим.
Перепланировка функция ОС. Планировщик программа, которая реализует некоторый алгоритм.
Перепланировка происходит после одного из следующих событий:
1.сигнал, что время на выполнение закончилось
2.выполнение системного вызова, а ресурс занят
3. системный вызов, связанный с освобождением ресурса. Если какой-то процесс ждет этот ресурс, то освобождение подождет.
Планирование бывает краткосрочное, среднесрочное и долгосрочное
- использование времени ЦП
- пропускная способность (кол-во процессов в единицу времени)
- время ожидания (в очереди готовых) влияет на приоритет процесса
- время оборота (полное время от момента поступления до завершения)
- время отклика (для интерактивных программ время от поступления в систему до момента первого обращения к
терминалу
- предельный срок выполнения процесса
и т.д.
Используется принцип кругового планирования в рамках очередей каждого приоритета.
Если процесс не завершается или не блокируется в рамках 1 секунды он вытесняется.
В общем случае значение приоритета есть функция
P=F (CPU, nice), т.е. в вычислении приоритета используются две изменяемые составляющие CPU (системная) и nice (пользовательская). Учитывается история выполнения, величины CPU и nice ограничены.
Пересчет приоритета процесса происходит в момент выбора процесса для выполнения на ЦП 1 раз в секунду.
Процессам назначается базовый приоритет, чтобы их можно было разделять на фиксированные группы уровней приоритетов.
Эти группы используются для оптимизации доступа к блочным устройствам (например, к диску) и обеспечения быстрого отклика операционной системы на системные вызовы.
Группы приоритетов
(в порядке убывания)
- программа свопинга
- управление блочными устройствами ввода/вывода
- управление файлами
- управление байт-ориентированными устройствами ввода/вывода
- пользовательские процессы
Иерархия обеспечивает эффективное использование устройств ввода/вывода
СPUj (i) - время использования ЦП процессом j за время i;
Pj (i) - приоритет процесса j в начале кванта i (приоритет выше, если значение меньше);
Basej - базовый приоритет j-го процесса (необходим для разделения процессов на фиксированные группы уровней приоритетов);
nicej - пользовательская составляющая приоритета (значение может только увеличиваться до некоторого уровня).
Пример традиционного планирования процессов в ОС Unix
Рассмотрим на примере, как это все работает. Предполагается, что есть три процесса, которые создаются одновременно с одним и тем же базовым приоритетом (в данном случае это число 60). Таймер прерывает выполнение процесса 60 раз в секунду. Предполагается, что не один процессов не блокируется сам, и нет других процессов, готовых к выполнению. Для простоты пользовательская составляющая nicej игнорируется. Происходит пересчет времени и приоритетов.
Рассмотрим, как работает первый процесс. Он проработал 1 секунду (счетчик 060) 60 раз его прерывали, по первой формуле получаем: 60/2=30, соответственно приоритет будет пересчитан по второй формуле, и полученное значение будет равно 75. После этого начинает работать второй процесс - третий процесс пока стоит. Когда проработает второй процесс, у него произойдет точно такой же пересчет, как и у первого, потому что они начинали с одинаковых позиций. У первого процесса время, которое он использовал, будет: 30/2=15, и приоритет: 60+15/2=67. У второго процесса приоритет соответственно 75, третий начал работать. Когда третий процесс дойдет до конца своего кванта времени у него будет приоритет 75. У первого процесса приоритет составит 63.
Квантование сочетается с использованием динамических абсолютных приоритетов.
В системе определено 32 уровня приоритетов.
Два класса нитей:
Нити с переменными приоритетами (0-15]
Нити “реального” времени (16-31] высокоприоритетные нити.
Критичны по времени выполнения.
Нити с переменными приоритетами
Изначально процессу присваивается базовый приоритет.
Базовый приоритет процесса может меняться ОС, следовательно, могут измениться базовые приоритеты составляющих его нитей.
Нить получает значение приоритета из диапазона базового приоритета.
Приоритет нити может отклоняться от своего базового приоритета, и это может быть не связано с изменением базового приоритета процесса ( см. диапазон значений динамического приоритета нитей).
Например, ОС повышает приоритет нити, если до конца не использован квант времени, и уменьшает в противном случае.
• Поддерживается группа очередей (для нитей с переменными приоритетами ) по одной для каждого приоритета. Система просматривает очереди, начиная с самой приоритетной.
• На выполнение выбирается нить с наивысшим приоритетом. Ей выделяется квант времени. Если во время выполнения в очереди появляется нить с более высоким приоритетом, то текущая нить вытесняется. Вытесненная нить становится в очередь готовых впереди тех, что имеют тот же приоритет.
• Если нить исчерпала квант ее приоритет понижается на единицу и она перемещается в соответствующую очередь
• Повышается значение приоритета при выходе из состояния ожидания окончания ввода-вывода
При принятии нового процесса на обработку необходимо освободить ресурсы. Какой - то процесс скидывается в область своппинга.
Область свопинга - специально выделенное системой пространство внешней памяти
P_TIME счетчик, находящийся в контексте процесса. Суммирует время нахождения процесса в состоянии мультипрограммной обработки или в области свопинга. При переходе из одного состояния в другое счетчик обнуляется. Для загрузки процесса в память из области свопинга выбирается процесс с максимальным значением P_TIME. Если для загрузки этого процесса нет свободного пространства оперативной памяти, то система ищет среди процессов в оперативной памяти процесс, ожидающий ввода/вывода (сравнительно медленных операций, процессы у которых приоритет выше значения P_ZERO) и имеющий максимальное значение P_TIME (т.е. тот, который находился в оперативной памяти дольше всех). Если такого процесса нет, то выбирается просто процесс с максимальным значением P_TIME.
Работа диспетчерского процесса.
Надо определить принципы, исходя из которых происходят откачка процесса на диск м запуск на выполнение.
- Поиск процесса для ввода в оперативную память. Иначе выбирается процесс с максимальным значением p_time (обозначим outage). Если (outage < 3), то процесс не загружается.
- Если места в ОП нет, диспетчерский процесс определяет кандидата на выгрузку, прежде всего среди процессов в системной фазе, ждущих завершения сравнительно медленных операций. (По определению их приоритет > PZERO)
Из них занимающий наибольшее место. Если такого нет с максимальным значением p_time (обозначим inage). При (inage < 2) процесс не выгружается.
При отказе загрузки (нет претендентов) / выгрузки (невозможен выбор) диспетчерский процесс переходит в состояние ожидания
Планирование обработки прерываний
Правильное планирование обработки прерываний залог правильного планирования процессов.
ОС должна обеспечивать контроль над ходом выполнения системных процедур, вызываемых по прерываниям . Это необходимое условие для правильного планирования пользовательских процессов.
Рассмотрим пример, в котором обработчик прерываний принтера блокирует на длительное время обработку прерываний от таймера, в результате чего системное время на некоторое время «замирает», и один из процессов (2), критически важный для пользователя, не получат управление в запланированное время.
Упорядоченное планирование прерываний
Механизм прерываний поддерживает приоритезацию и маскирование прерываний.
Источники прерываний делятся на классы каждому классу свой уровень приоритета запроса на прерывание.
Дисциплина обслуживания приоритетов
-относительная (выбор по наивысшему приоритету, но далее обработка не может быть отложена)
-абсолютная (происходит переход к обработке более приоритетного с откладыванием текущего)
В схеме с абсолютными приоритетами заложено маскирование, так как запрещаются запросы с равными или более низкими приоритетами.
В общем случае - возможность маскирования прерываний любого класса и любого приоритета на некоторое время.
Упорядочивание работы обработчиков прерываний механизм приоритетных очередей.
Наличие в ОС программного модуля диспетчера прерываний.
При возникновении прерывания вызов диспетчера.
Он блокирует все прерывания на некоторое время, устанавливает причину прерывания, сравнивает назначенный данному источнику прерывания приоритет с текущим приоритетом. В случае если у нового запроса на прерывание приоритет выше чем у текущего, то выполнение текущего приостанавливается и он помещается в соответствующую очередь. Иначе в соответствующую очередь помещается поступивший обработчик.
Планирование обработки прерываний в Windows NT
Все источники прерываний делятся на несколько классов, и каждому уровню присваивается уровень запроса прерывания Interrupt Request Level (IRQL). Этот уровень и представляет приоритет данного класса.
Поступление запроса на прерывание/исключение вызов диспетчера прерываний, который:
-Запоминает информацию об источнике прерывания
-Анализирует его приоритет
Если приоритет <= IRQL прерванного, то отложить в очередь,
иначе текущий обработчик в очередь, управление - новому
Особенности планирования ввода/ вывода
Одна из важных задач планирования обеспечение занятости внешних устройств
Для этого можно присваивать процессам высокий приоритет в периоды, когда они интенсивно используют ввод/ вывод
Эти периоды легко прослеживаются: - процесс блокируется про обращении к вводу/выводу. - операции ввода/вывода обычно бывают сконцентрированы в отдельных частях программ.
Применяется стратегия HPF.
Взаимодействие процессов: синхронизация, тупики
Процессы, выполнение которых хотя бы частично перекрывается по времени, называются параллельными процессами
В однопроцессорной системе имеет место так называемый псевдопараллелизм, т.е. параллельные процессы в действительности в каждый момент времени исполняется только один раз, однако несколько процессов находятся в состоянии выполнения, т.е. они выполняются по очереди и за счет быстрого переключения процессов между ними создается иллюзия параллелизма. Все такие процессы, которые все время находятся в буфере выполняемых процессов, будем называть параллельными. Действительный параллелизм может иметь место, когда на разных ЦП одновременно выполняются разные задачи. Для нас с точки зрения задач взаимодействия параллельных процессов и синхронизации их работы такие случаи ничем друг от друга не отличаются.
Они могут быть независимыми и взаимодействующими.
Независимые процессы процессы, использующие независимое множество ресурсов и на результат работы такого процесса не влияет работа независимого от него процесса.
Взаимодействующие процессы совместно используют ресурсы, и выполнение одного может оказывать влияние на результат другого.
Совместное использование несколькими процессами ресурса ВС, когда каждый из процессов одновременно владеет ресурсом называют разделением ресурса.
Разделению подлежат как аппаратные, так программные ресурсы.
Разделяемые ресурсы, которые должны быть доступны в текущий момент времени только одному процессу это так называемые критические ресурсы. Таковыми ресурсами могут быть, как внешнее устройство, так и некая переменная, значение которой может изменяться разными процессами.
Необходимо уметь решать две важнейшие задачи:
1. Распределение ресурсов между процессами.
2. Организация защиты адресного пространства и других ресурсов, выделенных определенному процессу, от неконтролируемого доступа со стороны других процессов.
Важнейшим требованием мультипрограммирования с точки зрения распределения ресурсов является следующее: результат выполнения процесса не должен зависеть от порядка переключения выполнения между процессами, т.е. от соотношения скорости выполнения процесса со скоростями выполнения других процессов.
Рассмотрим пример ситуации, в которой нарушается требование мультипрограммирования.
Посмотрим на рисунок и представить себе, что время идет сверху вниз. Оба процесса выполняют некоторую условную функцию if, в которой есть условный input (ввод некоторого символа) и условный output (вывод этой же переменной) понятно, что реализация этих функций нас сейчас не очень волнует, нас волнует в первую очередь то, что сейчас произойдет. Видно, что при такой ситуации у нас получается, что процесс А считал в разделяемую переменную in некоторый символ, после чего управление было передано на процесс В, и процесс В затер значение, которое считал процесс А. После чего он вывел новое значение, управление опять было передано процессу А, и процесс А вывел значение, не то которое он считал, а то, которое было затерто уже процессом В. Т.е. один из символов просто потерялся, в то время как другой был выведен дважды. Здесь предполагается, что in это некоторая разделяемая переменная, т.е. некоторый разделяемый ресурс. В данном случае эта переменная и будет разделяемым физическим ресурсом
Такие ситуации называются гонками (race conditions) между процессами, а процессы конкурирующими.
Часть программы (фактически набор операций), в которой осуществляется работа с критическим ресурсом, называется критической секцией, или критическим интервалом.
Единственный способ избежать гонок при использовании разделяемых ресурсов контролировать доступ к любым разделяемым ресурсам в системе. При этом необходимо организовать взаимное исключение т.е. такой способ работы с разделяемым ресурсом, при котором постулируется, что в тот момент, когда один из процессов работает с разделяемым ресурсом, все остальные процессы не могут иметь к нему доступ.
Заметим, что вопрос организации взаимного исключения актуален не только для взаимосвязанных процессов, совместно использующих определенные ресурсы для обмена информацией. Возможна ситуация, когда процессы, не подозревающие о существовании друг друга, используют глобальные ресурсы системы, такие как устройства ввода/вывода, принтеры и т.п. В с этом случае имеет место конкуренция за ресурсы, доступ к которым также должен быть организован по принципу взаимного исключения.
•Тупики (deadlocks)
•Блокирование (дискриминация)
При организации взаимного исключения могут возникнуть тупики (deadlocks), ситуации в которой конкурирующие за критический ресурс процессы вступают в клинч безвозвратно блокируются.
Есть два процесса А и В, каждому из которых в некоторый момент требуется иметь доступ к двум ресурсам R1 и R2. Процесс А получил доступ к ресурсу R1, и следовательно, никакой другой процесс не может иметь к нему доступ, пока процесс А не закончит с ним работать. Одновременно процесс В завладел ресурсом R2. В этой ситуации каждый из процессов ожидает освобождения недостающего ресурса, но оба ресурса никогда не будут освобождены, и процессы никогда не смогут выполнить необходимые действия.
В тупике могут «участвовать» произвольное количество ресурсов.
• Запрещение прерываний и специальные инструкции
• Алгоритм Петерсона
• Активное ожидание
• Семафоры Дейкстры
• Мониторы
• Обмен сообщениями
Семафоры Дейкстры
Тип данных, именуемый семафором. Семафор представляет собой переменную целого типа S, над которой определены две операции: down(s) (или P(S)) и up(S) (или V(S)). Оригинальные обозначения P и V, данные Дейкстрой и получившие широкое распространение в литературе, являются сокращениями голландских слов proberen проверить и verhogen увеличить.
down(S) проверяет значение семафора, и если оно больше нуля, то уменьшает его на 1. Если же это не так, процесс блокируется, причем операция down считается незавершенной.
Вся операция является неделимой, т. е. проверка значения, его уменьшение и, возможно, блокирование процесса производится как одно атомарное действие, которое не может быть прервано.
up(S) увеличивает значение семафора на 1. При этом, если в системе присутствуют процессы, блокированные ранее при выполнении down на этом семафоре, ОС разблокирует один из них с тем, чтобы он завершил выполнение операции down, т. е. вновь уменьшил значение семафора.
Увеличение значения семафора и, возможно, разблокирование одного из процессов и уменьшение значения являются атомарной неделимой операцией.
Семафоры это низкоуровневые средства синхронизации, для корректной практической реализации которых необходимо наличие специальных, атомарных семафорных машинных команд.
Для использования двоичного семафора требуется поддержка со стороны ОС, т.к. операции up и down должны быть атомарными.
Пример.
Представим себе супермаркет, посетители которого прежде чем войти в торговый зал должны обязательно взять себе инвентарную тележку. В момент открытия магазина на входе имеется N свободных тележек это начальное значение семафора. Каждый посетитель забирает одну из тележек (уменьшая тем самым количество оставшихся на 1) и проходит в торговый зал это аналог операции down. При выходе посетитель возвращает тележку на место, увеличивая количество тележек на 1 это аналог операции up. Теперь представим себе, что очередной посетитель обнаруживает, что свободных тележек нет он вынужден блокироваться на входе в ожидании появления тележки. Когда один из посетителей, находящихся в торговом зале, покидает его, посетитель, ожидающий тележку, разблокируется, забирает тележку и проходит в зал. Таким образом, наш семафор в виде тележек позволяет находиться в торговом зале (аналоге критической секции) не более чем N посетителям одновременно. Положив N=1, получим реализацию взаимного исключения. Семафор, начальное (и максимальное) значение которого равно 1, называется двоичным семафором (т. к. имеет только 2 состояния: 0 и 1).
Использование двоичного семафора для организации взаимного исключения проиллюстрировано на рисунке.
Здесь мы видим условную переменную типа int она здесь семафор, но на самом деле она не int, а типа данных семафор. Значит каждый из процессов, перед тем как работать с критическим ресурсом, т.е. перед тем как войти в свою критическую секцию делает операцию down() на семафор (семафор для всех один и тот же) и на выходе из своей критической секции он делает операцию up(). Представьте себе, что процесс 1 подошел к своей критической секции в то время, как семафор свободен (ресурс свободен). Он выполняет операцию down(), тем самым значение семафора становится равным 0 (т.к. вначале было равно 1), и процесс 1 получает возможность работать в своей критической секции. Если в этот момент процесс 2 захочет попасть в свою критическую секцию, т.е. тоже поработать с нашим ресурсом, то при попытке выполнить операцию down() он будет заблокирован, поскольку значение семафора 0 и уменьшиться оно уже не может. Соответственно он будет ожидать до тех пор, пока процесс 1 не выйдет из своей критической секции, после чего процесс 2 будет автоматически разблокирован в тот момент, когда процесс 1 выполнит up(), и тем самым попадет в свою критическую секцию. Понятно, что это реализует взаимное исключение.
Семафоры это мощное средство синхронизации, но проблемы с ними тоже есть, потому что при написании программ с использованием семафоров велика вероятность возникновения ошибок (т.е. средство достаточно низкоуровневое), т.к. достаточно в одном месте перепутать местами down() и up() или поставить не в том месте down(), не в том месте up(), и получается ситуация тупика. Кроме того, семафоры являются средством, которое требует поддержки со стороны ОС. Это выражается в том, что операции down() и up() должны быть атомарными, т.е. не должно происходить переключение контекстов. Иначе возможно, что в том момент, когда процесс проверил состояние семафора, но еще не изменил его значение, в этот момент произойдет переключение контекста, другой процесс изменит значение семафора, а 1-й процесс об этом уже не узнает и будет считать, что он прав и пойдет в свою критическую секцию, в то время как там уже находится другой процесс, поэтому требование атомарности оно принципиально важно. А раз говорится о том, что должно быть запрещено переключение контекстов, это требует естественно поддержки со стороны ОС.
Мониторы
Из-за этих проблем были предложены более высокоуровневые средства. И одним из самых мощных средств, которые были предложены это мониторы. Принципиальное отличие монитора от других средств в том, что монитор представляет собой языковую конструкцию, т.е. это средство языка программирования, средство, встроенное в язык. И соответственно поддержка здесь осуществляется не со стороны ОС, а со стороны компилятора с этого языка программирования, т.е. все необходимые действия вставляет компилятор.
Идея монитора была впервые сформулирована в 1974 г. Хоаром. В отличие от других средств, монитор представляет собой языковую конструкцию, т. е. Некоторое средство, предоставляемое языком программирования и поддерживаемое компилятором. Монитор представляет собой совокупность процедур и структур данных, объединенных в программный модуль специального типа.
Три основных свойства монитора:
1. структуры данных, входящие в монитор, могут быть доступны только для процедур, входящих в этот монитор (таким образом, монитор представляет собой некоторый аналог объекта в объектно-ориентированных языках и реализует инкапсуляцию данных);
2.процесс «входит» в монитор путем вызова одной из его процедур;
3.в любой момент времени внутри монитора может находиться не более одного процесса. Если процесс пытается попасть в монитор, в котором уже находится другой процесс, он блокируется. Таким образом, чтобы защитить разделяемые структуры данных, из достаточно поместить внутрь монитора вместе с процедурами, представляющими критические секции для их обработки.
Монитор представляет собой конструкцию языка программирования и компилятору известно о том, что входящие в него процедуры и данные имеют особую семантику, поэтому первое условие может проверяться еще на этапе компиляции, кроме того, код для процедур монитора тоже может генерироваться особым образом, чтобы удовлетворялось третье условие. Поскольку организация взаимного исключения в данном случае возлагается на компилятор, количество программных ошибок, связанных с организацией взаимного исключения, сводится к минимуму.
Несмотря на все эти плюсы, широкого распространения мониторы не получили. Т.е. мониторы реализованы в некоторых языках программирования, таких как Modula 2, но к сожалению эти языки программирования не очень широко распространены, и тем самым красивая идея осталась также осталась далека от широкого распространения.
Обмен сообщениями
Следующий способ реализации взаимного исключения это обмен сообщениями. Вообще обмен сообщениями это программное средство, которое используется очень широко, не только для решения проблем синхронизации, но и для проблем синхронизации оно тоже может быть использовано.
Основная функциональность метода обеспечивается двумя примитивами (являющимися, как и семафоры, в отличие от мониторов, системными вызовами, а не конструкциями языка) :
send (destination, message)
receive (source, message)
Основные особенности, которыми может обладать та или иная система обмена сообщениями:
•Синхронизация
- не блокирующий Процесс, осуществляющий не блокирующий send, выходит сразу же, а уже система берет на себя ответственность за то, чтобы куда-то буферизовать это сообщение и доставить его получающему процессу, тогда когда получающий процесс вызовет receive. Соответственно программа, вызывая не блокирующий receive, то если нет данных, подходящих под этот системный вызов (никто не писал еще сообщения), то выход будет осуществлен немедленно и не будет блокирования на ожидание.
- метод отправки блокирующий Процесс, осуществляющий отправку данных, при осуществлении блокирующего send, он будет заблокирован до тех пор, пока данные не будут получены, т.е. выход из send будет произведен только тогда, когда данные будут скопированы в буфер принимающего процесса. Аналогично, если осуществляется блокирующий receive, а данных еще нет, то мы будем заблокированы до тех пор пока не появятся данные, удовлетворяющие условиям нашего вызова.
•Адресация
- прямая При отправки сообщений указывается непосредственно некоторый идентификатор процесса. Предполагается, что управляющий процесс знает идентификатор того процесса, которому он хочет послать сообщение.
- косвенная Т.е. когда сообщение отправляется не непосредственно процессу, а в почтовый ящик. Почтовый ящик это специальная структура, которая накапливает сообщения, там уже сообщения складываются и можно их оттуда вынимать. Уже по названию понятно, что это некоторый аналог почтового ящика, к которому может иметь доступ как один процесс так и несколько процессов. Несколько процессов могут помещать туда данные и извлекать оттуда данные. В этом случает, когда используется режим почтового ящика при отправки сообщений и получении сообщений указывается уже не идентификатор процесса, а идентификатор почтового ящика. Кроме того, почтовые ящики получается, могут жить независимо от их хозяев, т.е. он существует сам по себе и уже процесс может завершиться, который его создал, и почтовый ящик продолжает существовать и использоваться другими процессами. Т.е. имеет место более гибкая схема.
•Длина сообщения
«Обедающие философы»
Пять философов собираются за круглым столом, перед каждым из них стоит блюдо со спагетти, и между каждыми двумя соседями лежит вилка. Каждый из философов некоторое время размышляет, затем берет две вилки (одну в правую руку, другую в левую) и ест спагетти, затем опять размышляет и так далее. Каждый из них ведет себя независимо от других, однако вилок запасено ровно столько, сколько философов, хотя для еды каждому из них нужно две. Таким образом, философы должны совместно использовать имеющиеся у них вилки (ресурсы). Задача состоит в том, чтобы найти алгоритм, который позволит философам организовать доступ к вилкам таким образом, чтобы каждый имел возможность насытиться, и никто не умер с голоду.
Рассмотрим простейшее решение, использующее семафоры. Когда один из философов хочет есть, он берет вилку слева от себя, если она в наличии, а затем - вилку справа от себя. Закончив есть, он возвращает обе вилки на свои места. Данный алгоритм может быть представлен следующим способом:
#define N 5 /* число философов*/
void philosopher (int i) /* i номер философа от 0 до 4*/
{
while (TRUE)
{
think(); /*философ думает*/
take_fork(i); /*берет левую вилку*/
take_fork((i+1)%N); /*берет правую вилку*/
eat(); /*ест*/
put_fork(i); /*кладет обратно левую вилку*/
put_fork((i+1)%N); /* кладет обратно правую вилку */
}
}
Функция take_fork() описывает поведение философа по захвату вилки: он ждет, пока указанная вилка не освободится, и забирает ее.
На первый взгляд, все просто, однако, данное решение может привести к тупиковой ситуации. Что произойдет, если все философы захотят есть в одно и то же время? Каждый из них получит доступ к своей левой вилке и будет находиться в состоянии ожидания второй вилки до бесконечности. Другим решением может быть алгоритм, который обеспечивает доступ к вилкам только четырем из пяти философов. Тогда всегда среди четырех философов по крайней мере один будет иметь доступ к двум вилкам. Данное решение не имеет тупиковой ситуации. Здесь определяется количество философов, далее идут макросы для определения номеров левой, правой вилки и три состояния философов: философ думает, философ голоден и философ ест. Определяется тип данных semaphore и массив состояний каждого из философов массив state. Далее определяется один семафор для реализации критической секции и по одному семафору на каждого философа это массив семафоров s. Вот, обратите внимание, они выделены цветом, и далее операции с этими семафорами тоже будут выделены цветом. Основная функция это философы. Философ думает затем, когда он проголодается, он вызывает функцию take_forks(), затем происходит еда, и вызывается функция put_forks(). В функции take_forks() в первую очередь идет вход в критическую секцию одного конкретного философа. Критическая секция охраняется семафором mutex. В момент входа в критическую секцию семафор mutex охраняет массив состояний философа. Идет изменение состояния на голоден, и вызывается функция test(). Затем производится выход из критической секции: поднятие семафора mutex и опускание семафора конкретного философа. В функции test() происходит проверка: что делает левый сосед, и что делает правый сосед. Если состояние голоден и левый сосед не ест и правый сосед не ест, т.е. вилки свободны, то состояние философа меняется на состояние поедания и поднимается семафор этого философа. Итак опускание семафора происходит в take_forks(), а поднятие в test(). Т.е. если семафор не был поднят в test() (не выполнилось условие, что оба соседа не едят), то в момент down() на семафоре в функции take_forks() философ будет заблокирован и будет ожидать, пока один из соседей не освободит его состояние, что происходит в функции put_forks(). Здесь тоже самое: опускается семафор для хранения критической секции, которая охраняет массив состояний. Состояние изменяется на «думающий» и производится тест на левого и правого соседа. В этот момент, когда посылается тест для обоих соседей, если один из этих соседей был ранее заблокирован из-за того, что его вилка была занята нашим философом, то в этот момент он разблокируется и сможет взять вилку. Обращаю ваше внимание на то, что семафор mutex необходим для охраны массива состояний философов. Т.е. здесь массив состояний философов является разделяемым ресурсом, потому что эти состояния проверяются как самим философом так и его левым и правым соседом (функция test()). Поэтому необходимо охранять их, потому что если доступ к ним будет осуществляться со стороны двух или более процессов одновременно, то один из процессов может проверить это состояние, в то время как другой процесс будет его изменять. Чтобы такого не происходило необходимо сделать этот ресурс критическим и охранять его семафором, что и делает семафор mutex. Массив семафоров s используется для того, чтобы блокировать философов, которые не могут в данный момент взять вилку и разблокирование философов происходит в тот момент, когда сосед вилки освобождает. Т.е. это решение уже более сложное, однако, оно позволяет избежать тупиковых ситуаций, но не гарантирует отсутствие дискриминации, т.е. здесь возможно ситуация, что поскольку в момент когда разблокируется один из соседей всегда возможна ситуация, что вилку захватит другой сосед, соответственно второй сосед может остаться без вилок.
Алгоритм решения может быть представлен следующим образом:
# define N 5 /* количество философов */
# define LEFT (i-1)%N /* номер легого соседа для i-ого философа */
# define RIGHT (i+1)%N /* номер правого соседа для i-ого философа*/
# define THINKING 0 /* философ думает */
# define HUNGRY 1 /* философ голоден */
# define EATING 2 /* философ ест */
typedef int semaphore; /* тип данных «семафор» */
int state[N]={0,0,0,0,0}; /* массив состояний философов */
semaphore mutex=1; /* семафор для критической секции */
semaphore s[N]; /* по одному семафору на философа */
void philosopher (int i)
/* i : номер философа от 0 до N-1 */
{
while (TRUE) /* бесконечный цикл */
{
think(); /* философ думает */
take_forks(i); /*философ берет обе вилки или блокируется */
eat(); /* философ ест */
put_forks(i); /* философ освобожает обе вилки */
}
}
void take_forks(int i)
/* i : номер философа от 0 до N-1 */
{
down(mutex); /* вход в критическую секцию */
state[i] = HUNGRY; /*записываем, что i-ый философ голоден */
test(i); /* попытка взять обе вилки */
up(mutex); /* выход из критической секции */
down(s[i]); /* блокируемся, если вилок нет */
}
void put_forks(i)
/* i : номер философа от 0 до N-1 */
{
down(mutex); /* вход в критическую секцию */
state[i] = THINKING; /* философ закончил есть */
test(LEFT);
/* проверить может ли левый сосед сейчас есть */
test(RIGHT);
/* проверить может ли правый сосед сейчас есть*/
up(mutex); /* выход из критической секции */
}
void test(i)
/* i : номер философа от 0 до N-1 */
{if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
{state[i] = EATING;
up (s[i]);}
}
Другой классической задачей синхронизации доступа к ресурсам является проблема «читателей и писателей», иллюстрирующая широко распространенную модель совместного доступа к данным. Представьте себе ситуацию, например, в системе резервирования билетов, когда множество конкурирующих процессов хотят читать и обновлять одни и те же данные. Несколько процессов могут читать данные одновременно, но когда один процесс начинает записывать данные (обновлять базу данных проданных билетов), ни один другой процесс не должен иметь доступ к данным, даже для чтения. Вопрос, как спланировать работу такой системы? Одно из решений представлено ниже:
typedef int semaphore; /* тип данных «семафор» */
semaphore mutex = 1; /* контроль за доступом к «rc» (разделямый ресурс) */
semaphore db = 1; /* контроль за доступом к базе данных */
int rc = 0; /* кол-во процессов читающих или пишущих */
void reader (void)
{
while (TRUE) /* бесконечный цикл */
{
down(mutex); /* получить эксклюзивный доступ к «rc»*/
rc = rc + 1; /* еще одним читателем больше */
if (rc == 1) down(db); /* если это первый читатель, нужно заблокировать эксклюзивный доступ к базе */
up(mutex); /*освободить ресурс rc */
read_data_base(); /* доступ к данным */
down(mutex); /*получить эксклюзивный доступ к «rc»*/
rc = rc - 1: /* теперь одним читателем меньше */
if (rc == 0) up(db); /*если это был последний читатель, разблокировать эксклюзивный доступ к базе данных */
up(mutex); /*освободить разделяемый ресурс rc */
use_data_read(); /* некритическая секция */
}
}
void writer (void)
{
while(TRUE) /* бесконечный цикл */
{
think_up_data(); /* некритическая секция */
down(db); /* получить эксклюзивный доступ к данным*/
write_data_base(); /* записать данные */
up(db); /* отдать эксклюзивный доступ */
}
}
Надо заметить, что приведенный алгоритм дает преимущество при доступе к базе данных процессам-читателям, т.к. процесс, ожидающий доступа по записи, будет ждать до тех пор, пока все читающие процессы не окончат работу, и если в это время появляется новый читающий процесс, он тоже беспрепятственно получит доступ. Чтобы этого избежать, можно модифицировать алгоритм таким образом, чтобы в случае, если имеется хотя бы один ожидающий процесс-писатель, новые процессы-читатели не получали доступа к ресурсу, а ожидали, когда процесс-писатель обновит данные. Однако, обратная сторона данного решения в том, что оно несколько снижает производительность процессов-читателей, т.к. вынуждает их ждать в тот момент, когда ресурс не занят в эксклюзивном режиме.
Эта задача о программировании и поведении разнородных процессов, если в предыдущем варианте первая задача у нас была, где все процессы были одинаковыми, вторая задача -уже не равноправные процессы. Здесь задача, где один процесс обслуживающий и все остальные клиенты.
Рассмотрим парикмахерскую, в которой работает один парикмахер, имеется одно кресло для стрижки и несколько кресел в приемной для посетителей, ожидающих своей очереди. Если в парикмахерской нет посетителей, парикмахер засыпает прямо на своем рабочем месте. Появившийся посетитель должен его разбудить, в результате чего парикмахер приступает к работе. Если в процессе стрижки появляются новые посетители, они должны либо подождать своей очереди, либо покинуть парикмахерскую, если в приемной нет свободного кресла для ожидания. Задача состоит в том, чтобы корректно запрограммировать поведение парикмахера и посетителей.
Понадобится целых 3 семафора: customers подсчитывает количество посетителей, ожидающих в очереди, barbers обозначает количество свободных парикмахеров (в случае одного парикмахера его значения либо 0, либо 1) и mutex используется для синхронизации доступа к разделяемой переменной waiting. Переменная waiting, как и семафор customers, содержит количество посетителей, ожидающих в очереди, она используется в программе для того, чтобы иметь возможность проверить, имеется ли свободное кресло для ожидания, и при этом не заблокировать процесс, если кресла не окажется. Заметим, что как и в предыдущем примере, эта переменная является разделяемым ресурсом, и доступ к ней охраняется семафором mutex.
#define CHAIRS 5
typedef int semaphore; /* тип данных «семафор» */
semaphore customers = 0; /* посетители, ожидающие в очереди */
semaphore barbers = 0; /* парикмахеры, ожидающие посетителей */
semaphore mutex = 1; /* контроль за доступом к переменной waiting */
int waiting = 0;
void barber()
{
while (true) {
down(customers); /* если customers == 0, т.е. посетителей нет, то заблокируемся до появления посетителя */
down(mutex); /* получаем доступ к waiting */
waiting = wating 1; /* уменьшаем кол-во ожидающих клиентов */
up(barbers); /* парикмахер готов к работе */
up(mutex); /* освобождаем ресурс waiting */
cut_hair(); /* процесс стрижки */
}
void customer()
{
down(mutex); /* получаем доступ к waiting */
if (waiting < CHAIRS) /* есть место для ожидания */
{
waiting = waiting + 1; /* увеличиваем кол-во ожидающих клиентов */
up(customers); /* если парикмахер спит, это его разбудит */
up(mutex); /* освобождаем ресурс waiting */
down(barbers); /* если парикмахер занят, переходим в состояние ожидания, иначе занимаем парикмахера*/
get_haircut(); /* процесс стрижки */
}
else
{
up(mutex); /* нет свободного кресла для ожидания придется уйти */
}
}
Реализация взаимодействия процессов
Проблемы, связанные с организацией взаимодействия процессов:
Первая именование процессов отправителей и получателей или именование некоторого объекта, через который осуществляется взаимодействие. Эта проблема решается по-разному в зависимости от конкретного механизма взаимодействия.
Так в системах, обеспечивающих взаимодействие процессов, функционирующих на различных компьютерах в сети используется адресация, принятая в конкретной сети ЭВМ (например, аппарат сокетов, MPI).
В средствах взаимодействия процессов, локализованных в пределах одной ЭВМ способ именования зависит от конкретного механизма взаимодействия. В частности, для ОС Unix взаимодействие процессов можно разделить на механизмы взаимодействия доступные исключительно родственным процессам и взаимодействие произвольных процессов (с точностью до прав процесса).
При взаимодействии родственных процессов проблема именования решается за счет наследования потомками некоторых свойств одного из прародителей. Например, в случае неименованных каналов процесс-родитель для организации взаимодействия создает канал. Этот канал наследуется сыновними процессами, тем самым создается возможность организации симметричного (ибо все процессы изначально равноправны) взаимодействия родственных процессов. Другой пример, это взаимодействие процессов по схеме главный-подчиненный (или трассировка). Данный тип взаимодействия ассиметричный, так как изначально один из взаимодействующих процессов получает статус и права «главного», второй - «подчиненного». Главный это родительский процесс, подчиненный сыновний. Соответственно именование жестко привязано к связке отец-сын (идентификаторы сына и отца всегда доступны и однозначно определены).
При взаимодействии произвольных процессов нет факта наследования некоторых свойств процессов, которые могут использоваться для именования. Поэтому, в данном случае обычно используются две схемы. Первая использование для именования идентификаторов взаимодействующих процессов (к примеру, аппарат передачи сигналов). Вторая схема предполагает использование некоторого системного ресурса, обладающего уникальным именем. Примером могут являться именованные каналы, использующие для организации взаимодействия процессов файлы специальных типов (например, FIFO).
Другая проблема организации взаимодействия это проблема синхронизации взаимодействующих процессов. Суть проблемы состоит в следующем. Взаимодействие процессов представимо в виде оказания одним процессом воздействия на другой процесс или использование некоторых разделяемых ресурсов, через которые возможна организация обмена данными.
Первое требование к средствам взаимодействия процессов это атомарность (неразделимость) базовых операций. То есть синхронизация должна обеспечить атомарность операций взаимодействий или обмена данными с разделяемыми ресурсами. К примеру, система должна блокировать начало чтения данных из некоторого разделяемого ресурса до того, пока начавшаяся к этому моменту операция записи по этому ресурсу не завершится.
Второе требование это обеспечение определенного порядка в операциях взаимодействия. Назовем это семантической синхронизацией. Например, попытка чтения данных, которых еще нет (и операция записи которых еще не начиналась). Уровней семантической синхронизации может быть достаточно много.
Комплексное решение проблемы синхронизации зависит от свойств используемых средств взаимодействия процессов. В некоторых случаях операционная система обеспечивает некоторые уровни синхронизации (например передача сигналов, использование каналов). В некоторых участие операционной системы в синхронизации минимально (например, разделяемая память IPC).
Но в любом случае, конкретная прикладная система должна учитывать, и при необходимости обеспечивать семантическую синхронизацию процессов.
Сигнал средство уведомления процесса о наступлении некоторого события в системе.
Инициаторы посылки сигнала - другой процесс или ОС.
Сигнал- программный аналог прерывания.
Количество различных сигналов в современных версиях UNIX около 30, каждый из них имеет уникальное имя и номер. Описания представлены в файле <signal.h>. Ниже приведено несколько примеров.
2 - SIGINT /*прерывание*/
3 - SIGQUIT /*аварийный выход*/
9 - SIGKILL /*уничтожение процесса*/
14 - SIGALRM /*прерывание от таймера*/.
18 - SIGCHLD /*процесс-потомок завершился*/.
В разных версиях UNIX имена сигналов могут различаться.
Сигналы, посылаемые ОС, уведомляют о наступлении некоторых строго предопределенных ситуаций (как, например, завершение порожденного процесса, прерывание процесса нажатием комбинации Ctrl-C, попытка выполнить недопустимую машинную инструкцию, попытка недопустимой записи в канал и т.п.), при этом каждой такой ситуации сопоставлен свой сигнал. Кроме того, зарезервировано один или несколько номеров сигналов, семантика которых определяется пользовательскими процессами по своему усмотрению (например, процессы могут посылать друг другу сигналы с целью синхронизации).
Сигналы являются механизмом асинхронного взаимодействия, т.е. момент прихода сигнала процессу заранее неизвестен. Однако процесс может предвидеть возможность получения того или иного сигнала и установить определенную реакцию на его приход. В этом плане сигналы можно рассматривать как программный аналог аппаратных прерываний.
При получении сигнала процессом возможны три варианта реакции на полученный сигнал:
- Процесс реагирует на сигнал стандартным образом, установленным по умолчанию (для большинства сигналов действие по умолчанию это завершение процесса).
- Процесс может установить специальную обработку сигнала, в этом случае по приходу сигнала вызывается функция-обработчик, определенная процессом (при этом говорят, что сигнал перехватывается)
- Процесс может проигнорировать сигнал.
Необходимо отметить, что некоторые сигналы невозможно ни перехватить, ни игнорировать. Они используются ядром ОС для управления работой процессов (например, SIGKILL, SIGSTOP).
Если в процесс одновременно доставляется несколько различных сигналов, то порядок их обработки не определен. Если же обработки ждут несколько экземпляров одного и того же сигнала, то ответ на вопрос, сколько экземпляров будет доставлено в процесс все или один зависит от конкретной реализации ОС.
Отдельного рассмотрения заслуживает ситуация, когда сигнал приходит в момент выполнения системного вызова. Обработка такой ситуации в разных версиях UNIX реализована по-разному, например, обработка сигнала может быть отложена до завершения системного вызова; либо системный вызов автоматически перезапускается после его прерывания сигналом; либо системный вызов вернет 1, а в переменной errno будет установлено значение EINTR
Для отправки сигнала существует системный вызов kill():
#include <signal.h>
int kill (pit_t pid, int sig);
pid идентификатор процесса, которому посылается сигнал
Существует также возможность одновременно послать сигнал нескольким процессам, например, если значение этого параметра есть 0, сигнал будет передан всем процессам, которые принадлежат той же группе, что и процесс, посылающий сигнал, за исключением процессов с идентификаторами 0 и 1.
sig номер посылаемого сигнала
Если этот параметр равен 0, то будет выполнена проверка корректности обращения к kill(), но никакой сигнал в действительности посылаться не будет.
Если процесс-отправитель не обладает правами привилегированного пользователя, то он может отправить сигнал только тем процессам, у которых реальный или эффективный идентификатор владельца процесса совпадает с реальным или эффективным идентификатором владельца процесса-отправителя.
При удачном выполнении возвращает 0, в противном случае возвращает 1
Для определения реакции на получение того или иного сигнала в процессе служит системный вызов signal():
#include <signal.h>
void (*signal ( int sig, void (*disp) (int))) (int);
sig номер сигнала, для которого устанавливается реакция
disp либо определенная пользователем функция обработчик сигнала, либо одна из констант:
SIG_DFL -обработка по умолчанию
SIG_IGN - игнорирование
При успешном завершении функция возвращает указатель на предыдущий обработчик данного сигнала.
В данном примере при получении сигнала SIGINT четырежды вызывается специальный обработчик, а в пятый раз происходит обработка по умолчанию.
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
int count = 0;
void SigHndlr (int s) /* обработчик сигнала */
{ printf("\n I got SIGINT %d time(s) \n",
++ count);
if (count == 5) signal (SIGINT, SIG_DFL);
/* ставим обработчик сигнала по умолчанию */
else signal (SIGINT, SigHndlr);
/* восстанавливаем обработчик сигнала */
}
int main(int argc, char **argv)
{ signal (SIGINT, SigHndlr); /* установка реакции на сигнал */
while (1); /*”тело программы” */
return 0;
}
При разработке программ нередко приходится создавать временные файлы , которые позже удаляются. Если произошло непредвиденное событие, такие файлы могут остаться не удаленными. Ниже приведено решение этой задачи.
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
const char * tempfile = “abc”;
void SigHndlr (int s)
{
unlink(tempfile);
/* уничтожение временного файла в случае прихода сигнала SIGINT. В случае, если такой файл не существует (еще не создан или уже удален), вызов вернет -1 */
exit(0);
}
int main(int argc, char **argv)
{
signal (SIGINT, SigHndlr); /*установка реакции на сигнал */
…
creat(tempfile, 0666); /*создание временного файла*/
…
unlink(tempfile);
/*уничтожение временного файла в случае нормального функционирования процесса */
return 0;
}
Существуют задачи, в которых необходимо прервать выполнение процесса по истечении некоторого количества времени. Средствами ОС “заводится” будильник, который будет поторапливать ввести некоторое имя. Системный вызов alarm():
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
инициализирует отложенное появление сигнала SIGALRM - процесс запрашивает ядро отправить ему самому сигнал по прошествии определенного времени.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void alrm(int s) /*обработчик сигнала SIG_ALRM */
{
printf(“\n жду имя \n”);
alarm(5); /* заводим будильник */
signal(SIGALRM, alrm); /* переустанавливаем реакцию на сигнал */
}
int main(int argc, char **argv)
{
char s[80];
signal(SIGALRM, alrm);
/* установка обработчика alrm на приход сигнала SIG_ALRM */
alarm(5); /* заводим будильник */
printf(“Введите имя \n”);
for (;;)
{
printf(“имя:”);
if (gets(s) != NULL) break; /* ожидаем ввода имени */
};
printf(“OK! \n”);
return 0;
}
В начале программы мы устанавливаем реакцию на сигнал SIGALRM - функцию alarm(), далее мы заводим будильник, запрашиваем “Введите имя” и ожидаем ввода строки символов. Если ввод строки задерживается, то будет вызвана функция alarm(), которая напомнит, что программа “ждет имя”, опять заведет будильник и поставит себя на обработку сигнала SIGALRM еще раз. И так будет до тех пор, пока не будет введена строка. Здесь имеется один нюанс: если в момент выполнения системного вызова возникает событие, связанное с сигналом, то система прерывает выполнение системного вызова и возвращает код ответа, равный «-1».
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
void alr(int s)
{
printf(“\n Быстрее!!! \n”);
signal(SIGALRM, alr);
/* переустановка обработчика alr на приход сигнала SIGALRM */
}
int main(int argc, char **argv)
{
char s[80];
int pid;
signal(SIGALRM, alr);
/* установка обработчика alr на приход сигнала SIGALRM */
if (pid = fork()) {
for (;;)
{
sleep(5); /*приостанавливаем процесс на 5 секунд */
kill(pid, SIGALRM);
/*отправляем сигнал SIGALRM процессу- сыну */
}
}
else {
printf(“Введите имя \n”);
for (;;)
{
printf(“имя:”);
if (gets(s) != NULL) break; /*ожидаем ввода имени*/
}
printf(“OK!\n”);
kill(getppid(), SIGKILL);
/* убиваем зациклившегося отца */
}
return 0;
}
В данном случае программа реализуется в двух процессах. Как и в предыдущем примере, имеется функция реакции на сигнал alr(), которая выводит на экран сообщение и переустанавливает функцию реакции на сигнал, опять же на себя. В основной программе мы также указываем alr() как реакцию на SIGALRM. После этого мы запускаем сыновний процесс, и отцовский процесс (бесконечный цикл) “засыпает” на 5 единиц времени, после чего сыновнему процессу будет отправлен сигнал SIGALRM. Все, что ниже цикла, будет выполняться в процессе-сыне: мы ожидаем ввода строки, если ввод осуществлен, то происходит уничтожение отца (SIGKILL).
Неименованные каналы.
Одним из простейших средств взаимодействия процессов в операционной системе UNIX является механизм каналов. Неименованный канал- область на диске, к которой не возможен доступ по имени, а только с помощью двух дискрипторов с ней ассоциированных. Один для чтения, другой для записи.
Отличитльные свойства:
Одним из простейших средств взаимодействия процессов в операционной системе UNIX является механизм каналов. Неименованный канал есть некая сущность, в которую можно помещать и извлекать данные, для чего служат два файловых дескриптора, ассоциированных с каналом: один для записи в канал, другой для чтения. Для создания канала служит системный вызов pipe():
int pipe (int *fd)
Данный системный вызов выделяет в оперативной памяти некоторое ограниченное пространство и возвращает че6рез параметр fd массив из двух файловых дескрипторов: один для записи в канал fd[1], другой для чтения fd[0].
Эти дескрипторы являются дескрипторами открытых файлов, с которыми можно работать, используя такие системные вызовы как read(), write(), dup() и пр.
Однако существуют различия в организации использования обычного файла и канала.
Особенности организации чтения данных из канала:
· если прочитано меньше байтов, чем находится в канале, оставшиеся сохраняются в канале;
· если делается попытка прочитать больше данных, чем имеется в канале, и при этом существуют открытые дескрипторы записи, ассоциированные с каналом, будет прочитано (т.е. изъято из канала) доступное количество данных, после чего читающий процесс блокируется до тех пор, пока в канале не появится достаточное количество данных для завершения операции чтения;
· процесс может избежать такого блокирования, изменив для канала режим блокировки с использованием системного вызова fcntl(), в этом случае будет считано доступное количество данных, и управление будет сразу возвращено процессу;
· при закрытии записывающей стороны канала, в него помещается символ EOF (т.е. ситуация когда закрыты все дескрипторы, ассоциированные с записью в канал), после этого процесс, осуществляющий чтение, может выбрать из канала все оставшиеся данные и признак конца файла, благодаря которому блокирования при чтении в этом случае не происходит.
Особенности организации записи данных в канал:
· если процесс пытается записать большее число байтов, чем помещается в канал (но не превышающее предельный размер канала) записывается возможное количество данных, после чего процесс, осуществляющий запись, блокируется до тех пор, пока в канале не появится достаточное количество места для завершения операции записи;
· процесс может избежать такого блокирования, изменив для канала режим блокировки с использованием системного вызова fcntl(). В неблокирующем режиме в ситуации, описанной выше, будет записано возможное количество данных, и управление будет сразу возвращено процессу.
· если же процесс пытается записать в канал порцию данных, превышающую предельный размер канала, то будет записано доступное количество данных, после чего процесс заблокируется до появления в канале свободного места любого размера (пусть даже и всего 1 байт), затем процесс разблокируется, вновь производит запись на доступное место в канале, и если данные для записи еще не исчерпаны, вновь блокируется до появления свободного места и т.д., пока не будут записаны все данные, после чего происходит возврат из вызова write()
· если процесс пытается осуществить запись в канал, с которым не ассоциирован ни один дескриптор чтения, то он получает сигнал SIGPIPE (тем самым ОС уведомляет его о недопустимости такой операции).
В стандартной ситуации (при отсутствии переполнения) система гарантирует атомарность операции записи, т. е. при одновременной записи нескольких процессов в канал их данные не перемешиваются.
Процесс посылает данные самому себе. Описан массив из двух целых чисел, который передается в функцию pipe (в системный вызов pipe), pipe его заполнил. Далее, используя нулевой дескриптор, осуществляем чтение из канала, используя первый запись. Понятно, здесь просто строчка записывается, потом считывается. Затем закрываются оба дескриптора из pipes. Прочитанная строка записывается на стандартный вывод и после этого программа завершается. Пример условный, потому что в рамках одного процесса каналы никто не использует.
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
char *s = ”chanel”;
char buf[80];
int pipes[2];
pipe(pipes);
write(pipes[1], s, strlen(s) + 1);
read(pipes[0], buf, strlen(s) + 1);
close(pipes[0]);
close(pipes[1]);
printf(“%s\n”, buf);
return 0;
}
Как правило каналы используются для взаимодействия между двумя процессами. И тут-то как раз и становится существенным, что это есть средство для родственных процессов. Поскольку канал настолько характеризуется файловыми дескрипторами, то для того, чтобы сделать его доступным для другого процесса, существует один единственный способ унаследовать дескрипторы при порождении сыновнего процесса. Т.е. должна быть следующая последовательность действий: сначала порождается канал, после чего появляется открытый файловый дескриптор, и затем порождается вся необходимая иерархия на процессы, при этом открытые дескрипторы, естественно, наследуются всеми процессами потомками. Тем самым все процессы потомки, порожденные после того, как этот канал будет создан, и их потомки, если они породят в свою очередь каких-то своих потомков, они имеют доступ к этому каналу, потому что у них есть открытый дескриптор к этому каналу. И больше никакие процессы к нему доступа не имеют и не могут его никак получить, потому что этот дескриптор никак не может быть передан, даже если передать это целое число какому-то другому процессу, то оно для него ничего не будет означать, поскольку у него в таблице файловых дескрипторов отсутствует специальная запись, которая ассоциирована с этим контролем. Именно в этом и заключается смысл фразы, когда говорится о том, что канал это средство взаимодействия для родственных процессов.
Сначала идет вызов pipe, затем fork(), благодаря которому образуется несколько процессов, и соответственно внутри if (fork()) процесс-отец, он закрывает дескриптор чтения и пользуется только записывающей стороной. В сыне происходит, соответственно, происходит все наоборот, он закрывает дескриптор записи и осуществляет чтение из канала. Как правило, канал используется как однонаправленное средство, т.е. данные будут передвигаться только в одном направлении, в данном случае от отца к сыну: отец записывает данные сын их читает в том же порядке, в котором их записал отец. Обратите внимание на закрывание дескрипторов. Понятно, что это, в принципе, не нужно, поскольку если программа будет завершена, то все дескрипторы и так закроются. Но в данном случае эта строка имеет очень важный смысл: ранее уже говорилось о том, что при попытке чтения большего числа байт из канала, чем в нем находится, чтение будет заблокировано в том случае, если в канале не находится символ EOF, который туда попадает, когда закрывается последний записывающий дескриптор. Последний записывающий дескриптор будет закрыт тогда, когда его закроет процесс-отец, т.е. он запишет все необходимые данные, после чего закроет дескриптор, и собственно это будет означать, что больше данных не будет. В случае, если сын не закроет свой унаследованный дескриптор записи, то после того, как отец закроет свой дескриптор записи останется еще один записывающий дескриптор к тому же каналу в процессе сыне. Хоть он его и не использует (но системе-то это неизвестно), в последнем чтении процесс-сын зациклится, т.к. символ EOF в канал не попадает (поскольку в момент закрытия записывающей стороны это не последний записывающий дескриптор). Поэтому ненужные дескрипторы записи в канал важно обязательно закрывать, потому что иначе последнее чтение из канала будет заблокировано навечно.
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd[2];
pipe(fd);
if (fork())
{/*процесс-родитель*/
close(fd[0]); /* закрываем ненужный дескриптор */
write (fd[1], …);
…
close(fd[1]);
…
}
else
{/*процесс-потомок*/
close(fd[1]); /* закрываем ненужный дескриптор */
while(read (fd[0], …))
{
…
}
…
}
}
. Конвейер это две программы (два процесса), которые исполняются параллельно и при этом стандартный вывод первой программы посылается на стандартный ввод второй программы, т.е. по мере того, как 1-й процесс генерирует свой вывод, он сразу же выдается на ввод второму процессу. Реализуется это очень легко с помощью каналов. Первым делом порождается канал, затем происходит порождение процесса потомка. В процессе потомке с помощью системного вызова dup2(), записывающая сторона канала дублируется на стандартный вывод, после чего системный вызов dup2() открывает второй дескриптор и теперь дескриптор с номером 1, описывающий стандартный вывод, будет смотреть в канал (весь вывод будет помешен в канал) после чего закрываются ненужные дескрипторы, записывающий в канал и читающий, и вызывается замена тела процесса на ту программу, которая собственно является первой программой. В процессе отце происходит всё наоборот, здесь в читающей стороне канала открывается второй дескриптор с номером 0 (стандартный ввод), это означает, что в дальнейшем весь стандартный ввод будет браться из канала, происходит тоже самое, закрываются ненужные дескрипторы, записывающий в канал и читающий, и происходит замена тела программы на программу wc.
Пример реализации конвейера print|wc вывод программы print будет подаваться на вход программы wc. Программа print печатает некоторый текст. Программа wc считает количество прочитанных строк, слов и символов.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd[2];
pipe(fd); /*организован канал*/
if (fork())
{
/*процесс-родитель*/
dup2(fd[1], 1); /* отождествили стандартный вывод с файловым дескриптором канала, предназначенным для записи */
close(fd[1]); /* закрыли файловый дескриптор канала, предназначенный для записи */
close(fd[0]); /* закрыли файловый дескриптор канала, предназначенный для чтения */
exelp(“print”, ”print”, 0); /* запустили программу print */
}
/*процесс-потомок*/
dup2(fd[0], 0); /* отождествили стандартный ввод с файловым дескриптором канала, предназначенным для чтения*/
close(fd[0]); /* закрыли файловый дескриптор канала, предназначенный для чтения */
close(fd[1]); /* закрыли файловый дескриптор канала, предназначенный для записи */
execl(“/usr/bin/wc”, ”wc”, 0); /* запустили программу wc */
}
Следующий пример - совместное использования сигналов и каналов.
Каналы, как правило, используются как однонаправленное средство. Но их можно использовать и как двунаправленное средство, т.е. для реализации передачи данных в обоих направлениях, но в этом случае понадобятся дополнительные средства синхронизации. Средством синхронизации являются сигналы. Здесь процессы посылают друг другу число, всякий раз увеличивая его на 1, и когда число достигнет определенного максимума, оба процесса завершаются, т.е. фактически они передают друг другу это число определенное количество раз. Средой передачи данных служит канал, средством синхронизации для того, чтобы осуществить двустороннюю передачу данных служит сигнал. Здесь определяется константа максимального значения числа, после которого нужно выйти.
Первым делом смотрим на обработчик сигнала. Обработчик сигнала, в данном случае будет использоватьcя сигнал SIGUSER1 для синхронизации, т.е. посылать его процессу всякий раз когда пришла его очередь читать из канала, т.к. взаимные скорости выполнения отца и сына неизвестны. SIGUSER1 - это сигнал, который не соответствует какому-либо определенному событию в ОС, а его семантика определяется пользователями, т.е. это как раз тот случай, когда семантика сигнала отдается на усмотрение программисту.
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define MAX_CNT 100
int target_pid, cnt;
int fd[2];
int status;
void SigHndlr(int s)
{
/* в обработчике сигнала происходит и чтение, и запись */
signal(SIGUSR1, SigHndlr);
if (cnt < MAX_CNT)
{
read(fd[0], &cnt, sizeof(int));
printf("%d \n", cnt);
cnt++;
write(fd[1], &cnt, sizeof(int));
/* посылаем сигнал второму: пора читать из канала */
kill(target_pid, SIGUSR1);
}
else
if (target_pid == getppid())
{
/* условие окончания игры проверяется потомком */
printf("Child is going to be terminated\n");
close(fd[1]); close(fd[0]);
/* завершается потомок */
exit(0);
} else
kill(target_pid, SIGUSR1);
}
int main(int argc, char **argv)
{
pipe(fd); /* организован канал */
signal (SIGUSR1, SigHndlr);
/* установлен обработчик сигнала для обоих процессов */
cnt = 0;
if (target_pid = fork())
{
/* Предку остается только ждать завершения потомка */
while(wait(&status) == -1);
printf("Parent is going to be terminated\n");
close(fd[1]); close(fd[0]);
return 0;
}
else
{
/* процесс-потомок узнает PID родителя */
target_pid = getppid();
/* потомок начинает пинг-понг */
write(fd[1], &cnt, sizeof(int));
kill(target_pid, SIGUSR1);
for(;;); /* бесконечный цикл */
}
}
Процесс сын начинает игру. Во-первых, устанавливается target_pid, который в процессе-отце был установлен в fork(), в процессе-сыне он устанавливается с помощью вызова getppid(), который возвращает pid по требованию. И он записывает текущее значение в канал и посылает сигнал SIGUSR1 своему предку. Далее происходит бесконечный цикл. Т.е. вся обработка «пинг-понг», которая заключается в чтении данных из канала, увеличения на 1, проверки значения (не достигло ли оно своего максимума) и записи нового увеличенного значения в канал все это происходит в обработчике канала, т.е. здесь функция main() в процессе-сыне - бесконечный цикл, а в процессе отце wait() ожидание завершения потомка. Процесс-сын завершается когда нашел максимум, а процесс-отец в этом случае выходит из wait(), закрывает канал и тоже выходит.
Неименованные каналы являются достаточно мощным средством передачи данных, однако, у них имеется важный недостаток, о котором уже говорилось, а именно то, что они доступны только родственным процессам. Т.е. поскольку к ним не возможен доступ по имени, а возможен только с помощью файловых дескрипторов, то нужно сначала породить канал, затем породить потомков и только таким образом они могут унаследовать дескрипторы записи и чтения в канал. Это важное ограничение, поскольку два не связанных между собой процесса, которые были порождены ранее, чем средства межпроцессного взаимодействия канал не могут использовать. Эту проблему решает средство - именованные каналы
Именованные каналы имеют имя, как и файлы. Каждому именованному каналу соответствует один элемент некоторого каталога ОС UNIX, поэтому возможна ссылка к нему по имени файла, которое хранится в поле имени соответствующего элемента каталога.
Системный доступ реализован последовательно. У именованных каналов имеются имя владельца, права доступа, размер не ограничен.
Рассмотренные ранее программные каналы имеют важное ограничение: т.к. доступ к ним возможен только посредством дескрипторов, возвращаемых при порождении канала, необходимым условием взаимодействия процессов через канал является передача этих дескрипторов по наследству при порождении процесса. Именованные каналы (FIFO-файлы) расширяют свою область применения за счет того, что подключиться к ним может любой процесс в любое время, в том числе и после создания канала. Это возможно благодаря наличию у них имен.
FIFO-файл представляет собой отдельный тип файла в файловой системе UNIX, который обладает всеми атрибутами файла, такими как имя владельца, права доступа и размер. Для его создания в UNIX System V.3 и ранее используется системный вызов mknod(), а в BSD UNIX и System V.4 вызов mkfifo() (этот вызов поддерживается и стандартом POSIX):
int mknod (char *pathname, mode_t mode, dev)
int mkfifo (char *pathname, mode_t mode)
В обоих вызовах первый аргумент представляет собой имя создаваемого канала, во втором указываются права доступа к нему для владельца, группы и прочих пользователей, и кроме того, устанавливается флаг, указывающий на то, что создаваемый объект является именно FIFO-файлом (в разных версиях ОС он может иметь разное символьное обозначение S_IFIFO или I_FIFO). Третий аргумент вызова mknod() игнорируется.
После создания именованного канала любой процесс может установит с ним связь посредством системного вызова open(). При этом действуют следующие правила:
- если процесс открывает FIFO-файл для чтения, он блокируется до тех пор, пока какой-либо процесс не откроет тот же канал на запись
- если процесс открывает FIFO-файл на запись, он будет заблокирован до тех пор, пока какой-либо процесс не откроет тот же канал на чтение
- процесс может избежать такого блокирования, указав в вызове open() специальный флаг (в разных версиях ОС он может иметь разное символьное обозначение O_NONBLOCK или O_NDELAY). В этом случае в ситуациях, описанных выше, вызов open() сразу же вернет управление процессу
Правила работы с именованными каналами, в частности, особенности операций чтения-записи, полностью аналогичны неименованным каналам. Только размер именованных каналов может быть неограничен.
Ниже рассматривается пример, где один из процессов является сервером, предоставляющим некоторую услугу, другой же процесс, который хочет воспользоваться этой услугой, является клиентом. Клиент посылает серверу запросы на предоставление услуги, а сервер отвечает на эти запросы.
. Итак: существует некоторый процесс сервер, оказывающий какие-либо услуги, все остальные процессы являются клиентами, они обращаются к серверу с запросами, сервер их обслуживает и возвращает результаты этих запросов клиентам вот такая идеология называется «клиент-сервер». В данном примере один из процессов является сервером, предоставляющим некоторую услугу. Он получает от клиента pid клиента и печатает строку на стандартный вывод о том, что он получил сообщение от клиента такого-то. Создается именованный канал, далее он открывается, и соответственно с отсутствием блокировки, и происходит попытка чтения данных из канала, не блокируясь, и если данных в канале нет то вызов read() в данном не блокирующем режиме будет сразу возвращать 1. Следующий while крутится до тех пор пока в канале не появятся данные. Затем данные появились, они были прочитаны, те данные которые были прочитаны печатаются на стандартный вывод, затем канал закрывается и уничтожаются. Процесс-клиент не должен создавать канал, а должен только пытаться к нему подключится, уже открыв его по записи, и после чего записать в него свой pid и завершится.
/* процесс-сервер*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
int main(int argc, char **argv)
{
int fd;
int pid;
mkfifo("fifo", S_IFIFO | 0666);
/*создали специальный файл FIFO с открытыми для всех правами доступа на чтение и запись*/
fd = open ("fifo", O_RDONLY | O_NONBLOCK);
/* открыли канал на чтение*/
while ( read (fd, &pid, sizeof(int) ) == -1) ;
printf ("Server %d got message from %d !\n", getpid(), pid);
close (fd);
unlink ("fifo");
/*уничтожили именованный канал*/
return 0;
}
/* процесс-клиент*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
int main(int argc, char **argv)
{
int fd;
int pid=getpid( );
fd = open ("fifo", O_RDWR);
write (fd, &pid, sizeof(int));
close (fd);
return 0;
}
Именованные каналы уже являются более общим средством, чем неименованные, к ним могут обращаться любые процессы на данной машине, т.е. это более широкое средство.
Последнее средство, которое будет рассмотрено в рамках этой темы это модель взаимодействия «главный-подчиненный». До сих пор все модели взаимодействия между процессами были равноправны, возможно, они выполняли разные функции: кто-то читал данные, а кто-то являлся отправителем, но так или иначе процессы были равноправными. Схема «главный-подчиненный» предполагает, что один из процессов является главным, т.е. управляет поведением другого процесса. В системе Unix к такой модели относится трассировка, т.е. отладка процессов. Трассировка осуществляется двумя процессами, которые родственны между собой. В ОС Unix возможна трассировка только непосредственно своего потомка, при этом трассирующий процесс управляет поведением, выполнением трассируемого процесса. При этом процесс-потомок, которого мы собираемся отлаживать, предварительно должен дать разрешение на трассировку. После того, как процесс-потомок разрешил трассировку, как правило, происходит замена тела этот потомка на ту программу, которую необходимо отладить.
Далее схема взаимодействия процессов путем трассировки такова: выполнение отлаживаемого процесса-потомка приостанавливается всякий раз при получении им какого-либо сигнала, а также при выполнении вызова exec(). Если в это время отлаживающий процесс осуществляет системный вызов wait(), этот вызов немедленно возвращает управление. В то время, как трассируемый процесс находится в приостановленном состоянии, процесс-отладчик имеет возможность анализировать и изменять данные в адресном пространстве отлаживаемого процесса и в пользовательской составляющей его контекста. Далее, процесс-отладчик возобновляет выполнение трассируемого процесса до следующего приостановка (либо, при пошаговом выполнении, для выполнения одной инструкции).
Основной системный вызов, используемый при трассировке, это ptrace(), прототип которого выглядит следующим образом:
#include <sys/ptrace.h>
int ptrace(int cmd, pid, addr, data)
cmd код выполняемой команды,
pid идентификатор процесса-потомка,
addr некоторый адрес в адресном пространстве процесса-потомка,
data слово информации.
рассмотрим основные коды - cmd операций этой функции.
cmd = PTRACE_TRACEME ptrace() с таким кодом операции сыновний процесс вызывает в самом начале своей работы, позволяя тем самым трассировать себя. Все остальные обращения к вызову ptrace() осуществляет процесс-отладчик.
cmd = PTRACE_PEEKDATA - чтение слова из адресного пространства отлаживаемого процесса по адресу addr , ptrace() возвращает значение этого слова.
cmd = PTRACE_PEEKUSER чтение слова из контекста процесса. Речь идет о доступе к пользовательской составляющей контекста данного процесса, сгруппированной в некоторую структуру, описанную в заголовочном файле <sys/user.h>. В этом случае параметр addr указывает смещение относительно начала этой структуры. В этой структуре размещена такая информация, как регистры, текущее состояние процесса, счетчик адреса и так далее. ptrace() возвращает значение считанного слова.
cmd = PTRACE_POKEDATA запись данных, размещенных в параметре data, по адресу addr в адресном пространстве процесса-потомка.
cmd = PTRACE_POKEUSER запись слова из data в контекст трассируемого процесса со смещением addr. Таким образом можно, например, изменить счетчик адреса трассируемого процесса, и при последующем возобновлении трассируемого процесса его выполнение начнется с инструкции, находящейся по заданному адресу.
cmd = PTRACE_GETREGS, PTRACE_GETFREGS чтение регистров общего назначения (в т.ч. с плавающей точкой) трассируемого процесса и запись их значения по адресу data.
cmd = PTRACE_SETREGS, PTRACE_SETFREGS запись в регистры общего назначения (в т.ч. с плавающей точкой) трассируемого процесса данных, расположенных по адресу data в трассирующем процессе.
cmd = PTRACE_CONT возобновление выполнения трассируемого процесса. Отлаживаемый процесс будет выполняться до тех пор, пока не получит какой-либо сигнал, либо пока не завершится.
cmd = PTRACE_SYSCALL, PTRACE_SINGLESTEP эта команда, аналогично PTRACE_CONT, возобновляет выполнение трассируемой программы, но при этом произойдет ее остановка после того, как выполнится одна инструкция. Таким образом, используя PTRACE_SINGLESTEP, можно организовать пошаговую отладку. С помощью команды PTRACE_SYSCALL возобновляется выполнение трассируемой программы вплоть до ближайшего входа или выхода из системного вызова. Идея использования PTRACE_SYSCALL в том, чтобы иметь возможность контролировать значения аргументов, переданных в системный вызов трассируемым процессом, и возвращаемое значение, переданное ему из системного вызова.
cmd = PTRACE_KILL завершение выполнения трассируемого процесса.
Рассмотрим некоторый модельный пример, демонстрирующий общую схему построения отладочной программы:
...
if ((pid = fork()) == 0)
{
ptrace(PTRACE_TRACEME, 0, 0, 0);
/* сыновний процесс разрешает трассировать себя */
exec(“трассируемый процесс”, 0);
/* замещается телом процесса, который необходимо трассировать */
}
else
{
/* это процесс, управляющий трассировкой */
wait((int ) 0);
/* процесс приостанавливается до тех пор, пока от трассируемого процесса не придет сообщение о том, что он приостановился */
for(;;)
{
ptrace(PTRACE_SINGLESTEP, pid, 0, 0);
/* возобновляем выполнение трассируемой программы */
wait((int ) 0);
/* процесс приостанавливается до тех пор, пока от трассируемого процесса не придет сообщение о том, что он приостановился */
…
ptrace(cmd, pid, addr, data);
/* теперь выполняются любые действия над трассируемым процессом */
…
}
}
Предназначение процесса-потомка разрешить трассировку себя. После вызова ptrace(PTRACE_TRACEME, 0, 0, 0) ядро устанавливает для этого процесса бит трассировки. Сразу же после этого можно заместить код процесса-потомка кодом программы, которую необходимо отладить. Отметим, что при выполнении системного вызова exec(), если для данного процесса ранее был установлен бит трассировки, ядро перед передачей управления в новую программу посылает процессу сигнал SIGTRAP. При получении данного сигнала трассируемый процесс приостанавливается, и ядро передает управление процессу-отладчику, выводя его из ожидания в вызове wait().
Процесс-родитель вызывает wait() и переходит в состояние ожидания до того момента, пока потомок не перейдет в состояние трассировки. Проснувшись, управляющий процесс, выполняя функцию ptrace(cmd, pid, addr, data) с различными кодами операций, может производить любое действие с трассируемой программой, в частности, читать и записывать данные в адресном пространстве трассируемого процесса, производить его пошаговое выполнение при этом, как показано в примере выше, применяется та же схема: процесс-отладчик вызывает wait() в состояние ожидания, а ядро возобновляет выполнение трассируемого потомка, исполняет трассируемую команду, и вновь передает управление отладчику, выводя его из ожидания .
Процесс, который мы будем отлаживать, осуществляет деление на 0. Это сделано для того, чтобы показать, как происходит прерывание выполнения отлаживаемого процесса, когда к нему приходит сигнал. Т.е. когда было произведено деление на 0, приходит сигнал, который мы и будем ловить в нашем отлаживаемом процессе, но в отличии от обычной ситуации, когда обработка по умолчанию на этот сигнал означала бы завершение процесса, в данном случае он будет только приостановлен. В процессе-отладчике, во-первых, описывается структура, при помощи которой будет осуществляться чтение из регистра; далее порождается процесс; в процессе-сыне запускается трассировка (вызов ptrace с командой PTRACE_TRACEME) и запускается замена тела процесса на процесс, который состоит из единственного деления на 0. Отец запускает wait(), первый выход из этого wait() будет сразу после execl() в сыне. После этого он добывает содержимое некоторых регистров, печатает их содержимое. Обратите внимание, что имена этих регистров EIP(индексный регистр) и ESP(stack pointer) являются машинно-зависимыми, т.е. на другой машине структура REG, а именно имена полей в ней могут быть другими, но в целом структура REG описывает регистры общего назначения на данной архитектуре. Соответственно, они были получены с помощью команды PTRACE_GETREGS в вызове ptrace, затем они распечатываются, затем идет проверка на то, был ли процесс приостановлен с помощью сигнала SIGTRAP и, если этот так, то продолжается его выполнение.Если это не так (это будет в том случае, когда уже второй раз был получен сигнал SIGTRAP от отлаживаемого процесса, когда он остановился в результате деления на 0) он проверяет, что действительно в статусе передано, что этот процесс должен завершится, и он (процесс) закрывается.
Эта схема работает следующим образом: процесс сын запускает трассировку и запускает execl(), после чего сразу приостанавливается, потому что к нему пришел сигнал SIGTRAP. Процесс-отец в цикле ожидает в wait(), выходит из ожидания, потому что процесс-сын приостанавливается, далее проверяет содержимое его регистров, печатает это содержимое и запускает опять его выполнение. Теперь процесс-сын выполняет программу, которая осуществляет деление на 0, так только он осуществил деление на 0 ему приходит сигнал SIGFPE, и так как он находится в ситуации трассировки, то он приостанавливается, и управление вновь передается процессу-отцу. Процесс-отец вновь вышел из wait(), проанализировал ситуацию, увидел из-за какого сигнала прервалось выполнение отлаживаемого процесса. Для этого он использует следующие макросы: WSTOPSIG() это макрос, который позволяет из статуса процесса (из того что вернулось в функции wait()) получить информацию из-за чего тот был приостановлен: т.е. то, что он был приостановлен в результате сигнала или в результате какого сигнала. Далее из этого же статуса с помощью макроса WIFEXITED(), что процесс-потомок должен был завершиться, и в этом случае он просто убивается с помощью использования команды PTRACE_KILL в вызове ptrace.
/* Процесс-сын: */
int main(int argc, char **argv)
{
/* деление на ноль здесь процессу будет послан сигнал SIGFPE floating point exception */
return argc/0;
}
/* Процесс-родитель:*/
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
int status;
struct user_regs_struct REG;
if ((pid = fork()) == 0) {
/*находимся в процессе-потомке, разрешаем трассировку */
ptrace(PTRACE_TRACEME, 0, 0, 0);
execl(“son”, ”son”, 0); /* замещаем тело процесса */
/* здесь процесс-потомок будет остановлен с сигналом SIG_TRAP, ожидая команды продолжения выполнения от управляющего процесса*/
}
/* в процессе-родителе */
while (1) {
/* ждем, когда отлаживаемый процесс приостановится */
wait(&status);
/*читаем содержимое регистров отлаживаемого процесса */
ptrace(PTRACE_GETREGS, pid, ®, ®);
/* выводим статус отлаживаемого процесса, номер сигнала, который его остановил и значения прочитанных регистров */
printf("signal = %d, status = %#x, EIP=%#x ESP=%#x\n", WSTOPSIG(status), status, REG.eip, REG.esp);
if (WSTOPSIG(status) != SIGTRAP) {
if (!WIFEXITED(status)) {
/* завершаем выполнение трассируемого процесса */
ptrace (PTRACE_KILL, pid, 0, 0);
}
break;
}
/* разрешаем выполнение трассируемому процессу */
ptrace (PTRACE_CONT, pid, 0, 0);
}
}
Система IPC альтернатива именованным каналам. Позволяет организовывать работу именованных каналов в произвольные моменты времени.
Состав :
- Очереди сообщений
- Семафоры
- Разделяемая память
Общие концепции
Для каждого разделяемого ресурса используется «уникальная» система именования. Существует код, идентифицирующий конкретный разделяемый ресурс. Для работы с этим ресурсам необходимо знать этот код.
Каждый IPCресурс обладает набором атрибутов:
-Владельца ресурса (идентификатор процесса, создавшего ресурс, хотя процесс может делегировать ресурс другим процессам)
- Права доступа
Для всех средств IPC приняты общие правила именования объектов, позволяющие процессу получить доступ к такому объекту. Для именования объекта IPC используется ключ, представляющий собой целое число. Ключи являются уникальными во всей UNIX-системе идентификаторами объектов IPC, и зная ключ для некоторого объекта, процесс может получить к нему доступ. При этом процессу возвращается дескриптор объекта, который в дальнейшем используется для всех операций с ним. Проведя аналогию с файловой системой, можно сказать, что ключ аналогичен имени файла, а получаемый по ключу дескриптор файловому дескриптору, получаемому во время операции открытия файла. Ключ для каждого объекта IPC задается в момент его создания тем процессом, который его порождает, а все процессы, желающие получить в дальнейшем доступ к этому объекту, должны указывать тот же самый ключ.
Для именования ресурсов IPC используется уникальные ключи. Для каждого из разделяемых ресурсов.
Генерация ключей: функция ftok().
Все процессы, которые хотят работать с одним и тем же IPC-ресурсом, должны знать некий целочисленный ключ, по которому можно получить к нему доступ.
Необходим механизм уникального именования ресурса, но вместе с тем нужно, чтобы этот механизм позволял всем процессам, желающим работать с одним ресурсом, получить одно и то же значение ключа.
Итак, все процессы, которые хотят работать с одним и тем же IPC-ресурсом, должны знать некий целочисленный ключ, по которому можно получить к нему доступ. В принципе, программист, пишущий программы для работы с разделяемым ресурсом, может просто жестко указать в программе некоторое константное значение ключа для именования разделяемого ресурса. Однако, возможна ситуация, когда к моменту запуска такой программы в системе уже существует разделяемый ресурс с таким значением ключа, и в виду того, что ключи должны быть уникальными во всей системе, попытка породить второй ресурс с таким же ключом закончится неудачей (подробнее этот момент будет рассмотрен ниже).
Для решения этой задачи служит функция ftok():
#include <sys/types.h>
#include<sys/ipc.h>
key_t ftok (char *filename, char proj)
Эта функция генерирует значение ключа по некоторой строке символов и добавочному символу, передаваемым в качестве параметров. Гарантируется, что полученное таким образом значение будет отличаться от всех других значений, сгенерированных функцией ftok() с другими значениями параметров, и в то же время, при повторном запуске ftok() с теми же параметрами, будет получено то же самое значение ключа.
Смысл второго аргумента функции ftok() добавочного символа в том, что он позволяет генерировать разные значения ключа по одному и тому же значению первого параметра строки. Это позволяет программисту поддерживать несколько версий своей программы, которые будут использовать одну и ту же строку, но разные добавочные символы для генерации ключа, и тем самым получат возможность в рамках одной системы работать с разными разделяемыми ресурсами.
Следует заметить, что функция ftok() не является системным вызовом, а предоставляется библиотекой.
Для создания разделяемого ресурса с заданным ключом, либо подключения к уже существующему ресурсу с таким ключом используются ряд системных вызовов, имеющих общий суффикс get. Общими параметрами для всех этих вызовов являются ключ и флаги. В качестве значения ключа при создании любого IPC-объекта может быть указано значение IPC_PRIVATE. При этом создается ресурс, который будет доступен только породившему его процессу. Такие ресурсы обычно порождаются родительским процессом, который затем сохраняет полученный дескриптор в некоторой переменной и порождает своих потомков. Т.к. потомкам доступен уже готовый дескриптор созданного объекта, они могут непосредственно работать с ним, не обращаясь предварительно к «get»-методу. Таким образом, созданный ресурс может совместно использоваться родительским и порожденными процессами. Однако, важно понимать, что если один из этих процессов повторно вызовет «get»-метод с ключом IPC_PRIVATE, в результате будет получен другой, совершенно новый разделяемый ресурс, т.к. при обращении к «get»-методу с ключом IPC_PRIVATE всякий раз создается новый объект нужного типа.
Если при обращении к «get»-методу указан ключ, отличный от IPC_PRIVATE, происходит следующее:
· Происходит поиск объекта с заданным ключом среди уже существующих объектов нужного типа. Если объект с указанным ключом не найден, и среди флагов указан флаг IPC_CREAT, будет создан новый объект. При этом значение параметра флагов должно содержать побитовое сложение флага IPC_CREAT и константы, указывающей права доступа для вновь создаваемого объекта.
· Если объект с заданным ключом не найден, и среди переданных флагов отсутствует флаг IPC_CREAT, «get»-метод вернет 1, а в переменной errno будет установлено значение ENOENT
· Если объект с заданным ключом найден среди существующих, «get»-метод вернет дескриптор для этого существующего объекта, т.е. фактически, в этом случае происходит подключение к уже существующему объекту по заданному ключу. Если процесс ожидал создания нового объекта по указанному ключу, то для него такое поведение может оказаться нежелательным, т.к. это будет означать, что в результате случайного совпадения ключей (например, если процесс не использовал функцию ftok()) он подключился к чужому ресурсу. Чтобы избежать такой ситуации, следует указать в параметре флагов наряду с флагом IPC_CREAT и правами доступа еще и флаг IPC_EXCL в этом случае «get»-метод вернет -1, если объект с таким ключом уже существует (переменная errno будет установлена в значение EEXIST)
Следует отметить, что при подключении к уже существующему объекту дополнительно проверяются права доступа к нему. В случае, если процесс, запросивший доступ к объекту, не имеет на то прав, «get»-метод вернет 1, а в переменной errno будет установлено значение EACCESS
Нужно заметить, что для каждого типа объектов IPC существует некое ограничение на максимально возможное количество одновременно существующих в системе объектов данного типа. Если при попытке создания нового объекта окажется, что указанное ограничение превышено, «get»-метод, совершавший попытку создания объекта, вернет -1, а в переменной errno будет указано значение ENOSPC.
Отметим, что даже если ни один процесс не подключен к разделяемому ресурсу, система не удаляет его автоматически. Удаление объектов IPC является обязанностью одного из работающих с ним процессов и для этого определена специальная функция. Для этого системой предоставляются соответствующие функции по управлению объектами System V IPC.
Очередь сообщений представляет собой некое хранилище типизированных сообщений, организованное по принципу FIFO. Любой процесс может помещать новые сообщения в очередь и извлекать из очереди имеющиеся там сообщения. Каждое сообщение имеет тип, представляющий собой некоторое целое число. Благодаря наличию типов сообщений, очередь можно интерпретировать двояко рассматривать ее либо как сквозную очередь неразличимых по типу сообщений, либо как некоторое объединение подочередей, каждая из которых содержит элементы определенного типа. Извлечение сообщений из очереди происходит согласно принципу FIFO в порядке их записи, однако процесс-получатель может указать, из какой подочереди он хочет извлечь сообщение, или, иначе говоря, сообщение какого типа он желает получить в этом случае из очереди будет извлечено самое «старое» сообщение нужного типа.
Системный вызов msgget()
Для создания новой или для доступа к существующей используется системный вызов msgget:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
int msgget (key_t key, int msgflag)
В случае успеха вызов возвращает положительный дескриптор очереди, который может в дальнейшем использоваться для операций с ней, в случае неудачи -1. Первым аргументом вызова является ключ, вторым флаги, управляющие поведением вызова.
Функция msgsnd()
Для отправки сообщения используется функция msgsnd():
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg)
Ее первый аргумент идентификатор очереди, полученный в результате вызова msgget(). Второй аргумент указатель на буфер, содержащий реальные данные и тип сообщения, подлежащего посылке в очередь, в третьем аргументе указывается размер буфера.
В качестве буфера необходимо указывать структуру, содержащую следующие поля (в указанном порядке):
long msgtype тип сообщения
char msgtext[ ] данные (тело сообщения)
В заголовочном файле <sys/msg.h> определена константа MSGMAX, описывающая максимальный размер тела сообщения. При попытке отправить сообщение, у которого число элементов в массиве msgtext превышает это значение, системный вызов вернет 1.
Четвертый аргумент данного вызова может принимать значения 0 или IPC_NOWAIT. В случае отсутствия флага IPC_NOWAIT вызывающий процесс будет блокирован (т.е. приостановит работу), если для посылки сообщения недостаточно системных ресурсов, т.е. если полная длина сообщений в очереди будет больше максимально допустимого. Если же флаг IPC_NOWAIT будет установлен, то в такой ситуации выход из вызова произойдет немедленно, и возвращаемое значение будет равно 1.
В случае удачной записи возвращаемое значение вызова равно 0.
Функция msgrcv()
Для получения сообщения имеется функция msgrcv:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
msqid идентификатор очереди
msgp указатель на буфер
msgsz размер буфера
msgtyp - тип сообщения, которое процесс желает получить
= 0 любого типа
> 0 типа msgtyp
< 0 наименьшее значение среди типов, которые меньше модуля msgtyp
msgflg побитовое сложение флагов
IPC_NOWAIT если сообщения в очереди нет, то возврат 1
MSG_NOERROR разрешение получать сообщение, даже если его длина превышает емкость буфера
В случае успеха возвращается 0
Функция msgctl()
Функция управления очередью сообщений выглядит следующим образом:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl (int msqid, int cmd, struct msgid_ds *buf)
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с очередью и уничтожения очереди. Ее аргументы идентификатор ресурса, команда, которую необходимо выполнить, и структура, описывающая управляющие параметры очереди. Тип msgid_ds описан в заголовочном файле <sys/message.h>, и представляет собой структуру, в полях которой хранятся права доступа к очереди, статистика обращений к очереди, ее размер и т.п.
Возможные значения аргумента cmd:
IPC_STAT скопировать структуру, описывающую управляющие параметры очереди по адресу, указанному в параметре buf
IPC_SET заменить структуру, описывающую управляющие параметры очереди, на структуру, находящуюся по адресу, указанному в параметре buf
IPC_RMID удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
В случае успеха возвращается 0.
Пример. Использование очереди сообщений.
Основной процесс читает некоторую текстовую строку из стандартного ввода, и в случае, если строка начинается с буквы 'a', эта строка в качестве сообщения будет передана процессу А, если 'b' - процессу В, если 'q' - то процессам А и В, затем будет осуществлен выход. Процессы А и В распечатывают полученные строки на стандартный вывод.
Основной процесс.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
struct {
long mtype; /* тип сообщения */
char Data[256]; /* сообщение */
} Message;
int main(int argc, char **argv)
{ key_t key; int msgid; char str[256];
key = ftok("/usr/mash",'s');
/*получаем уникальный ключ, однозначно определяющий доступ к ресурсу */
msgid=msgget(key, 0666 | IPC_CREAT);
/*создаем очередь сообщений , 0666 определяет права доступа */
for(;;) {
/* запускаем вечный цикл */
gets(str); /* читаем из стандартного ввода строку */
strcpy(Message.Data, str);
/* и копируем ее в буфер сообщения */
switch(str[0]){
case 'a':
case 'A':
Message.mtype = 1;
/* устанавливаем тип */
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
/* посылаем сообщение в очередь */
break;
case 'b':
case 'B':
Message.mtype = 2;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
break;
case 'q':
case 'Q':
Message.mtype = 1;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
Message.mtype = 2;
msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str) + 1, 0);
sleep(10);
/* ждем получения сообщений процессами А и В */
msgctl(msgid, IPC_RMID, NULL);
/* уничтожаем очередь*/
return 0;
default:
break;
}
}
}
Процесс-приемник А
/* процесс В аналогичен с точностью до четвертого параметра в msgrcv */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
struct {
long mtype;
char Data[256];
} Message;
int main(int argc, char **argv)
{
key_t key; int msgid;
key = ftok("/usr/mash",'s');
/* получаем ключ по тем же параметрам */
msgid = msgget(key, 0666 | IPC_CREAT);
/*подключаемся к очереди сообщений */
for(;;) {
/* запускаем вечный цикл */
msgrcv(msgid, (struct msgbuf*) (&Message), 256, 1, 0);
/* читаем сообщение с типом 1*/
if (Message.Data[0]=='q' || Message.Data[0]=='Q') break;
printf("\nПроцесс-приемник А: %s", Message.Data);
}
return 0;
}
Благодаря наличию типизации сообщений, очередь сообщений предоставляет возможность мультиплексировать сообщения от различных процессов, при этом каждая пара взаимодействующих через очередь процессов может использовать свой тип сообщений, и таким образом, их данные не будут смешиваться.
Пример. Очередь сообщений. Модель «клиент-сервер».
Рассмотрим еще один пример - пусть существует процесс-сервер и несколько процессов-клиентов. Все они могут обмениваться данными, используя одну очередь сообщений. Для этого сообщениям, направляемым от клиента к серверу, присваиваем значение типа 1. При этом процесс, отправивший сообщение, в его теле передает некоторую информацию, позволяющую его однозначно идентифицировать. Тогда сервер, отправляя сообщение конкретному процессу, в качестве его типа указывает эту информацию (например, PID процесса). Таким образом, сервер будет читать из очереди только сообщения типа 1, а клиенты сообщения с типами, равными идентификаторам их процессов.
server
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main(int argc, char **argv)
{ struct {
long mestype;
char mes [100];
} messageto;
struct {
long mestype;
long mes;
} messagefrom;
key_t key;
int mesid;
key = ftok("example",'r');
mesid = msgget (key, 0666 | IPC_CREAT);
while(1)
{
if (msgrcv(mesid, &messagefrom, sizeof(messagefrom), 1, 0) <= 0) continue;
messageto.mestype = messagefrom.mes;
strcpy( messageto.mes, "Message for client");
msgsnd (mesid, &messageto, sizeof(messageto), 0);
}
msgctl (mesid, IPC_RMID, 0);
return 0;
}
client
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
struct {
long mestype; /*описание структуры сообщения*/
long mes;
} messageto;
struct {
long mestype; /*описание структуры сообшения*/
char mes[100];
} messagefrom;
key_t key;
int mesid;
long pid = getpid();
key = ftok("example", 'r');
mesid = msgget(key, 0); /*присоединение к очереди сообщений*/
messageto.mestype = 1;
messageto.mes = pid;
msgsnd (mesid, &messageto, sizeof(messageto), 0); /* отправка */
while ( msgrcv (mesid, &messagefrom, sizeof(messagefrom), pid, 0) <= 0);
/*прием сообщения */
printf("%s\n", messagefrom.mes);
return 0;
}
Механизм разделяемой памяти позволяет нескольким процессам получить отображение некоторых страниц из своей виртуальной памяти на общую область физической памяти. Данные, находящиеся в этой области памяти, будут доступны для чтения и модификации всем процессам, подключившимся к данной области памяти.
Процесс, подключившийся к разделяемой памяти, может затем получить указатель на некоторый адрес в своем виртуальном адресном пространстве, соответствующий данной области разделяемой памяти. После этого он может работать с этой областью памяти аналогично тому, как если бы она была выделена динамически (например, путем обращения к malloc()), однако, как уже говорилось, разделяемая область памяти не уничтожается автоматически даже после того, как процесс, создавший или использовавший ее, перестанет с ней работать.
shmget()
Создание общей памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, int size, int shmemflg)
Аргументы этого вызова: key - ключ для доступа к разделяемой памяти; size задает размер области памяти, к которой процесс желает получить доступ. Если в результате вызова shmget() будет создана новая область разделяемой памяти, то ее размер будет соответствовать значению size. Если же процесс подключается к существующей области разделяемой памяти, то значение size должно быть не более ее размера, иначе вызов вернет 1. Заметим, что если процесс при подключении к существующей области разделяемой памяти указал в аргументе size значение, меньшее ее фактического размера, то впоследствии он сможет получить доступ только к первым size байтам этой области.
Третий параметр определяет флаги, управляющие поведением вызова.
В случае успешного завершения вызов возвращает положительное число дескриптор области памяти, в случае неудачи - -1.
shmat()
Доступ к разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
char *shmat(int shmid, char *shmaddr, int shmflg)
При помощи этого вызова процесс подсоединяет область разделяемой памяти, дескриптор которой указан в shmid, к своему виртуальному адресному пространству. После выполнения этой операции процесс сможет читать и модифицировать данные, находящиеся в области разделяемой памяти, адресуя ее как любую другую область в своем собственном виртуальном адресном пространстве.
В качестве второго аргумента процесс может указать виртуальный адрес в своем адресном пространстве, начиная с которого необходимо подсоединить разделяемую память. Чаще всего, однако, в качестве значения этого аргумента передается 0, что означает, что система сама может выбрать адрес начала разделяемой памяти. Передача конкретного адреса в этом параметре имеет смысл в том случае, если, к примеру, в разделяемую память записываются указатели на нее же (например, в ней хранится связанный список) в этой ситуации для того, чтобы использование этих указателей имело смысл и было корректным для всех процессов, подключенных к памяти, важно, чтобы во всех процессах адрес начала области разделяемой памяти совпадал.
Третий аргумент представляет собой комбинацию флагов. В качестве значения этого аргумента может быть указан флаг SHM_RDONLY, который указывает на то, что подсоединяемая область будет использоваться только для чтения.
Эта функция возвращает адрес, начиная с которого будет отображаться присоединяемая разделяемая память.
В случае неудачи вызов возвращает -1.
shmdt()
Открепление разделяемой памяти.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt(char *shmaddr)
Данный вызов позволяет отсоединить разделяемую память, ранее присоединенную посредством вызова shmat()
shmaddr - адрес прикрепленной к процессу памяти, который был получен при вызове shmat()
В случае успешного выполнения функция возвращает 0, в случае неудачи -1
shmctl()
Управление разделяемой памятью.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с областью разделяемой памяти, наложения и снятия блокировки на нее и ее уничтожения. Аргументы вызова дескриптор области памяти, команда, которую необходимо выполнить, и структура, описывающая управляющие параметры области памяти. Тип shmid_ds описан в заголовочном файле <sys/shm.h>, и представляет собой структуру, в полях которой хранятся права доступа к области памяти, ее размер, число процессов, подсоединенных к ней в данный момент, и статистика обращений к области памяти.
Возможные значения аргумента cmd:
IPC_STAT скопировать структуру, описывающую управляющие параметры области памяти по адресу, указанному в параметре buf
IPC_SET заменить структуру, описывающую управляющие параметры области памяти, на структуру, находящуюся по адресу, указанному в параметре buf. Выполнить эту операцию может процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя, при этом процесс может изменить только владельца области памяти и права доступа к ней.
IPC_RMID удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
SHM_LOCK, SHM_UNLOCK блокировать или разблокировать область памяти. Выполнить эту операцию может только процесс с правами привилегированного пользователя.
Пример. Работа с общей памятью в рамках одного процесса.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int putm(char *);
int waitprocess(void);
int main(int argc, char **argv)
{
key_t key;
int shmid;
char *shmaddr;
key = ftok(“/tmp/ter”,S);
shmid = shmget(key, 100, 0666|IPC_CREAT);
shmaddr = shmat(shmid, NULL, 0); /* подключение к памяти */
putm(shmaddr); /* работа с ресурсом */
waitprocess();
shmctl(shmid,IPC_RMID,NULL); /* уничтожение ресурса */
return 0;
}
В данном примере считается, что putm() и waitprocess() некие пользовательские функции, определенные в другом месте
Семафоры
Семафоры представляют собой одну из форм IPC и используются для синхронизации доступа нескольких процессов к разделяемым ресурсам, т.е. фактически они разрешают или запрещают процессу использование разделяемого ресурса. В начале излагалась идея использования такого механизма. Речь шла о том, что при наличии некоторого разделяемого ресурса , с которым один из процессов работает, необходимо блокировать доступ к нему других процессов. Для этого с ресурсом связывается некоторая переменная-счетчик, доступная для всех процессов. При этом считаем, что значение счетчика, равное 1 будет означать доступность ресурса, а значение, равное 0 его занятость. Далее работа организуется следующим образом: процесс, которому необходим доступ к файлу, проверяет значение счетчика, если оно равно 0, то он в цикле ожидает освобождения ресурса, если же оно равно 1, процесс устанавливает значение счетчика равным 0 и работает с ресурсом. После завершения работы необходимо открыть доступ к ресурсу другим процессам, поэтому снова сбрасывается значение счетчика на 1. В данном примере счетчик и есть семафор.
Семафор находится адресном пространстве ядра и все операции выполняются также в режиме ядра.
В System V IPC семафор представляет собой группу (вектор) счетчиков, значения которых могут быть произвольными в пределах, определенных системой (не только 0 и 1).
Схема использования семафоров
• С каждым разделяемым ресурсом связывается один семафор из набора
• Значение >0 ресурс свободен, <0 ресурс занят
• Перед обращением к ресурсу процесс уменьшает значение соответствующего семафора
• Закончив работу с ресурсом, процесс увеличивает значение семафора
• В случае реализации взаимного исключения используется двоичный семафор.
semget()
Для получения доступа (или его создания) к семафору используется системный вызов:
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semget (key_t key, int nsems, int semflag).
Первый параметр функции semget() - ключ, второй - количество семафоров (длина массива семафоров) и третий параметр - флаги. Через флаги можно определить права доступа и те операции, которые должны выполняться (открытие семафора, проверка, и т.д.). Функция semget() возвращает целочисленный идентификатор созданного разделяемого ресурса, либо -1, если ресурс не удалось создать.
semop()
C полученным идентификатором созданного объекта можно производить операции с семафором, для чего используется системный вызов semop():
int semop (int semid, struct sembuf *sem_op, size_t nops)
Первый аргумент идентификатор ресурса, второй аргумент является указателем на структуру, определяющую операции, которые необходимо произвести над семафором. Третий параметр - количество указателей на эту структуру, которые передаются функцией semop(). То есть операций может быть несколько и операционная система гарантирует их атомарное выполнение.
Структура sembuf имеет вид:
struct sembuf { short sem_num; /*номер семафора в векторе*/
short sem_op; /*производимая операция*/
short sem_flg; /*флаги операции*/
}
Поле операции интерпретируется следующим образом. Пусть значение семафора с номером sem_num равно sem_val. В этом случае, если значение операции не равно нулю, то оценивается значение суммы sem_val + sem_op. Если эта сумма больше либо равна нулю, то значение данного семафора устанавливается равным сумме предыдущего значения и кода операции, т.е. sem_val:= sem_val+sem_op. Если эта сумма меньше нуля, то действие процесса будет приостановлено до наступления одного из следующих событий:
1. Значение суммы sem_val + sem_op станет больше либо равно нулю.
2. Пришел какой-то сигнал. Значение semop в этом случае будет равно -1.
Если код операции semop равен нулю, то процесс будет ожидать обнуления семафора. Если мы обратились к функции semop с нулевым кодом операции, а к этому моменту значение семафора стало равным нулю, то никакого ожидания не происходит.
Рассмотрим третий параметр - флаги. Если третий параметр равен нулю, то это означает, что флаги не используются. Флагов имеется большое количество в т.ч. IPC_NOWAIT (при этом флаге во всех тех случаях, когда мы говорили, что процесс будет ожидать, он не будет ожидать).
semctl ()
Управление массивом семафоров
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl (int semid, int num, int cmd, union semun arg)
semid дескриптор массива семафоров
num индекс семафора в массиве
cmd операция
IPC_SET заменить управляющие наборы семафоров на те, которые указаны в arg.buf
IPC_RMID удалить массив семафоров и др.
arg управляющие параметры
Возвращает значение, соответствующее выполнявшейся операции (по умолчанию 0), в случае неудачи -1
union semun { int val; /* значение одного семафора */
struct semid_ds *buf; /* параметры массива семафоров в целом (количество, права доступа, статистика доступа)*/
ushort *array; /* массив значений семафоров */
}
Пример. Использование разделяемой памяти и семафоров.
Программа будет оперировать с разделяемой памятью.
1 процесс создает ресурсы “разделяемая память” и “семафоры”, далее он начинает принимать строки со стандартного ввода и записывает их в разделяемую память.
2 процесс читает строки из разделяемой памяти.
Таким образом мы имеем критический участок в момент, когда один процесс еще не дописал строку, а другой ее уже читает. Поэтому следует установить некоторые синхронизации и задержки.
1й процесс:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#define NMAX 256
int main(int argc, char **argv)
{
key_t key;
int semid, shmid;
struct sembuf sops;
char *shmaddr;
char str[NMAX];
key = ftok(“/usr/ter/exmpl”, S);
/* создаем уникальный ключ */
semid = semget(key, 1, 0666 | IPC_CREAT);
/* создаем один семафор с определенными правами доступа */
shmid = shmget(key, NMAX, 0666 | IPC_CREAT);
/* создаем разделяемую память на 256 элементов */
shmaddr = shmat(shmid, NULL, 0);
/* подключаемся к разделу памяти, в shaddr указатель на буфер с разделяемой памятью */
semctl(semid,0,SETVAL, (int) 0);
/* инициализируем семафор значением 0 */
sops.sem_num = 0;
sops.sem_flg = 0;
do { /* запуск цикла */
printf(“Введите строку:”);
if (fgets(str, NMAX, stdin) == NULL)
{
/* окончание ввода */
/* пишем признак завершения строку “Q” */
strcpy(str, “Q”);
}
/* в текущий момент семафор открыт для этого процесса */
strcpy(shmaddr, str); /* копируем строку в разд. память */
/* предоставляем второму процессу возможность войти */
sops.sem_op = 3; /* увеличение семафора на 3 */
semop(semid, &sops, 1);
/* ждем, пока семафор будет открыт для 1го процесса - для следующей итерации цикла */
sops.sem_op = 0; /* ожидание обнуления семафора */
semop(semid, &sops, 1);
} while (str[0] != Q);
/* в данный момент второй процесс уже дочитал из разделяемой памяти и отключился от нее можно ее удалять*/
shmdt(shmaddr) ; /* отключаемся от разделяемой памяти */
shmctl(shmid, IPC_RMID, NULL);
/* уничтожаем разделяемую память */
semctl(semid, 0, IPC_RMID, (int) 0);
/* уничтожаем семафор */
return 0;
}
2й процесс:
/* необходимо корректно определить существование ресурса, если он есть - подключиться */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#define NMAX 256
int main(int argc, char **argv)
{
key_t key;
int semid, shmid;
struct sembuf sops;
char *shmaddr;
char str[NMAX];
key = ftok(“/usr/ter/exmpl”,S);
/* создаем тот же самый ключ */
semid = semget(key, 1, 0666 | IPC_CREAT);
shmid = shmget(key, NMAX, 0666 | IPC_CREAT);
/* аналогично предыдущему процессу - инициализации ресурсов */
shmaddr = shmat(shmid, NULL, 0);
sops.sem_num = 0;
sops.sem_flg = 0;
/* запускаем цикл */
do {
printf(“Waiting… \n”); /* ожидание на семафоре */
sops.sem_op = -2;
/* будем ожидать, пока “значение семафора” + ”значение sem_op” не станет положительным, т.е. пока значение семафора не станет как минимум 3 (3-2=1 > 0) */
semop(semid, &sops, 1);
/* теперь значение семафора равно 1 */
strcpy(str, shmaddr); /* копируем строку из разд.памяти */
/*критическая секция - работа с разделяемой памятью - в этот момент первый процесс к разделяемой памяти доступа не имеет*/
if (str[0] == Q)
{
/*завершение работы - освобождаем разделяемую память */
shmdt(shmaddr);
}
/*после работы обнулим семафор*/
sops.sem_op=-1;
semop(semid, &sops, 1);
printf(“Read from shared memory: %s\n”, str);
} while (str[0] != Q);
return 0;
}
Данный пример демонстрирует два разных приема использования семафоров для синхронизации: первый процесс блокируется в ожидании обнуления семафора, т.е. для того, чтобы он мог войти в критическую секцию, значение семафора должно стать нулевым; второй процесс блокируется при попытке уменьшить значение семафора до отрицательной величины, для того, чтобы этот процесс мог войти в критическую секцию, значение семафора должно быть не менее 3. В данном примере, помимо взаимного исключения процессов, достигается строгая последовательность действий двух процессов: они получают доступ к критической секции строго по очереди.
Механизм сокетов
Средства межпроцессного взаимодействия ОС UNIX, представленные в системе IPC, решают проблему взаимодействия двух процессов, выполняющихся в рамках одной операционной системы. Однако, очевидно, их невозможно использовать, когда требуется организовать взаимодействие процессов в рамках сети. Это связано как с принятой системой именования, которая обеспечивает уникальность только в рамках данной системы, так и вообще с реализацией механизмов разделяемой памяти, очереди сообщений и семафоров, очевидно, что для удаленного взаимодействия они не годятся. Следовательно, возникает необходимость в каком-то дополнительном механизме, позволяющем общаться двум процессам в рамках сети. Однако если разработчики программ будут иметь два абсолютно разных подхода к реализации взаимодействия процессов, в зависимости от того, на одной машине они выполняются или на разных узлах сети, им, очевидно, придется во многих случаях создавать два принципиально разных куска кода, отвечающих за это взаимодействие. Понятно, что это неудобно и хотелось бы в связи с этим иметь некоторый унифицированный механизм, который в определенной степени позволял бы абстрагироваться от расположения процессов и давал бы возможность использования одних и тех же подходов для локального и нелокального взаимодействия. Кроме того, как только мы обращаемся к сетевому взаимодействию, встает проблема многообразия сетевых протоколов и их использования. Понятно, что было бы удобно иметь какой-нибудь общий интерфейс, позволяющий пользоваться услугами различных протоколов по выбору пользователя.
Обозначенные проблемы был призван решить механизм, впервые появившийся в Берклиевском UNIX BSD, начиная с версии 4.2, и названный сокетами (sockets). Ниже подробно рассматривается этот механизм.
Сокеты представляют собой в определенном смысле обобщение механизма каналов, но с учетом возможных особенностей, возникающих при работе в сети. Кроме того, они предоставляют по сравнению с каналами больше возможностей по передаче сообщений, например, могут поддерживать передачу экстренных сообщений вне общего потока данных. Общая схема работы с сокетами любого типа такова: каждый из взаимодействующих процессов должен на своей стороне создать и отконфигурировать сокет, после чего они должны осуществить соединение с использованием этой пары сокетов. По окончании взаимодействия сокеты уничтожаются.
Механизм сокетов чрезвычайно удобен при разработке взаимодействующих приложений, образующих систему «клиент-сервер». Клиент посылает серверу запросы на предоставление услуги, а сервер отвечает на эти запросы. Схема использования механизма сокетов для взаимодействия в рамках модели «клиент-сервер» такова. Процесс-сервер запрашивает у ОС сокет и, получив его, присваивает ему некоторое имя (адрес), которое предполагается заранее известным всем клиентам, которые захотят общаться с данным сервером. После этого сервер переходит в режим ожидания и обработки запросов от клиентов. Клиент, со своей стороны, тоже создает сокет и запрашивает соединение своего сокета с сокетом сервера, имеющим известное ему имя (адрес). После того, как соединение будет установлено, клиент и сервер могут обмениваться данными через соединенную пару сокетов. Ниже мы подробно рассмотрим функции, выполняющие все необходимые действия с сокетами, и напишем пример небольшой серверной и клиентской программы, использующих сокеты.
Сокеты подразделяются на несколько типов в зависимости от типа коммуникационного соединения, который они используют. Два основных типа коммуникационных соединений и, соответственно, сокетов представляет собой соединение с использованием виртуального канала и датаграммное соединение.
Соединение с использованием виртуального канала
Последовательный поток байтов, гарантирующий надежную доставку сообщений с сохранением порядка их следования. Данные начинают передаваться только после того, как виртуальный канал установлен, и канал не разрывается, пока все данные не будут переданы.
Примером соединения с установлением виртуального канала является механизм каналов в UNIX, аналогом такого соединения из реальной жизни также является телефонный разговор. Заметим, что границы сообщений при таком виде соединений не сохраняются, т.е. приложение, получающее данные, должно само определять, где заканчивается одно сообщение и начинается следующее. Такой тип соединения может также поддерживать передачу экстренных сообщений вне основного потока данных, если это возможно при использовании конкретного выбранного протокола.
Соединение с использованием виртуального канала соответствует протоколу TCP.
Датаграммное соединение используется для передачи отдельных пакетов, содержащих порции данных датаграмм. Для датаграмм не гарантируется доставка в том же порядке, в каком они были посланы. Вообще говоря, для них не гарантируется доставка вообще, надежность соединения в этом случае ниже, чем при установлении виртуального канала. Однако датаграммные соединения, как правило, более быстрые. Примером датаграммного соединения из реальной жизни может служить обычная почта: письма и посылки могут приходить адресату не в том порядке, в каком они были посланы, а некоторые из них могут и совсем пропадать.
Датаграммное соединение соответствует протоколу UDP.
Поскольку сокеты используются как для локального, так и для удаленного взаимодействия, встает вопрос о пространстве адресов сокетов.
Система сокетов позволяет по одним и тем же структурным последовательности системных вызовов организовать взаимодействие процессов как на отдельно взятом компьютере, так и в сети.
Поскольку сокеты могут использоваться как для локального, так и для удаленного взаимодействия, встает вопрос о пространстве адресов сокетов. При создании сокета указывается коммуникационный домен, к которому данный сокет будет принадлежать. Коммуникационный домен определяет форматы адресов и правила их интерпретации. Мы будем рассматривать два основных домена: для локального взаимодействия домен AF_UNIX и для взаимодействия в рамках сети домен AF_INET (префикс AF обозначает сокращение от address family семейство адресов). В домене AF_UNIX формат адреса это допустимое имя файла, в домене AF_INET адрес образуют имя хоста + номер порта.
Заметим, что фактически коммуникационный домен определяет также используемые семейства протоколов. Так, для домена AF_UNIX это будут внутренние протоколы ОС, для домена AF_INET протоколы семейства TCP/IP. BSD UNIX поддерживает также третий домен AF_NS, использующий протоколы удаленного взаимодействия Xerox NS, но мы его рассматривать не будем.
Функция создания сокета так и называется socket()
#include <sys/types.h>
#include <sys/socket.h>
int socket (int domain, int type, int protocol);
domain коммуникационный домен, к которому должен принадлежать создаваемый сокет
AF_UNIX
AF_INET
type тип соединения, которым будет пользоваться сокет (тип сокета)
SOCK_STREAM виртуальный канал
SOCK_DGRAM датаграммы
protocol протокол, который будет использоваться в рамках данного коммуникационного домена для создания соединения.
Если установить значение данного аргумента в 0, система автоматически выберет подходящий протокол.
Константы для протокола AF_INET:
IPPROTO_TCP обозначает протокол TCP (корректно при создании сокета типа SOCK_STREAM)
IPPROTO_UDP обозначает протокол UDP(корректно при создании сокета типа SOCK_DGRAM)
В случае успеха функция возвращает положительное целое число дескриптор сокета (аналог файлового дескриптора). В случае неудачи (например, при некорректном сочетании коммуникационного домена, типа сокета и протокола), функция возвращает 1.
Связывание
bind()
Связывание
#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd, struct sockaddr *myaddr, int addrlen);
sockfd дескриптор сокета
myaddr указатель на структуру, содержащую адрес сокета
Для домена AF_UNIX формат структуры описан в <sys/un.h>.
#include <sys/un.h>
struct sockaddr_un {
short sun_family; /* == AF_UNIX */
char sun_path[108];
};
Для домена AF_INET формат структуры описан в <netinet/in.h>
#include <netinet/in.h>
struct sockaddr_in {
short sin_family; /* == AF_INET */
u_short sin_port; /* port number */
struct in_addr sin_addr; /* host IP address */
char sin_zero[8]; /* not used */
};
addrlen последний аргумент функции задает реальный размер структуры, на которую указывает myaddr.
В случае успешного связывания bind возвращает 0, в случае ошибки -1.
Если мы имеем дело с локальными сокетами и адрес сокета представляет собой имя файла, то при выполнении функции bind система в качестве побочного эффекта создает файл с таким именем. Поэтому для успешного выполнения bind необходимо, чтобы такого файла не существовало к данному моменту. Это следует учитывать, если мы «зашиваем» в программу определенное имя и намерены запускать нашу программу несколько раз необходимо удалять этот файл перед связыванием. Также должно быть достаточно прав доступа, иначе может не установиться связь.
Различают сокеты с предварительным установлением соединения, когда до начала передачи данных устанавливаются адреса сокетов отправителя и получателя данных сокеты соединяются друг с другом и остаются соединенными до окончания обмена данными и сокеты без установления соединения, когда соединение до начала передачи данных не устанавливается, а адреса сокетов отправителя и получателя передаются с каждым сообщением. Если тип сокета виртуальный канал, то сокет должен устанавливать соединение, если же тип сокета датаграмма, то, как правило, это сокет без установления соединения, хотя последнее не является требованием
#include <sys/types.h>
#include <sys/socket.h>
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd дескриптор сокета
serv_addr указатель на структуру, содержащую адрес сокета, с которым производится соединение, в формате, который мы обсуждали выше
addrlen реальная длина структуры
В случае успешного связывания функция возвращает 0, в случае ошибки -1. Код ошибки заносится в errno.
Заметим, что в рамках модели «клиент-сервер» клиенту, вообще говоря, не важно, какой адрес будет назначен сокету, т.к. никакой процесс не будет пытаться непосредственно установить соединение с сокетом клиента. Поэтому клиент может не вызывать предварительно функцию bind, в этом случае при вызове connect система автоматически выберет приемлемые значения для локального адреса клиента. Однако сказанное справедливо только для взаимодействия в рамках домена AF_INET, в домене AF_UNIX клиентское приложение само должно позаботиться о связывании сокета.
Следующие два вызова используются сервером только в том случае, если используются сокеты с предварительным установлением соединения.
#include <sys/types.h>
#include <sys/socket.h>
int listen (int sockfd, int backlog);
sockfd дескриптор сокета
backlog максимальный размер очереди запросов на соединение.
В случае успешного связывания функция возвращает 0, в случае ошибки -1. Код ошибки заносится в errno.
Этот вызов используется процессом-сервером для того, чтобы сообщить системе о том, что он готов к обработке запросов на соединение, поступающих на данный сокет. До тех пор, пока процесс владелец сокета не вызовет listen, все запросы на соединение с данным сокетом будут возвращать ошибку.
ОС буферизует приходящие запросы на соединение, выстраивая их в очередь до тех пор, пока процесс не сможет их обработать. В случае если очередь запросов на соединение переполняется, поведение ОС зависит от того, какой протокол используется для соединения. Если конкретный протокол соединения не поддерживает возможность перепосылки (retransmission) данных, то соответствующий вызов connect вернет ошибку ECONNREFUSED. Если же перепосылка поддерживается (как, например, при использовании TCP), ОС просто выбрасывает пакет, содержащий запрос на соединение, как если бы она его не получала вовсе. При этом пакет будет присылаться повторно до тех пор, пока очередь запросов не уменьшится и попытка соединения не увенчается успехом, либо пока не произойдет тайм-аут, определенный для протокола. В последнем случае вызов connect завершится с ошибкой ETIMEDOUT. Это позволит клиенту отличить, был ли процесс-сервер слишком занят, либо он не функционировал. В большинстве систем максимальный допустимый размер очереди равен 5.
#include <sys/types.h>
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *addr, int *addrlen);
sockfd дескриптор сокета
addr указатель на структуру, в которой возвращается адрес клиентского сокета, с которым установлено соединение (если адрес клиента не интересует, передается NULL).
addrlen возвращается реальная длина этой структуры максимальный размер очереди запросов на соединение.
Возвращает дескриптор нового сокета, соединенного с сокетом клиентского процесса.
Этот вызов применяется сервером для удовлетворения поступившего клиентского запроса на соединение с сокетом, который сервер к тому моменту уже прослушивает (т.е. предварительно была вызвана функция listen). Accept извлекает первый запрос из очереди и устанавливает с ним соединение. Если к моменту вызова accept никаких запросов на соединение с данным сокетом еще не поступало, процесс, вызвавший accept, блокируется до поступления запросов. Когда запрос поступает и соединение устанавливается, accept возвращает дескриптор нового сокета, соединенного с сокетом клиентского процесса. Через этот новый сокет и осуществляется обмен данными, в то время как старый сокет продолжает обрабатывать другие поступающие запросы на соединение (напомним, что именно первоначально созданный сокет связан с адресом, известным клиентам, поэтому все клиенты могут слать запросы только на соединение с этим сокетом). Это позволяет процессу-серверу поддерживать несколько соединений одновременно. Обычно это реализуется путем порождения для каждого установленного соединения отдельного процесса-потомка, который занимается собственно обменом данными только с этим конкретным клиентом, в то время как процесс-родитель продолжает прослушивать первоначальный сокет и порождать новые соединения. Во втором параметре передается указатель на структуру, в которой возвращается адрес клиентского сокета, с которым установлено соединение, а в третьем параметре возвращается реальная длина этой структуры. Благодаря этому сервер всегда знает, куда ему в случае надобности следует послать ответное сообщение. Если адрес клиента нас не интересует, в качестве второго аргумента можно передать NULL.
Собственно для приема и передачи данных через сокет используются три пары функций.
#include <sys/types.h>
#include <sys/socket.h>
int send(int sockfd, const void *msg, int len, unsigned int flags);
int recv(int sockfd, void *buf, int len, unsigned int flags);
Аргументы функций:
sockfd дескриптор сокета, через который передаются данные
Для send:
len длина сообщения
Если сообщение слишком длинное для того протокола, который используется при соединении, оно не передается и вызов возвращает ошибку EMSGSIZE. Если же сокет окажется переполнен, т.е. в его буфере не хватит места, чтобы поместить туда сообщение, выполнение процесса блокируется до появления возможности поместить сообщение.
Для recv:
len его первоначальная длина буфера.
При использовании сокетов с установлением виртуального соединения границы сообщений не сохраняются, поэтому приложение, принимающее сообщения, может принимать данные совсем не теми же порциями, какими они были посланы. Вся работа по интерпретации сообщений возлагается на приложение.
Последний аргумент обеих функций flags может содержать комбинацию специальных опций. Нас будут интересовать две из них:
MSG_OOB - тот флаг сообщает ОС, что процесс хочет осуществить прием/передачу экстренных сообщений
MSG_PEEK - данный флаг может устанавливаться при вызове recv. При этом процесс получает возможность прочитать порцию данных, не удаляя ее из сокета, таким образом, что последующий вызов recv вновь вернет те же самые данные.
Функция send() возвращает количество переданных байт в случае успеха и -1 в случае неудачи. Код ошибки при этом устанавливается в errno.
В случае успеха функция recv() возвращает количество считанных байт, в случае неудачи 1.
Другая пара функций, которые могут использоваться при работе с сокетами с предварительно установленным соединением это обычные read() и write(), в качестве дескриптора которым передается дескриптор сокета.
В качестве параметра этим функциям передается дескриптор сокета
Пара функций, которая может быть использована как с сокетами с установлением соединения, так и с сокетами без установления соединения:
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
Первые 4 аргумента у них такие же, как и у рассмотренных выше. В последних двух в функцию sendto() должны быть переданы указатель на структуру, содержащую адрес получателя, и ее размер, а функция recvfrom() в них возвращает соответственно указатель на структуру с адресом отправителя и ее реальный размер. Перед вызовом recvfrom() параметр fromlen должен быть установлен равным первоначальному размеру структуры from. Здесь, как и в функции accept, если нас не интересует адрес отправителя, в качестве from можно передать NULL.
Если процесс закончил прием либо передачу данных, ему следует закрыть соединение. Это можно сделать с помощью функции shutdown.
#include <sys/types.h>
#include <sys/socket.h>
int shutdown (int sockfd, int mode);
mode - Целое число, которое определяет, какую режим закрытия соединения.
Если mode=0, сокет закрывается для чтения, при этом все дальнейшие попытки чтения будут возвращать end-of-file.
Если mode=1, то сокет закрывается для записи, и дальнейшие попытки передать данные вернут ошибку (-1).
Если mode=2, то сокет закрывается и для чтения, и для записи.
В принципе, для закрытия сокета можно было бы воспользоваться просто функцией close, но тут есть одно отличие. Если используемый для соединения протокол гарантирует доставку данных (тип сокета виртуальный канал), то вызов close будет блокирован до тех пор, пока система будет пытаться доставить все данные, находящиеся «в пути», в то время как вызов shutdown извещает систему о том, что эти данные уже не нужны и можно не предпринимать попыток их доставить.
В случае успеха функция возвращает 0, в случае неудачи 1
Мы рассмотрели все основные функции работы с сокетами. Обобщая изложенное, можно изобразить общую схему работы с сокетами с установлением соединения в следующем виде
Общая схема работы с сокетами без предварительного установления соединения проще
Ниже приведена небольшая программа, которая в зависимости от параметра командной строки исполняет роль клиента или сервера. Клиент и сервер устанавливают соединение с использованием датаграммных сокетов. Клиент читает строку со стандартного ввода и пересылает серверу; сервер посылает ответ в зависимости от того, какова была строка. При введении строки «quit» и клиент, и сервер завершаются.
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <string.h>
#define SADDRESS "mysocket"
#define CADDRESS "clientsocket"
#define BUFLEN 40
int main(int argc, char **argv)
{ struct sockaddr_un party_addr, own_addr;
int sockfd;
int is_server;
char buf[BUFLEN];
int party_len;
int quitting;
if (argc != 2) {
printf("Usage: %s client|server.\n", argv[0]);
return 0;
}
quitting = 1;
/* определяем, кто мы: клиент или сервер*/
is_server = !strcmp(argv[1], "server");
memset(&own_addr, 0, sizeof(own_addr));
own_addr.sun_family = AF_UNIX;
strcpy(own_addr.sun_path, is_server ? SADDRESS : CADDRESS);
/* создаем сокет */
if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
{
printf("can't create socket\n");
return 0;
}
/* связываем сокет */
unlink(own_addr.sun_path);
if (bind(sockfd, (struct sockaddr *) &own_addr, sizeof(own_addr.sun_family)+ strlen(own_addr.sun_path)) < 0)
{
printf("can't bind socket!");
return 0;
}
if (!is_server)
{
/* это клиент */
memset(&party_addr, 0, sizeof(party_addr));
party_addr.sun_family = AF_UNIX;
strcpy(party_addr.sun_path, SADDRESS);
printf("type the string: ");
while (gets(buf)) {
/* не пора ли выходить? */
quitting = (!strcmp(buf, "quit"));
/* считали строку и передаем ее серверу */
if (sendto(sockfd, buf, strlen(buf) + 1, 0, (struct sockaddr *) &party_addr, sizeof(party_addr.sun_family) +
strlen(SADDRESS)) != strlen(buf) + 1)
{
printf("client: error writing socket!\n");
return 0;
}
/*получаем ответ и выводим его на печать*/
if (recvfrom(sockfd, buf, BUFLEN, 0, NULL, 0) < 0)
{
printf("client: error reading socket!\n");
return 0;
}
printf("client: server answered: %s\n", buf);
if (quitting) break;
printf("type the string: ");
} // while
close(sockfd);
return 0;
} // if (!is_server)
/* это сервер */
while (1)
{
/* получаем строку от клиента и выводим на печать */
party_len = sizeof(party_addr);
if (recvfrom(sockfd, buf, BUFLEN, 0,(struct sockaddr *) &party_addr, &party_len) < 0)
{
printf("server: error reading socket!");
return 0;
}
printf("server: received from client: %s \n", buf);
/* не пора ли выходить? */
quitting = (!strcmp(buf, "quit"));
if (quitting)
strcpy(buf, "quitting now!");
else
if (!strcmp(buf, "ping!"))
strcpy(buf, "pong!");
else
strcpy(buf, "wrong string!");
/* посылаем ответ */
if (sendto(sockfd, buf, strlen(buf) + 1, 0, (struct sockaddr *) &party_addr, party_len) != strlen(buf)+1)
{
printf("server: error writing socket!\n");
return 0;
}
if (quitting) break;
} // while
close(sockfd);
return 0;
}
В качестве примера работы с сокетами в домене AF_INET напишем простенький web-сервер, который будет понимать только одну команду :
GET /<имя файла>
Сервер запрашивает у системы сокет, связывает его с адресом, считающимся известным, и начинает принимать клиентские запросы. Для обработки каждого запроса порождается отдельный потомок, в то время как родительский процесс продолжает прослушивать сокет. Потомок разбирает текст запроса и отсылает клиенту либо содержимое требуемого файла, либо диагностику (“плохой запрос” или “файл не найден”).
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#define PORTNUM 8080
#define BACKLOG 5
#define BUFLEN 80
#define FNFSTR "404 Error File Not Found "
#define BRSTR "Bad Request "
int main(int argc, char **argv)
{
struct sockaddr_in own_addr, party_addr;
int sockfd, newsockfd, filefd;
int party_len;
char buf[BUFLEN];
int len;
int i;
/* создаем сокет */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("can't create socket\n");
return 0;
}
/* связываем сокет */
memset(&own_addr, 0, sizeof(own_addr));
own_addr.sin_family = AF_INET;
own_addr.sin_addr.s_addr = INADDR_ANY;
own_addr.sin_port = htons(PORTNUM);
if (bind(sockfd, (struct sockaddr *) &own_addr,
sizeof(own_addr)) < 0)
{
printf("can't bind socket!");
return 0;
}
/* начинаем обработку запросов на соединение */
if (listen(sockfd, BACKLOG) < 0)
{
printf("can't listen socket!");
return 0;
}
while (1) {
memset(&party_addr, 0, sizeof(party_addr));
party_len = sizeof(party_addr);
/* создаем соединение */
if ((newsockfd = accept(sockfd, (struct sockaddr *)&party_addr, &party_len)) < 0)
{
printf("error accepting connection!");
return 0;
}
if (!fork())
{
/*это сын, он обрабатывает запрос и посылает ответ*/
close(sockfd); /* этот сокет сыну не нужен */
if ((len = recv(newsockfd, &buf, BUFLEN, 0)) < 0)
{
printf("error reading socket!");
return 0;
}
/* разбираем текст запроса */
printf("received: %s \n", buf);
if (strncmp(buf, "GET /", 5))
{ /*плохой запрос!*/
if (send(newsockfd, BRSTR, strlen(BRSTR) + 1, 0) != strlen(BRSTR) + 1)
{
printf("error writing socket!");
return 0;
}
shutdown(newsockfd, 1);
close(newsockfd);
return 0;
}
for (i=5; buf[i] && (buf[i] > ' '); i++);
buf[i] = 0;
/* открываем файл */
if ((filefd = open(buf+5, O_RDONLY)) < 0)
{
/* нет файла! */
if (send(newsockfd, FNFSTR, strlen(FNFSTR) + 1, 0) != strlen(FNFSTR) + 1)
{
printf("error writing socket!");
return 0;
}
shutdown(newsockfd, 1);
close(newsockfd);
return 0;
}
/* читаем из файла порции данных и посылаем их клиенту */
while (len = read(filefd, &buf, BUFLEN))
if (send(newsockfd, buf, len, 0) < 0) {
printf("error writing socket!");
return 0;
}
close(filefd);
shutdown(newsockfd, 1);
close(newsockfd);
return 0;
}
/* процесс отец. Он закрывает новый сокет и
продолжает прослушивать старый */
close(newsockfd);
}
}
182
Аппаратные средства ЭВМ
Управление физическими ресурсами
Управление логическими ресурсами
Системы программирования
Прикладные системы
набор функциональных средств прикладной системы.
трансляторы языков высокого уровня, библиотеки...
интерфейсы драйверов виртуальных устройств.
интерфейсы драйверов физических ресурсов
Система команд, аппаратные интерфейсы программного управления физическими устройствами
адрес
ячейки
N-1
…
1
0
ТЕГ
(поле служебной
информации)
Регистровая память
(регистровый файл)
Регистры общего назначения (РОН)
ОС
Доступно
(выделено)
Выделено, но не используется
Реально
используется
...
Физическая
память:
…
…
…
…
…
Одна очередь
(Вариант Б)
N входных очередей
(Вариант А)
Таблица
страниц:
Физический
адрес:
offset
fp
Вирт. адрес:
offset
VP
hit
miss
...
физ. стр.
вирт. стр.
TLB
Vвирт.= 232
Смещение по странице, указанной через VP1
20
12
12
10
10
VP2
VP1
Offset
Offset
VP
Двухуровневая организация
Физический
адрес
base + offset
нет
Прерывание
да
offset > size
Таблица
сегментов
Nseg
base
size
offset
Индекс по «внешней» таблице страниц
Nseg
Завершение
Завершение
1
1
2
Обработка ЦП
Обработка ЦП
Ожидание
начала
обработки
Ожидание
начала
обработки
0
0
6
4
Очередь
на выполнение
Ожидания
операции в/в
3
5
2
БВП
БОП