- •Введение
- •1. Основные понятия системного программного обеспечения
- •1.1. Понятия прикладного и системного программного обеспечения
- •1.2. Состав системного программного обеспечения
- •2. Состав и архитектура операционных систем
- •2.1. Состав операционных систем
- •2.2. Архитектура ос
- •3. Процессы и потоки
- •3.1. Концепция процессов и потоков
- •3.2. Многозадачность. Формы программной работы
- •3.3. Подсистема управления процессами и потоками
- •3.4. Роль процессов, потоков и волокон в многозадачности
- •3.5. Создание процессов
- •3.6. Потоки и их модели
- •3.7. Планирование и синхронизация процессов и потоков
- •3.7.1. Виды планирования
- •3.7.2. Алгоритмы планирования потоков
- •3.7.3. Алгоритмы приоритетного планирования
- •3.7.4. Взаимоисключения
- •3.7.5. Семафоры
- •3.7.6. Тупики
- •4. Управление памятью
- •4.1. Функции ос по управлению памятью
- •4.2. Классификация методов распределения памяти
- •4.3. Распределение памяти без использования внешней памяти
- •4.4. Методы структуризации виртуальной памяти
- •4.4.1. Страничная организация виртуальной памяти
- •4.4.2. Сегментная организация виртуальной памяти
- •4.4.3. Странично-сегментная организация памяти
- •5. Файловые системы
- •5.1. Цели и задачи файловой системы
- •5.2. Организация файлов и доступ к ним
- •5.3. Логическая организация файла
- •5.4. Каталоговые системы
- •5.5. Основные возможности файловой системы ntfs
- •5.6. Структура тома с файловой системой ntfs
- •5.7. Возможности ntfs по ограничению доступа к файлам и каталогам
- •6. Управление вводом-выводом
- •6.1. Физическая организация устройств ввода-вывода
- •6.2. Организация программного обеспечения ввода-вывода
- •6.3. Обработка прерываний
- •6.4. Драйверы устройств
- •6.5. Независимый от устройств слой ос
- •6.6. Пользовательский слой программного обеспечения
- •7. Построение операционных систем
- •7.1. Принципы построения операционных систем
- •7.1.1. Принцип модульности
- •7.1.2. Принцип функциональной избирательности
- •7.1.3. Принцип генерируемости ос
- •7.1.4. Принцип функциональной избыточности
- •7.1.5. Принцип виртуализации
- •7.1.6. Принцип независимости программ от внешних устройств
- •7.1.7. Принцип совместимости
- •7.1.8. Принцип открытой и наращиваемой ос
- •7.1.9. Принцип мобильности
- •7.1.10. Принцип обеспечения безопасности вычислений
- •7.2. Построение интерфейсов операционных систем
- •7.3. Интерфейс прикладного программирования
- •7.3.1. Реализация функций api на уровне ос
- •7.3.2. Реализация функций api на уровне системы программирования
- •7.3.3. Реализация функций api с помощью внешних библиотек
- •7.4. Классификация системных вызовов
- •7.5. Интерфейс пользователя
- •7.6. Пользовательский интерфейс приложений
- •7.7. Архитектура, управляемая событиями
- •8. Семейство операционных систем unix
- •8.1. Основные понятия системы unix
- •8.1.1. Виртуальная машина
- •8.1.2. Пользователь
- •8.1.3. Интерфейс пользователя
- •8.1.4. Привилегированный пользователь
- •8.1.5. Команды
- •8.1.6. Процессы
- •8.1.7. Выполнение процессов
- •8.1.8. Структура файловой системы
- •8.2. Операционная система Linux
- •9.1.2. Определение компилятора. Отличие компилятора от транслятора
- •9.1.3. Определение интерпретатора. Разница между интерпретаторами и трансляторами
- •9.1.4. Этапы трансляции. Общая схема работы транслятора
- •9.1.5. Понятие прохода. Многопроходные и однопроходные компиляторы
- •9.2. Таблицы идентификаторов. Организация таблиц идентификаторов
- •9.2.1. Назначение таблиц идентификаторов
- •9.2.2. Принципы организации таблиц идентификаторов
- •9.2.3. Простейшие методы построения таблиц идентификаторов
- •9.2.4. Построение таблиц идентификаторов по методу бинарного дерева
- •9.2.8. Комбинированные способы построения таблиц идентификаторов
- •9.3. Лексические анализаторы
- •9.3.1. Назначение лексического анализатора
- •9.3.2. Принципы построения лексических анализаторов
- •9.3.3. Определение границ лексем
- •9.3.4. Выполнение действий, связанных с лексемами
- •9.4. Формальные языки и грамматики
- •9.4.1. Первичные понятия
- •9.4.2. Примеры, иллюстрирующие первичные понятия
- •9.4.3. Типы формальных языков и грамматик
- •9.4.3.1. Грамматики типа 0
- •9.4.3.2. Грамматики типа 1
- •9.4.3.3. Грамматики типа 2
- •9.4.3.4. Грамматики типа 3
- •9.4.3.5. Вывод в кс-грамматиках и правила построения дерева вывода
- •9.4.3.6. Синтаксический разбор
- •9.4.3.7. Левый и правый выводы
- •9.4.3.8. Неоднозначные и эквивалентные грамматики
- •9.4.4. Способы задания схем грамматик
- •9.4.4.1. Форма Наура-Бэкуса
- •9.4.4.2. Итерационная форма
- •9.4.4.3. Синтаксические диаграммы
- •9.4.5. Построение грамматик и грамматики, описывающие основные конструкции языков программирования
- •9.4.5.1. Рекомендации по построению грамматик
- •9.4.5.2. Описание списков
- •9.4.5.3. Пример построения грамматик
- •9.4.5.4. Грамматики, описывающие целые числа без знака и идентификаторы
- •9.4.5.5. Грамматики для арифметических выражений
- •9.4.5.6. Грамматика для описаний
- •9.4.5.7. Грамматика, задающая последовательность операторов присваивания
- •9.4.5.8. Грамматики, описывающие условные операторы и операторы цикла
- •9.4.5.9. Бесскобочные выражения
- •9.4.5.10. Префиксная польская запись
- •9.4.5.11. Вычисление префиксных польских записей
- •9.4.5.12. Постфиксная польская запись
- •9.4.5.13. Вычисление постфиксных записей
- •9.5. Конечные автоматы и регулярные грамматики
- •9.6. Макроязыки и макрогенерация
- •9.6.1. Определения макрокоманд и макрогенерации
- •9.6.2. Примеры макрокоманд
- •9.6.3. Макроязыки и препроцессоры
- •Заключение
- •Библиографический список
- •Оглавление
- •394026 Воронеж, Московский просп., 14
3.7.4. Взаимоисключения
В многозадачных однопроцессорных системах процессы чередуются, обеспечивая эффективное выполнение программ. В многопроцессорных системах возможно не только чередование, но и перекрытие процессов.
Способы взаимодействия процессов (потоков) можно классифицировать по степени осведомленности одного процесса о существовании другого.
1. Процессы не осведомлены о наличии друг друга (например, процессы разных заданий одного или различных пользователей). Это независимые процессы, не предназначенные для совместной работы. Хотя эти процессы и не работают совместно, ОС должна решать вопросы конкурентного использования ресурсов. Например, два независимых приложения могут затребовать доступ к одному и тому же диску или принтеру. ОС должна регулировать такие обращения.
2. Процессы косвенно осведомлены о наличии друг друга (например, процессы одного задания). Эти процессы не обязательно должны быть осведомлены о наличии друг друга с точностью до идентификатора процесса, однако они разделяют доступ к некоторому объекту, например буферу ввода-вывода, к файлу или БД. Такие процессы демонстрируют сотрудничество при разделении общего объекта.
3. Процессы непосредственно осведомлены о наличии друг друга (например, процессы, работающие последовательно или поочередно в рамках одного задания). Такие процессы способны общаться один с другим с использованием идентификаторов процессов и изначально созданы для совместной работы. Эти процессы также демонстрируют сотрудничество при работе.
Совместное использование общего ресурса различными задачами (или процессами) при отсутствии согласованности может привести к ошибкам функционирования. Эту задачу решают путем взаимоисключения, т.е. путем предоставления каждому процессу монопольного исключительного права доступа к разделяемым данным.
Взаимоисключение необходимо только в том случае, когда процессы обращаются к общим данным.
Если они выполняют операции, не приводящие к конфликтным ситуациям, они должны работать параллельно.
Когда процесс производит обращение к разделяемым данным, то говорят, что он находится на критическом участке. Таким образом, решение проблемы взаимоисключения в том, что если один процесс находится на своем критическом участке, необходимо исключить вхождение другого процесса на свой критический участок. Выполнение другого процесса продолжается, но без входа в критический участок (рис. 11). Если же это невозможно, процесс должен ожидать освобождения критических данных. Это одна из ключевых проблем параллельного программирования. Она решается программно, а в особо ответственных случаях аппаратно.
Рис. 11. Поведение процессов в критической области
Требования к критическому участку:
– в любой момент времени только один процесс может находиться внутри критического участка;
– ни один процесс не может оставаться внутри критического участка бесконечно долго;
– ни один процесс не должен ждать бесконечно долго входа в критический участок.
Для двух процессов задача решается алгоритмом Деккера. В этом алгоритме две задачи конкурируют за использование общего критического участка. Доступ к критическому участку предоставляется задачам попеременно. Данный алгоритм был усовершенствован Петерсоном.
shared int ready[2] = {0, 0};
shared int turn;
while (some condition)&&
{
ready [i] = 1;
turn =1- i;
while (ready [1-i]&& turn == 1-i);
critical section ready [i] = 0;
remainder section
}
При исполнении пролога критической секции процесс Pi заявляет о своей готовности исполнить критический участок и одновременно предлагает другому процессу приступить к его выполнению. Если оба процесса подошли к прологу практически одновременно, то они оба объявят о своей готовности и предложат выполняться друг другу. При этом одно из предложений всегда последует после другого. Тем самым работу в критическом участке продолжит процесс, которому было сделано последнее предложение.
Для трех или более процессов разработан так называемый «алгоритм булочной» (bakery algorithm). Основная его идея выглядит так. Каждый вновь прибывающий клиент (он же процесс) получает «талончик» на обслуживание с номером. Клиент с наименьшим номером на «талончике» обслуживается следующим. К сожалению, из-за специфики операции вычисления следующего номера алгоритм булочной не гарантирует, что у всех процессов будут «талончики» с разными номерами. В случае равенства номеров на «талончиках» у двух или более клиентов первым обслуживается клиент с меньшим значением имени (имена можно сравнивать в лексикографическом порядке).
Разделяемые структуры данных для алгоритма – это два массива:
shared enum {false, true} choosing[n];
shared int number[n];
Изначально элементы этих массивов инициируются значениями false и 0 соответственно. Введем следующие обозначения:
(a,b) < (c,d), если а < с или если а == с и b < d
max(a0, al,…,an) - это число k такое, что k >= ai для всех i = 0, ...,n.
Структура процесса Pi для алгоритма булочной приведена ниже
while (some condition):
{
choosing[i] = true;
number[i] = max (number[0],number[n-1]) + 1;
choosing[i] = false;
for(j = 0; j<n; j++)
{
while (choosing[i]);
while (number[j] != 0 && (number[j],j) < (number[i],i));
}
critical section
number[i ] = 0;
remainder section
}