|
|
Кери Джонсон (Kerry Johnson), старший менеджер по продуктам, QNX Software Systems
Обеспечивая гарантированное время процессорной обработки для каждой программной подсистемы, адаптивная декомпозиция заметно сокращает трудозатраты на интеграцию всей системы.
Возрастающая сложность
Еще совсем недавно большинство промышленных систем управления имело ограниченные требования к программному обеспечению, – как правило, оно содержало лишь несколько тысяч строк исходного кода. В настоящее время встраиваемая система управления может содержать в себе миллионы строк исходного кода и использовать большое количество взаимодействующих сложным образом программных компонентов. И все это осуществляется при условии ограниченного объема памяти и процессорного времени.
Чтобы ускорить процесс разработки сложных систем, проект разделяется между многочисленными группами разработчиков, каждая из которых в ходе исполнения имеет свои собственные цели, схемы назначения приоритетов задач и подходы по оптимизации процесса выполнения. При данном способе разработки в процессе интегрирования подсистем неизменно возникают вопросы, связанные с производительностью, прежде всего из-за того, что различные подсистемы начинают конкурировать между собой за системные ресурсы. Подсистемы, прекрасно работающие в изолированном режиме, в этом случае начинают медленно реагировать на запросы, а порой и вообще дают сбои. Все усугубляется еще и тем, что многие из этих проблем возникают только в процессе интеграции и проверочного тестирования, когда стоимость перепроектирования и перекодирования программного обеспечения чрезвычайно высока.
Диагностика и решение таких проблем чрезвычайно трудны. Разработчики должны умело манипулировать приоритетами задач с возможным, изменением поведения потоков в системе, а затем проводить повторное тестирование и уточнять собственные модификации. На этот процесс может уйти несколько рабочих недель, что приведет к увеличению затрат и задержке выпуска продукта.
Разделение как метод решения
В последнее время понятие разделения стало рассматриваться как способ управления сложностью системы и обеспечения более высокой системной доступности. Кратко это можно определить как подход, дающий возможность командам разработчиков делить программное обеспечение на отдельные группы, где каждой группе назначается выделенная часть (или запас) системных ресурсов.
Как результат каждая составная часть обеспечивает стабильную, известную среду исполнения, которую команды разработчиков могут формировать и проверять индивидуально. Если программный процесс в пределах выделенной области без сбоев выполняется в ходе тестирования, можно с полной уверенностью сказать, что и на стадии интеграции он покажет такую же производительность.
Если деление на разделы такого статистического ресурса как память не вызывает особых трудностей, распределить время процессорной обработки гораздо сложней. Так как стоимость и энергопотребление для встраиваемых систем изначально имеют ограничения, процессоры для таких систем имеют сравнительно небольшой запас производительности. Принимая во внимание эти условия, можно сказать, что процессорное время это ограниченный ресурс, распоряжаться которым нужно очень бережно, особенно во время высокой загрузки. Адаптивное распределение процессорного времени позволяет достичь этой цели.
Планировщик ОСРВ
Чтобы понять необходимость распределения процессорного времени, нужно рассмотреть роль планирования во встраиваемой системе реального времени.
Для обеспечения переносимости кода, большинство современных операционных систем поддерживает интерфейс прикладного программирования POSIX. Этот открытый стандарт определяет потоковую модель, в которой единичный процесс может содержать один или несколько исполняемых потоков. Для того, чтобы достичь параллельности выполнения нескольких операций и производительности реального времени, необходимой для встраиваемых систем, используют потоковый планировщик с приоритетными прерываниями.
Такой тип планировщика всегда позволяет назначить процессорное время потоку с более высоким приоритетом требующему обработки. Когда поток с более высоким приоритетом (обычно запускаемый внешним событием) готов к обработке, происходит замещение им любого запущенного потока с более низким приоритетом.
Планировщик, основанный на приоритетах, обладает рядом преимуществ, включая:
- Предсказуемость периодов ожидания - назначая требующие немедленного исполнения функции потокам с высокими приоритетами, разработчики могут четко контролировать время, которое понадобится системе для обработки внешних событий.
- Параллельность выполнения и гибкость – используя приоритетный планировщик, встраиваемые системы могут одновременно обрабатывать различные типы задач: регулярно возникающие с установленным временем исполнения, высокоприоритетные событийно-управляемые и задачи, выполняемые в фоновом режиме.
- Привычность и надежность – приоритетный планировщик широко используется в промышленных приложениях и хорошо понятен для разработчиков встраиваемых систем.
Когда поток обработан и готов к запуску, он располагается в очереди готовых потоков с одинаковым уровнем приоритетов. Политика работы планировщика, установленная разработчиком, определяет какой из очереди готовых к запуску потоков, будет запущен следующим. Стандарт POSIX описывает три политики работы планировщика:
- Правило "первым прибыл – первым обслужен" (FIFO) – гарантирует, что поток, выбранный для запуска, будет выполняться, пока не остановится или не будет вытеснен потоком с более высоким приоритетом.
- Правило циклического алгоритма (Round-robin) – гарантирует, что поток будет выполняться, пока не остановится, не будет вытеснен потоком с более высоким приоритетом или не закончится отведенное ему время выполнения.
- Спорадическое правило – определяет, как долго процесс будет выполняться в пределах отведенного ему времени.
При работе планировщика в спорадическом режиме, поток имеет два назначенных приоритета – нормальный и низкий и запускается с нормальным приоритетом в определенный период времени. Когда время работы с нормальным приоритетом заканчивается, поток запускается с низким приоритетом. Период работы с нормальным приоритетом регулярно обновляется. Такой подход помогает предотвратить работу единичного потока с высоким приоритетом слишком длительное время. Часто разработчики ограничивают использование спорадического режима работы планировщика так как его трудно использовать в системах с большим количеством потоков.
Управление приоритетами
Планировщик, основанный на приоритетах, функционирует не совсем «честно». Высокоприоритетная задача, требующая обработки, может захватить все процессорное время, лишив обработки все другие задачи. В результате разработчикам приходится очень осторожно назначать и тестировать приоритеты задач во всей системе.
Когда система становится сложней и увеличивается количество разработчиков, назначение и учет приоритетов для большого количества потоков становится непосильно трудной задачей и причиной разногласий среди разработчиков. Понимая, что неограниченное назначение приоритетов может привести к хаосу (или нефункционированию системы), разработчики часто сокращают количество используемых приоритетов. Однако, такое решение имеет нежелательный побочный эффект: увеличение периодов ожидания. Так как большое количество потоков имеет одинаковый приоритет, очередь готовых к запуску потоков становится очень длинной. Готовый к запуску поток, ожидает выполнения до тех пор, пока до него не дойдет очередь.
Рис. 1. Приоритетное планирование гарантирует, что наиболее критические задачи получают доступ к ресурсам процессора, но оно также может быть причиной проблем в случае, когда высокоприоритетная задача неумышленно или намеренно потребляет все доступные циклы процессора. Например, задача А препятствует всем другим задачам получить доступ к процессору после времени своего запуска (4-я единица времени).
В довершении проблемы, планировщик, основанный на приоритетах, может допустить полную загрузку процессора вредоносным программным обеспечением или процессом отказа от обслуживания поступивших запросов (DoS attack), делая, таким образом, систему недоступной для пользователей.
В целях предотвращения таких проблем, разработчики и инженеры программного обеспечения выбирают одно из двух возможных решений:
- Использование управляющего процесса-диспетчера или «сторожевого» процесса, который отслеживает загрузку процессора потоками.
- Использование алгоритма выделения процессорного времени, управляемого операционной системой, который назначает определенное процессорное время каждому потоку.
«Сторожевые» процессы
Иногда группа связанных потоков лишает другие потоки процессорного времени. В других случаях, недостаток времени процессорной обработки является результатом зацикливания одного высокоприоритетного потока. Чтобы предотвратить такие моменты, «сторожевой» процесс следит за загрузкой процессора и осуществляет корректирующие действия в случае обнаружения превышения потоком бюджета установленного времени процессорной обработки.
Тем не менее, не существует простого способа нейтрализовать использование процессора вышедшим из под контроля потоком. В связи с этим, «сторожевой» процесс должен использовать кардинальные методы, такие как перезапуск защищенного процесса (и всех его порожденных процессов) или снижать приоритеты защищенных потоков или процессов.
Более того, метод «сторожевого» процесса не работает гладко со всеми процессами и потоками. Например, некоторым процессам необходимо полностью использовать процессор в определенные моменты времени, это порождает необходимость обращения «сторожевого» процесса к списку исключений, чтобы поддерживать правильное функционирование системы.
«Сторожевой» процесс может также поглощать значительный объем процессорного времени. Например, приходится через определенные интервалы времени регулярно посылать запрос операционной системе, чтобы правильно определять использование процессора всеми потоками, а затем сравнивать полученную информацию с данными предыдущего запроса. Также необходимо учитывать все случаи исключения упомянутые выше. Так как «сторожевой» процесс выполняется с более высоким приоритетом, чем все другие потоки, за которыми он следит, он и сам может стать причиной лишения задач процессорного времени.
«Сторожевой» процесс обладает и другими побочными эффектами, включая замедленное время отклика, ограничение использования процессорного времени «законным» процессом.
Алгоритм выделения процессорного времени, управляемого операционной системой
Некоторые операционные системы реального времени поддерживают фиксированное выделение процессорных ресурсов группам потоков. Такой подход обеспечивает некий контейнер, называемый разделом, которому выделяется фиксированная доля процессорного времени. Например, для группы потоков имеющих общее предназначение, разработчик выделяет раздел с фиксированным бюджетом в 5% от общего объема процессорного времени. Планировщик раздела будет в этом случае гарантировать, что данный раздел получит назначенный объем процессорного времени.
Используя подход временного разделения, при разработке можно задать бюджет гарантированного процессорного времени для каждой значимой подсистемы программного продукта. Разработчик, таким образом, получает гарантию того, что полная загрузка системных ресурсов одной подсистемой не повлияет на функционирование других подсистем. Такой подход предотвращает поглощение всех ресурсов процессора одним потоком, даже если поток запущен с самым высоким уровнем приоритета.
В пределах раздела обработка потоков планируется с учетом традиционных правил прерывания планировщика, основанного на приоритетах. К разделу применяются стандартные правила планировщика: "первым прибыл – первым обслужен" (FIFO), правило циклического алгоритма (Round-robin) и спорадическое правило. В результате каждый раздел становится «мини» средой исполнения.
Работа планировщиков разделов различается. Некоторые четко распределяют бюджетное время процессорной обработки, так что каждый раздел получает установленный бюджет процессорных ресурсов даже когда в этом нет особой необходимости. Другие могут динамически распределять неиспользуемые ресурсы процессора по другим разделам, максимизируя таким образом использование процессора и позволяя системе справляться с пиками запросов.
Упрощение процесса разработки и интеграционного тестирования
Распределение процессорного времени облегчает процесс параллельной разработки, позволяя разработчику назначить гарантированный бюджет процессорных ресурсов для каждой субсистемы. Такой подход устраняет необходимость применять глобальные схемы приоритетов и позволяет разработчикам определять схемы приоритетов на уровне субсистем в зависимости от потребностей. В результате становится возможным ведение процесса параллельной разработки.
При тестировании функционирования субсистемы внутри раздела, разработчики могут создавать условия полной загрузки процессора вне этого раздела, симулируя работу при полной загрузке процессора. Это позволяет разработчикам производить отладку своего программного кода в неблагоприятных условиях полного использования системных ресурсов. В результате решается большое количество проблем связанных с производительностью и использованием процессорного времени до стадии интеграции системы.
Чтобы оценить все преимущества, рассмотрим относительно простую систему, спроектированную без использования алгоритма распределения процессорного времени. Система, изображенная на рисунке 2, содержит следующие процессы:
- Процесс со средним приоритетом, ответственный за функционирование локального человеко-машинного интерфейса.
- Процесс со средним приоритетом, производящий периодическое сенсорное сканирование.
- Процесс с высоким приоритетом, ответственный за управление двигателем.
- Процесс с низким приоритетом, ответственный за функционирование удаленной системы мониторинга, которая посылает обновленные данные центральной системе мониторинга основанный на интернет-технологии.
На этапе интеграции, когда вся система собрана, система веб-мониторинга работает прекрасно, до тех пор, пока не используется локальный человеко-машинный интерфейс. При его использовании система мониторинга «замирает» и перестает отображать обновленные данные. Проблемы возникают, когда к процессу управления двигателем работающим с высоким приоритетом, добавляется процесс отвечающий за команды локального человеко-машинного интерфейса, абсолютно вытесняя процесс мониторинга с более низким приоритетом. Анализ приоритетов объясняет, почему это происходит. При полной загрузке процессора, процессы с малым приоритетом не получают процессорного времени обработки.
Чтобы решить данную проблему, разработчик назначает процессу, отвечающему за команды локального человеко-машинного интерфейса более низкий приоритет, чем процессу мониторинга. Однако это делает невозможным адекватную работу человеко-машинного интерфейса. Назначение среднего приоритета трем процессам: процессу производящему сенсорное сканирование, процессу ответственному за функционирование удаленной системы мониторинга и процессу, отвечающему за команды локального человеко-машинного интерфейса – также ничего не дает. Производительность трех процессов снижается. Так как переназначение приоритетов не дает должного результата, разработчик должен сделать следующий шаг и попытаться изменить поведение потока – дорогостоящее решение на стадии интеграции.
Рис. 2. Пример упрощенной системы автоматизации.
Разделение процессорного времени позволяет избежать всех этих проблем. Например, разработчик может установить бюджет процессорного времени для каждого из четырех разделов: 10% для процесса, отвечающему за команды локального человеко-машинного интерфейса, 10% для процесса, ответственного за функционирование удаленной системы мониторинга, 30% для процесса, производящего сенсорное сканирование и 50% для процесса, ответственного за управление двигателем. См. рисунок 3.
Рис. 3. Распределение бюджетов процессорного времени между процессами.
При таком подходе каждый раздел будет функционировать в соответствии с выделенным бюджетом процессорного времени. При сборке системы на стадии интеграции, все процессы гарантированно получат выделенную бюджетом долю процессорного времени. В результате ни один процесс не будет обделен процессорными ресурсами и, более того, у разработчика появится возможность настройки производительности системы простой регулировкой бюджетов процессорного времени для каждого из разделов.
Подсчет экономической эффективности
Использовать схему «создание, тестирование, обнаружение, отладка» в случаях, когда задачам недостает процессорных ресурсов, – это очень дорого. Часто недостаток системных ресурсов является следствием сбойного, необъяснимого поведения системы, а не серьезных поломок. И как результат, бывает достаточно трудно собрать адекватную информацию о происходящих проблемах. Обычно поиск такого рода неисправностей требует как наличия детальных знаний об устройстве системы, так и большого объема кропотливой работы. В итоге, для того, чтобы найти и устранить проблему, требуется целая команда специалистов, Процесс включает в себя следующие действия:
- Тестировщик создает отчет о проблеме, описывающий неожиданное поведение системы при тестировании. Так как проблему сложно воспроизвести, тестировщик не может собрать достаточную информацию, способную облегчить решение проблемы.
- Разработчик производит серию тестовых запусков, пытаясь воспроизвести описанную проблему. Как правило, разработчик обнаруживает, что дело не в конкретном процессе, а в другом, который полностью загружает процессор, лишая, таким образом, остальные процессы процессорного времени обработки.
- На этом этапе границы поиска решения проблемы расширяются, вовлекая в процесс все больше людей. Решение может состоять в настройке приоритетов потока или в изменении поведения процесса.
- Каждый, привлеченный к процессу разработчик, выполняет необходимые изменения и тестирует свою часть проекта, затем интегрирует изменения в систему.
- Тестировщик выполняет повторное тестирование и закрывает отчет об ошибке при условии, что в ходе этих мероприятий не было выявлено дополнительных проблем.
Исходя из вышеперечисленных рассуждений, составляем таблицу затрат на отладку одной проблемы:
Задача |
Необходимое время |
1. Проверка и создание отчета о проблеме (1 человек тестирует и создает отчет)
|
1 день
|
2. Первоначальный этап выявление неисправности (1 человек назначен для устранения проблемы)
|
2 дня
|
3. Совместное выявление неисправности (3 человека принимают участие)
|
3 дня
|
4. Совместное решение проблемы (3 человека принимают участие, каждый тратит 3 рабочих дня)
|
9 дней
|
5. Перепроверка (1 человек заново тестирует систему)
|
1 день
|
Всего трудозатрат
|
16 дней
|
Из этого примера видно, как недостаток процессорного времени обработки может увеличить затраты на разработку и задержать сдачу проекта; в нашем случае это две-три календарные недели. И это при том, что в нашем примере рассматривается система только с четырьмя потоками – многие промышленные системы содержат сотни и даже тысячи потоков, для каждого из которых существует сотня способов занять все процессорное время.
Так как стадия интеграции системы занимает больше всего времени проекта, способы оптимизации этой стадии сокращают издержки производства и тем самым способствуют более быстрому выходу на рынок.
Минимум усилий
С ростом сложности и объема программного кода, вероятность проявления проблем нехватки процессорного времени и других дефектов в конечном продукте возрастает. Стоимость устранения таких неполадок после того как система была собрана возрастает многократно, не говоря уже об ударе по репутации разработчика и других финансовых издержках с этим связанных. Разработчики и вендоры, которые разрабатывают программные продукты для систем промышленной автоматизации, должны использовать любые возможности и методы, находящиеся в их распоряжении, чтобы обеспечить корректность отладки и полноценное тестирование своего программного кода.
Более сложная задача, найти и воплотить технологии разработки, которые требуют минимум усилий разработчиков и используемых компьютерных ресурсов. При условии правильного применения, технология адаптивного распределения процессорного времени является таким решением. Более того, она обеспечивает повышение надежности и отказоустойчивости систем, предотвращая захват ресурсов процессора вредоносными программами или процессами отказа от обслуживания поступивших запросов (DoS attack). Таким образом, мы имеем технологию, которая позволяет разработчикам встраиваемых приложений создавать хорошо интегрируемые и хорошо защищенные системы.
|
|