Начнем-ка мы рассматривать, полную подводными камнями, тему Real-Time программирования.Давайте для начала опишем то, что я понимаю под программным обеспечением такого рода.
Краткое вступление в тему Real-Time.
Под это определение можно подвести любую ОС - умеющую работать с процессами и потоками в режиме разделения ресурсов и времени процессора. В том числе и Windows. Ведь реально события происходят: сетевые сообщения, нажатия кнопок пользователем и т.д. и т.п.
Сказать так все же было бы не верно.
Под системами реального времени (РВ в дальнейшем) понимают систему, отвечающую за управление каким-либо реальным процессом. ОС общего назначения, особенно многопользовательские, такие как UNIX/Windows, ориентированы на оптимальное распределение ресурсов компьютера между пользователями и задачами (системы разделения времени), В ОС РВ подобная задача отходит на второй план - все отступает перед главной задачей - успеть среагировать на события, происходящие на объекте. Это собственно и есть главная задача ОСРВ.
В отличии от ОС - есть программы, которые вынуждены быть программами РВ внутри не РВ операционных систем. К таковым в принципе относятся программы серверов (Apache), программы сетевых протоколов, типа различных ускорителей интернета и т.д.
Внутри Embeded system эти задачи становятся особенно актуальными, когда мы говорим о Wireless коммуникациях. Системам связи приходится постоянно следить за наличием сигнала, приходу вызовов от абонента, и т.д. Давайте рассмотрим несколько аспектов работы таких программ.
Ждущий режим.
Для огромного числа программ РВ - существуют понятия потоков и процессов. Без понимания, чем они отличаются - дальше нам говорить нечего, поэтому давайте разберемся.
Процесс - самостоятельная единица ОС - имеющая доступ к собственной памяти из системных ресурсов, системы сообщений, собственный Instance, т.е. системный идентификатор.
Поток - процесс, имеющий ограничения - он не имеет собственного самостоятельного ID и обязательно должен быть связан с родительским процессом.
Для организации одновременной работы, как с клиентской частью, так и с постоянным, но нерегулярным притоком внешних событий, нам необходимо организовать, как минимум, один поток - отвечающий за работу с клиентом и второй с внешним источником информации, которым мы управляем. В этом случае у нас сразу же возникает первый подводный камень. Если в ждущем от управляемого объекта потоке, поставить обычный напрашивающийся цикл типа
Код:
do{
}while(!Exit)
мы окажемся в ситуации съедания 90% процессорного времени этим самым потоком.
Чтобы избежать попадания в такую ситуацию, мы обязаны использовать не просто цикл, а цикл с задержкой на время, пока сигнала нет. В каждой ОС - для внешних коммуникаций существует API, отвечающий за ожидание входящих событий. В WinCE, системе рекламирующейся Майкрософтом как РВ, существует функция WaitCommEvent для ожидания события на COM порте. Правда она же есть и в обычном Windows, но не в этом дело. Поток, который занимается ожиданием сигнала, висит на ожидании события, причем не всегда это приход каких-либо данных, а иногда и командное событие ждет, не занимая процессорное время. В случае если событий несколько, они организуются в систему флагов, которые после выхода из ожидания необходимо оценивать. В системе Nucleus мы имеем такую же систему API. Однако во всем этом мы увидим еще один подводный камушек. Выход из такого цикла невозможен, ибо ожидание внешних событий блокирует поток, если вдруг внешний управляемый модуль ничего не шлет, по причине поломки. В этом случае мы программно выйти можем только одним способом, поставить предварительно событие выхода, которое будет ждать следящий поток, наравне с событиями внешними. В этом случае если выясняется, что система сломана - возможен легальный выход из программы и закрытие потока внутренними программными средствами корректно, посылкой события выхода.
Оставим на время тему организации ожидания и вспомним, что кроме этого протоки должны обмениваться информацией между собой. Однако это не возможно организовать, так же как и в обычном однопоточном варианте, где в каждый момент времени, только одна область памяти подвергается чтению или изменению. Если мы используем общую переменную типа int, для передачи номера сигнала от ждущего процесса к ответственному за обработку, то в нашем случае мы обязаны соблюсти "приличия".
Например: ждущий поток заполнил переменную номером задания. Однако в процессе выполнения этого действия второй поток по приказу пользователя считывает эту переменную. В этом случае мы можем считать первые два байта нового значения, а вторые старого, т.к. на деле все равно не возможно точно знать, свободна ли переменная или нет. Для того, что бы избежать такой ошибки, мы обязаны предусмотреть защиту общей памяти от несанкционированного доступа в момент операций чтения/записи. Когда первый поток обращается к одному из общих мест в памяти задачи, то он должен проверить имеет ли он право на операцию. Если ответ да, то происходит захват ресурса, блокировка, затем процедура изменения содержимого и деблокировка, освобождение ресурса. Если во время блокировки, еще один поток попытается обратиться к этому же месту памяти, то он наткнется на блок, и будет ждать, пока первый не освободит ему ресурс.
Заключение.
В качестве домашнего задания - предлагаю вам сделать небольшой пример не на бумаге, а в голове. Что произойдет, если при чтении блокированного куска, первый поток будет заблокирован ожиданием освобождения доступа к памяти, а второй в процессе работы с данными, зависнет? И как такую ситуацию обойти? Я специально в тексте опустил эту деталь.
Information
- Posted on 01.02.2010 01:00
- Просмотры: 3010