Автору не удалось найти ни одной открытой программной библиотеки, в которой последовательно выполнялись бы все описанные здесь принципы. Поэтому при создании пакета CRW-DAQ была разработана собственная библиотека функций и классов на языке Object Pascal, и главной её особенностью стала по мере возможности более полная реализация принципов безопасного многопоточного программирования задач мягкого реального времени. Рассмотрим реализацию этой библиотеки более подробно.
Классическая концепция объектно-ориентированного программирования предлагает три основные принципа – инкапсуляцию (объединение данных и кода в одном объекте), полиморфизм (виртуальные функции) и наследование (сохранение методов и данных предка у дочернего класса). С точки зрения безопасности лишь наследование в большинстве языков не вызывает нареканий: повторное использование отлаженного кода в классах снижает риск новых ошибок. А вот реализация полиморфизма в распространенных языках (C++, Object Pascal) небезопасна, т. к. в ней нет механизма верификации указателей на объекты, и они не проверяются при вызове. В результате, например, вызов виртуального метода для пустого объекта (с указателем Nil) приводит к аварийному завершению программы и является распространенной причиной программных сбоев. Инкапсуляция в строгом смысле предполагает, что все данные класса должны быть приватными (private или protected), а общедоступным (public) должен быть только интерфейс класса, то есть только его методы: процедуры, функции и свойства (property). Распространенные библиотеки классов (VCL, MFC, …) не являются строго инкапсулированными, т. к. часто содержат открытые поля данных, не «спрятанные» за методами. Это резко снижает безопасность классов, т. к. без строгой инкапсуляции, имея возможность произвольно менять открытые поля данных, нельзя гарантировать самосогласованности объектов.
Даже соблюдение трех принципов ООП еще не являются достаточным условием для безопасного многопоточного программирования. Для реализации безопасных классов требуется более сильное условие – «атомарность», то есть неделимость всех открытых методов объекта. Атомарность, включая строгую инкапсуляцию как необходимое условие, требует также отсутствия у класса «промежуточных», «незавершенных» состояний, чтобы все его доступные извне состояния были самосогласованны. Это значит, что вызов любого метода объекта либо завершается неудачей, возвращая код ошибки, либо неделимым образом переводит объект в новое самосогласованное состояние. Для этого все открытые методы объекта должны быть защищены критическими секциями, мютексами или другими объектами синхронизации потоков. Другим свойством, необходимым для реализации безопасных классов, является «герметичность», т. е. гарантированное отсутствие «утечки» ресурсов при вызове любой функции класса, даже при возникновении в ней исключений. Реализация атомарности и герметичности требует методичного использования техники структурной обработки исключений. Наконец, безопасные классы должны обладать свойством «рефлексии», то есть иметь средства для самодиагностики, проверки корректности ссылок и самосогласованности объектов, вести журнал статистики использования ресурсов.
При реализации атомарных, потокобезопасных объектов используются объекты синхронизации потоков, что создает дополнительные проблемы, главными из которых являются взаимная блокировка и инверсия приоритета. Взаимная блокировка возникает, когда два потока (A, B) заблокированы на общих ресурсах (C, D), причем поток A захватил ресурс C и ждет освобождения D, а поток B захватил ресурс D и ждет освобождения C, при этом оба потока повисают. Инверсия приоритета возникает в случае трех потоков (A, B,C) с высоким, средним и низким приоритетом. Если поток C захватил ресурс R, а затем поток A заблокировался при попытке его захвата, то поток B может вытеснить более высокоприоритетный поток A на неопределенное время, так как поток B может законно вытеснить низкоприоритетный поток C, который задерживает разблокировку потока A, пока не освободит ресурс R. Наконец, блокировка общих ресурсов создает задержки, приостанавливая выполнение потоков, что также нежелательно. Проблема инверсии приоритетов решается введением механизма наследования приоритетов, когда ОС временно повышает приоритет потока C до уровня потока A, если поток A блокируется на ресурсе R, захваченном потоком C. Однако наследование приоритетов реализовано лишь в ОСРВ, таких как QNX [68], и не реализовано в обычных ОС, включая Windows и Linux. Однако это не значит, что проблема никак не решается в этих системах.
Чтобы минимизировать проблемы, связанные с блокировками ресурсов, в библиотеке CRW-DAQ используется принцип «копируй и пользуйся». Это значит, что для использования какого-то объекта поток блокирует его на минимально возможное время лишь для того, чтобы сделать локальную копию интересующих данных, а затем блокировка снимается и работа происходит с этой копией данных. Например, при прорисовке кривой, которая может занимать значительное время, система визуализации блокирует кривую, копирует видимый фрагмент кривой во временный массив и сразу освобождает кривую. Лишь после этого начинается длительная операция прорисовки кривой. Такой подход позволяет снизить взаимное влияние потоков до минимума, хотя и не снимает проблему полностью.
Одной из главных тем безопасного программирования является контроль использования ресурсов: процессора, памяти, дисков, сетевых каналов и т. д. Вопрос использования процессора решается за счет возможностей приоритетной вытесняющей многозадачности, как было подробно описано выше (Рис.15,Рис.64). Напомним, что задача управления АСКУ в CRW-DAQ всегда разбивается на ряд параллельно работающих потоков (и процессов), приоритет и период опроса которых регулируется. Принятый в пакете асинхронный стиль программирования также способствует распараллеливанию задачи.
а)
б)
Рис.72. Консоль монитора ресурсов: а) загрузка процессора и частота опроса потоков, здесь - от 1 до 512 раз в секунду; б) гистограммы частоты опроса потоков. Потоки с высоким приоритетом имеют узкий пик, с низким - сильно «размазаны».
Корректная работа с большим числом (обычно более 20) потоков практически невозможна без развитых средств диагностики. Поэтому в пакете имеется набор средств для слежения за ресурсами. Есть монитор ресурсов (Рис.72,а), позволяющий контролировать использование процессора, памяти, дисков и наблюдать частоту опроса всех потоков программы и загрузку процессора по каждому из них. Кроме того, по каждому из потоков постоянно набирается гистограмма периода опроса (Рис.72,б), которая позволяет оценивать временные характеристики системы.
Служба системного времени, используя наличие в компьютере нескольких независимых таймеров, следит за корректностью системных часов и выдает предупреждение, если системное время было изменено. Служба программного сторожевого таймера отслеживает частоту опроса потоков и выдает предупреждение, если какие-то потоки долго не отвечают, то есть, возможно, «повисли». Звуковая система оповещения выдает речевые сообщения в случае ошибок связи с измерительными модулями, так что при обнаружении отклонений поведения программы от нормы оператор получает звуковое или речевое уведомление, что позволяет своевременно принять восстановительные меры.
Наиболее частыми проблемами, связанными с остальными ресурсами (память, диск, сеть), являются нехватка и утечка ресурсов, а также задержка их захвата. Нехватка ресурсов возникает при неудачной попытке динамического выделения ресурсов после их исчерпания, что приводит к сбою в работе алгоритмов управления. Она может быть спровоцирована не только ошибками в самой АСКУ, но и посторонними ресурсоемкими программами, захватившими требуемые ресурсы. Утечка ресурсов возникает, когда алгоритм для выполнения каких-то действий временно захватывает динамические ресурсы, но потом забывает их освободить. При этом сам ресурс (объект, область памяти, открытый файл) остается занятым, но программа теряет ссылку на него и не может его в дальнейшем использовать. Утечка ресурсов – весьма трудно обнаружимая ошибка, поскольку она имеет латентный период (проявляется не сразу): АСКУ может сутками работать без видимых ошибок. Однако утечка ресурсов обладает кумулятивным свойством: со временем утечки накапливаются и неизбежно ведут к исчерпанию ресурсов, после чего следует неизбежный сбой управления из-за их нехватки. Задержка захвата ресурсов играет роль в алгоритмах управления реального времени, ведь даже если свободные ресурсы есть, их поиск и захват может занять слишком много времени.
Перечислим меры борьбы с ошибками ресурсов в пакете CRW-DAQ. Однократный (квазистатический) захват ресурсов является хорошей превентивной мерой против их нехватки, утечки и задержки, поэтому большинство необходимых для работы АСКУ ресурсов выделяется при загрузке её конфигурации и освобождается при её завершении. Хотя это явно неоптимальное использование ресурсов, так как не все захваченные ресурсы могут потребоваться в конкретном сеансе работы АСКУ, но квазистатический захват ресурсов действительно снимает проблему их нехватки (они уже захвачены), утечки и задержки (в процессе работы АСКУ они не захватываются и не освобождаются). Конечно, квазистатический захват ресурсов требует более тщательного конфигурирования АСКУ, чтобы однократно выделенных ресурсов хватало для решения данной задачи управления. Однако после такого конфигурирования АСКУ становится более предсказуемой: она либо не загрузится вовсе, если ресурсов мало, либо загрузится и будет гарантированно работать сколь угодно долго без проблем с ресурсами. Другой мерой борьбы с утечками ресурсов являются счетчики: в каждую функцию захвата и освобождения ресурсов, а также в конструкторы и деструкторы классов включаются процедуры инкремента и декремента глобальных счетчиков. Много времени это не требует, однако дает возможность в любой момент оценить интегральное количество выделенных ресурсов. Ненулевое значение счетчиков при завершении программы, когда все объекты должны быть уже уничтожены, является явным признаком утечки ресурсов, поэтому все счетчики перед завершением программы записываются в журнальный файл для последующего анализа. Можно сказать, что фактически все время работы программы идет ее самотестирование. Хотя счетчики - чисто диагностическое средство, они позволяют своевременно найти и затем устранить утечки ресурсов. Наконец, в качестве восстановительных мер борьбы с утечками ресурсов применяются методы структурной обработки исключений.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |


