Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
8-CHto-takoe-mnogozadachnost.docx
Скачиваний:
4
Добавлен:
18.09.2019
Размер:
5.81 Mб
Скачать

Более детальное описание понятия «Задача», используемого в осрв uC/os-II

Что же в данном контексте (и далее) называется термином «Задача» (Task)? Это понятие эквивалентно уже использованному ранее термину модуль. Это фрагмент кода, который выполняет определенный объем работы, характеризующийся определенной законченностью. Другими словами, части программного кода, принадлежащего Задаче, связаны между собой как по управлению, так и по данным весьма сильно, в то время как отдельные Задачи связаны между собой гораздо слабее. Термины «сильно» и «слабее» в данном контексте весьма относительны, «силу» связей оценивает разработчик приложения. Это означает, что разбиение всего приложения на части, каждая из которых будет представлять собой отдельную задачу, может быть выполнено различными способами, приложение можно разбить на большее или меньшее количество Задач. Однако, можно утверждать, что одни способы разбиения более удачны, чем другие. Некоторые соображения по выбору способа разбиения будут приведены далее.

С учетом особенностей, отмеченных ранее при рассмотрении примера с многоканальной регистрацией непрерывных величин, можно сказать, что Задача есть не что иное, как фрагмент кода, который:

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

Как увидим далее, каждая Задача должна иметь свой собственный стек.

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

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

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

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

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

Действие, заключающееся в приостановке выполнения одной Задачи и возобновлении выполнения другой Задачи называют переключением Задач.

Чтобы выполнить переключение Задач, необходимо выполнить следующие действия:

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

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

  3. восстановить значения переменных величин, характеризующих состояние возобновляемой Задачи (эти значения были сохранены в недалеком прошлом, когда была приостановлена возобновляемая теперь Задача);

  4. передать управление на первую еще не выполненную команду возобновляемой Задачи.

Описанную последовательность действий называют переключением контекста.

Отметим некоторые моменты относительно переключения контекста.

  1. Промежуточные значения (элементы контекста), сформированные Задачей, могут находиться либо в переменных в памяти, либо в регистрах процессора. Каждая из Задач может иметь собственную область памяти (не разделяемую с другой Задачей) для хранения переменных, поэтому содержимое этих переменных необязательно где-то сохранять при переключении Задач. В то же время, регистры процессора являются ресурсом, который разделяется многими Задачами (за очень редкими исключениями процессоров, в которых реализовано несколько комплектов – банков регистров). Поэтому, говоря о сохранении-восстановлении контекста при переключении Задач, имеют в виду сохранение содержимого регистров.

  • Адрес очередной (еще не выполненной) команды при выполнении программы содержится в счетчике команд. Поэтому счетчик команд целесообразно рассматривать, как элемент контекста, наряду с содержимым прочих регистров процессора. Тогда в приведенном чуть выше перечне нецелесообразно выделять действия по сохранению-восстановлению содержимого счетчика команд, а действие под номером 1) рассматривать, как часть действия 2), и аналогично, действие 4) как часть действия 3). Это тем более целесообразно, поскольку во многих процессорах счетчик команд является одним из регистров общего назначения, программно доступен по чтению и по записи.

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

  • Если контекст Задачи сохраняется в ее (индивидуальном) стеке, то там бессмысленно сохранять содержимое указателя стека, его следует сохранять где-то в другом месте.

Описанную последовательность действий называют переключением контекста. Эту последовательность действий выполняет основная часть Многозадачной Операционной Системы Реального Времени, который называют диспетчером Задач (Task Manager). В ОСРВ можно выделить набор основных функций (сервисов), которые всегда (или в подавляющей части конфигураций) присутствуют в системе, этот набор принято называть ядром (реального времени) Real Time Kernel. Диспетчер Задач является главной и обязательной компонентой ядра.

Обеспечить передачу процессора ядру можно одним из двух способов.

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

При этом в стек Задачи заносится адрес, с которого следует эту Задачу в будущем продолжить (когда созреют условия).

Ядро, получив управление, ‑ сохраняет контекст прерванной Задачи (текущие значения ее регистров) в стеке этой Задачи, ‑ затем определяет, какой (другой) Задаче из готовых к продолжению следует передать управление, и переключается на ее стек. ‑ Затем ядро, загружает (из стека этой задачи) в регистры процессора контекст Задачи, выполнение которой следует возобновить, и выполняет команду «возврат из подпрограммы». Из стека Задачи в счетчик команд загружается адрес команды, сохраненный когда-то в прошлом (когда эта Задача вызывала функцию ядра, чтобы уступить процессор), и выполнение этой Задачи продолжится.

Описанный первый способ предполагает, что (как мы видели) Задачи сами уступают ядру процессор, вызывая некоторую его функцию. Возможен случай, когда некая Задача слишком долго НЕ уступает процессора, из-за этого другая (критичная ко времени выполнения) Задача может не успеть уложиться в требования реального времени – оказывается проблематичной исполнимость!

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

Задача в многозадачной ОС РВ может находиться в одном из нескольких состояний:

Dormant, Inactive (Drm) – задача находится в неактивном состояние (сон), так как выполняемая ею функциональность не требуется. Однако, как уже было ранее сказано, программный код Задачи находится в памяти. Для активации Задачи (запуска функции) используются срества ОС.

Ready (Rdy) – состояние готовности, задача может продолжить выполнение, если освободится процессор. Задача может быть переведена в состояние Rdy из разных состояний, в частности, из состояния Drm функцией ОС, «создающей» Задачу.

Run – состояние выполнения, данная (единственная в каждый момент времени) Задача выполняется, т.е. занимает процессор в текущий интервал времени. Это задача из перечня готовых (Rdy), имеющая наивысший приоритет.

Waiting (Wtn)– состояние ожидания события (условия, делающего возможным продолжение этой задачи).

Interrupted (Isr)Задача прервана внешним событием, вызвавшим запрос аппаратного прерывания, работает обработчик прерывания (ISR), но когда он закончит работу, будет продолжено выполнение прерванной задачи (либо другой задачи, если она Rdy и имеет наивысший приоритет).

Рисунок показывает эти состояния и события, которые могут переводить задачу из одного состояния в другое в uC/OS-II v/2.80.

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

Для получения информации о системных функциях обращайтесь к «Справочнику по функциям uC/OS-II v.2.80.

Приоритет задачи

Каждая Задача в ОСРВ uC/OS-II имеет характеристику ее важности, называемую термином приоритет. Эта характеристика формально имеет вид целого числа (чаще всего, чем меньше значение этого числа, тем выше приоритет, важность Задачи). В некоторых ОСРВ (и в uC/OS-II) каждая Задача имеет уникальное значение приоритета, в других ОСРВ разрешается иметь несколько Задач с одинаковым значением приоритета.

Приоритет задачи может быть фиксированным, назначенным при написании приложения (статическим), либо изменяться во время исполнения (динамическим).

Как (на основании каких соображений) назначают приоритеты, поговорим позже.

Если Задача по какой-либо причине не готова продолжаться (приостанавливается), то управление получает Ядро ОС. Оно должно передать управление одной из готовых к продолжению Задач.

Планирование (Schedule) ‑ выбор того, какую Задачу следует запустить на продолжение.

Диспетчеризация – переключение контекста.

Алгоритмы планирования

Планирование состоит из двух шагов, выполняющихся друг за другом:

1. Собственно планирование (scheduling), т.е. «составление расписания» – перевод некоторых задач в состояние Ready.

2. Диспетчеризация (dispatching) - выбор задачи из списка и назначение ей процессора (переключение контекста).

Типы планирования:

Системы с программой, управляемой запросами прерывания. (Обработчики прерывания с «фоновым» потоком).

Круговая диспетчеризация (round-robin)

(Рассматриваемые Задачи имеют одинаковый приоритет)

Квантование времени (time-sliced)

Невытесняющее (non preempted)

Задача вытесняется только тогда, когда ей «не хватает» чего-либо для продолжения работы.

Вытесняющее (preempted)

Задача1 вытесняется тогда, когда другая (более приоритетная) Задача2 стала готова к продолжению: для Задачи2 есть все условия: исходные данные, необходимые ресурсы, не хватает лишь освобождения процессора.

Что представляет собой ОСРВ внешне (для программиста):

Набор файлов с исходным кодом ОСРВ, которые транслируются совместно с кодом приложения, написанным прикладным программистом, и решающим конкретную прикладную проблему.

Большая часть кода uCOS-II процессорно-независимая и написана на языке Си.

Процессорно-зависимые файлы содержат код, написанный на Си (файл OS_CPU_C.C) и на языке Ассемблера для используемого процессора (OS_CPU_A.S для процессоров ARM).

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

Эта возможность реализована с помощью директив условной трансляции, которые анализируют значения управляющих констант. Программист выполняет настройку конфигурации, задавая значения этих констант перед трансляцией своего приложения. В uCOS-II управляющие константы сосредоточены в файле OS_CFG.H.

В справочнике по функциям ОС для каждой системной функции указаны имена конфигурационных констант, которые имеют отношение к управлению трансляцией этой функции. Глава 17 книги «MicroC/OS-II The Real Time Kernel» содержит инструкцию по конфигурированию системы.

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