Академический Документы
Профессиональный Документы
Культура Документы
Чтобы этого не допустить в ОС работают семафоры как для "производителя", так и для
"потребителя".
77) Критические секции.
Критическая секция (англ. critical section) — объект синхронизации потоков позволяющий
предотвратить одновременное выполнение некоторого набора операций (обычно связанных с
доступом к данным) несколькими потоками. Если вход был произведен, то ни одна другая нить
не имеет доступа к этой критической секции, выполнение будет приостановлено, а
возобновление произойдет, когда первая нить освободит критическую секцию.
78)Защита доступа к переменным
Существует ряд функций, которые разрешают работу с глобальными переменными без
синхронизации, потому что эти функции сами решают проблемы синхронизации. Это
следующие функции: InterlockedIncrement, InterlockedDecrement, InterlockedExchange,
InterlockedExchangeAdd и InterlockedCompareExchange. К примеру функция
InterlockedIncrement инкрементирует значение 32-х битной переменной.
79) синхронизация в MFC.
Библиотека MFC содержит специальные классы для синхронизации нитей (CMutex, CEvent,
CCriticalSection şi CSemaphore) Эти классы соответствуют объектам синхронизации WinAPI,
и являются производными класса CSyncObject. Для использования этих классов необходимо
обращаться к их методам и конструкторам Lock и Unlock. Другая модель использования этих
классов состоит в создании так называемых thread-safe. Класс thread-safe представляет собой
определенный ресурс. Вся работа с ресурсом реализуется в среде самого класса, который
содержит все необходимые методы. Класс спроектирован таким образом, что его методы
решают задачи синхронизации, а в рамках приложения он выглядит как обычный класс.
Функции Lock и Unlock могут быть использованы непосредственно, а в косвенном режиме через
функции CSingleLock и CmultiLock.
80) пример синхронизации в Windows.
#include <windows.h>
#include <iostream.h>
void main()
{
DWORD res;
// eliberăm obiectul
cout<<"Acum eliberăm obiectul\n"; cout.flush();
ReleaseMutex(mutex);
}
// închidem descriptorul
CloseHandle(mutex);
}
Для проверки модуля взаимного исключения необходимо запустить два экземпляра
приложения. Первый экземпляр занимает объект и освобождает его через 10 сек. Только после
этого объект занимает второй экземпляр.
81)использование критических секций в Windows.
В этом случае критические секции используются для доступа к данным в определенный
момент времени одной нити приложения, а остальные заблокированы. Например, существует
переменная m_pObject и несколько нитей, которые вызывают методы объекта m_pObject.
Предположим, что эта переменная может время от времени изменять свое значение. Фрагмент
кода:
// Firul #1
void Proc1()
{
if (m_pObject)
m_pObject->SomeMethod();
}
// Firul #2
void Proc2(IObject *pNewObject)
{
if (m_pObject)
delete m_pObject;
m_pObject = pNewObject;
}
В этом примере существует потенциальная опасность вызова m_pObject->SomeMethod()
после чего объект будет уничтожен с помощью delete m_pObject, потому что в многозадачных
ОС выполнение любой нити может быть прервано в самый неблагоприятны для нее момент, и
начнется выполнение другой нити.
Этого можно избежать следующим образом:
// Firul #1
void Proc1()
{
::EnterCriticalSection(&m_lockObject);
if (m_pObject)
m_pObject->SomeMethod();
::LeaveCriticalSection(&m_lockObject);
}
// Firul #2
void Proc2(IObject *pNewObject)
{
::EnterCriticalSection(&m_lockObject);
if (m_pObject)
delete m_pObject;
m_pObject = pNewObject;
::LeaveCriticalSection(&m_lockObject);
}
Во время выполнения нить номер один заблокирует критическую секцию, и вторая нить получет
к ней доступ только после выполнения первой нити.
82) Structura RTL_CRITICAL_SECTION
Определена следующим образом:
typedef struct _RTL_CRITICAL_SECTION
{
PRTL_CRITICAL_SECTION_DEBUG DebugInfo; // Folosit de sistemul de operare
LONG LockCount; // Contorul de utilizări
LONG RecursionCount; // Contorul accesării repetate din firul utilizatorului
HANDLE OwningThread; // ID firului utilizatorului (unic)
HANDLE LockSemaphore; // Obiectul nucleului folosit pentru aşteptare
ULONG_PTR SpinCount; // Numărul de cicluri goale înaintea apelării nucleului
}
RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
LockCount – инкрементируется при каждом вызове ::EnterCriticalSection() и
декрементируется при каждом вызове ::LeaveCriticalSection();
RecursionCount – хранится количество повторных вызовов ::EnterCriticalSection() из
одной и той же нити.
OwningThread – содержит 0 если критические секции свободны, или идентификатор
выполняемой нити.
83) Функции API для критических областей.
функции BOOL InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) и BOOL
InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD
dwSpinCount). Заполняют поля структуры lpCriticalSection. Псевдокод функции
RtlInitializeCriticalSection din ntdll.dll
VOID RtlInitializeCriticalSection(LPRTL_CRITICAL_SECTION pcs)
{
RtlInitializeCriticalSectionAndSpinCount(pcs, 0)
}
VOID RtlInitializeCriticalSectionAndSpinCount(LPRTL_CRITICAL_SECTION pcs,
DWORD dwSpinCount)
{
pcs->DebugInfo = NULL;
pcs->LockCount = -1;
pcs->RecursionCount = 0;
pcs->OwningThread = 0;
pcs->LockSemaphore = NULL;
pcs->SpinCount = dwSpinCount;
if (0x80000000 & dwSpinCount)
_CriticalSectionGetEvent(pcs);
}
Функция DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION
lpCriticalSection, DWORD dwSpinCount) устанавливает значение поля SpinCount и
возвращает их предыдущие значения.
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) освобождает
ресурсы занятые критической секцией.
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection), BOOL
TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) – разрешает доступ в
критическую секцию.
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) – освобождает
критическую секцию.
84) классы критических областей.
Для корректного использования критических секций используются классы:
class CLock
{
friend class CScopeLock;
CRITICAL_SECTION m_CS;
public:
void Init() { ::InitializeCriticalSection(&m_CS); }
void Term() { ::DeleteCriticalSection(&m_CS); }
class CScopeLock
{
LPCRITICAL_SECTION m_pCS;
public:
CScopeLock(LPCRITICAL_SECTION pCS) : m_pCS(pCS) { Lock(); }
CScopeLock(CLock& lock) : m_pCS(&lock.m_CS) { Lock(); }
~CScopeLock() { Unlock(); }
void Lock() { ::EnterCriticalSection(m_pCS); }
void Unlock() { ::LeaveCriticalSection(m_pCS); }
};
Классы Clock и CAutoLock используются как правило для синхронизации доступа переменных
классов, а CScopeLock предназначен для использования в процедурах.
85) Устранение повреждений критических секций
Устранение повреждений критических секций является весьма сложным процессом. Ошибки
критических секций бывают двух типов: ошибки реализации и архитектурные ошибки. Ошибки
реализации как правило легко обнаружить. Они возникают при вызове ::EnterCriticalSection() и
::LeaveCriticalSection()
void Proc()
{
m_lockObject.Lock();
if (!m_pObject)
return;
// ...
m_lockObject.Unlock();
}
Здесь пропущен вызов ::LeaveCriticalSection().
Из архитектурных ошибок наиболее распространенная роковое объятие (deadlock) когда две
нити пытаются одновременно получить доступ к двум и более критических секций.
void Proc1()
// Firul #1
{
::EnterCriticalSection(&m_lock1);
// ...
::EnterCriticalSection(&m_lock2);
// ...
::LeaveCriticalSection(&m_lock2);
// ...
::LeaveCriticalSection(&m_lock1);
}
// Firul #2
void Proc2()
{
::EnterCriticalSection(&m_lock2);
// ...
::EnterCriticalSection(&m_lock1);
// ...
::LeaveCriticalSection(&m_lock1);
// ...
::LeaveCriticalSection(&m_lock2);
}
Важно:
- Критические секции выполняются относительно быстро и не требуют много ресурсов от
системы;
- Для синхронизации доступа нескольких независимых переменных лучше использовать
несколько критических секций (а не одну для всех переменных);
- код критической секции должен быть сведен к минимум;
- не рекомендуется вызывать из критической секции «чужие» объекты.
86. Администрирование процессов предполагает управление и синхронизацию в рамках
некоторой операционной системы. Используемые механизмы основаны на реализации принципа
взаимного исключения.
Реализация взаимного исключения. Механизмы, реализующие взаимное исключение для
некоторого набора программ основаны на общем принципе: использование механизма
взаимоисключений действующего уже на нижнем уровне. В результате используются общие
переменные конкурирующих процессов, а связанность этих переменных должна быть
гарантированной. На базовом уровне существуют два элементарных механизма: взаимного
исключения доступа к одному участку памяти и маска прерываний. Это основные механизмы,
но на уровне физических ресурсов или микропрограмм существуют более сложные устройства,
например инструкции Test and test или семафоры.
Спецификация задачи. Файл {p1, p2,...,pn} – множество процессов, которые мы считаем циклами;
программа каждого процесса содержит критическую секцию. Взаимное исключение
застраховано двумя фрагментами (prolog,epilog), которые внедряют критическую секцию
каждого процесса. Предположим, что каждый процесс, который входит в критическую секцию,
покидает ее за конечный интервал времени.
Решения должны обладать следующими свойствами:
А) взаимное исключение – в каждый момент времени более одного процесса выполняют
критическую секцию (?????????)
Б)Снисходительность к неполадкам – решение должно оставаться действующим и в случае
некоторых ошибок в одном или более процессов, которые находятся вне критической секции.
В) процесс, который требует входа в критическую секцию не должен ожидать бесконечно.
Г) симметричность – prolog и epilog должны быть идентичными для всех процессов и
независимы от их количества.
89. Для обработки с помощью метода активного ожидания случая, в случае, когда несколько
процессов инициируют и обращаются к общим переменным, некоторые машины имеют
инструкцию, которая реализует способ неделимого обращения и актуализации некоторого
участка памяти. Эта инструкция называется Test And Set (tas), эта инструкция используется в
мультипроцессорных системах. В однопроцессорных системах маскировки прерывания
достаточно для страховки взаимоисключений.
90. Семафор – Элементарный инструмент для взаимного исключения. Определения.
Семафор s образован соединением некоторого счетчика с целочисленными значениями,
обозначенного s.c. и некоторой нити ожидания, обозначенной s.f. При создании семафора
счетчика i приписывается исходное значение s0 (s0>0) и нить ожидания является пустой.
Семафор обслуживает блокировку процессов, ожидающих создания условий для их
разблокировки. Заблокированные процессы помещаются в нить ожидания s.f. Один процессор
может оперировать только с помощью двух операций P(s) и V(s), называемых примитивами.
Значение счетчика и состояние нити ожидания недоступны даже для чтения. Существует
процесс p который выполняет P(s) или V(s), и процесс q, который находится в нити ожидания.
Алгоритм примитивов следующий: P(s): V(s):
s.c.:=s.c.-1; s.c.:=s.c.+1;
if s.c.<0 then if s.c.≤0 then
stare(p):=blocat; extragere(q,s.f.);
introducere(p,s.f.) stare(q):=activ
endif endif
blocat
(wait)
Fig.4.1. Stările unui
proces
Переходы (3) и (4) - внутренние переходы. Управляемые синхронизацией процессов.
Переходы (1) и (2) – «технологические» переходы на физическом уровне процессора.
Если предположить, что следующий выбранный процесс будет всегда первым из нити
процессов в состоянии ready, алгоритм выделения может быть определен:
- с помощью алгоритма включения в нить процессов ready;
- с помощью алгоритма, определяющего отказ физического процессора.
Множество программ, которые реализую эти алгоритмы называются планификатором
(scheduler).
Программа, которая реализует выбор называется диспетчером.
planificator
fire de procese
blocate
dispecer proces ales
firul proceselor eligibile
Fig.4.2. Fire de aşteptare ale proceselor, administrate de nucleul sistemului
de operare
94) Администрирование контекста и схемы примитивов
Операция выделения физического процесса сохраняет копию контекста. Эта копия сохраняет
состояние процессора. Каждому процессу сопоставляется резидентный участок памяти который
называется вектор состояния, блок управления процесса или блок контекста, который включает:
- информация о состоянии процессора;
- значения атрибутов процесса;
- указатели на выделенное пространство;
- управляющая информация.
Организация ядра.
Выполнение программ ядра определено в двух модулях:
- через выбор примитива управления процессом
- через прерывания
В обоих случаях механизм входа в ядро сохраняет слово состояния процессора и его регистры.
alocare procesor apelare supervizor
proces
programul
nucleului
periferice,
lansare ceasul întreruperi
Bloc Reg[i]
de
contex Stare[i]
t
Drepturi legături de
[i] înlănţuire în
context în
blocul FA
Prio[i] memorie
.. contextului
. Fig.4.4. Organizarea unui
i
FA de procese
98)Реализация мониторов.
А)семантика примитива semnalizare. Спецификация примитива c.semnalizare заключается во
временном переходе процесса, выполняющего примитив semnalizare в состояние блокировки.
Разблокированный процесс переходит в состояние ready и требует входа в монитор. Условие
провоцирующее разблокировку может быть изменено до того, как сам процесс разблокирован.
if continuare_non_posibilă then
c.aşteptare
endif
В этом случае:
while continuare_non_posibilă do
c.aşteptare
endwhile
intrare(M): ieşire(M):
prolog; prolog;
p:=<proces apelant>; p:=<proces apelant>;
cerere_disp(M, p); eliberare_disp(M);
alocare_procesor; intrare(p, f_eligibil);
alocare_procesor;
c.aşteptare: c.semnalizare:
prolog; prolog;
p:=<proces apelant>; p:=<proces apelant>;
intrare(p, M.c.fir); if non_vid(M.c.fir) then
stare[p]:=blocat; ieşire(q, M.c.fir);
eliberare_disp(M); cerere_disp(M, p);
alocare_procesor; cerere_disp(M, q);
eliberare_disp(M)
else
intrare(p, f_eligibil)
endif
alocare_procesor;
последовательность prolog обеспечивает сохранение контекста и входа в критическую секцию.
alocare_procesor обеспечивает выделение процессора и защиту критической секции.
100) обработка прерваний.
Для защиты информации механизмы синхронизации любого прерывания содержат:
Условие в некотором мониторе,
Циклический процесс, который реализует обработку прерываний.
Условие может быть сопоставлено единственному уровню прерывания. Вызов прерывания
запускает функцию сигнализации для определенного условия. Относительный приоритет
прерываний транслируется в приоритет процессов, которые его обрабатывают. Может
возникнуть ситуация, когда прерывание будет вызвано в тот момент, когда процесс,
обрабатывающий это прерывание, находится в состоянии выполнения и прерывание будет
утеряно. Чтобы этого не происходило используется специальный булев индикатор,
регистрирующий прерывания.
ciclu
test if nonM.c.într_sosită then
c.aşteptare; -- evitarea pierderii unei întreruperi
go to test
endif;
<tratarea întreruperii>
endciclu
<sosirea unei întreruperi asociate lui M.c>
M.c.într_sosită := true;
c.semnalizare;
101) Обработка ошибок.
Принцип обработки ошибок состоит в блокировке процесса, спровоцировавшего
возникновение ошибки, и отправки сообщения родительскому процессу. Для этого используется
специальная нить f_eroare. Ошибка, возникающая при выполнении некоторого процесса,
провоцирует отклонение, которое может быть обработано следующим образом:
prolog;
p:=<proces apelant>;
intrare(p, f_eroare);
<tratare specifică>;
stare[p]:=suspendat;
alocare_procesor;
Определяется новое состояние suspend, которое применяется к процессу, выполнение которого
было прервано прерыванием.
103) Создание и уничтожение процесса
Главная проблема динамического управления процессами это выделение контекста и описателя процесса. Для
этого используются два основных метода:
Для блоков контекста зарезервирован фиксированный номер месторасположения;
неиспользованное место определено специальным значением (nil) поля состояния;
Места, зарезервированные блокам контекста выделяются динамически в памяти; номера
выделяются процессам тоже в динамическом режиме, в специальной таблице номер процесса
сопоставляется адресу в памяти его блока контекста.
В обоих случаях используется процедура alocare_context(p), которая реализует выделение контекста и
возвращает номер p процесса. Номер созданного процесса возвращается в результате выполнения следующего
примитива:
creare(p, context iniţial):
prolog;
control; -- verificarea drepturilor
alocare_context(p);
if p ≠ nil then
iniţializare_context(i);
intrare(p, f_eligibil)
endif;
intrare(proces apelant, f_eligibil);
alocare_procesor; -- este întors p drept rezultat
исходный контекст определен создавшим его процессом: ему необходимо определить исходное состояния
регистров и слова состояния процессора, состояние рабочего пространства и атрибутов.
Для уничтожения процесса необходимо освободить ранее выделенные ресурсы. Этим управляет непосредственно
ядро. Уничтожение процесса, находящегося в критической секции может привести к блокировке. Критические
секции мониторов управляются непосредственно ядром.
Принцип примитива уничтожения:
distrugere (p):
prolog;
control; -- verificarea drepturilor
eliberare_context(p);
intrare(proces apelant, f_eligibil);
alocare_procesor;
Процедура eliberare_context(p) должна обеспечить освобождение ресурсов используемых уничтожаемым
процессом.
eliberare_context(p):
listă:=<lista firelor procesului p>;
restituire_bloc_context(p);
restituire_memorie(p);
for q∈ listă do
eliberare_context(q)
endfor;
----------------------------------------------------------------------------------------------------------