Виртуальное адресное пространство
Виртуальное адресное пространство - это системе адресации , используемая в современных операционных системах - в частности - при работе операционной системы с процессами (/потоками). ]
Такая система удобна по различным причинам - в том числе и потому, что позволяет изолировать "рабочие пространства" процессов друг от друга .
Очередной адрес, создаваемый процессом для идентификации области памяти, а которой процесс хранит некоторые данные называется логическим(виртуальным)адресом и относится именно к виртуальному адресному пространству.
Каждый раз, когда программа запускается внутри операционной системы - операционная система (ОС) создаёт хотя быодин новый процесс и новое виртуальное адресное пространство (ВАП) для него.
Виртуальное адресное пространство зависит от:
· архитектуры процессора;
· операционной системы (которая может накладывать дополнительные ограничения)
Виртуальное адресное пространство не зависит от:
· - объема реальной физической(оперативной) памяти, установленной в компьютере.
· - объема жёсткого диска
Адреса команд и переменных в готовой машинной программе, подготовленной к выполнению системой программирования, как раз и являются виртуальными адресами.
Прилагательное «виртуальное» применительно к адресному пространству означает, что это общее число доступных приложению уникально адресуемых ячеек памяти, но не общий объём памяти, установленной в компьютере, или выделенной в конкретный момент времени данному приложению -
в том числе -адреса в виртуальном пространстве не обязательно постоянно соответствуют одним и тем же адресам реальной физической памяти. Например - когда физической (оперативной) памяти не хватает, диспетчер памяти выгружает часть содержимого памяти на диск. При обращении потока по виртуальному адресу, соответствующему переписанным на диск данным, диспетчер памяти снова загружает эти данные с диска в память.
Виртуальное адресное пространство процесса.
Виртуальное адресное пространство процесса - это фактически всего лишь диапазон адресов, который может данный процесс использовать - при это, как было сказано выше ,
13) Критические области. Семафоры. Мьютексы. Мониторы
В некоторых операционных системах процессы, работающие совместно, могут сообща использовать некое общее хранилище данных.
Каждый из процессов может считывать из общего хранилища данных и записывать туда информацию.
Это хранилище представляет собой участок в основной памяти (возможно, в структуре данных ядра) или файлобщего доступа. Местоположение совместно используемой памяти не влияет на суть взаимодействия и возникающие проблемы.
Ситуации, в которых два (и более) процесса считывают или записывают данные одновременно и конечный результат зависит от того, какой из них был первым, называютсясостояниями состязания.
Отладка программы, в которой возможно состояние состязания, вряд ли может доставить удовольствие. Результаты большинства тестовых прогонов будут хорошими, но изредка будет происходить
Нечто странное и необъяснимое
Как избежать состязания?
Основным способом предотвращения проблем в этой и любой другой ситуации, связанной с совместным использованием памяти, файлов и чего-либо еще, является запрет одновременной записи и чтения разделенных данных более чем одним процессом.
Говоря иными словами, необходимо взаимное исключение.
Это означает, что в тот момент, когда один процесс использует разделенные данные, другому процессу это делать будет запрещено.
Некоторый промежуток времени процесс занят внутренними расчетами и другими задачами, не приводящими к состояниям состязания. В другие моменты времени процесс обращается к совместно используемым данным или выполняет какое-то другое действие, которое может привести к состязанию.
Часть программы, в которой есть обращение к совместно используемым данным, называется критической областью или критической секцией.
Если нам удастся избежать одновременного нахождения двух процессов в критических областях, мы сможем избежать состязаний.
Несмотря на то что это требование исключает состязание, его недостаточно для правильной совместной работы параллельных процессови эффективного использования общих данных. Для этого необходимо выполнение четырех условий:
· 1. Два процесса не должны одновременно находиться в критических областях.
· 2. В программе не должно бытьпредположенийо скорости или количестве процессоров.
· 3. Процесс, находящийся вне критической области, не может блокировать другие процессы.
· 4. Невозможна ситуация, в которой процесс вечно ждет попадания в критическую область.
Семафо́р — объект, позволяющий войти в заданный участок кода не более чем N потокам. Определение введено Эдсгером Дейкстрой
В 1965 году Дейкстра предложил использовать целую переменную для подсчета сигналов запуска, сохраненных на будущее.
Им был предложен новый тип переменных, так называемые семафоры, значение которых может быть нулем (в случае отсутствия сохраненных сигналов активизации) или некоторым положительным числом, соответствующим количеству отложенных сигналов активации.
Дейкстра предложил две операции,down и up (обобщения sleep и wakeup).
1) Операция down сравнивает значение семафора с нулем. Если значение семафора больше нуля, операция down уменьшает его(то есть расходует один из сохраненных сигналов активации)и просто возвращает управление. Если значение семафора равно нулю, процедура down не возвращает управление процессу, а процесс переводится в состояние ожидания. Все операции проверки значения семафора, его изменения и перевода процесса в состояние ожидания выполняютсякак единое и неделимое элементарное действие.Тем самым гарантируется, что после начала операции ни
один процесс не получит доступа к семафору до окончания или блокирования операции.Элементарность операции чрезвычайно важна для разрешения проблемы синхронизации и предотвращения состояния состязания.
2) Операция up увеличивает значение семафора. Если с этим семафором связаны один или несколько ожидающих процессов, которые не могут завершить более раннюю операцию down, один из них выбирается системой (например, случайным образом) и ему разрешается завершить свою операцию down. Таким образом, после
операции up, примененной к семафору, связанному с несколькими ожидающими процессами, значение семафора так и останется равным 0, но число ожидающих процессов уменьшится на единицу.Операция увеличения значения семафора и активизации процесса тоже неделима.
Ни один процесс не может быть блокирован во время выполнения операции up.
Иногда используется упрощенная версия семафора, называемаямьютексом (mutex, сокращение от mutual exclusion — взаимное исключение).
Мьютекс не способен считать, он может лишь управлять взаимным исключением доступа к совместно используемым ресурсам или кодам.
Реализация мьютекса проста и эффективна, что делает использование мьютексов особенно полезным в случае потоков, действующих только в пространстве пользователя.
Мьютекс — переменная, которая может находиться в одном из двух состояний: блокированном или неблокированном. Поэтому для описания мьютекса требуется всего один бит, хотя чаще используется целая переменная, у которой 0 означает неблокированное состояние, а все остальные значения соответствуют блокированному состоянию. Значение мьютекса устанавливается двумя процедурами. Если поток (или процесс) собирается войти в критическую область, он вызывает процедуру mutex_lock(). Если мьютекс не заблокирован (то есть вход в критическую область разрешен), запрос выполняется и вызывающий поток может попасть в критическую область.
Напротив, если мьютекс заблокирован, вызывающий поток блокируется до тех пор, пока другой поток, находящийся к критической области, не выйдет из нее, вызвав процедуру mutex_unlock.
Если мьютекс блокирует несколько потоков, то из них случайным образомвыбираетсяодин.
соответственно, операции изменения состояния мьютекса проводятся без прерываний - неделимо.
Чтобы упростить написание программ, в 1974 году Хоар (Ноаге) [155] и Бринч Хансен (Brinch Hansen) [43] предложилипримитив синхронизации более высокого уровня, называемый монитором.
Их предложения несколько отличались друг от друга, как мы увидим дальше.
Монитор — набор процедур, переменных и других структур данных, объединенных в особый модуль или пакет.
Процессы могут вызывать процедуры монитора, но у процедур, объявленных вне монитора, нет прямого доступа к внутренним структурам данных монитора.
Реализации взаимных исключений способствует важное свойство монитора: при обращении к монитору в любой момент времени активным может быть только один процесс.
Мониторы являются структурным компонентом языка программирования, поэтому компилятор знает, что обрабатывать вызовы процедур монитора следует иначе, чем вызовы остальных процедур.
Обычно при вызове процедуры монитора первые несколько команд процедуры проверяют, нет ли в мониторе активного процесса. Если активный процесс есть, вызывающему процессу придется подождать, в противном случае запрос удовлетворяется.
Реализация взаимного исключения зависит от компилятора, но обычно используется мьютекс или бинарный семафор. Поскольку взаимное исключение обеспечивает компилятор, а не программист, вероятность ошибки гораздо меньше. В любом случае программист, пишущий код монитора, не должен задумываться о том, как компилятор организует взаимное исключение.
Достаточно знать, что, обеспечив попадание в критические области через процедуры монитора, можно не
бояться попадания в критическую область двух процессов одновременно.
Хотя мониторы предоставляют простой способ реализации взаимного исключения, этого недостаточно. Необходим также способ блокировки процессов, которые не могут продолжать свою деятельность.В случае проблемы производителя и потребителя достаточно просто поместить все проверки буфера на заполненность
и пустоту в процедуры монитора, но как процесс заблокируется, обнаружив полный буфер?
Решение заключается во введении переменных состояния и двух операций, wait и signal. Когда процедура монитора обнаруживает, что она не в состоянии продолжать работу (например, производитель выясняет, что буфер заполнен), она выполняет операцию wait на какой-либо переменной состояния, скажем, full. Это приводит к блокировке вызывающего процесса и позволяет другому процессу войти в монитор.
Другой процесс, в нашем примере потребитель может активизировать ожидающего напарника, например, выполнив операцию signal на той переменной состояния, на которой он был заблокирован.
Чтобы в мониторе не оказалось двух активных процессов одновременно, нам необходимо правило, определяющее последствия операции signal. Хоар предложил запуск «разбуженного» процесса и остановку второго.
Бринч Хансен предложил другое решение:процесс, выполнивший signal, должен немедленно покинуть монитор. Иными словами, операция signal выполняется только в самом конце процедуры монитора. Мы будем использовать это решение, поскольку оно в принципепроще и к тому же легче в реализации.
Если операция signal выполнена на переменной, с которой связаны несколько заблокированных процессов, планировщик выбирает и «оживляет» только один из них. Кроме этого, существует третье решение, не основывающееся на предположениях Хоара и Бринча Хансена: позволить процессу, выполнившему signal, продолжать работу и запустить ждущий процесс только после того, как первый процесс покинет монитор.
Переменные состояния не являются счетчиками. В отличие от семафоров они не аккумулируют сигналы, чтобы впоследствии воспользоваться ими. Это означает, что в случае выполнения операции signal на переменной состояния, с которой не связано ни одного блокированного процесса, сигнал будет утерян. Проще говоря, операция wait должна выполняться прежде, чем signal. Это правило существенно упрощает реализацию. На практике это правило не создает проблем, поскольку отслеживать состояния процессов при необходимости не очень трудно.