Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Ядра и операционные системы реального времени



При­мем как очевидные следующие момен­ты.

1. Когда-то операционных систем со­всем не было.

2. Через некоторое время послеих появ­ления возникло направление ОС РВ.

3. Все ОС РВ являются многозадачны­ми операционными системами. Зада­чи делят между собой ресурсы вы­числительной системы, в том числе и процессорное время.

Четкой границы между ядром (Kernel) и операционной системой нет. Различают их, как правило, по набору функциональных возможностей. Ядра предоставляют пользователю такие базовые функции, как планирование и синхронизация задач, межзадачная коммуникация, управление памятью и т.п. Операционные системы в дополнение к этому имеют файловую систему, сетевую поддержку, интерфейс с оператором и другие средства высоко­го уровня.

По своей внутренней архитектуре ОС РВ можно условно разделить на монолитные ОС, ОС на основе микроядра и объектно-ориентированные ОС. Графически различия в этих подходах иллюстрируются рисунками 2.1, 2.2, 2.3. Пре­имущества и недостатки различных архитектур достаточно очевидны, поэто­му подробно мы на них останавливать­ся не будем.

 

Рис. 2.1. ОС РВ с монолитной архитектурой

 

Рис. 2.2. ОС РВ на основе микроядра

 

Рис. 2.3. Объектно-ориентированная ОС РВ

Задачи, процессы, потоки

Существуют различные оп­ределения термина «задача» для многозадачной ОС РВ. Мы будем считать задачей набор операций (машинных инструкций), предназначен­ный для выполнения логи­чески законченной функции системы. При этом задача конкурирует с другими задачами за получение контроля над ресурсами вычислительной системы.

Принято различать две разно­видности задач: процессы и пото­ки. Процесс представляет собой отдельно загружаемый программный модуль (файл), который, как правило, во время исполнения имеет в памяти свои независимые области для кода и данных. В отличие от этого потоки мо­гут пользоваться общими участками кода и данных в рамках единого про­граммного модуля.

Хорошим примером многопоточ­ной программы является редактор текста WORD, где в рамках одного приложения может одновременно происходить и набор текста, и провер­ка правописания.

Преимущества потоков.

1. Так как множество потоков способ­но размещаться внутри одного EXE-модуля, это позволяет экономить ре­сурсы как внешней, так и внутрен­ней памяти.

2. Использование потоками общей об­ласти памяти позволяет эффективно организовать межзадачный обмен сообщениями (достаточно передать указатель на сообщение). Процессы не имеют общей области памяти. По­этому ОС должна либо целиком скопировать сообщение из области па­мяти одной задачи в область памяти другой (что для больших сообщений весьма накладно), либо предусмот­реть специальные механизмы, кото­рые позволили бы одной задаче по­лучить доступ к сообщениюиз об­ласти памяти другой задачи.

3. Как правило, контекстпотоковменьше, чем контекст процессов, а значит, время переключения между задачами-потоками меньше, чем между задачами-процессами.

4. Так как все потоки, а иногда и само ядро РВ размещаются в одном ЕХЕ-модуле, значительно упрощается ис­пользование программ-отладчиков (debugger).

Недостатки потоков.

1. Как правило, потоки не могут быть подгружены динамически. Чтобы добавить новый поток, необходимо провести соответствующие измене­ния в исходных текстах и переком­пилировать приложение. Процессы, в отличие от потоков, подгружаемы, что позволяет динамически изме­нять функции системы в процессе ее работы. Кроме того, так как процес­сам соответствуют отдельные про­граммные модули, они могутбытьразработаны различными компани­ями, чем достигается дополнитель­ная гибкость и возможность исполь­зования ранее наработанного ПО.

2. То, что потоки имеют доступ к областям данных друг друга, может при­вести к ситуации, когда некорректно работающий поток способен испор­тить данные другого потока. В отли­чие от этого процессы защищены от взаимного влияния, а попытка запи­си в «не свою» память приводит, как правило, к возникновению специ­ального прерывания по обработке «исключительных ситуаций».

Реализация механизмов управления процессами и потоками, возможность их взаимного сосуществования и взаи­модействия определяются конкрет­ным ПО РВ.

 

Основные свойства задач

Как правило, вся важная, с точки зре­ния операционной системы, информа­ция о задаче хранится в унифициро­ванной структуре данных – управляющем блоке (Task Control Block, TCB). В блоке хранятся такие параметры, как имя и номер задачи, верхняя и нижняя границы стека, ссылка на очередь сооб­щений, статус задачи, приоритет и т. п.

Приоритет – это некое целое число, присваиваемое задаче и характеризую­щее ее важность по сравнению с други­ми задачами, выполняемыми в системе. Приоритет используется в основном планировщиком задач для определе­ния того, какая из готовых к работе за­дач должна получить управление. Раз­личают системы с динамической и ста­тической приоритетностью. В первом случае приоритет задач может менять­ся в процессе исполнения, в то время как во втором приоритет задач жестко задается на этапе разработки или во время начального конфигурирования системы.

Контекст задачи – это набор дан­ных, содержащий всю необходимую информацию для возобновления вы­полнения задачи с того места, где она была ранее прервана. Часто контекст хранится в TCB и включает в себя такие данные, как счетчик команд, указатель стека, регистры CPU и FPU и т. п. Плани­ровщик задач в случае необходимости сохраняет контекст текущей активной задачи и восстанавливает контекст за­дачи, назначенной к исполнению. Та­кое переключение контекстов и явля­ется, по сути, основным механизмом ОС РВ при переходе от выполнения од­ной задачи к выполнению другой.

Состояние (статус) задачи. С точ­ки зрения операционной системы, за­дача может находиться в нескольких состояниях. Число и название этих со­стояний различаются от одной ОС к другой. По-видимому, наибольшее чис­ло состояний задачи определено в язы­ке Ada. Тем не менее практически в лю­бой ОС РВ загруженная на выполнение задача может находиться, по крайней мере, в трех состояниях.

1. Активная задача – это задача, выпол­няемая системой в текущий момент времени.

2. Готовая задача – это задача, готовая к выполнению и ожидающая у плани­ровщика своей «очереди».

3. Блокированная задача – это задача, выполнение которой приостановле­но до наступления определенных со­бытий. Такими событиями могут быть освобождение необходимого задаче ресурса, поступление ожидае­мого сообщения, завершение интер­вала ожидания и т. п.

Пустая задача (Idle Task) – это зада­ча, запускаемая самой операционной системой в момент инициализации и выполняемая только тогда, когда в сис­теме нет других готовых для выполнения задач. Пустая задача запускается с самым низким приоритетом и, как пра­вило, представляет собой бесконечный цикл «ничего не делать». Наличие пус­той задачи предоставляет операцион­ной системе удобный механизм отра­ботки ситуаций, когда нет ни одной го­товой к выполнению задачи.

Многократный запуск задач. Как правило, многозадачные ОС позволя­ют запускать несколько копий одной и той же задачи. При этом для каждой та­кой копии создается свой TCB и выде­ляется своя область памяти. В целях экономии памяти может быть предус­мотрено совместное использование одного и того же исполняемого кода для всех запущенных копий. В этом случае программа должна обеспечивать повторную входимость (реентера­бельность). Кроме того, программа не должна использовать временные фай­лы с фиксированными именами и должна корректно осуществлять до­ступ к глобальным ресурсам.

Реентерабельность (повторная входимость) означает возможность без негативных последствий временно пре­рвать выполнение какой-либо функции или подпрограммы, а затем вызвать эту функцию или подпрограмму снова. Частным проявлением реентерабельности является рекурсия, когда тело подпрограммы содержит вызов самой себя.Классическим примером нереентерабельной системы является DOS, a типичной причиной нереентерабель­ности служит использование глобаль­ных переменных. Предположим, что у нас есть функция, реализующая низкоу­ровневую запись на диск, и пусть она использует глобальную переменную write_sector, которая устанавливается в соответствии с параметром, передавае­мым этой функции при вызове. Пред­положим теперь, что Задача А вызывает эту функцию с параметром 3, т.е. хо­чет записать данные в сектор номер 3. Допустим, что когда переменная write_sector уже равна 3, но сама запись еще не произведена, выполнение Зада­чи А прерывается и начинает выпол­няться Задача В, которая вызывает ту же функцию, но с аргументом 10. После то­го как запись в сектор номер 10 будет произведена, управление рано или поздно вернется к Задаче А, которая продолжит работу с того же места. Од­нако так как переменная write_sector имеет теперь значение 10, данные Зада­чи А, предназначавшиеся для сектора номер 3, будут вместо этого записаны в сектор номер 10. Из приведенного при­мера видно, что ошибки, связанные с нереентерабельностью, трудно обнару­жить, а последствия они могут вызвать самые катастрофические.

 

Планирование задач

Важной частью любой ОС РВ являет­ся планировщик задач. Несмотря на то, что в разных источниках он может на­зываться по-разному (диспетчер задач, супервизор и т. п.), его функции оста­ются теми же: определить, какая из за­дач должна выполняться в системе в каждый конкретный момент времени. Самым простым методом планирова­ния, не требующим никакого специ­ального ПО и планировщика как тако­вого, является использованиецикли­ческого алгоритма в стиле round robin:

 

void main (void)

{

for(;; ){

task0();

task1();

task2();

/* и т. д. */

}

}

Каждая «задача», представляющая со­бой отдельную подпрограмму, выпол­няется циклически. При этом надо придерживаться следующих правил:

1. Подпрограммы не должны содер­жать циклов ожидания в стиле

 

while (TRUE) {

if(switch_up()){

lamp_off();

break;

}

}

 

2. Подпрограммы должны выполнять свою работу как можно быстрее, что­бы дать возможность работать следу­ющей подпрограмме.

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

Можно отметить следующие преиму­щества циклического алгоритма.

1. Простота использования и прозрач­ность для понимания.

2. Если исключить из рассмотрения прерывания, система полностью де­терминирована. Задачи всегда вызы­ваются в одной и той же последова­тельности, что позволяет достаточно просто произвести анализ «наихуд­шего случая» и вычислить макси­мальную задержку.

3. Минимальные размеры кода и дан­ных. Кроме того, в отличие от алго­ритмов с вытеснением, для всех задач необходим только один стек.

4. Отсутствуют ошибки, обусловлен­ные «гонками».

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

Перейдем теперь к другому широко используемому алгоритму планирова­ния. Речь пойдет о режимеразделе­ния времени. Существуют различные реализации в рамках этого алгоритма, и некоторые западные специалисты даже различают такие, в общем-то идентичные для нас, понятия, как time-slicing (нарезание времени) и time-sharing (разделение времени). Как правило, алго­ритм реализуется следующим образом: каждой задаче отводится определен­ное количество квантов времени (обычно кратно 1 мс), в течение кото­рых задача может монопольно зани­мать процессорное время. После того как заданный интервал времени исте­кает, управление передается следую­щей готовой к выполнению задаче, имеющей наивысший приоритет. Та, в свою очередь, выполняется в течение отведенного для нее промежутка вре­мени, после чего все повторяется в сти­ле round robin. Легко заметить, что та­кой алгоритм работы может привести к определенным проблемам. Предста­вим, что в системе работают 7 задач, 3 из которых имеют высокий приоритет, а 4 – низкий. Низкоприоритетные за­дачи могут никогда не получить управ­ление, так как три высокоприоритет­ные задачи будут делить все процес­сорное время между собой. Единственную возможность для низкоприоритетных задач получить управление предоставляет ситуация, когда все высокоприоритетные задачи находятся в блокированном состоянии.

Для решения этой проблемы применяется прием, получивший название равнодоступность (fairness). При этом реализуется принцип адаптивной приоритетности, когда приоритет за­дачи, которая выполняется слишком долго, постепенно уменьшается, позво­ляя менее приоритетным задачам по­лучить свою долю процессорного времени. Равнодоступность применяется главным образом в многопользовательских системах и редко применяется в системах реального времени.

Кооперативная многозадачность– это еще один алгоритм переключе­ния задач, с которым широкие массы компьютерной общественности знакомы по oперационной системе Windows. Задача, получившая управление, выполняется до тех пор, пока она сама по своей инициативе не передаст уп­равление другой задаче. По сути это продолжение идеологии round robin, и нет нужды объяснять, почему алго­ритм кооперативной многозадачнос­ти в чистом виде мало применяется в системах реального времени.

Приоритетная многозадачность с вытеснением – это, по-видимому, наиболее часто используемый в ОС РВ принцип планирования. Основная идея состоит в том, что высокоприо­ритетная задача как только для нее появляется работа, немедленно прерыва­ет (вытесняет) низкоприоритетную. Другими словами, если какая-либо задача переходит в состояние готовнос­ти, она немедленно получает управле­ние, если текущая активная задача имеет более низкий приоритет. Такое «вытеснение» происходит, например, когда высокоприоритетная задача по­лучила ожидаемое сообщение, освободился запрошенный ею ресурс, про­изошло связанное с ней внешнее со­бытие, исчерпался заданный интервал времени и т. п.

Заканчивая рассмотрение основных принципов планирования задач, необ­ходимо отметить, что тема эта далеко не исчерпана. Диапазон систем реаль­ного времени весьма широк, начиная от полностью статических систем, где все задачи и их приоритеты заранее определены, до динамических систем, где набор выполняемых задач, их при­оритеты и даже алгоритмы планиро­вания могут меняться в процессе функционирования. Существуют, на­пример, системы, где каждая отдель­ная задача может участвовать в любом из трех алгоритмов планирования или их комбинации (вытеснение, разделе­ние времени, кооперативность).

В общем случае алгоритмы плани­рования должны соответствовать кри­териям оптимальности функциониро­вания системы. Однако, если для сис­тем «жесткого» реального времени та­кой критерий очевиден: «ВСЕГДА и ВСЁ делать вовремя», то для систем «мягкого» реального времени это мо­жет быть, например, минимальное «максимальное запаздывание» или средневзвешенная своевременность завершения операций. В зависимости от критериев оптимальности могут применяться алгоритмы планирова­ния задач, отличные от рассмотрен­ных. Например, может оказаться, что планировщик должен анализировать момент выдачи критичных по време­ни управляющих воздействий и запус­кать на выполнение ту задачу, которая отвечает за ближайшие из них (алгоритм earliest deadline first, EDF).

Необходимо отметить, что в одной вычислительной системе могут одно­временно сосуществовать задачи и «жесткого», и «мягкого» реального вре­мени, и что только одна из этих задач, обладающая наивысшим приорите­том, может быть по-настоящему де­терминированной.

Не стоит особо увлекаться приори­тетами. Если система нормально рабо­тает, когда все задачи имеют одинако­вый приоритет, то и слава Богу. Если нет, то можно присвоить высокий приоритет «критической» задаче и низкий приоритет всем остальным. Если у вас больше одной «критичес­кой» задачи, при недостаточном быстродействии системы имеет смысл рассмотреть многопроцессорную конфигурацию или, отказавшись от ПО РВ, перейти к простому цикличес­кому алгоритму.

Как правило, разработчики стара­ются свести свою систему реального времени к наиболее простым конфи­гурациям, характерным для систем «жесткого» реального времени, иногда даже в ущерб эффективности исполь­зования вычислительных ресурсов. Причина понятна: сложные динами­ческие системы весьма трудно анали­зировать и отлаживать, поэтому лучше заплатить за более мощный процес­сор, чем иметь в будущем проблемы из-за непредвиденного поведения системы. В связи с этим большинство существующих систем реального вре­мени представляют собой статичес­кие системы с фиксированными при­оритетами. Часто в системе реализует­ся несколько «режимов» работы, каж­дый из которых имеет свой набор вы­полняемых задач с заранее заданными приоритетами. Значительная часть особо ответственных систем по-преж­нему реализуется без применения коммерческих ОС РВ вообще.

 

Синхронизация задач

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

1. Функции, выполняемые различны­ми задачами, связаны друг с другом. Например, если одна задача подго­тавливает исходные данные для дру­гой, то последняя не выполняется до тех пор, пока не получит от первой задачи соответствующего сообще­ния. Одна из вариаций в этом случае – это когда задача при определенных условиях порождает одну или не­сколько новых задач.

2. Необходимо упорядочить доступ не­скольких задач к разделяемому ре­сурсу.

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

4. Необходима синхронизация задачи по времени. Диапазон различных ва­риантов в этом случае достаточно широк, от привязки момента выдачи какого-либо воздействия к точному астрономическому времени до про­стой задержки выполнения задачи на определенный интервал времени. Для решения этих вопросов в конеч­ном счете используются специаль­ные аппаратные средства, называе­мые таймером.

Давайте рассмотрим все четыре слу­чая более подробно.

Связанные задачи. Взаимное согласование задач с по­мощью сообщений является одним из важнейших принципов операционных систем реального времени. Способы реализации межзадачного обмена от­личаются большим разнообразием, что не в последнюю очередь приводит к обилию терминов в этой области. Можно встретить такие понятия, как сообщение (message), почтовый ящик (mail box), сигнал (signal), событие (event), прокси (proxy) и т. п. Если, чи­тая описание какой-либо ОС РВ, вы встретите уже знакомое название, не спешите делать выводы. Даже один и тот же термин может для разных ОС РВ обозначать разные вещи. Чтобы не за­путаться, мы будем в дальнейшем назы­вать сообщениями любой механизм яв­ной передачи информации от одной задачи к другой (такие объекты, как се­мафоры, можно отнести к механизму неявной передачи сообщений).

Объем информации, передаваемой в сообщениях, может меняться от 1 бита до всей свободной емкости памяти ва­шей системы. Во многих ОС РВ компо­ненты операционной системы, так же как и пользовательские задачи, способ­ны принимать и передавать сообщения. Сообщения могут быть асинхронными и синхронными. В первом случае до­ставка сообщений задаче производится после того, как она в плановом порядке получит управление, а во втором случае циркуляция сообщений оказывает не­посредственное влияние на планирова­ние задач. Например, задача, пославшая какое-либо сообщение, немедленно блокируется, если для продолжения ра­боты ей необходимо дождаться ответа, или если низкоприоритетная задача шлет высокоприоритетной задаче со­общение, которого последняя ожидает, то высокоприоритетная задача, если, конечно, используется приоритетная многозадачность с вытеснением, не­медленно получит управление.

Иногда сообщения передаются через отведенный для этого буфер опреде­ленного размера («почтовый ящик»). При этом, как правило, новое сообще­ние затирает старое, даже если послед­нее не было обработано.

Однако наиболее часто используется принцип, когда каждая задача имеет свою очередь сообщений, в конец ко­торой ставится всякое вновь получен­ное сообщение. Стандартный принцип обработки очереди сообщений по принципу «первым вошел, первым вы­шел» (FIFO) не всегда оптимально со­ответствует поставленной задаче. В некоторых ОС РВ предусматривается та­кая возможность, когда сообщение от высокоприоритетной задачи обраба­тывается в первую очередь (в этом слу­чае говорят, что сообщение наследует приоритет пославшей его задачи).

Иногда полезным оказывается не­посредственное управление приорите­том сообщений. Представим, что зада­ча послала серверу (драйверу) принте­ра несколько сообщений, содержащих данные для печати. Если теперь задача хочет отменить всю печать, ей надо послать соответствующее сообщение с более высоким приоритетом, чтобы оно встало в очередь впереди всех по­сланных ранее заданий на печать.

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

Общие ресурсы. Трудно переоценить важность пра­вильной организации взаимодействия различных задач при доступе к общим ресурсам. Хорошей аналогией может служить обед в многодетной крестьян­ской семье позапрошлого века. Едокам (за­дачам) не разрешалось одновременно лезть ложками в общую миску (ресурс). Нарушители порядка могли получить от отца семейства (супервизора) лож­кой по лбу.

Ресурс – это общий термин, описы­вающий физическое устройство или область памяти, которые могут одно­временно использоваться только од­ной задачей. Процессорное время тоже представляет собой своеобразный кон­курентно используемый ресурс вычис­лительной системы. Примерами физических устройств могут служить кла­виатура, дисплей, дисковый накоп­итель, принтер и т. п. Представим, на­пример, что несколько задач пытаются одновременно выводить данные на принтер. На распечатке в результате ничего, кроме странной мешанины символов, мы не увидим. В качестве другого примера рассмотрим ситуа­цию, когда в бортовом компьютере мирно летящего самолета МиГ-29 сре­ди прочих работают две задачи. Одна из них, взаимодействуя с радиолокаци­онной системой, выдаст удаление и на­правление до цели, а другая задача ис­пользует эти данные для пуска ракет класса «воздух-воздух». Не исключено, что первая задача, записав в глобаль­ную структуру данных удаление до це­ли, будет прервана второй задачей, не успев записать туда направление до це­ли. В результате вторая задача считает из этой структуры ошибочные данные, что может привести к неудачному пус­кусо всеми вытекающими отсюда не­приятными последствиями. Прервись первая задача чуть позже, и все было бы нормально. Упомянутые здесь пробле­мы обусловленывремязависимыми ошибками (time dependent error), или «гонками», и характерны для многоза­дачных ОС, применяющих алгоритмы планирования с вытеснением (кстати, системы с разделением времени также относятся к категории «вытесняющих»).

Приведенный пример показывает, что ошибки, обусловленные «гонками»: а) характерны для работы с любыми ресурсами, доступ к которым имеют несколько задач, и б) происходят толь­ко в результате совпадения определен­ных условий, а потому с трудом обна­руживаются на этапе отладки.

Вот возможные пути решения проб­лемы.

1. He использовать алгоритмы плани­рования задач с вытеснением. Это ре­шение, правда, не всегда приемлемо.

2. Использовать специальный сервер ресурса, т.е. задачу, ответствен­ную за упорядочивание доступа к ре­сурсу. В этом случае запрос на изме­нение значения глобальных данных посылается этому серверу в виде со­общения. Аналогичный подход при­меним и для физических устройств. Так, например, задача может послать данные на печать в виде сообщения, направленного к серверу принтера.

3. Запретить прерывания на время до­ступа к разделяемым данным. Карди­нальное решение, которое, впрочем, не приветствуется в системах реаль­ного времени.

4. Использовать для упорядочивания доступа к глобальным данным сема­форы. Наиболее часто применяемое решение, которое, впрочем, может привести в некоторых случаях к «ин­версии приоритетов».

Последний пункт стоит прокоммен­тировать подробнее, поскольку поня­тие «семафор» встречается первый раз.

Семафор – это как раз то средство, которое часто используется для син­хронизации доступа к ресурсам. В про­стейшем случае семафор представляет собой байтовую переменную, прини­мающую значение 0 или 1. Задача, пе­ред тем как использовать ресурс, захва­тывает семафор, после чего остальные задачи, желающие использовать тот же ресурс, должны ждать, пока семафор (ресурс) освободится. Существуют так­же так называемые счетные семафоры, где семафор представляет собой счет­чик. Пусть к системе подключено три принтера. Семафор, отвечающий за до­ступ к функциям печати, инициализи­руется со значением 5, а затем каждый раз, когда какая-либо задача запраши­вает семафор для осуществления печа­ти, его значение уменьшается на 1. После завершения печати задача осво­бождает семафор, в результате чего значение последнего увеличивается на 1. Если текущее значение семафора равно 0, то ресурс считается недоступ­ным, и задачи, запрашивающие печать, должны ждать, пока не освободится хо­тя бы один принтер. Таким образом мо­жет производиться синхронизация до­ступа множества задач к группе из трех принтеров. Так как по своей сути сема­фор также представляет собой гло­бальную переменную, все неприятнос­ти, которые упоминались ранее в связи с самолетом МиГ-29, по идее, должны поджидать нас и здесь. Однако так как работа с семафорами происходит на уровне системных вызовов, програм­мист может быть уверен, что разработ­чики операционной системы обо всем заранее позаботились.

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

Так как процессы обычно не имеют доступа к данным друг друга, а ресурсы физических устройств, как правило, управляются специальными задачами-серверами (драйверами), наиболее ти­пична ситуация, когда «гонки» за до­ступ к глобальным переменным устраивают различные потоки, исполняе­мые в рамках одного программного модуля. Для того чтобы гарантировать, что критическая секция кода исполня­ется в каждый момент времени только одним потоком, используют механизм взаимоисключающего доступа, или попростумутексов (Mutual Exclusion Locks, Mutex). Практически мутекс представляет собой разновидность се­мафора, который сигнализирует дру­гим потокам, что критическая секция кода кем-то уже выполняется.

Критическая секция, использующая мутекс, должна иметь определенные суффиксную и префиксную части. На­пример:

 

int global_counter;

void main (void)

{

mutex_t mutex;

( /* И все это лишь для того, чтобы увеличить глобальную переменную на единицу.*/

mutexjnit (& mutex, USYNC, NULL);

mutex_lock (& mutex);

global_counter++;

mutex_unlock (& mutex);

}

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

На этом рассмотрение средств синхронизации доступа к об­щим ресурсам можно закончить, хотя, разумеется, множество тем осталось за скобками. Например, в WINDOWS исполь­зуется в числе прочего специальная разновидность мутексов под названи­ем Critical Section Object. Необходимо также помнить, что, кроме ОС, имею­щих WINDOWS или POSIX API, существует большое число ни с чем не совмести­мых ОС, поэтому наличие средств синхронизации и особенности их ре­ализации должны рассматриваться от­дельно для каждой конкретной ОС РВ.

А вот возможные неприятности в борьбе за ресурсы.

Смертельный захват (Deadlock). Внароде побочные проявления этой си­туации называются более прозаично «зацикливание» или «зависание». А причина этого может быть достаточ­но проста – задачи не поделили ре­сурсы. Пусть, например, Задача А за­хватила ресурс клавиатуры и ждет, ког­да освободится ресурс дисплея, а в это время Задача В также хочет пообщать­ся с пользователем и, успев захватить ресурс дисплея, ждет теперь, когда ос­вободится клавиатура. Так и будут за­дачи ждать друг друга до второго пото­па. В таких случаях рекомендуется придерживаться тактики «или все, или ничего». Другими словами, если задача не смогла получить все необходимые для дальнейшей работы ресурсы, она должна освободить всё, что уже захва­чено, и, как говорится, «зайти через полчаса». Другим решением, которое уже упоминалось, является использо­вание серверов ресурсов.

Инверсия приоритетов (Priority inversion). Как уже отмечалось, алго­ритмы планирования задач (управле­ние доступом к процессорному времени) должны находиться в соответст­вии с методами управления доступом к другим ресурсам, а все вместе – соот­ветствовать критериям оптимального функционирования системы. Эффект инверсии приоритетов является след­ствием нарушения гармонии в этой области. Ситуация здесь похожа на «смертельный захват», однако сюжет закручен еще более лихо. Представим, что у нас есть высокоприоритетная Задача А, среднеприоритетная Задача В и низкоприоритетная Задача С. Пусть в начальный момент времени Задачи А и В блокированы в ожидании какого-либо внешнего события. До­пустим, получившая в результате этого управление Задача С захватила Сема­фор А, но не успела она его отдать, как была прервана Задачей А. В свою оче­редь, Задача А при попытке захватить Семафор А будет блокирована, так как этот семафор уже захвачен Задачей С. Если к этому времени Задача В нахо­дится в состоянии готовности, то уп­равление после этого получит именно она, как имеющая более высокий, чем у Задачи С, приоритет. Теперь Задача В может занимать процессорное время, пока ей не надоест, а мы получаем си­туацию, когда высокоприоритетная Задача А не может функционировать из-за того, что необходимый ей ресурс занят низкоприоритетной Задачей С.

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

1. Стремление обеспечить максималь­но быструю и детерминированную реакцию системы на внешнее собы­тие.

2. Старание добиться минимально возможных периодов времени, ког­да в системе запрещены прерывания.

3. Подпрограммы обработки прерыва­ний выполняют минимальный объ­ем функций за максимально корот­кое время. Это обусловлено несколь­кими причинами. Во-первых, не все ОС РВ обеспечивают возможность «вытеснения» во время обработки подпрограмм прерывания. Во-вто­рых, приоритеты аппаратных прерываний не всегда соответствуют приоритетам задач, с которыми они связаны. В-третьих, задержки с обра­боткой прерываний могут привести к потере данных.

Как правило, закончив элементарно необходимые действия, подпрограмма обработки прерываний генерирует в той или иной форме сообщение для за­дачи, с которой это прерывание связа­но, и немедленно возвращает управле­ние. Если это сообщение перевело задачу в разряд готовых к исполнению, планировщик в зависимости от ис­пользуемого алгоритма и приоритета задачи принимает решение о том, не­обходимо или нет немедленно пере­дать управление получившей сообще­ние задаче. Разумеется, это всего лишь один из возможных сценариев, так как каждая ОС РВ имеет свои особенности при обработке прерываний. Кроме то­го, свою специфику может накладывать используемая аппаратная платформа.

Синхронизация по времени. Совсем не так давно (лет 20 назад) аппаратные средства, отвечающие в вычислительных системах за службу времени, были совершенно не развиты. В те приснопамятные времена счита­лось достаточным, если в системе гене­рировалось прерывание с частотой се­ти переменного тока. Те же, кто не знал, что частота сети в США 60 Гц, а не 50, как у нас, постоянно удивлялись тому, что системное время в RSX-11 М никог­да не бывает правильным. Програм­мисты для получения задержек по вре­мени часто использовали программ­ные циклы ожидания, и, разумеется, пользователи таких программ получа­ли массу сюрпризов при попытке их переноса на следующее поколение компьютеров с более высокими такто­выми частотами. Благодаря науч­но-техническому прогрессу, сейчас любой мало-мальски приличный компьютер имеет часы/календарь с ба­тарейной поддержкой и многофункци­ональный таймер (а то и несколько) с разрешением до единиц микросекунд.

Как правило, в ОС РВ задается эталон­ный интервал (квант) времени, кото­рый иногда называют тиком (Tick) и ко­торый используется в качестве базовой единицы измерения времени. Размерность этой единицы для разных ОС РВ может быть разной, как, впрочем, раз­ными могут быть набор функций и ме­ханизмы взаимодействия с таймером. Функции по работе с таймером используют для приостановки выполнения за­дачи на какое-то время, для запуска за­дачи в определенное время, для относи­тельной синхронизации нескольких задач по времени и т.п. Если в программе для ОС РВ вы увидите операнд delay (50), то, скорее всего, это обозначает, что в этом месте задача должна прер­ваться (блокироваться), а через 50 мс возобновить свое выполнение, а точнее, перейти в состояние готовности. Все это время процессор не простаивает, а решает другие задачи, если таковые имеются. Множество задач одновре­менно могут запросить сервис таймера, поэтому если для каждого такого запро­са используется элемент в таблице вре­менных интервалов, то накладные рас­ходы системы по обработке прерыва­ний от аппаратного таймера растут пропорционально размерности этой таблицы и могут стать недопустимыми. Для решения этой проблемы можно вместо таблицы использовать связный список и алгоритм так называемого дифференциального таймера, когда во время каждого тика уменьшается только один счетчик интервала времени.

Для точной синхронизации таймера вычислительной системы с астрономическим временем могут применять­ся специальные часы с подстройкой по радиосигналам точного времени или навигационные приемники GPS, кото­рые позволяют воспользоваться атом­ными часами на борту орбитальных космических аппаратов, запущенных по программе Navstar.

 

Тестирование


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-03-17; Просмотров: 841; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.057 с.)
Главная | Случайная страница | Обратная связь