Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
16-19.doc
Скачиваний:
1
Добавлен:
29.08.2019
Размер:
84.99 Кб
Скачать

16 Использование семафоров для синхронизации

Семафор - объекты ядра, используются для учета ресурсов, содержат счетчик числа пользователей, поддерживает два 32-битных значения со знаком: одно определяет максимальное число ресурсов (контролируемое семафором), другое используется как счетчик текущего числа ресурсов.

Для семафоров определены следующие правила:

1. когда счетчик текущего числа ресурсов становится больше 0, семафор переходит в свободное состояние;

2. если этот счетчик равен 0, семафор занят;

3. система не допускает присвоения отрицательных значений счетчику текущего числа ресурсов;

4. счетчик текущего числа ресурсов не может быть больше максимального числа ресурсов.

Семафор создается вызовом CreateSemaphore:

HANDLE CreateSemaphore( PSECURITY_ATTRIBUTE psa, LONG lInitialCount, LONG lMaximumCount, PCTSTR pszName);

получить описатель существующего объекта “семафор”, можно вызвав OpenSemaphore:

HANDLE OpenSemaphore(DWORD fdwAccess, BOOL bInhentHandle, PCTSTR pszName);

Параметр lMaximumCount сообщает системе максимальное число ресурсов, обрабатываемое приложением. Поскольку это 32-битное значение со знаком, предельное число ресурсов может достигать 2 147 483 647. Параметр

UnitialCount указывает, сколько из этих ресурсов доступно изначально (на данный момент). Инициализации серверного процесса, клиентских запросов нет

CreateSemaphore так: HANDLE hSem = GreateSemaphore(NULL, 0, 5. NULL),

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

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

Если Wait-функция определяет, что счетчик текущего числа ресурсов равен 0 (семафор занят), система переводит вызывающий поток в состояние ожидания. Когда другой поток увеличит значение этого счетчика, система вспомнит о ждущем потоке и снова начнет выделять ему процессорное время (а он, захватив ресурс, уменьшит значение счетчика на 1).

Поток увеличивает значение счетчика текущего числа ресурсов, вызывая функцию ReleaseSemaphore:

BOOL ReleaseSemaphore(HANDLE hSem,LONG lReleaseCount,PLONQ plPreviousCount);

№ 17 Использование мъютексов для синхронизации

Объекты ядра “мьютексы” гарантируют потокам взаимоисключающий доступ к единственному ресурсу. Они содержат счетчик числа пользователей, счетчик рекурсии и переменную, в которой запоминается идентификатор потока. Мьютексы ведут себя точно так же, как и критические секции. Но критические секции(объектами пользовательского режима), а мьютексы — объектами ядра.Единственный объект- мьютекс позволяет синхронизировать доступ к ресурсу нескольких потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.

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

Для мьютексов определены следующие правила:

  1. если его идентификатор потока равен 0 (у самого потока не может быть такой идентификатор), мьютекс не захвачен ни одним из потоков и находится в свободном состоянии;

  2. если его идентификатор потока не равен 0, мьютекс захвачен одним из потоков и находится в занятом состоянии;

  3. в отличие от других объектов ядра мьютексы могут нарушать обычные правила, действующие в операционной системе.

Для использования объекта-мьютекса один из процессов должен сначала создать его вызовом CreateMutex:

HANDLE CreateMutex(PSECURITY_ATTRIBUTES psa, BOOL fInitialOwner, PCTSTR pszName);

Получить описатель существующего объекта “мьютекс”, вызвав OpenMutex:

HANDLE OpenMutex( DWORD fdwAccess, BOOL bInhentHandle,PCTSTR pszName),

Параметр fInitialOwner определяет начальное состояние мьютекса Если в нем передается FALSE, объект-мьютекс не принадлежит ни одному из потоков и поэтому находится в свободном состоянии. При этом его идентификатор потока и счетчик рекурсии равны 0. Если же в нем передается TRUE, идентификатор потока, принадлежащий мьютексу, приравнивается идентификатору вызывающего потока, а счетчик рекурсии получает значение 1. Поскольку теперь идентификатор потока отличен от 0, мьютекс изначально находится в занятом состоянии.

Поток получает доступ к разделяемому ресурсу, вызывая одну из Wait-функций и передавая ей описатель мьютекса, который охраняет этот ресурс. Wait-функция проверяет у мьютекса идентификатор потока: если его значение не равно 0, мьютекс свободен; в ином случае оно принимает значение идентификатора вызывающего потока, и этот поток остается планируемым.

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

Для мьютексов сделано одно исключение в правилах перехода объектов ядра из одного состояния в другое. Допустим, поток ждет освобождения занятого объекта-мьютекса. В этом случае поток обычно засыпает (переходит в состояние ожидания). Однако система проверяет, не совпадает ли идентификатор потока, пытающегося захватить мьютекс, с аналогичным идентификатором у мьютекса. Если они совпадают, система по-прежнему выделяет потоку процессорное время, хотя мьютекс все еще занят. Подобных особенностей в поведении нет ни у каких других объектов ядра в системе. Всякий раз, когда поток захватывает объект-мьютекс, счетчик рекурсии в этом объекте увеличивается на 1. Единственная ситуация, в которой значение счетчика рекурсии может быть больше 1, — поток захватывает один и тот же мьютекс несколько раз, пользуясь упомянутым исключением из общих правил. Когда ожидание мьютекса потоком успешно завершается, последний получает монопольный доступ к защищенному ресурсу. Все остальные потоки, пытающиеся обратиться к этому ресурсу, переходят в состояние ожидания. Когда поток, занимающий ресурс, заканчивает с ним работать, он должен освободить мьютекс вызовом функции ReleaseMutex:

BOOL ReleaseMutex(HANDLE hMutex);

Эта функция уменьшает счетчик рекурсии в объекте-мьютексе на 1. Если данный объект передавался во владение потоку неоднократно, поток обязан вызвать ReleaseMutex столько раз, сколько необходимо для обнуления счетчика рекурсии. Как только счетчик станет равен 0, переменная, хранящая идентификатор потока, тоже обнулится, и объект-мьютекс освободится. После этого система проверит, ожидают ли освобождения мьютекса какие-нибудь другие потоки. Если да, система “по-честному” выберет один из ждущих потоков и передаст ему во владение объект-мьютекс.

18 Организация памяти в Windows

Использование виртуальной памяти в приложениях

В Windows три механизма работы с памятью:

  1. виртуальная память — наиболее подходящая для операций с большими массивами объектов или структур;

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

  1. кучи — наиболее подходящие для работы с множеством малых объектов.

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

Виртуальное адресное пространство процесса

Каждому процессу выделяется собственное виртуальное адресное пространство. Для 32-разрядных процессов его размер составляет 4 Гб. 32-битный указатель может быть любым числом от 0х00000000 до 0xFFFFFFFF и перекрывает четырехгигабайтовый диапазон. Для 64-разрядных процессов размер адресного пространства равен 16 экзабайтам, поскольку 64-битный указатель может быть любым числом от 0х00000000 00000000 до 0xFFFFFFFF FFFFFFFF охватывая диапазон в 16 экзабайтов.

Можно удалить

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

В Windows 2000 память, принадлежащая собственно операционной системе, тоже скрыта от любого выполняемого потока. Иными словами, ни один поток не может случайно повредить ее данные. А в Windows 98 последнее, увы, не реализовано, и есть вероятность, что выполняемый поток, случайно получив доступ к данным операционной системы, тем самым нарушит ее нормальную работу. И все-таки в Windows 98, как и в Windows 2000, ни один поток не может получить доступ к памяти чужого процесса.

Адресное пространство процесса закрыто. Отсюда вытекает, что процесс. А в своем адресном пространстве может хранить какую-то структуру данных по адресу 0х12345678, и одновременно у процесса В по тому же адресу — но уже в его адресном пространстве — может находиться совершенно иная структура данных. Обращаясь к памяти по адресу 0х12345678, потоки, выполняемые в процессе А, получают доступ к структуре данных процесса А. Но, когда по тому же адресу обращаются потоки, выполняемые в процессе В, они получают доступ к структуре данных процесса В. Иначе говоря, потоки процесса А не могут обратиться к структуре данных в адресном пространстве процесса В, и наоборот.

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

Как адресное пространство разбивается на разделы

Виртуальное адресное пространство каждого процесса разбивается на разделы. Их размер и назначение в какой-то мере зависят от конкретного ядра Windows.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]