Текст работы - Московский институт электроники и математики

advertisement
МОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОНИКИ И
МАТЕМАТИКИ НИУ ВШЭ
Кафедра ИТАС
ПОЯСНИТЕЛЬНАЯ ЗАПИСКА
к дипломному проекту
На тему: Разработка программного модуля диспетчера высокой готовности
для ОСРВ QNX 4.25_______________________________________________
Студент: _Сараев Дмитрий Алексеевич_________________________________
Руководитель проекта: _Софийский Гурий Дмитриевич___________________
Допущен к защите _________________________ 2013 г.
КОНСУЛЬТАНТЫ ПРОЕКТА:
Специальная часть _Терехов Алексей Анатольевич______________________
Экологическая часть ________________________________________________
Охрана труда_______________________________________________________
Зав. кафедрой _Тумковский Сергей Ростиславович__________________
МОСКВА
АННОТАЦИЯ
Данный дипломный проект посвящен разработке программного модуля
диспетчера высокой готовности для ОСРВ QNX 4.25.
Разработанное программное средство позволяет запускать различные
конфигурации отказоустойчивого кластера, а также в режиме реального
времени проводить мониторинг его состояния.
В первой главе проведен анализ предметной области, рассмотрены
аналоги и сформулирована постановка задачи.
Во второй главе представлена разработка алгоритмов для блоков,
входящих в программную систему. Эта глава также содержит описание
разрабатываемых программных блоков и примеры графического интерфейса
для них.
В третьей главе описана технология отладки и тестирования для
разрабатываемого программного обеспечения.
Четвертая глава представлена контрольным тестовым примером.
В заключении приведены итоги выполненной работы. В нем
содержится оценка результатов и выводы по проведенной работе.
1
ОГЛАВЛЕНИЕ
Введение ................................................................................................................... 4
Глава 1. Анализ предметной области .................................................................... 5
1.1. Анализ исходных данных ............................................................................ 5
1.2. Актуальность разработки и аналоги .......................................................... 7
1.2.1. Менеджер высокой готовности QNX Neutrino .................................. 7
1.2.2. Мультиплекс-ОВ ................................................................................. 10
1.2.3. Обоснование разработки .................................................................... 13
1.3. Обзор методов резервирования данных и выбор наиболее
оптимального для бортового вычислителя..................................................... 14
1.3.1. Системы с «холодным» резервированием........................................ 15
1.3.2. Кластерные системы с «горячим» резервированием ...................... 17
1.3.3. Мажоритарные системы ..................................................................... 18
1.3.4. Выбор метода резервирования данных для бортового вычислителя
......................................................................................................................... 21
1.3.5. Реализация механизма резервирования данных для бортового
вычислителя ................................................................................................... 22
1.4. Обзор операционной системы реального времени QNX 4.25 .............. 25
1.4.1. Обзор технологий QNX 4.25 для создания кластерного
программного комплекса ............................................................................. 30
1.5. Постановка задачи...................................................................................... 31
Глава 2. Разработка алгоритмов программного обеспечения .......................... 34
2.1. Разработка алгоритмов и программного обеспечения модуля создания
и изменения конфигураций кластера .............................................................. 34
2
2.1.1. Блок создания, изменения и хранения конфигураций ................... 34
2.1.2. Блок инициализации кластера по созданной конфигурации .......... 43
2.1.3. Графический интерфейс пользователя для создания конфигураций
кластера .......................................................................................................... 44
2.2. Разработка алгоритмов и программного обеспечения модуля
менеджера проектов кластера .......................................................................... 47
2.2.1. Блок работы с файлами проектов: созданию, сохранению,
редактированию и открытию проектов ...................................................... 47
2.2.2. Графический интерфейс пользователя в режиме работы с
проектами ....................................................................................................... 51
2.3. Разработка алгоритмов и программного обеспечения модуля
мониторинга состояния кластера .................................................................... 53
2.3.1. Блок обработки получаемой по кластеру статистики от стороннего
программного обеспечения .......................................................................... 53
2.3.2. Графический интерфейс отображения общего состояния узлов сети
в режиме мониторинга кластера на основе статистики ............................ 57
Глава 3. Отладка и тестирование программного обеспечения ......................... 59
Глава 4. Контрольный пример ............................................................................. 63
Заключение ............................................................................................................ 71
Список литературы ............................................................................................... 72
Приложение ........................................................................................................... 73
3
ВВЕДЕНИЕ
В настоящий момент получили широкое распространение кластерные
системы повышенной готовности. Это класс систем, обеспечивающих
высокую отказоустойчивость на протяжении больших периодов времени за
счет
технологий
резервирования
данных.
Использование
технологий
кластеризации при разработке бортовых вычислителей крайне актуально
ввиду высоких требований к надежности и безопасности таких систем. Для
того, чтобы системы были гибкими в настройке и имели возможность
работать в разных конфигурациях, необходимо использовать программное
обеспечение,
конфигурации.
позволяющее
Также
создавать,
для
сохранять
повышения
и
запускать
надежности
эти
создаваемых
конфигураций нужно иметь возможность наблюдать за работающей
системой и диагностировать ошибки ее работы.
Главной задачей разработки программного обеспечения является
создание удобного инструмента, позволяющего создавать, модифицировать и
отлаживать конфигурации отказоустойчивого кластера, обеспечивающего
«горячее» резервирование данных и их быстрое восстановление в случаях их
утраты при аварии одного из процессоров бортового вычислительного
комплекса. Также в рамках поставленной задачи требуется разработать
графическую систему отображения состояния узлов вычислительного
комплекса для последующего мониторинга оператором с удаленного
терминала.
Разрабатываемое
программное
обеспечение
должно
функционировать под операционной системой реального времени QNX 4.25,
ввиду ее соответствия высоким стандартам надежности, безопасности и
масштабируемости, предъявляемых к бортовым системам.
Решению описанной задачи и посвящен данный дипломный проект.
4
ГЛАВА 1. АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ
1.1. Анализ исходных данных
Для
разработки
программного
обеспечения
заказчиком
были
установлены следующие исходные данные:
 операционная система реального времени QNX 4.25;
 унифицированный отказоустойчивый бортовой вычислитель (шифр
«Кластер»);
 модуль сбора статистики о функционировании каждого из узлов
кластера;
 язык программирования высокого уровня (Си/Си++).
Разрабатываемое программное обеспечение должно обеспечивать
возможности создания и слежения за конфигурациями отказоустойчивого
кластера. В связи с тем, что разработка кластера ведется для бортового
вычислителя, на него будет установлена операционная система реального
времени QNX 4.25, которая отвечает требованиям высокой надежности,
непрерывности управления и времени реакции при аварийных ситуациях.
Операционная система QNX 4.25 будет установлена на бортовой
вычислитель, состоящий из 3-х узлов, также разрабатываемый на ОАО «НИИ
«Аргон». В связи с тем, что вычислитель создается для использования на
борту и крайне важны такие параметры как вес и размер, вычислитель
спроектирован таким образом, чтобы имея в своем составе 3 полноценных
процессорных модуля, ограничиваться всего одним физическим корпусом с
централизованной системой охлаждения и вентиляции. Такая конструкция
призвана
сэкономить
вычислителя.
Между
пространство
узлами
и
будет
электроэнергию
осуществляться
при
работе
соединение
по
технологии Ethernet, что обеспечит высокую пропускную способность при
обмене данными между узлами сети.
5
Для работы отказоустойчивого кластера в его состав входит модуль
слежения за узлами. Этот программный компонент устанавливается на
каждый узел сети и выполняет сбор статистики о их работе, которая будет в
дальнейшем пересылаться блоку мониторинга, который входит в состав
программного комплекса, рассматриваемого в данном дипломном проекте.
Помимо статистики модуль также ведет сбор данных для резервирования
всех работающих на каждом из узлов процессов, также пересылая всю
информацию между узлами для дальнейшего перехвата в случае сбоя. С
данным
программным
модулем
будет
производиться
фактически
непрерывный обмен информации посредством встроенного в операционную
систему QNX механизма передачи сообщений.
В состав программного обеспечения также будут входить графические
пользовательские интерфейсы. Для создания графических пользовательских
интерфейсов используется визуальное средство разработки приложений
Photon Application Builder 1.14, поставляемое вместе с графической
оболочкой Photon для операционной системы реального времени QNX 4.25.
6
1.2. Актуальность разработки и аналоги
1.2.1. Менеджер высокой готовности QNX Neutrino
Менеджер высокой готовности (High availability manager (HAM))
функционирует в среде операционной системы реального времени QNX
Neutrino.
Менеджер высокой готовности обеспечивает:
 Мгновенное обнаружение сбоя. HAM применяет сообщения проверки
работоспособности (heartbeating), чтобы отслеживать состояние любого
компонента, позволяя фиксировать сбои как можно раньше. Если
HAM фиксирует определенное условие или сбой, он может немедленно
и автоматически посылать сообщение о неисправности другим
компонентам;
 Адаптированное к пользователю восстановление после сбоя. Используя
библиотеку HAM, приложение может дать указание HAM, какие
действия
по
восстановлению должны
быть
предприняты,
в
соответствии с порядком, в котором произошли ошибочные условия;
 Немедленное повторное соединение. HAM также обеспечивает
библиотеку программ для выполнения действий по восстановлению
клиента,
которая
позволяет
системе
немедленно
повторно
устанавливать разорванное соединение в случае сбоя компонента;
 Анализ после аварийного завершения. Если процесс завершился в
результате сбоя, HAM может генерировать полный дамп памяти для
последующего анализа этой ситуации. Просмотрев этот файл, можно
немедленно точно определить, какая строка кода вызвала ошибку и
анализировать данные переменных, чтобы точно определить, что
произошло.
7
В качестве самоуправляемого менеджера HAM устойчив к внутренним
сбоям. Если он по каким-либо причинам аварийно останавливается, он может
немедленно и полностью реконструировать свое собственное состояние.
Менеджер высокой доступности
позволяющий
выполнять
(HAM) обеспечивает механизм,
мониторинг
процессов
и
сервисов
в
функционирующей системе. Целью менеджера является восстановление
вычислительного процесса, когда системные сервисы или процессы выходят
из строя, не отвечают или обеспечивают неприемлемый уровень сервиса.
Структура HAM прозрачно расширяет механизм локального мониторинга в
сетевой мониторинг.
HAM действует как проводник, через который остальная система
может как получать, так и доставлять информацию, имеющую отношение к
состоянию системы в целом. Система может быть единственным узлом или
набором узлов, соединенных через QNET.
мониторинг
HAM может выполнять
специфических процессов и может управлять поведением
системы, когда специфические компоненты выйдут из строя и должны быть
восстановлены. HAM также позволяет внешним компонентам запрашивать
информацию об интересующих событиях в системе, и может выполнять
требуемые действия в момент возникновения этих событий.
HAM состоит из следующих трех компонентов:

Объекты (Entities)

Условия (Conditions)

Действия (Actions)
Объекты
(Entities)
являются
фундаментальными
единицами
мониторинга в системе. По существу объектом является процесс, каждый
процесс имеет уникальный идентификатор
8
pid.
Каждому объекту
соответствует символическое имя, которое может использоваться, чтобы
обращаться к этому специфическому объекту. Имена, соответствующие
объектам уникальны в системе. Менеджеры
в настоящий момент
соответствующие узлу, также применяют к узлу уникальные правила. Эти
уникальные
требования
очень
похожи
на
схему
наименований,
используемую в иерархической файловой системе.
Условия соответствуют объектам. Эти условия представляют собой
состояние объекта. Примеры условий:
 объект завершился;
 объект пропустил сообщение heartbeat;
 объект аварийно завершился, генерируется файл дампа памяти;
 выполнен рестарт объекта.
Условия (Conditions)соответствуют
символические имена,
которые
также должны быть уникальны внутри объекта.
Действия
множество
соответствуют
действий.
условиям. Условие может содержать
Действия
выполняются
каждый
раз,
когда
соответствующее условие выполнено, т.е. истинно. Действия внутри условия
выполняются в порядке FIFO (порядок, в котором они были добавлены в
условие). Множество условий, которые являются истинными запускаются
одновременно
в
произвольном
специфицированные как
(arbitrary)
порядке.
Условия,
HCONDINDEPENDENT будут выполняться в
отдельном потоке (separate thread) выполнения, параллельно с другими
условиями.
Примеры действий:
 рестарт объекта;
 посылка сигнала некоторому процессу.
9
Действия также ассоциируются с символическими именами, которые
уникальны в пределах специфического условия.
Когда действие в списке действий выполняется ошибочно, можно
определить альтернативный список действий, которые будут выполнены,
чтобы реализовать восстановление после сбоя данного действия.
Механизм восстановления позволяет выполнить восстановление в
случае сбоя единичного сервиса или процесса.
Фактически внутреннее состояние HAM похоже на иерархическую
файловую
систему,
где
объекты
похожи
на
директории,
условия
соответствуют этим объектам как поддиректории, и действия внутри этих
условий похожи на узлы листьев этой структуры дерева.
HAM также представляет это состояние как файловую систему в
режиме только чтения (read-only) под управлением директории /proc/ham. В
результате такого представления произвольные процессы
могут также
просматривать текущее состояние (например, можно выполнить команду
ls /proc/ham).
1.2.2. Мультиплекс-ОВ
Мультиплекс-ОВ представляет собой комплект средств (КС) для
организации
отказоустойчивых
вычислений.
Он
предназначен
для
обеспечения отказоустойчивого функционирования серверных приложений в
локальной вычислительной сети под управлением ОС МСВС 3.0.
Основные возможности:
 Автоматическое восстановление функционирования приложения
после сбоя (время восстановления не более 10 сек);
10
 Возможность
балансировки
вычислительной
нагрузки
на
серверах;
 Возможность
изменения
логики
принятия
решения
при
осуществлении балансировки;
 Возможность мониторинга и управления работой компонентов
кластера серверов и выполняемых на нем приложений;
 Возможность расширения списка регистрируемых событий;
 Взаимодействие внешних клиентов с КС ОВ;
 Контроль
технологических
параметров
функционирования
кластера.
В состав комплекта входят следующие программы:
1)
Программа
функционированием
КС
«Управление
ОВ»
«Мультиплекс-ОВ»,
обеспечивает
его
управление
инициализацию
и
конфигурирование. Для организации логики управления КС «МультиплексОВ» используются две основные технологии:
 технология управления ресурсами основана на распределении и
перераспределении ресурсов между ЦВМ КС «Мультиплекс-ОВ»
в зависимости от настроек конфигурации, состояния ЦВМ,
состояния самих ресурсов;
 технология балансировки нагрузки основана на виртуализации
ЦВМ КС «Мультиплекс-ОВ» и перераспределении процессов
обработки клиентских запросов между ЦВМ;
2) Программа «Организация ОВ» обеспечивает реализацию функций
управления процессом организации отказоустойчивых вычислений. В
процессе подключения и отключения новых ЦВМ к системе ОВ происходит
11
масштабирование системы, При этом логика управления ресурсами берет на
себя функции их распределения между работающими ЦВМ системы. Кроме
того, на основе анализа состояния системы ОВ в целом, определяется
наличие кворума и целесообразность продолжения функционирования
сегмента, как элемента ОВ;
3) Программа «Мониторинг
ОВ» обеспечивает мониторинг и
управление работой компонентов КС «Мультиплекс-ОВ». Мониторинг
необходим для отслеживания событий, требующих вмешательства оператора.
При правильных настройках конфигурационных файлов КС «МультиплексОВ» оператор автоматически извещается о всех нарушениях работы. Задачей
оператора является своевременное информирование соответствующих или
иных служб и инициирование ликвидации сбоя;
4) Программа «Сопряжение ОВ» обеспечивает сопряжение различных
модулей и их совместное функционирование в составе КС «МультиплексОВ»;
5)
Программа «Тестирование ОВ» обеспечивает тестирование
функций КС «Мультиплекс-ОВ».
На ЦВМ, входящих в состав кластера серверов Мультиплекс-ОВ, для
выполнения программ должно быть настроено сетевое взаимодействие
между ЦВМ по протоколу TCP/IP.
Реализация отказоустойчивого функционирования достигается за счет
реализации двух механизмов: механизма управления ресурсами кластера и
механизма балансировки нагрузки.
Входными данными для КС Мультиплекс-ОВ являются события
изменения узлов и/или ресурсов кластера. Выходными данными КС
Мультиплекс-ОВ являются новые оптимальные состояния узлов и ресурсов
12
кластера как результат соответствующей миграции процессов, ресурсов и
приложений.
1.2.3. Обоснование разработки
При разработке отказоустойчивого бортового вычислителя возникла
необходимость создания программного обеспечения, которое позволяло бы
человеку-оператору, находящемуся за удаленным терминалом или рядом с
самим комплексом, настраивать работу системы, создавать различные
конфигурации кластера, запускать и тестировать их, а также проводить
быструю диагностику состояния работающего кластера.
Актуальность разработки отказоустойчивого кластера подтверждается
полным
отсутствием
рассматриваемой
программного
операционной
обеспечения
системы
QNX
такого
4.25.
типа
для
Необходимость
разработки именно под данную операционную систему обусловлена ее
гибкостью, встраиваемостью и масштабируемостью, а также низкими
системными требованиями и очень высокой надежность, которые полностью
соответствуют заявленным заказчиком требованиям. Также стоит отметить,
что операционная система QNX 4.25 сертифицирована для использования
Государственной технической комиссией при Президенте РФ для подобных
разработок.
13
1.3. Обзор методов резервирования данных и выбор наиболее
оптимального для бортового вычислителя
Сегодня распространены несколько типов систем высокой готовности.
Среди них кластерная система является воплощением технологий, которые
обеспечивают высокий уровень отказоустойчивости при низкой стоимости.
Отказоустойчивость кластера обеспечивается дублированием всех жизненно
важных компонентов. Максимально отказоустойчивая система должна не
иметь ни единой точки, то есть активного элемента, отказ которого может
привести к потере функциональности системы. Такую характеристику как
правило
называют
–
отсутствие
единой
точки
отказа.
При построении систем высокой готовности, главная цель - обеспечить
минимальное время простоя.
Для того, чтобы система обладала высокими показатели готовности,
необходимо:
1. чтобы ее компоненты были максимально надежными;
2. чтобы она была отказоустойчивая, желательно, чтобы не имела
точек отказов;
3. а также важно, чтобы она была удобна в обслуживании и
разрешала проводить замену компонент без останова.
Отказоустойчивость
кластеризации.
обеспечивается
Благодаря
кластеризации
с
помощью
достигается
технологий
такая
схема
функционирования, когда при отказе одного из компьютеров задачи
перераспределяются
между
другими
узлами
кластера,
которые
функционируют исправно. Причем одной из важнейших задач кластерного
программного обеспечения является обеспечение минимального времени
восстановления системы в случае сбоя, так как отказоустойчивость системы
нужна именно для минимизации так называемого внепланового простоя.
14
В
рассматриваемой
ситуации,
когда
необходимо
обеспечить
бесперебойную работу такого важного коммуникационного компонента, как
модуля связи с наземными объектами, чаще всего используются 3 вида
систем резервирования (обеспечения отказоустойчивости):
 «Холодное» резервирование
 «Горячее» резервирование
 Мажоритарные системы.
Рассмотрим каждый из принципов построения отказоустойчивой
системы для выбора наиболее подходящего.
1.3.1. Системы с «холодным» резервированием
В случае отказоустойчивого вычислителя система состоит из трех
узлов. Реализация отказоустойчивости в системе основана на периодической
обработке средствами ОС контрольных точек, заданных временными
рамками или сложностью задачи. В контрольной точке прерывается
выполнение прикладной задачи, после чего между ЦВМ происходит:
1. обмен текущими результатами выполнения задачи;
2. определение признака сбоя обработки данных;
3. при наличии признака - восстановление вычислительного
процесса на минимально-загруженном узле или на изначальном
узле в случае его корректного функционирования.
Для обеспечения перезапуска процесса вычислений необходимо
создавать контрольные точки, то есть запоминать результаты вычислений.
Рестарт (перезапуск) осуществляется с ближайшей контрольной точки. Чем
чаще устанавливаются контрольные точки, тем меньше времени будет
15
потрачено на повторение вычислений в процессе рестарта, однако создание
контрольных точек также требует дополнительного времени и объема памяти
для их хранения.
Данная
бортовая
система
характеризуется
следующими
особенностями:
 полная поддержка замены одних узлов другими в горячем режиме;
 прозрачное переключение задачи на другой узел при отказе;
 возможность диагностики в реальном времени;
 мониторинг и составление отчетов.
Под управлением ресурсами отказоустойчивого комплекса понимается
искусственная коррекция работы ПО и средств внутрикомплексного обмена,
позволяющая в течение нескольких контрольных точек после момента
обнаружения неисправности иметь в каждой ЦВМ достаточную информацию
о признаках ее проявления для того, чтобы неисправный элемент системы
был им однозначно определен. В число действий, реализуемых при такой
коррекции, входят:
 запоминание/восстановление в памяти значений параметров
состояния
вычислительного
процесса,
обеспечивающих
возможность восстановления с помощью процедуры рестарта;
 перераспределение полномочий управления процессами между
ЦВМ.
Под
процедурой
рестарта
понимается
способ
восстановления
вычислительного процесса в комплексе ЦВМ, требующем повторного
выполнения функциональных задач с исходными данными, записанными в
КТ, обработанной до момента обнаружения неисправности.
16
После того, как при обработке контрольной точки обнаружено
проявление неисправности, выполняется процедура рестарта. При этом в
памяти каждой ЦВМ восстанавливаются значения параметров состояния
вычислительного процесса, записанные в последней точке хранения до
проявления
неисправности,
после
чего
осуществляется
повторное
выполнение прикладных задач.
1.3.2. Кластерные системы с «горячим» резервированием
Кластер высокой готовности – это группа модулей, работающих как
единая система для предоставления высокой скорости доступа к данным.
Кластерная
система
характеризуется
высокой
надежностью,
производительностью, гибким масштабированием и легкостью в управлении.
Высокая надежность достигается путем дублирования всех критически
важных
для
работы
компонентов.
Высокая
производительность
и
масштабируемость – за счет распределения нагрузки между узлами
(серверами)
кластера.
Легкость
управления
подразумевает
собой
возможность управлять как централизованно всем кластером, так и
отдельными модулями, входящими в его состав.
Кластеры высокой готовности используются в тех ситуациях, когда
отказы информационной системы недопустимы и доступ к информации
должен быть непрерывен. В нашем случае это система связи с землей,
которая является крайне важным элементом управления самолетом и должна
быть доступна абсолютно всегда на протяжении полета.
Высокая доступность ресурсов кластерной системы в случае сбоя
одного из серверов обеспечивается за счет перемещения задач или
приложений между узлами. Для элементов других систем, связанных с
кластерным модулем, кластер выглядит как единое целое и его неполадки
могут выразиться в кратковременном снижении производительности или
17
недоступности какого-либо ресурса на время от нескольких секунд до
нескольких минут в зависимости от конфигурации.
Для того, чтобы задача могла корректно работать в кластере, она
должна регулярно сохранять данные на общем носителе информации, а не
хранить в оперативной памяти, тогда в случае сбоя одного из модулей, все
данные, обработанные на момент отказа будут доступны и другой модуль,
перехватывающий работу отказавшего, сможет продолжить с места отказа, а
не будет вынужден начинать выполнение задачи с самого начала.
1.3.3. Мажоритарные системы
В случае построения отказоустойчивого вычислителя по принципу
мажоритарной системы, он состоит из трех процессорных модулей,
соединенных между собой.
Каждый процессорный модуль представляет
собой отдельную БЦВМ.
Начальная загрузка БЦВМ мажоритарной системы (МС) производится
одновременно при включении питания. В процессе начальной загрузки
каждая БЦВМi (i -номер БЦВМ) имеет в памяти идентичные программные
средства. В памяти каждой БЦВМ имеется таблица конфигурации, в которой
отражается состояние всех БЦВМ МС. Строки таблицы конфигурации
содержат следующие данные:
 признак наличия БЦВМ в конфигурации;
 состояние БЦВМ;
 признак текущей БЦВМ;
 режим работы БЦВМ
для каждого внешнего интерфейса
(активный, пассивный);
 признак главной БЦВМ. Одна из БЦВМ является главной;
18
 типы сбоев и реакция на них;
 информация для реконфигурации.
При
инициализации
таблица
конфигурации
соответствует
конфигурации МС. В процессе функционирования таблица конфигурации
отражает текущее состояние МС. Программа реконфигурации может менять
данные в таблице конфигурации МС.
После выполнения начальной загрузки и тестирования на каждой из
БЦВМ МС выполняется обмен сообщениями между БЦВМ о готовности
БЦВМ к работе.
При этом главный процессор отвечает за решение о
готовности к работе всех
БЦВМ МС, он же выполняет синхронизацию
работы всех ЦВМ после принятия этого решения.
В
процессе
функционирования
системы
синхронизация
БЦВМ
выполняется по контрольным точкам. Параллельный просчет выполняется
на всех трех БЦВМ, при этом содержимое памяти этих БЦВМ должно быть
идентичным.
Для проверки правильности результатов вычислений и
синхронизации работы БЦВМ
при параллельном просчете используется
синхронный механизм контрольных точек сравнения.
В контрольных точках вычислительный процесс в каждой из БЦВМ
приостанавливается, подготавливаются данные, отражающие результаты
вычислений,
и
происходит
обмен
этими
данными
между
БЦВМ.
Правильность результатов фиксируется только в том случае, если данные во
всех БЦВМ МС совпали.
Восстановление вычислительного процесса в МС выполняется в случае
несовпадения результатов при параллельном просчете,
или в случае
обнаружения каких- либо случайных сбоев при контрольном тестировании.
При
этом выполняется
синхронное восстановление вычислительного
19
процесса во всех БЦВМ МС. Процедура восстановления вычислительного
процесса заключается в выполнении следующих действий:
1. рестарте всех БЦВМ МС с последней контрольной точки
хранения;
2. определении правильных исходных данных для продолжения
вычислительного процесса.
3. Если перечисленные действия не дали результата, определяется
неисправная БЦВМ. Неисправной БЦВМ считается в следующих
случаях:
4. происходят
ошибки
при
выполнении
операций,
которые
фиксируются процессором (например, переполнение, ошибка
адресации и т.п.), и которые не исправляются при выполнении
рестарта с контрольной точки хранения;
5. данные БЦВМ не совпали с данными двух других БЦВМ (в двух
других БЦВМ данные совпали);
6. контрольные тесты выявили неисправимые ошибки;
7. зафиксировано отсутствие ответа от какой-либо БЦВМ МС в
течение определенного промежутка времени (истекает тайм-аут).
В случае неисправной БЦВМ,
выполняется
автоматическая
реконфигурация, неисправная БЦВМ удаляется из системы. Реконфигурация
МС выполняется в соответствии с таблицей конфигурации МС, в которой
отражается состояние каждой БЦВМ, и определяется порядок исключения
неисправной БЦВМ из конфигурации.
Реконфигурация выполняется по
инициативе главной БЦВМ, она рассылает информацию о текущей
конфигурации всем остальным БЦВМ. Если выходит из строя главная
20
БЦВМ, происходит голосование, и определяется новая главная БЦВМ,
которая управляет реконфигурацией.
1.3.4. Выбор метода резервирования данных для бортового
вычислителя
Из обзоров всех трех методов кластеризации, можно каждую систему
охарактеризовать определенными преимуществами. Оформим их в виде
таблицы для наглядности.
Метод
Особенности
Холодное
Восстановление задачи происходит быстро, но
резервирование
только с начальными данными, все вычисления
придется проводить заново
Горячее
резервирование
Восстановление задачи быстрое, данные и
прогресс выполнения задачи будут восстановлены с
момента отказа, т.к. данные доступны всем узлам
сразу
Мажоритарн
ая система
Задачи выполняются одновременно на всех
узлах и результаты сравниваются, при ошибке
происходит синхронное возвращение всех данных на
последнее верное значение
Таблица 1. Особенности методов кластеризации
21
Исходя из приведенных в таблице 1 особенностей каждого из методов,
второй является самым оптимальным, поскольку позволит восстановить
данные максимально быстро. Значит, для бортового вычислителя будет
использоваться метод «горячего» резерва.
1.3.5. Реализация механизма резервирования данных для бортового
вычислителя
Из рассмотренных ранее методов кластеризации вычислительных
процессов в нашем случае наиболее подходящим является метод «горячего»
резервирования данных. Это объясняется тем, что в каждый момент времени
может произойти миграция ресурсов с одного узла на другой практически без
временных и вычислительных потерь за счет сохранения всех результатов
вычислений для каждого из процессов на каждом из узлов.
В общем виде схема бортового вычислителя, построенного на
принципе «горячего» резервирования будет выглядеть следующим как на
рис. 1.
Рисунок 1. Схема кластерного ПО бортового вычислителя
Рассмотрим подробнее схему, показанную на рис. 1. Сам бортовой
вычислитель состоит из нескольких (минимум 3-х) узлов. Каждый из них
может выполнять свою задачу параллельно с работой остальных. Все
22
результаты своей работы он хранит на общем носителе так, чтобы они были
доступны всем остальным узлам. С точки зрения внешних систем,
взаимодействующих с бортовым вычислителем, он выглядит как единое
целое и вне зависимости от количества узлов будет дальше работать и
передавать данные наружу.
При выходе какого либо узла из строя, данные, накопленные им за
период обработки задачи, остаются доступны другим узлам и новый узел,
взявший на себя его задачу, может начать ее выполнение с той самой точки,
на которой был прерван предыдущий.
То есть, после к примеру выхода из строя узла №4, на котором были
запущены несколько задач(процессов) произойдет реконфигурация системы
на взаимодействие меньшего количества узлов(4 вместо 5). Во время
реконфигурации будет произведен перенос всех выполняемых ранее
процессов с узла №4 на другой, наиболее свободный узел сети.
Перенесенные процессы на новом узле будут запущены с того самого
состояния, на котором было прервано их выполнение на родном узле. Таким
образом, с точки зрения вычислительного процесса не произойдет никаких
изменений и конечный вывод результатов из вычислителя будет на выходе
ничем не отличаться от вывода системы со всеми работоспособными узлами.
Схематично работу системы до и после аварии можно изобразить как
показано на рис. 2.
23
Рисунок 2. Работа системы до и после аварии одного из узлов
Таким образом, система становится гораздо более устойчивой к
отказам, становится вычислительно более мощной и способна за счет
резервирования данных потерять до 30% своих аппаратных ресурсов, не
теряя при этом данных и продолжая выполнение поставленной задачи.
24
1.4. Обзор операционной системы реального времени QNX 4.25
Разработка комплекса программного обеспечения для
отказоустойчивого кластера
создания
производилось для операционной системы
реального времени QNX версии 4.25.
QNX — это POSIX-совместимая операционная система реального
времени, предназначенная преимущественно для встраиваемых систем. QNX
является коммерческим продуктом, то есть проприетарным программным
обеспечением, а следовательно требует лицензии для установки на
вычислительные машины и комплексы.
Как микроядерная операционная система, QNX основана на идее
работы основной части своих компонентов как небольших задач, называемых
сервисами. Это отличает ее от традиционных монолитных ядер, в которых
ядро операционной системы – одна большая программа, состоящая из
большого
количества
«частей»,
каждая
со
своими
особенностями.
Использование микроядра в QNX позволяет пользователям (разработчикам)
отключить любую ненужную им функциональность, не изменяя ядро. Вместо
этого можно просто не запускать определённый процесс. Система достаточно
небольшая, вместе с этим она считается очень быстрой и должным образом
«законченной» (практически не содержащей ошибок).
25
Менеджер
файловой
системы
Менеджер
процессов
Микроядро
Менеджер
устройств
Менеджер сети
Рисунок 3. Схема архитектуры операционной системы QNX
QNX
состоит
из
небольшого
ядра,
координирующего
работу
взаимодействующих процессов. Как показано на рисунке 3, структура
больше напоминает не иерархию, а команду, в которой несколько игроков
одного уровня взаимодействуют между собой и со своим "защитником" ядром.
Микроядро операционной системы выполняет основные следующие
функции:
 передача сообщение – микроядро обеспечивает маршрутизацию всех
сообщений между всеми процессами в системе;
 диспетчеризация – планировщик - это часть микроядра, и он получает
управление всякий раз, когда процесс изменяет свое состояние в
результате получения сообщения или прерывания.
В отличие от всех остальных процессов, ядро никогда не получает
управления в результате диспетчеризации.
26
Входящий в состав ядра код
выполняется только в результате прямых вызовов из процесса или
аппаратного прерывания.
Все функции операционной системы, за исключением тех, которые
выполняются ядром, в QNX предоставляются через стандартные процессы.
Типичная конфигурация QNX имеет следующие системные процессы:
 менеджер процессов;
 менеджер файловой системы;
 менеджер устройств;
 менеджер сети.
Для разработки требуемого комплекса программного обеспечения
важен принцип работы менеджера процессов, позволяющего производить
межпроцессное взаимодействие через специальный механизм связи через
сообщения QNX.
Менеджер процессов тесно взаимодействует с Микроядром, чтобы
обеспечить услуги, составляющие сущность операционной системы. Хотя он
и является единственным процессом, который использует то же адресное
пространство, что и Микроядро, Менеджер процессов выполняется как
истинный процесс. И он, как и все остальные процессы, подвергается
диспетчеризации
со
стороны
Ядра
и
использует
предоставляемые
Микроядром примитивы передачи сообщений для связи с другими
процессами в системе.
Менеджер процессов отвечает за создание новых процессов в системе и
за управление основными ресурсами, связанными с процессом. Все эти
услуги предоставляются посредством сообщений. Так, например, если
процесс хочет породить новый процесс, он делает это, посылая сообщение с
указанием атрибутов создаваемого процесса. Обратите внимание, что т.к.
27
сообщения передаются по сети, вы можете легко создать процесс на другом
узле сети, послав сообщение Менеджеру процессов на этом узле.
Любому процессу, запускаемого в системе присваивается свой
идентификационный номер – pid, который можно определить для каждого
процесса системной функцией. После определения pid требуемого процесса,
ему можно отравлять запросы. Стоит отметить, что при этом формат данных
запроса должен полностью совпадать с форматом данных, используемых в
запрашиваемом процесса.
Микроядро QNX поддерживает три важнейшие формы связи между
процессами: сообщения, прокси и сигналы.

Сообщения - это основополагающая форма IPC в QNX. Они
обеспечивают синхронную связь между взаимодействующими процессами,
когда
процессу,
посылающему
сообщение,
требуется
получить
подтверждение того, что оно получено и, возможно, ответ.

Прокси - это особый вид сообщения. Они больше всего подходят
для извещения о наступлении какого-либо события, когда процессу,
посылающему сообщение, не требуется вступать в диалог с получателем.

Сигналы - это традиционная форма IPC. Они используются для
асинхронной связи между процессами.
Для непосредственной связи друг с другом взаимодействующие
процессы
используют
встроенные
в
программирования Си (табл. 2).
28
систему
функции
языка
Функция языка Си
Назначение
Send()
посылка сообщений
Receive()
получение сообщений
Reply()
ответ процессу, пославшему
сообщение
Таблица 2. Функции взаимодействия процессов
Однако рассмотренные выше функции работают с сообщениями,
состоящими из непрерывной последовательности байт. На практике
сообщения часто состоят из двух или более отдельных частей. Например,
сообщение может иметь заголовок фиксированной длины, за которым
следуют данные переменной длины. Для того чтобы избежать копирования
частей такого сообщения во временные промежуточные буферы при
передаче или приеме, может быть использовано составное сообщение,
состоящее из двух или более отдельных буферов сообщений. Именно
благодаря этой возможности менеджеры ввода/вывода QNX, такие как Dev и
Fsys, достигают своей высокой производительности.
Следующие функции позволяют обрабатывать составные сообщения:
Creceivemx(), Readmsgmx(), Receivemx(), Replymx(), Sendmx(), Writemsgmx().
Некоторые
из
них
будут
использоваться
в
блоке
взаимодействия
программного обеспечения с драйвером и со сторонним программным
обеспечением. При этом составные сообщения описываются с помощью
встроенной mx структуры.
29
1.4.1. Обзор технологий QNX 4.25 для создания кластерного
программного комплекса
Операционная система реального времени QNX 4.25 изначально не
располагает функциями параллельных вычислений и не предусматривает
встроенных технологий резервирования данных между узлами кластерной
сети. Однако, сама система предоставляет очень гибкие возможности для
работы в сети. Это связано с тем, что в QNX реализован специальный
механизм обмена сообщениями между процессами. Причем место работы
процесса не имеет значения, т.к. каждый процесс, запущенный на узле,
включенном в сеть, будет занесен в глобальную таблицу, используя которую
любой другой процесс, как локальный, так и нет, сможет обратиться к
первому. Таким образом, объединив узлы в сеть, появляется возможность
мгновенного обмена сообщениями между узлами, а также между процессами
одного узла. Именно за счет этой особенности рассматриваемое в данном
дипломном проекте программное обеспечение может быть запущено с
любого из узлов кластера, при этом никаких различий от запусков и работы с
других узлов в системе не будет. Также, используя глобальную таблицу
процессов, появляется возможность работы с удаленного терминала,
подключенного к сети и удаляемого из нее после конфигурации кластера.
Еще одной особенностью, способствующей созданию кластера именно
на QNX, является система мгновенных ответов и оповещений процессами
друг друга, позволяющей моментально понять, находится процесс, к
которому идет обращение, в работе, не активен или завис. Это позволяет
свести время реакции программы на возникновение сбоев при выполнении к
возможному минимуму, увеличив таким образом надежность системы в
целом.
30
1.5. Постановка задачи
Целью дипломного проекта является разработка программного модуля
диспетчера высокой готовности для ОСРВ QNX 4.25, позволяющего
запускать различные конфигурации отказоустойчивого кластера, а также в
режиме реального времени проводить мониторинг его состояния. Исходя из
требований, предъявляемых проекту необходимо разработать 3 основных
блока программного комплекса:
 блок менеджера проектов;
 блок создания и изменения конфигураций кластера;
 блок мониторинга состояния кластера.
31
Рисунок
4.
Структурная
схема
комплексного
32
программного
обеспечения
«Кластер»
При
этом
пользователь,
каждый
должен
из
блоков,
обладать
с
своим
которыми
взаимодействует
графическим
интерфейсом,
позволяющим оператору производить нужные операции в максимально
упрощенной форме, а режим мониторинга должен быть максимально
информативным визуально.
Кроме того, каждый из блоков будет взаимодействовать со сторонними
элементами операционной системы:
 блок менеджера проектов должен производить функции записи и
чтения из файловой системы
 блок конфигуратора кластера должен иметь доступ как к
файловой системе для хранения и изменения конфигураций, так и
доступ к сети, позволяющей отдавать команды стороннему
программному
обеспечению
для
инициализации
конфигурационных файлов будущих узлов кластера
 блок мониторинга состояния должен иметь доступ к сети для
постоянного обмена пакетами статистики состояния узлов и
последующего ее отображения оператору
Следовательно, каждую из особенностей блоков необходимо вынести
как отдельную составляющую каждого блока.
33
ГЛАВА 2. РАЗРАБОТКА АЛГОРИТМОВ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
2.1. Разработка алгоритмов и программного обеспечения модуля
создания и изменения конфигураций кластера
Одним из основных блоков комплекса программного обеспечения
отказоустойчивого кластера является модуль конфигуратора кластера.
Создание конфигураций и их дальнейшая инициализация – первая задача,
которую должен решать программный продукт, рассматриваемый в данном
дипломном проекте.
Отказоустойчивый кластер – вычислительный комплекс из 3-х узлов,
на каждом из которых могут быть запущены различные процессы.
Конфигурацией в текущем контексте называется совокупность параметров,
характеризующих каждый из узлов кластера, перечень задач, запущенных на
узлах и особенности взаимосвязи этих задач между собой. Поскольку такой
набор
параметров
представляет
собой
достаточно
большой
объем
структурированной информации, необходимо реализовать программный
пользовательский интерфейс, позволяющий задавать эти параметры в
удобной оператору форме и дающий возможность максимально быстро их
изменять и модифицировать.
2.1.1. Блок создания, изменения и хранения конфигураций
Структура конфигураций кластера состоит главным образом из
элементов, отвечающих за параметры узла и элементов, отвечающих за
параметры процессов запущенных на каждом из узлов. Для того, чтобы
информация была в достаточной мере структурирована и удобна в
использовании,
была
разработана
иерархия
хранения
конфигурации и формат хранения данных конфигурации.
34
информации
Суть
иерархии
заключается
во
вложенности
параметров.
Для
пользователя главной сущность является сама конфигурация, модифицируя
которую он прежде всего столкнется с добавлением в нее узлов и заданием
их параметров. После того как узел добавлен в конфигурацию, можно
приступать
к
добавлению
процессов,
которые
при
инициализации
конфигурации будут запущены на выбранном узле.
Схематично иерархия конфигурации представлена на Рис. 5.
Кластер
Узел 1
1
Узел 2
3
Узел 3
N
1
Процессы на первом узле
2
3
Процессы на третьем узле
1
3
7
M
Процессы на втором узле
Рисунок 5. Иерархия формата конфигурации кластера
Настройки узлов в конфигурации кластера
В соответствии с требованиями заказчика каждый узел должен иметь в
конфигурации
некий
минимум
информации,
характеризующей
его
назначение. В связи с этим при создании узла оператор назначает
уникальный цифровой идентификатор узла кластера, задает имя узла (к
примеру «Главный») и может добавить к нему краткое описание, чтобы в
дальнейшем, при редактировании или пересмотре конфигурации можно было
35
понять функции выбранного узла. К примеру, на первом узле будут
запущены все процессы, которые должны выполняться системой. А
оставшиеся 2 узла будут выполнять резервирующую функцию. Тогда
первому можно будет в описании указать, что все процессы должны
выполняться на нем, а двум другим, что они резервные. В итоге изменяя
информацию о процессах, оператор уже заранее будет знать, что
распределение процессов на другие узлы в начальной конфигурации
выбранного проекта не предусматривалось и, следовательно, не допустит
ошибок.
Также стоит отметить, что после распределения процессов по узлам
пользователю доступна возможность упорядочивания запуска заданных
процессов, что позволяет изменять очередность запуска задач не прибегая к
редактированию самих задач.
Вся конфигурация хранится единой структурой, записываемой в
конфигурационный файл проекта. Для хранения параметров узлов в ее состав
входят структуры, отвечающие за узлы.
Разработанный тип хранения данных узлов, оформленный в виде
структуры, выглядит на языке Си следующим образом:
typedef struct
{
int
NodeId;
char Name[20];
char Descr[255];
char ProcFile[20];
struct Node *next;
} Node;
36
Node Nodes[3];
Следовательно, для трех узлов объявляется 3 структуры такого типа.
Настройки процессов на узлах в конфигурации кластера
После добавления в конфигурацию узлов оператор может распределить
процессы по узлам кластера. При этом есть 2 возможных варианта
распределения, подходящих для разных ситуаций:
 распределение всех процессов по узлам после добавления узлов;
 распределение процессов на конкретном узле сразу же после его
создания.
Первый вариант удобен когда между процессами на разных узлах
должна быть установлена некая зависимость или связь, тогда невозможно
задать эту зависимость, если узла и, соответственно, нужного процесса на
нем в конфигурации еще нет. Второй же вариант позволяет распределить
процессы на узле сразу же после его создания, используется, если
конфигурация линейна.
Каждый процесс, также как и узел, имеет свой уникальный
идентификатор, имя и описание. Но помимо стандартных параметров
процессу могут быть заданы дополнительные свойства:
 параметры запуска процесса;
 приоритет запуска процесса;
 флаг запуска при старте;
 весовой коэффициент процесса;
 индекс очередности запуска;
 привязка процесса к определенному IP-адресу;
37
 зависимость от другого процесса.
Такие параметры как приоритет, индекс и флаг запуска, а также
весовой коэффициент созданы для дальнейшего учета при возникновении
ситуаций, когда процессы необходимо перераспределить. К примеру, чем
выше приоритет процесса, тем больше процессорного времени он получит,
чем выше весовой коэффициент, т.е. важность процесса, тем на более
свободный узел он будет перенаправлен. Привязка к адресу может быть
сделана для того, чтобы при миграции с одного узла на другой, система учла
этот параметр и переназначила новому узлу старый адрес для дальнейшего
корректного функционирования мигрирующего процесса.
Каждый из данных параметров также хранится в структурах,
характеризующих каждый из процессов. Для этого был разработан формат
хранения структур и на языке Си он выглядит следующим образом:
typedef struct
{
int
Num;
int
Descr[255];
char FileName[100];
char Opts[20];
int
Deps[299];
int
Prio;
int
Run;
int
Order;
int
Weight;
38
} Process NodePcs[3][300];
Нижняя строчка информирует нас о том, что данным типом будет
объявлен двумерный массив, первый идентификатор которого привязывает
процесс к узлу, а второй закрепляет за процессом его уникальный
идентификатор на этом узле. Количество процессов в 300 штук вызвано
ограничением на узел, установленным заказчиком.
Помимо возможностей добавления узлов, редактор конфигурации
также позволяет удалять и изменять уже имеющиеся в конфигурации
элементы. При этом данные, загруженные ранее из структур, обнуляются, а
флаги их существования устанавливаются в значение «-1», что делает
адресное пространство, ранее используемое ими, снова доступным для
заполнения уже новыми данными.
После создания в визуальном редакторе конфигурации для кластера
для ее дальнейшего использования ее необходимо сохранить. Сохранение
конфигурации происходит на жесткий диск в конфигурационный файл,
находящийся в папке с проектом, в бинарном виде для простоты
последующего считывания.
Перед сохранением конфигурации она может быть проверена
встроенным анализатором. Это первичная диагностика ошибок ввода
оператора. К примеру, пользователь ввел вместо имени узла
последовательность знаков препинания, тогда процедура проверки,
обнаружив некорректный ввод, выведет предупреждение и предложит
пользователю заполнить имя названием по умолчанию, к примеру, Node1.
Стоит отметить очень важную особенность, присутствующую в каждой
конфигурации – наличие главного узла сети. Главный узел – такой же узел
кластера, как и остальные с технической точки зрения, но имеющий
39
некоторые отличия в полномочиях и функционале. Прежде всего главный
узел тот, который был добавлен в систему первым и в случае его аварии все
его права и обязанности будут переданы следующему узлу по порядку
добавления. Основная задача главного узла – сбор статистики со всех
второстепенных узлов, превращение общей статистики в единый пакет
заранее разработанного формата и его пересылка остальным узлам для
резервирования. То есть с точки зрения информации, основное отличие
главного узла – то, что он является первым из узлов, имеющих полную
статистику по всем узлам, а не только по самому себе. После пересылки ее
остальным узлам, уже все узлы будут располагать достаточной информацией
для восстановления.
Вторым отличием главного узла от остальных является наличие
полномочий принятия решения о перераспределении ресурсов между узлами
кластера. Поскольку именно главному узлу первому доступна вся статистики
использования и загрузки всех узлов сети, то именно на него возложена роль
анализа состояния кластера и его оптимизация. Функции анализа загрузки и
операции распределения задач по узлам кластера выполняются сторонним
статистическим модулем, входящим в состав программного комплекса
«Кластер».
Обязанности узлов и пути передачи информации иллюстрированы на
рис. 6.
40
Узел 3
·
·
·
Узел 2
·
Сбор собственной
статистики
Отсылка статистики
главному узлу
Прием данных для
резервирования от
главного узла
·
·
·
Сбор собственной
статистики
Отсылка статистики
главному узлу
Прием данных для
резервирования от
главного узла
Узел 1(Главный)
·
·
·
Удаленный терминал
с монитором
·
·
·
·
·
Получение статистики
по всем узлам от
главного узла
Отображение данных
о состоянии узлов
Конфигурирование
узлов кластера
Начальное
распределение
процессов по узлам
·
·
·
Сбор собственной
статистики
Прием статистики
второстепенных узлов
Прием данных для
резервирования от
второстепенных узлов
Отсылка общей
статистики и всех
данных каждому из
узлов для возможного
восстановления
Распределение
процессов
Рисунок 6. Обязанности узлов кластера
Алгоритм, отражающий работу блока создания конфигураций,
приведен на рис. 7.
41
Начало
Да
Да
Добавить
в конфигурацию новый
узел?
Нет
Выбор узла для
редактирования его
процессов
Максимальное
количество узлов
достигнуто?
Нет
Нет
Вводи имени,
нормера и описания
добавляемого узла
Нет
Вводи имени,
нормера и описания
добавляемого на
узел процесса
Узел
с такими данными не
существует?
Добавление в конфигурацию
созданного узла
Узел
с такими данными
существует?
Нет
Закончить
редактирование
узлов?
Сохранить созданную
конфигурацию?
Нет
Да
Нет
Задание приоритета
запуска процесса, привязки
к IP адресу, порядкового
номера для запуска,
установка зависимости от
других процессов
Да
Да
Добавить
на выбранный узел новый
процесс?
Максимальное
количество процессов еще
не достигнуто?
Да
Да
Добавление к узлу созданного
процесса
Нет
Да
Сохранение конфигурации
Конец
Рисунок 7. Алгоритм работы модуля создания конфигурации кластера
42
2.1.2. Блок инициализации кластера по созданной конфигурации
После создания конфигурации в редакторе, она может быть сразу
превращена в реальную систему. Для этого в интерфейсе конфигуратора
предусмотрена кнопка «Создать».
После нажатия пользователем кнопки «Создать» узел, на котором
запущена программа конфигурации, дает команды всем узлам сети для
создания в корневом разделе их файловых систем файловой структуры
определенной иерархии, хранящей конфигурационные файлы, отвечающие за
параметры узлов и процессов. После создания файловой иерархии, узел
рассылает
всем
узлам
соответствующие
пакеты
с
данными,
характеризующими каждый из узлов, и этими данными заполняются рабочие
файлы. Файловая иерархия, создаваемая на каждом из узлов, приведена на
рис. 8.
Рисунок 8. Файловая иерархия узла кластера
43
Каталог с рабочими фалами всех узлов находится по адресу
//home/cluster каждого из узлов и содержит приведенный на рис. 6 набор
файлов и директорий.
Папка
+bin
при
работе
кластера
будет
содержать
уже
скомпилированные для исполнения файлы процессов. Файл f_cluster.cfg
содержит конфигурационную информацию, касающуюся узла. Папка
+node_1 хранит в себе все файлы, отвечающие за пути к выбранным
процессам, временные файлы, общие ресурсы узла и конфигурационные
файлы процессов. Цифра в названии этой папки говорит об уникальном
идентификаторе рассматриваемого узла.
После того, как файлы были скопированы и заполнены, на узле в
соответствии с настройками начинается последовательный запуск процессов,
указанных в файле procfile.cfg. Фактически, каждый узел кластера начинает
функционирование в своем рабочем режиме.
2.1.3.
Графический
интерфейс
пользователя
для
создания
конфигураций кластера
Графический
интерфейс
пользователя
в
режиме
создания
и
редактирования конфигураций кластера оформлен в виде отдельного
всплывающего окна, вызываемого из главного меню программы.
Для удобства восприятия окно конфигуратора поделено на 3 области:
1. область задания и настройки узлов;
2. область задания и настройки процессов;
3. управляющая область – кнопки сохранения, проверки и применения
созданной конфигурации.
44
Каждый управляющий блок кнопок, относящийся к свой области (узлы,
процессы), имеет отличный от всего стиля интерфейса цвет надписей и
хорошо выделяется, что делает интерфейс интуитивно понятным. Интерфейс
окна конфигуратора показан на рис. 9.
После
создания
узла
или
процесса
его
можно
повторно
отредактировать в дальнейшем. Для этого его просто необходимо выделить в
списке узлов/процессов и нажать клавишу изменить. Поля параметров станут
доступными для редактирования. Также для удаления элемента из
конфигурации его необходимо выделить и нажать соответствующую кнопку
удаления.
Рисунок 9. Пользовательский интерфейс в режиме конфигуратора
45
Все поля, где необходимо вводить цифровые значения, дополнены
«качельками» вверх-вниз, для возможности альтернативного ввода без
использования клавиатуры.
Также стоит отметить, что несмотря на то, что окно является
всплывающим, оно не имеет кнопок закрытия и его невозможно свернуть.
Это одна из мер безопасности, принятых для того, чтобы пользователь не мог
выйти из режима редактирования, сознательно сохранив или отменив
внесенные в конфигурацию изменения.
Все списковые элементы, а именно: узлы, процессы и зависимости
между процессами выделяются синим цветом, как показано на рисунке 8.
Яркий цвет выбран для того, чтобы пользователь всегда четко видел
выделенный элемент, с которым он работает и не мог допустить случайную
ошибку.
Рисунок 10. Пользовательский интерфейс в режиме конфигуратора
при добавлении процессов на узел
46
2.2. Разработка алгоритмов и программного обеспечения модуля
менеджера проектов кластера
Менеджер проектов – модуль, входящий в состав рассматриваемого в
дипломном проекте программного обеспечения, призванный облегчить
сохранение конфигураций кластера и навигацию по ранее сохраненным.
Согласно требованиям, предъявляемым заказчиком, программа должна
быть рассчитана на использование и хранение множества различных
конфигураций.
Следовательно,
необходимо
предоставить
оператору
возможность осуществлять быструю навигацию по файловой системе через
графический интерфейс пользователя.
Просмотр названий директорий, содержащих в себе однотипные файлы
конфигураций, малоинформативен и не позволит быстро найти нужную
конфигурацию, если список достаточно велик. Для того, чтобы оператор мог
не открывая конфигурацию, узнать о том, для чего она предназначена или
другую информацию, конфигурационные файлы было решено связывать с
проектом. В данном случае под проектом понимается отдельная папка,
расположенная в корневой директории диска, имеющая уникальное имя и
содержащая в себе как сам конфигурационный файл, так и файл с полным
названием проекта и его описанием.
2.2.1. Блок работы с файлами проектов: созданию, сохранению,
редактированию и открытию проектов
Блок менеджера проектов работает в 2х режимах:
 открытие, просмотр и редактирование проекта;
 создание, заполнение и сохранение проекта.
47
Проект состоит из главной папки с уникальным именем, файла с описанием и
полным названием проекта и двух папок: temp для хранения временных
файлов и work для сохранения готовых к работе конфигураций в
дальнейшем. Содержание проекта в файловой системе представлено на
рис.11.
Рисунок 11. Файловая структура проекта
Для работы с файловой системой, а именно указания проекта для
открытия и указания директории для сохранения проекта, используется
созданный файловый браузер, отображающий древовидную вложенную
структуру каталогов жесткого диска. Вызвать файловый браузер можно через
главное меню, выбрав либо «Файл -> Создать», либо «Файл -> Открыть».
Для предотвращения потери уже имеющихся проектов реализован
механизм проверки уникальности имен сохраняемых проектов. В случае,
если имя уже существует или содержит некорректные символы, программой
будет выдано предупреждение во всплывающем окошке. В случае успешного
открытия или сохранения проекта, напротив, выдается подтверждение
произведенной операции. Пример такого уведомления приведен на рис. 12.
48
Рисунок 12. Внешний вид всплывающих уведомлений
Также для защиты от неправильных действий оператора пункт меню,
отвечающий за заполнение названия и описания проекта, становится
доступным для нажатия лишь после успешного выбора директории для
сохранения или файла проекта для открытия, до этого он заблокирован.
Общий алгоритм работы менеджера проектов иллюстрирует рис. 13.
49
Начало
Да
Нет
Создать новый проект?
Открытие окна изменения
названия и описания проекта
Ввод пользователем
названия и краткого
описания проекта
Да
Название и описание
были изменены?
Открытие окна навигации по
файловой системе для выбора
места сохранения проекта
Ввод уникального имени нового
проекта
Проверка успеха создания
файлов проекта и выдача
сообщения с результатом
Нет
Сохранение изменений в
файловой системе
Закрытие менеджера проектов
И предоставление
пользователю перейти в режим
конфигурации кластера
Конец
Рисунок 13. Алгоритм работы менеджера проектов
50
Открытие окна навигации по
файловой системе для выбора
уже имеющегося проекта
Проверка успеха открытия
проекта и выдача сообщения с
результатом
2.2.2. Графический интерфейс пользователя в режиме работы с
проектами
Внешний вид меню файлового навигатора менеджера проектов и его окно с
древовидным отображением структуры каталогов представлено на рис. 14.
Рисунок 14. Меню для открытия проектов и окно навигации по папкам
Пользователю всегда отображаются зеленые подсказки в виде облачка с
инструкциями для дальнейших действий, как на рис. 15.
В окне изменения названия и описания проектов доступно 2 текстовых поля
ввода соответственно. Также присутствуют 3 кнопки; «Загрузить», «Сохранить» и
«Закрыть». Последняя была введена вместо стандартного крестика закрывания
окна, чтобы предотвратить случайное закрытие и потерю данных. Нажатие на
довольно крупную кнопку с соответствующей надписью будет с большей
степенью вероятности сознательным, также при закрытии поля ввода будут
51
проверены на корректность и заполненность, а в случае ошибки будет выдано
всплывающее предупреждение.
Рисунок 15. Окно ввода описания проекта и подсказка
пользователю
52
2.3. Разработка алгоритмов и программного обеспечения модуля
мониторинга состояния кластера
Режим мониторинга состояния кластера – это режим, в котором
главный узел системы принимает информацию 2-х типов (статистическую и
для резервирования) от всех узлов кластера, анализирует статистику
использования и загрузки узлов и отображает ее в интуитивно понятном
графическом виде на пользовательский интерфейс. Таким образом, любые
сбои, которые безусловно отражаются в собранной статистике ( т.е. статус
узла №2, к примеру, становится в значение «Не активен», значит узел вышел
из строя и не доступен), анализируются главным узлом и в соответствии с
типом возникновения неисправностей отображаются пользователю.
К примеру, в случае аварии одного узла и его недоступности для
приема и передачи информации от других узлов линии, соединяющие
графически узлы кластера, окрашиваются в красный цвет там, где они входят
в недоступный узел вместо зеленого, когда узел активен. Также тексты,
отображающие состояние узлов сменяются с «Активен» на «Не активен», что
дает пользователю возможность быстро оценить состояние системы.
2.3.1. Блок обработки получаемой по кластеру статистики от
стороннего программного обеспечения
Фактически, в режиме мониторинга состояния кластера анализ
информации происходит в 2 этапа с зависимости от степени подробности
информации
о
кластере.
Первым
делом
анализируются
структуры,
отражающие работу каждого из узлов. В такие структурах хранится
информация о доступности узла, его уникальном идентификаторе, статусе
узла. И эта информация служит для отображения статуса узла на главном
53
окне мониторинга, предназначенном для отражения состояния работы
кластера в целом.
Но
помимо
общей
статистики,
если
анализируемый
узел
по
статистическим данным доступен, имеется еще и подробная статистика по
узлу, которая включает в себя:
 информацию о запущенных на узле процессах;
 информацию о загруженности узла задачами.
Запущенные задачи на узле делятся на 3 типа:
1.
задачи, изначально запущенные на узле;
2.
задачи, переданные на обработку узлу другим узлом;
3.
задачи, отданные другому узлу на обработку.
Для того, чтобы оператор мог быстро диагностировать проблемы с
загруженностью или работоспособностью узла, существует окно подробной
статистики по узлам, где все запущенные процессы разделены на 3 списка по
вышеперечисленным свойствам. Соответственно, если на узле изначально
было запущено 20 процессов, а осталось на обработке через какой-то период
времени всего 2, а остальные отданы на обработку другому узлу, значит
конфигурация скорее всего не оптимальная, узел не справляется с таким
количеством задач в связи, допустим, с их сложностью, и необходимо
изменение начального распределения задач по узлам.
Также в интерфейсе предусмотрено поле, отвечающее за отображение
информации о загрузке узла. Опытный оператор, имеющий представление о
характеристиках узлов кластера, по, к примеру, использованию оперативной
памяти сможет определить, что узел находится в состоянии постоянной
загрузки на 70% своих возможностей. То же самое можно сделать и по
статистике использования процессора и нагрузке на него.
54
Обновление изображений обоих режимов происходит по системному
таймеру, периоды которого зависят от частоты обновлений приходящей на
главный узел статистики работы узлов. Подробная статистика по узлу еще
раз обновляется (считывается из памяти) при щелчке оператором по
изображению узла и загружается в поля соответствующего всплывающего
окна. Алгоритм работы модуля мониторинга состояния кластера отображен
на рис. 16.
55
Начало
Посылка запроса на получение
статистики
главным узлом остальным
узлам
Режим
мониторинга еще
включен?
Получение от узлов структур со
статистикой работы каждого
Нет
Да
Узлы активны и отвечают?
Нет
Вывод на экран
информации о том, что
узел вышел из строя
Вывод на экран состояния
работающих на узле
процессов
В статистике были
найдены перехваченные
процессы?
Конец
Да
Вывод на экран
перехваченных с других
узлов процессов
Нет
Удаление узла из списка
актиывных, окрашивание линии
соединения в красный цвет,
запись статуса неактивности на
картинке экрана узла
В статистике были
найдены переданные
процессы?
Да
Вывод на экран отданных
другим узлам на
выполнения процессов
Нет
Рисунок 16. Алгоритм работы модуля мониторинга состояния кластера
56
Да
2.3.2. Графический интерфейс отображения общего состояния
узлов сети в режиме мониторинга кластера на основе статистики
Графический интерфейс режима мониторинга состоит из:
1. изображений компьютеров, обозначающих узлы кластера;
2. текстовых
полей,
закрепленных
«вместо»
мониторов
компьютеров, служащих для отображения состояния узлов в
текстовом виде;
3. мигающий надписи «Главный узел», которая раз в секунду
меняет цвет между черным и красным;
4. линий, соединяющих узлы, отражающих активность сетевого
соединения между узлами в соответствии с цветами линии:
красный – линия не активна, зеленый – линия функционирует.
Главной особенностью интерфейса являются изображения узлов,
являющиеся кнопками, нажатие которых открывает подробную статистику
узла.
Рисунок 17. Главное окно мониторинга состояния кластера
57
2.3.3. Графический интерфейс отображения состояния выбранного
пользователем узлу кластера в режиме мониторинга кластера
При нажатии на кнопку с изображением узла происходит открытие
нового окна подробной статистики по узлу. Окно отображает;
 номер выбранного для просмотра узла;
 статус его активности в последний момент времени;
 данные о загруженности узла работой в виде статистики
использования оперативной памяти и процессора;
 процессы, имеющие отношение к узлу и разделенные на 3
группы в зависимости от их текущего места исполнения.
Процессы, обрабатывающиеся уже на других узлах будут иметь
текстовую приписку, содержащую номер узла, на котором задача в данный
момент обрабатывается.
Рисунок 18. Окно подробной статистики выбранного узла в режиме
мониторинга
58
ГЛАВА 3. ОТЛАДКА И ТЕСТИРОВАНИЕ
ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
Тестирование
программного
обеспечения
является
обязательной
частью цикла разработки любого программного продукта. Рассматриваемый
в данном дипломном проекте программный продукт состоит из 3-х основных
частей (модулей) и каждый из них имеет свои особенности:
1. менеджер проектов в первую очередь работает с файловой системой и
осуществляет операции чтения, записи и вывода информации;
2. конфигуратор кластера собирает и модифицирует конфигурационные
структуры разработанного формата в выделяемой оперативной памяти
с последующей их записью или «превращением» в реальные
конфигурационные файлы узлов;
3. блок мониторинга практически непрерывно ведет обмен информацией
между узлами кластера через систему встроенную сообщений QNX и
визуализирует статистическую информацию в графической форме на
интерфейсе пользователя.
Как видно, каждый из модулей работает с разными сферами
информационной среды вычислительного комплекса, что с одной стороны
позволяет тестировать каждый из блоков раздельно, а с другой стороны
обязывает тщательно проверять влияние друг на друга и совместную работу
блоков.
Таким образом, при проведении тестирования данного программного
продукта необходимо использовать модульное тестирование, которое
предполагает отладку всех модулей в отдельности, а затем совместное
тестирование их работы и взаимосвязей.
59
Для проведения модульного тестирования был составлен алгоритм,
показанный на рис. 19, отражающий последовательность действий оператора
при работе с программным комплексом на всех стадиях подготовки кластера
к функционированию: от создания проекта, до инициализации созданной
конфигурации и слежения за ее работоспособностью. Согласно алгоритму
проводилось тестирование программы, включающее создание тестовых
конфигураций кластера. В качестве тестовой конфигурации создавалось 3
узла в конфигураторе и за каждым из них закреплялось по несколько
тестовых процессов, с заданными параметрами (порядок запуска, приоритет
и т.д.). Каждый раз процессы распределялись по разному и конфигурация
инициализировалась
инициализации
подключалась
в
реальную
программа
к
сети.
работающую
запускалась
Отображаемая
в
систему.
режиме
После
мониторинга
статистическая
и
информация
сравнивалась с реальным состоянием узлов и в случае возникновения ошибок
код корректировался.
Также довольно большую часть проекта составляют графические
интерфейсы пользователя, включающие в себя огромное количество
управляющих элементов (списков, кнопок, окон), взаимосвязь между
которыми
может
Следовательно,
быть
не
программный
учтена
в
продукт
ходе
разработки
прошел
и
программы.
тестирование
потенциальным пользователем, включающим учет «человеческого» фактора.
В данном случае проверялась корректность реакций программы на
некорректные
действия
ее
оператора.
К
примеру,
открыв
окно
конфигуратора, пользователь пытался «загородить» его главным окном
программы так, чтобы его не было видно и пытался из главного окна
повторно вызвать форму конфигуратора. В такой ситуации программа
должна вместо создания нового окна, отображать уже открытое или
запрещать его сокрытие вовсе. Вторым примером тестирования интерфейса
можно рассмотреть попытки сохранить проект с именем, уже имеющимся в
60
файловой
системе.
предупреждающее
В
таком
сообщение
случае
об
уже
программа
должна
существующем
выдавать
уникальном
идентификаторе и заставлять оператора выбрать иное название для проекта,
предотвращая потерю ранее сохраненных данных.
61
Начало
Создание новой конфигурации
кластера
Запуск кластера по созданной
конфигурации
Заполнение параметров узлов и
процессов
Переход в режим мониторинга
состояния кластера
Запись конфигурации в файл
Проверка отображаемого
состояния и выводимой
статистики
Попытка открытия и чтения
записанной конфигурации.
Состояние
и статистики соответствуют
реальности?
Создание нового проекта
Заполнение названий и
описаинй
Запись проекта в файл
Попытка открытия и чтения
записанного проекта
Проект
успешно считался и
открылся?
Нет
Открытие и
загрузка конфигурации
успешна?
Да
Да
Исправление ошибок
Нет
Исправление ошибок
Нет
Исправление ошибок
Рисунок 19. Алгоритм модульного тестирования блоков программного обеспечения
62
Да
Конец
ГЛАВА 4. КОНТРОЛЬНЫЙ ПРИМЕР
Проверка правильного функционирования программного комплекса
проводится с помощью специально разработанных тестов. Для каждого из
узлов были созданы тестовые конфигурации, с помощью которых можно
будет проверить работоспособность созданного программного обеспечения.
Для того чтобы эмулировать различные отказы, были использованы
стандартные утилиты операционных систем типа unix:
 утилита /bin/cat – для эмуляции процесса, выполняющегося без
каких-либо ошибок;
 утилиты
/bin/echo
и
/bin/ls
–
для
эмуляции
процессов,
завершившихся после непродолжительной работы в системе;
 утилита /bin/true – для эмуляции процесса, завершившегося сразу
после создания;
 утилита /bin/sleep – для эмуляции процессов, завершающихся в
произвольный момент времени.
Конфигурация для каждого из узлов приведены в таблицах 3-5.
63
Идентификатор Путь к
Аргументы Зависимости Начальный
процесса в
процессу командной от других
приоритет
системе
строки
процессов
1
/bin/echo Cluster test
8, 2, 4, 5
20
2
/bin/cat
8, 7
10
3
/bin/cat
4, 7
10
4
/bin/cat
10
101
/bin/cat
10
102
/bin/true
10
103
/bin/sleep 60
10
-u -c
Таблица 3. Конфигурация первого узла кластера
64
Идентификатор Путь к
Аргументы
процесса в
процессу командной строки
системе
Зависимости Начальный
от других
приоритет
процессов
5
/bin/cat
2, 7
10
6
/bin/cat
18, 8
10
7
/bin/cat
8
10
201
/bin/cat
10
202
/bin/true
10
203
/bin/true
10
204
/bin/ls
-l
/home/cluster/node_2
10
205
sleep
120
10
-u
Таблица 4. Конфигурация второго узла кластера
65
Идентификатор Путь к
Аргументы
процесса в
процессу командной строки
системе
Зависимости Начальный
от других
приоритет
процессов
8
/bin/cat
17, 18
20
9
/bin/cat
1
20
10
/bin/cat
17, 13
20
13
/bin/cat
17
20
17
/bin/cat
18
/bin/cat
20
/bin/cat
21
/bin/cat
18
10
22
/bin/cat
21
10
23
/bin/cat
22
10
10
26
-u
10
10
66
24
/bin/cat
23
10
25
/bin/cat
24
10
28
/bin/cat
31
/bin/cat
35, 32, 33
10
32
/bin/cat
35, 33
10
33
/bin/cat
10
35
/bin/cat
10
301
/bin/cat
10
302
/bin/ls
303
/bin/sleep 600
10
-l
/home/cluster/node_3
10
10
Таблица 5. Конфигурация третьего узла кластера
67
Главное окно режима мониторинга состояния кластера (рис. 20)
создано с максимально понятным визуальным интерфейсом, позволяющим
почти моментально оценить текущее состояния всей сети.
Рисунок 20. Графический интерфейс пользователя в режиме
мониторинга состояния кластера
Для этого каждый из узлов изображен в виде персонального
компьютера, на экране которого отображается его текущее состояние в
текстовом виде. Если узел корректно подключен к сети и исправно
функционирует – на его экране будет виден его номер и статус «Ок», если же
нет, будет отображена ошибка.
Также, предусмотрено отслеживание корректности подключений
между узлами кластера и статус их функционирования. При работающем
68
подключении и обмене сообщениями между узлами линия связи горит
зеленым цветом, в противном случае, красным – при сбое соединения.
Однако, пользователю может понадобиться и подробная статистика по
конкретному узлу. Для этих целей каждое из изображений совмещает в себе
еще и функцию кнопки, при нажатии на которую откроется окно с подробной
информацией об узле.
На рис. 21 изображено окно, открывающееся при нажатии оператором
на один из узлов в режиме мониторинга. Основная цель такого окна –
отобразить подробную информацию о состоянии узла в текущий момент
времени.
Рисунок 21. Окно режима мониторинга узлов
69
В первую очередь пользователь сможет увидеть активен ли в данный
момент выбранный узел. Если это так, то строкой ниже будет выдана
информация о загрузке процессора, количестве свободной оперативной
памяти.
Но главная задача окна статистики – отображение миграции процессов
внутри сети.
Процессы, запущенные на узле изначально являются собственными;
процессы, переданные на обработку другому узлу в следсвие какой-либо
ошибки - переданными, а процессы, взятые на обработку с другого узла –
перехваченными. Все 3 вида процессов выводятся в виде списка в отдельных
секциях, позволяя оценить обработку задач качественно и количественно.
70
ЗАКЛЮЧЕНИЕ
Целью данного дипломного проекта была разработка программного
обеспечения для создания унифицированного отказоустойчивого бортового
вычислителя. В результате работы над дипломным проектом были решены
следующие задачи:
 Разработан блок создания, применения, изменения и сохранения
различных конфигурация кластера;
 Разработан блок менеджера проектов, позволяющего производить
удобную
навигацию
между
сохраненными
конфигурациями
с
возможность вывода их описания;
 Разработан
блок
мониторинга
узлов
кластера
и
графического
отображения их состояния на экране удаленного терминала;
 Разработан
блок
взаимодействия
со
сторонним
программным
обеспечением, собирающим статистику работы узлов и данные для
резервирования;
 Создан удобный графический пользовательский интерфейс для работы
в программе в режимах мониторинга, создания конфигураций и
навигации по проектам;
 Разработан программный модуль диспетчера высокой готовности для
ОСРВ QNX 4.25;
 Проведена отладка и тестирование разработанного программного
продукта.
Результатом работы стал программный продукт, входящий в состав
комплекса
кластерного
программного
обеспечения.
Продукт
прошел
автономное тестирование и был полностью отлажен. В настоящий момент
программное
обеспечение
передано
предприятию
для
проведения
комплексных испытаний в режиме рабочего применения и совместного
тестирования на реальном оборудовании.
71
СПИСОК ЛИТЕРАТУРЫ
1. Алексеев Д. – Практика работы с QNX.- М.: Издательский дом
«КомБук», 2004.
2. Зыль С.Н. – QNX: Основы применения. – СПб.: БХВ-Петербург, 2005.
3. Робачевский А.М. – Операционная система UNIX. – СПб.: БХВПетербург, 2002.
4. Кондукова Е. – Операционная система реального времени QNX.
Системная архитектура. – СПб.: БХВ-Петербург, 2006.
5. Зыль С.Н. – Операционная система реального времени QNX: от теории
к практике. – СПб.: БХВ-Петербург, 2004.
6. К.А. Иыуду, С.А. Кривощеков, Математические модели
отказоустойчивых ВС. –М., МАИ, 1989.
7. Л.П. Глазунов и др. Основы теории надежности автоматических систем
управления. -М.,Энергоатомиздат, 1984.
8. К.Ю. Богачев. Операционные системы реального времени. М. 2001.
9. Зыль С. Н. Проектирование, разработка и анализ программного
обеспечения систем реального времени
10.Русский форум поддержки разработчиков QNX http://qnx.org.ru
11.Раздел форума об ОСРВ http://citforum.ru/operating_systems/rtos/
72
ПРИЛОЖЕНИЕ
//communication.cpp
//Dmitry Saraev
//ARGON, 2012
#include "communication.h"
Communication::Communication(WCValSList <int> listOfNodes)
{
int regState;
nodeList = listOfNodes;
nodeNamePrefix = "/ARGON_CLUSTER_NODE_";
activeNodeName = "/ARGON_CLUSTER_NODE_MAIN";
regState = registerMyNodeName();
if(regState == -1)
{
cerr << "Error! Cant attach name: "
<< nodeNamePrefix << getnid() << endl;
}
}
int Communication::registerMyNodeName()
{
return qnx_name_attach(0, nodeNamePrefix + String(getnid()));
}
void Communication::registerMyNodeAsMainNode()
{
mainNodeNameId = qnx_name_attach(0, nodeNamePrefix + "MAIN");
}
void Communication::unregisterMyNodeAsMainNode()
{
qnx_name_detach(0, mainNodeNameId);
73
}
int Communication::nextNode(int nodeNum)
{
int nodeIndex;
int totalNodes = nodeList.entries();
if(totalNodes == 1)
{
return 0;
}
nodeIndex = nodeList.index(nodeNum);
assert(nodeIndex != -1);
if((nodeIndex + 1) < totalNodes)
{
cout
<<
"COMMUNICATION.CPP:if
nodeList.find(nodeIndex + 1) << endl;
next
node
=
"
<<
"
<<
return nodeList.find(nodeIndex + 1);
}
else
{
cout
<<
nodeList.find(0) << endl;
"COMMUNICATION.CPP:else
next
node
=
return nodeList.find(0);
}
}
int Communication::sendMessageToNextNode(message_struct *message)
{
int sendStatus = -1;
int receiverNode = getnid();
do
{
receiverNode = nextNode(receiverNode);
if((receiverNode == getnid()) || (receiverNode == 0))
74
{
return MESSAGE_CANT_SEND;
}
pid_t receiver = qnx_name_locate(0,
nodeNamePrefix
sizeof(message_struct), NULL);
+
String(receiverNode),
if(receiver != -1)
{
sendStatus = Send(receiver, message, message,
sizeof(message_struct), sizeof(message_struct));
qnx_vc_detach(receiver);
}
} while(sendStatus != 0);
return MESSAGE_SEND;
}
pid_t Communication::getMessage(message_struct *message)
{
pid_t sender;
sender = Creceive(0, message, sizeof(message_struct));
if(sender == -1)
{
return NO_NEW_MESSAGES;
}
return sender;
}
int
sender)
Communication::sendReplyMessage(message_struct
*message,
pid_t
{
int replyState = Reply(sender, message, sizeof(message_struct));
return replyState;
}
75
//communication.h
//Dmitry Saraev
//ARGON, 2012
#ifndef COMMUNICATION_H
#define COMMUNICATION_H
#include "message_types.h"
#include "parse_config.h"
#include <sys/types>
#include <sys/name.h>
#include <sys/kernel.h>
#include <sys/vc.h>
#include <unistd.h>
#include <assert.h>
//info about state of nodes
#define NODE_STATE_ACTIVE 1
#define NODE_STATE_PASSIVE 0
#define NODE_STATE_UNAVALIBLE 2
//info about state of send message
#define MESSAGE_CANT_SEND 0
#define MESSAGE_SEND 1
//info about state of send reply to message
#define REPLY_CANT_SEND -1
#define REPLY_SEND 0
//info about avalible nodes
#define NODE_AVALIBLE 1
#define NODE_UNAVALIBLE 0
#define NO_NEW_MESSAGES 0
76
class Communication
{
public:
Communication(WCValSList <int> listOfNodes);
int sendMessageToNextNode(message_struct *message);
pid_t getMessage(message_struct *message);
int sendReplyMessage(message_struct *message, pid_t sender);
void registerMyNodeAsMainNode();
void unregisterMyNodeAsMainNode();
private:
String nodeNamePrefix;
String activeNodeName;
int registerMyNodeName();
int mainNodeNameId;
WCValSList <int> nodeList;
int nextNode(int nodeNum);
};
#endif
//proc_watch.cpp
//Dmitry Saraev
//ARGON, 2012
#include "proc_watch.h"
LogEvents * log;
SysProc::operator==(const SysProc &proc2) const
{
return (*procInfo == *proc2.procInfo);
}
SysProc::SysProc(ProcInfo *proc)
{
77
procInfo = proc;
pid = -1;
vid = -1;
}
SysProc::startOn(nid_t node)
{
pid_t procId;
if((realStartNode == node) && (isRun()))
{
state = PROC_RUN;
return PROC_START_OK;
}
//change qnx_spawn_options
qnx_spawn_options.node = node;
qnx_spawn_options.priority = procInfo->prio;
log->startEvent("START-PROC :tag " + toString(procInfo->tag) + "
:onNode " + toString(node));
if(procInfo->options == "")
{
procId = spawnl(P_NOWAIT, procInfo->filename,
procInfo->filename, NULL);
}
else
{
char** options = stringToOptionList(
procInfo->filename + " " + procInfo->options);
procId = spawnv(P_NOWAIT, procInfo->filename,
(char const * const *)options);
}
//restore qnx_spawn_options
qnx_spawn_options.node = 0L;
qnx_spawn_options.priority = -1;
78
if(procId == -1)
{
state = PROC_NOT_RUN;
log->endEvent("ERROR");
return PROC_START_ERROR;
}
realStartNode = node;
vid = procId;
if(node == getnid())
{
pid = vid;
}
else
{
_psinfo data;
qnx_psinfo(0, vid, &data, 0, NULL);
pid = data.un.vproc.remote_pid;
}
qnx_vc_detach(vid);
state = PROC_RUN;
log->endEvent("OK :pid " + toString(pid));
return PROC_START_OK;
}
int SysProc::isRunLocalCheck()
{
if(pid == -1)
{
state = PROC_NOT_RUN;
return PROC_NOT_RUN;
}
_psinfo data;
if(qnx_psinfo(0, pid, &data, 0, NULL) == -1)
79
{
state = PROC_NOT_RUN;
return PROC_NOT_RUN;
}
else if(data.pid != vid)
{
state = PROC_NOT_RUN;
return PROC_NOT_RUN;
}
else if(data.state == STATE_DEAD)
{
state = PROC_NOT_RUN;
return PROC_NOT_RUN;
}
else
{
state = PROC_RUN;
return PROC_RUN;
}
}
int SysProc::isRunGlobalCheck()
{
if(pid == -1)
{
state = PROC_NOT_RUN;
return PROC_NOT_RUN;
}
waitpid(vid, NULL, WNOHANG);
int oldVid = vid;
vid = qnx_vc_attach(realStartNode, pid, 1000, 0);
if(vid == -1)
{
80
state = PROC_NOT_RUN;
qnx_vc_detach(oldVid);
return PROC_NOT_RUN;
}
_psinfo data;
if(qnx_psinfo(0, vid, &data, 0, NULL) == -1)
{
state = PROC_NOT_RUN;
qnx_vc_detach(vid);
return PROC_NOT_RUN;
}
else if(data.pid != vid)
{
state = PROC_NOT_RUN;
qnx_vc_detach(vid);
return PROC_NOT_RUN;
}
else if(data.state == STATE_DEAD)
{
state = PROC_NOT_RUN;
qnx_vc_detach(vid);
return PROC_NOT_RUN;
}
else
{
state = PROC_RUN;
qnx_vc_detach(vid);
return PROC_RUN;
}
}
int SysProc::isRun()
{
81
log->startEvent("IS-PROC-RUN :tag " + toString(procInfo->tag));
if(realStartNode == getnid())
{
isRunLocalCheck();
}
else
{
isRunGlobalCheck();
}
if(state == PROC_RUN)
{
log->endEvent("RUN");
}
else
{
log->endEvent("NOT-RUN");
}
return state;
}
int SysProc::stop()
{
log->startEvent("STOP-PROC :tag " + toString(procInfo->tag) + "
:node "
+ toString(realStartNode) + " :pid " + toString(pid));
if(isRun() == PROC_NOT_RUN)
{
log->endEvent("OK");
return PROC_STOPPED;
}
sendSignalToMe(SIGTERM);
if(isRun() == PROC_NOT_RUN)
82
{
log->endEvent("OK");
return PROC_STOPPED;
}
sendSignalToMe(SIGKILL);
if(isRun() == PROC_NOT_RUN)
{
log->endEvent("OK");
return PROC_STOPPED;
}
log->endEvent("ERROR");
return PROC_CANT_STOPPED;
}
void SysProc::sendSignalToMe(int signalNumber)
{
String killCommand;
char strNode[3];
char strPid[10];
char strSignal[3];
itoa(realStartNode, strNode, 10);
itoa(pid, strPid, 10);
itoa(signalNumber, strSignal, 10);
killCommand = "on -n " + String(strNode) + " kill -" +
String(strSignal) + " " + String(strPid);
system(killCommand);
}
char ** SysProc::stringToOptionList(String s)
{
WCPtrSList <String> list;
String sep = " ";
String sepOpen = "\"";
83
String sepClose = "\"";
int done = 0;
int beginPos = 0;
int endPos = 0;
while(!done)
{
String * tmpStr = new(String);
if(s[beginPos] == sepOpen)
{
++beginPos;
endPos = s.index(sepClose, beginPos);
*tmpStr = String(s, beginPos, endPos - beginPos);
if(s.length() == endPos + 1)
{
done = 1;
}
++endPos;
}
else
{
endPos = s.index(sep, beginPos);
if(endPos == -1)
{
endPos = s.length();
done = 1;
}
*tmpStr = String(s, beginPos, endPos - beginPos);
}
list.append(tmpStr);
beginPos = endPos + 1;
}
//create option list
84
char** opList = (char**)malloc(sizeof(char*) * (list.entries() +
1));
//convert from strings to char* []
int opNum = list.entries();
int opCounter = 0;
while(opCounter < opNum)
{
String strOpt = *list.get();
char* option = (char*)malloc(sizeof(char) * (strOpt.length()
+ 1));
strcpy(option, strOpt);
opList[opCounter] = option;
++opCounter;
}
opList[opCounter] = NULL;
list.clearAndDestroy();
return opList;
}
ProcWatch::ProcWatch(WCPtrSList <NodeInfo> nodes, WCPtrSList <ProcInfo>
procs)
{
nodeNamePrefix = NODE_NAME_PREFIX;
int nodeNum = nodes.entries();
int nodeCount = 0;
WCPtrSList <RealNodeInfo> realNodesInfo;
while(nodeCount < nodeNum)
{
NodeInfo * node = nodes.find(nodeCount);
realNodes.append(node->node);
RealNodeInfo * realNodeInfo = new RealNodeInfo(node->node,
NS_PASSIVE);
realNodesInfo.append(realNodeInfo);
cout <<
realNodesInfo" << endl;
"debug:
add
node
85
"
<<
node->node
<<
"
to
++nodeCount;
}
nodesInfo = new NodesInfo(realNodesInfo);
}
int ProcWatch::myState()
{
return nodesInfo->realNodeState(getnid());
}
void ProcWatch::startMonitoring()
{
clusterState = new ClusterState();
int selectionTimeout = getnid() * 5;
registerMyNodeName();
delay(5000);
int noNodesTime;
while(1)
{
delay(MESSAGE_TIMEOUT);
++clusterState->cycle;
log->startEvent("MONITORING
toString(clusterState->cycle));
:cycle
"
+
answerToAllMessages();
if(myState() == NS_PASSIVE)
{
if(clusterState->cycleOfLastMessage
>cycle - (getnid() * 5))
<
clusterState-
{
nodesInfo->reset();
if(startSelection() == MESSAGE_CANT_SEND)
{
log->startEvent("SET-MY-NODE-AS-MAIN");
setMyState(NS_ACTIVE);
86
log->endEvent("OK");
}
}
}
if(myState() == NS_ACTIVE)
{
if(sendMsgClusterState() == MESSAGE_CANT_SEND) //TODO
this function
{
int myOldState = myState();
nodesInfo->reset();
if(myOldState != myState())
{
RealNodeInfo
*
myNode
=
nodesInfo-
>getNode(getnid());
myNode->status = myOldState;
}
}
}
log->endEvent("OK");
}
}
int ProcWatch::sendMsgClusterState()
{
message_struct message;
message.magic_number = ARGON_MAGIC_NUMBER;
message.type = MT_CLUSTER_CHANGES;
message.sender = getnid();
message.message_body.changes_in_cluster.cluster_state_rev
clusterState->currentRev;
=
message.message_body.changes_in_cluster.node_count
realNodes.entries();
=
int nodesTotal = realNodes.entries();
int nodesCount = 0;
87
while(nodesCount < nodesTotal)
{
int currentNode = realNodes.find(nodesCount);
assert(currentNode != -1);
if(currentNode == getnid())
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
getnid();
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_ACTIVE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(getnid());
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
else
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
currentNode;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_UNAVAILIBLE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(currentNode);
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
++nodesCount;
}
return sendMessageToNextNode(&message);
}
int ProcWatch::updateNodesList()
{
88
cout << "(SEND-MESSAGE LIST_OF_NODES)" << endl;
message_struct message;
message.magic_number = ARGON_MAGIC_NUMBER;
message.type = MT_LIST_OF_NODES;
message.sender = getnid();
message.message_body.node_list.node_count = 1;
message.message_body.node_list.nodes[0].node_number = getnid();
message.message_body.node_list.nodes[0].node_status = myState();
message.message_body.node_list.nodes[0].node_weight
>getWidthOnNode(getnid());
=
nodesInfo-
return sendMessageToNextNode(&message);
}
int ProcWatch::startSelection()
{
cout << "(SEND-MESSAGE SELECTION)" << endl;
message_struct message;
message.magic_number = ARGON_MAGIC_NUMBER;
message.type = MT_SELECTION;
message.sender = getnid();
message.message_body.changes_in_cluster.cluster_state_rev = 0;
message.message_body.changes_in_cluster.node_count
realNodes.entries();
=
int nodesTotal = realNodes.entries();
int nodesCount = 0;
while(nodesCount < nodesTotal)
{
int currentNode = realNodes.find(nodesCount);
assert(currentNode != -1);
if(currentNode == getnid())
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
getnid();
89
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_ACTIVE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(getnid());
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
else
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
currentNode;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_UNAVAILIBLE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(currentNode);
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
++nodesCount;
}
return sendMessageToNextNode(&message);
}
void ProcWatch::setMyState(int nodeState)
{
if(myState() == nodeState)
{
return;
}
else if(myState() == NS_ACTIVE)
{
unregisterMyNodeAsMainNode();
}
90
else if(myState() == NS_PASSIVE)
{
registerMyNodeAsMainNode();
}
RealNodeInfo * myNode = nodesInfo->getNode(getnid());
myNode->status = nodeState;
}
void ProcWatch::setNodeState(int nodeNum, int nodeState)
{
RealNodeInfo * node = nodesInfo->getNode(nodeNum);
node->status = nodeState;
}
int ProcWatch::answerMessage(message_struct * message, message_struct *
newMessage)
{
log->startEvent("(GET_MESSAGE)");
int sendToNextNode;
switch(message->type)
{
case MT_SELECTION:
sendToNextNode
=
answerToMtSelection(message,
newMessage);
break;
case MT_NEW_ACTIVE_NODE:
sendToNextNode
=
answerToMtNewActiveNode(message,
newMessage);
break;
case MT_LIST_OF_NODES:
sendToNextNode
=
newMessage);
break;
case MT_CLUSTER_CHANGES:
91
answerToMtListOfNodes(message,
sendToNextNode
=
answerToMtClusterChanges(message,
newMessage);
break;
case MT_TOTAL_PROCS_COUNT:
sendToNextNode
=
answerToMtTotalProcsCount(message,
newMessage);
break;
case MT_PROCS_STATUS:
sendToNextNode
=
answerToMtProcsStatus(message,
newMessage);
break;
case MT_TOTAL_UPDATED_PROCS:
sendToNextNode = answerToMtTotalUpdatedProcs(message,
newMessage);
break;
case MT_UPDATE_IN_PROCS_LIST:
sendToNextNode = answerToMtUpdateInProcsList(message,
newMessage);
break;
case MT_UPDATE_IN_NODES_LIST:
sendToNextNode = answerToMtUpdateInNodesList(message,
newMessage);
break;
}
log->endEvent("[OK]");
return sendToNextNode;
}
int
ProcWatch::answerToMtSelection(message_struct
message_struct * newMessage)
*
message,
{
cout << "debug: ProcWatch::answerToMtSelection" << endl;
//int answer = answerToMtUpdateInNodesList(message, newMessage);
if(getnid() != message->sender)
{
*newMessage = *message;
92
int
nodesTotal
>message_body.changes_in_cluster.node_count;
=
message-
int nodesCounter = 0;
while(nodesCounter < nodesTotal)
{
node_changes_struct
node
>message_body.changes_in_cluster.nodes[nodesCounter];
=
message-
if(node.node_number == getnid())
{
node.node_status = myState();
node.node_weight
=
nodesInfo-
>getWidthOnNode(getnid());
node.node_have_changes = NC_NONE;
newMessage>message_body.changes_in_cluster.nodes[nodesCounter] = node;
break;
}
++nodesCounter;
}
return SEND_MESSAGE_TO_NEXT_NODE;
}
else
{
if(nodesInfo->haveActiveNode())
{
cout << "debug: active Node exist" << endl;
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
else
{
int newMainNode = nodesInfo->findMinWidthRealNode();
*newMessage = generateMtNewActiveNode(newMainNode);
cout << "active Node not exist new active node is: "
<< newMainNode << endl;
return SEND_MESSAGE_TO_NEXT_NODE;
93
}
}
}
int
ProcWatch::answerToMtListOfNodes(message_struct
message_struct * newMessage)
*
message,
{
int totalAvailibleNodes = 0;
int nodesCount = 0;
int totalNodes = realNodes.entries();
while(nodesCount < totalNodes)
{
int nodeNum = realNodes.find(nodesCount);
if(nodesInfo->isRealNodeAvailible(nodeNum))
{
RealNodeInfo * node = nodesInfo->getNode(nodeNum);
message>message_body.node_list.nodes[nodesCount].node_number = node->node;
message>message_body.node_list.nodes[nodesCount].node_status = node->status;
message>message_body.node_list.nodes[nodesCount].node_weight
>getWidthOnNode(nodeNum);
=
nodesInfo-
++totalAvailibleNodes;
}
++nodesCount;
}
message->message_body.node_list.node_count = totalAvailibleNodes;
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
int
ProcWatch::answerToMtClusterChanges(message_struct
message_struct * newMessage)
{
if(getnid() == message->sender)
{
94
*
message,
int myOldState = myState();
int nodesCount = 0;
int totalNodes = message->message_body.node_list.node_count;
while(nodesCount < totalNodes)
{
node_changes_struct
node
>message_body.changes_in_cluster.nodes[nodesCount];
=
message-
RealNodeInfo
>getNode(node.node_number);
=
nodesInfo-
*
realNode
realNode->status = node.node_status;
realNode->haveChanges = node.node_have_changes;
++nodesCount;
}
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
else
{
*newMessage = *message;
int nodesCount = 0;
int
totalNodes
>message_body.node_list.node_count;
=
newMessage-
while(nodesCount < totalNodes)
{
if(newMessage>message_body.changes_in_cluster.nodes[nodesCount].node_number == getnid())
{
newMessage>message_body.changes_in_cluster.nodes[nodesCount].node_status = myState();
newMessage>message_body.changes_in_cluster.nodes[nodesCount].node_weight
>getWidthOnNode(getnid());
return SEND_MESSAGE_TO_NEXT_NODE;
}
++nodesCount;
}
return SEND_MESSAGE_TO_NEXT_NODE;
95
=
nodesInfo-
}
}
int
ProcWatch::answerToMtNewActiveNode(message_struct
message_struct * newMessage)
*
message,
{
if((myState() == NS_ACTIVE)
&& (getnid() != message->message_body.node_number))
{
setMyState(NS_PASSIVE);
}
else if(getnid() == message->message_body.node_number)
{
setMyState(NS_ACTIVE);
}
setNodeState(message->message_body.node_number, NS_ACTIVE);
*newMessage = *message;
if(getnid() == message->sender)
{
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
else
{
return SEND_MESSAGE_TO_NEXT_NODE;
}
}
int
ProcWatch::answerToMtTotalProcsCount(message_struct
message_struct * newMessage)
*
message,
{
//message->message_body.proc_count = runtimeProcs.entries();
//driver_begin
message->message_body.proc_count = 123;
//driver_end
96
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
int
ProcWatch::answerToMtProcsStatus(message_struct
message_struct * newMessage)
*
message,
{
int procCount = 0;
int
totalProcInMsg
>message_body.procs_status.procs_count;
=
message-
proc_struct proc;
while(procCount < totalProcInMsg)
{
proc = message->message_body.procs_status.procs[procCount];
++procCount;
}
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
int ProcWatch::answerToMtTotalUpdatedProcs(message_struct
message_struct * newMessage)
*
message,
{
if(message->sender == getnid())
{
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
else
{
clusterState->newRev
>message_body.num_of_updates.cluster_state_rev;
=
message-
if((clusterState->newRev == (clusterState->currentRev + 1))
|| ((clusterState->newRev != clusterState->currentRev)
&&
>message_body.num_of_updates.update_type == UT_FULL)))
(message-
{
clusterState->remainingUpdates
>message_body.num_of_updates.total_updates;
97
=
message-
}
}
return SEND_MESSAGE_TO_NEXT_NODE;
}
int ProcWatch::answerToMtUpdateInProcsList(message_struct
message_struct * newMessage)
*
message,
*
message,
{
if(message->sender == getnid())
{
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
return SEND_MESSAGE_TO_NEXT_NODE;
}
int ProcWatch::answerToMtUpdateInNodesList(message_struct
message_struct * newMessage)
{
if(message->sender == getnid())
{
return NOT_SEND_MESSAGE_TO_NEXT_NODE;
}
else
{
int
totalLogicalNodes
>message_body.logical_nodes.total;
=
message-
int logicalNodesCount = 0;
while(logicalNodesCount < totalLogicalNodes)
{
int
logicalNode
=
message>message_body.logical_nodes.nodes[logicalNodesCount].logical_node_num;
int
realNode
=
>message_body.logical_nodes.nodes[logicalNodesCount].real_node_num;
message-
nodesInfo->setRealNodeToLogicalNode(logicalNode,
realNode);
++logicalNodesCount;
98
}
}
return SEND_MESSAGE_TO_NEXT_NODE;
}
message_struct ProcWatch::generateBaseMessage()
{
message_struct message;
message.magic_number = ARGON_MAGIC_NUMBER;
message.sender = getnid();
return message;
}
message_struct ProcWatch::generateMtSelection()
{
message_struct message = generateBaseMessage();
message.type = MT_SELECTION;
message.message_body.changes_in_cluster.cluster_state_rev = 0;
message.message_body.changes_in_cluster.node_count
realNodes.entries();
=
int nodesTotal = realNodes.entries();
int nodesCount = 0;
while(nodesCount < nodesTotal)
{
int currentNode = realNodes.find(nodesCount);
assert(currentNode != -1);
if(currentNode == getnid())
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
getnid();
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_ACTIVE;
=
99
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(getnid());
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
else
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
currentNode;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_UNAVAILIBLE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(currentNode);
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
++nodesCount;
}
return message;
}
message_struct ProcWatch::generateMtClusterChanges()
{
message_struct message = generateBaseMessage();
message.type = MT_CLUSTER_CHANGES;
message.message_body.changes_in_cluster.cluster_state_rev
clusterState->currentRev;
=
message.message_body.changes_in_cluster.node_count
realNodes.entries();
=
int nodesTotal = realNodes.entries();
int nodesCount = 0;
while(nodesCount < nodesTotal)
{
int currentNode = realNodes.find(nodesCount);
100
assert(currentNode != -1);
if(currentNode == getnid())
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
getnid();
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_ACTIVE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(getnid());
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
else
{
message.message_body.changes_in_cluster.nodes[nodesCount].node_number
currentNode;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_status
NS_UNAVAILIBLE;
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_weight
nodesInfo->getWidthOnNode(currentNode);
=
message.message_body.changes_in_cluster.nodes[nodesCount].node_have_cha
nges = NC_NONE;
}
++nodesCount;
}
return message;
}
message_struct ProcWatch::generateMtNewActiveNode(int newActiveNode)
{
message_struct message = generateBaseMessage();
message.type = MT_NEW_ACTIVE_NODE;
message.message_body.node_number
>findMinWidthRealNode();
101
=
nodesInfo-
return message;
}
message_struct ProcWatch::generateMtTotalProcsCount()
{
message_struct message = generateBaseMessage();
message.type = MT_TOTAL_PROCS_COUNT;
message.message_body.proc_count = runtimeProcs.entries();
return message;
}
message_struct ProcWatch::generateMtTotalUpdatedProcs(int updateType)
{
assert((updateType == UT_FULL) || (updateType == UT_PARTIAL));
message_struct message = generateBaseMessage();
message.type = MT_TOTAL_UPDATED_PROCS;
message.message_body.num_of_updates.cluster_state_rev
clusterState->currentRev;
=
message.message_body.num_of_updates.update_type = updateType;
if(updateType == UT_FULL)
{
message.message_body.num_of_updates.total_updates
runtimeProcs.entries();
=
}
else if(updateType == UT_PARTIAL)
{
message.message_body.num_of_updates.total_updates
procsInfo->totalChangedProcs()*/;
}
return message;
}
message_struct ProcWatch::generateMtUpdateInProcsList()
{
102
=
0/*
message_struct message = generateBaseMessage();
message.type = MT_UPDATE_IN_PROCS_LIST;
message.message_body.procs_status.cluster_state_rev
clusterState->currentRev;
int procsTotal = 0;
int procsCounter = 0;
while(procsCounter < procsTotal)
{
//TODO: add info about procs
++procsCounter;
}
return message;
}
message_struct ProcWatch::generateMtUpdateInNodesList()
{
message_struct message = generateBaseMessage();
message.type = MT_UPDATE_IN_NODES_LIST;
int nodesTotal = realNodes.entries();
message.message_body.logical_nodes.total = nodesTotal;
int nodesCounter = 0;
while(nodesCounter < nodesTotal)
{
++nodesCounter;
}
return message;
}
int ProcWatch::messageSize(int messageType)
{
int size = 3 * sizeof(int); //magic_number + type + sender
switch(messageType)
{
103
=
case MT_SELECTION:
size += sizeof(changes_in_cluster_struct);
break;
case MT_NEW_ACTIVE_NODE:
size += sizeof(int);
break;
case MT_LIST_OF_NODES:
size += sizeof(node_list_struct);
break;
case MT_TOTAL_PROCS_COUNT:
size += sizeof(int);
break;
case MT_PROCS_STATUS:
size += sizeof(procs_status_struct);
break;
case MT_CLUSTER_CHANGES:
size += sizeof(changes_in_cluster_struct);
break;
case MT_TOTAL_UPDATED_PROCS:
size += sizeof(num_of_updates_struct);
break;
case MT_UPDATE_IN_PROCS_LIST:
size += sizeof(procs_status_struct);
break;
case MT_UPDATE_IN_NODES_LIST:
size += sizeof(logical_nodes_struct);
break;
case MT_REPLY:
size += sizeof(int);
break;
}
//return size;
return sizeof(message_struct);
104
}
int ProcWatch::nextNode(int nodeNum)
{
log->startEvent("ProcWatch::nextNode
toString(nodeNum));
:forNode"
WCValSList <int> nodeList = realNodes;
int nodeIndex;
int totalNodes = nodeList.entries();
if(totalNodes == 1)
{
log->endEvent("Have only one node in list");
return 0;
}
nodeIndex = nodeList.index(nodeNum);
assert(nodeIndex != -1);
if((nodeIndex + 1) < totalNodes)
{
log->endEvent(toString(nodeList.find(nodeIndex + 1)));
return nodeList.find(nodeIndex + 1);
}
else
{
log->endEvent(toString(nodeList.find(0)));
return nodeList.find(0);
}
}
int ProcWatch::sendMessageToNextNode(message_struct * message)
{
log->startEvent("ProcWatch::sendMessageToNextNode");
int sendStatus = -1;
int receiverNode = getnid();
105
+
do
{
answerToAllMessages();
receiverNode = nextNode(receiverNode);
if((receiverNode == getnid()) || (receiverNode == 0))
{
log->endEvent("Cant selfsend message");
return MESSAGE_CANT_SEND;
}
pid_t receiver = qnx_name_locate(0,
nodeNamePrefix
sizeof(message_struct), NULL);
+
toString(receiverNode),
if(receiver != -1)
{
sendStatus = Send(receiver, message, message,
messageSize(message->type),
sizeof(message_struct));
qnx_vc_detach(receiver);
//node who send message now unavailible
if((sendStatus
!=
0)
&&
(receiverNode
==
message-
>sender))
{
log->endEvent("message cant send to sender");
return MESSAGE_CANT_SEND;
}
}
else if(receiverNode == message->sender)
{
log->endEvent("message cant send to sender");
return MESSAGE_CANT_SEND;
}
} while(sendStatus != 0);
delay(MESSAGE_TIMEOUT);
log->endEvent("message send");
106
return MESSAGE_SEND;
}
int ProcWatch::registerMyNodeName()
{
return qnx_name_attach(0, nodeNamePrefix + toString(getnid()));
}
void ProcWatch::registerMyNodeAsMainNode()
{
mainNodeNameId = qnx_name_attach(0, nodeNamePrefix + "main");
}
void ProcWatch::unregisterMyNodeAsMainNode()
{
qnx_name_detach(0, mainNodeNameId);
}
pid_t ProcWatch::getMessage(message_struct *message)
{
pid_t sender;
sender = Creceive(0, message, sizeof(message_struct));
if(sender == -1)
{
return NO_NEW_MESSAGES;
}
return sender;
}
int ProcWatch::sendReplyMessage(message_struct *message, pid_t sender)
{
int replyState = Reply(sender, message, sizeof(message_struct));
return replyState;
107
}
void ProcWatch::answerToAllMessages()
{
message_struct newMsg;
message_struct incomingMsg;
pid_t sender;
while((sender = getMessage(&incomingMsg)) != NO_NEW_MESSAGES)
{
clusterState->cycleOfLastMessage = clusterState->cycle;
log->startEvent("HAVE_NEW_MSG");
int sendNextNode = answerMessage(&incomingMsg, &newMsg);
sendReplyMessage(&incomingMsg, sender);
log->endEvent("MSG_PROCESSED");
if(sendNextNode == SEND_MESSAGE_TO_NEXT_NODE)
{
sendMessageToNextNode(&newMsg);
}
}
}
LRNode::LRNode(int lNode, int nodeWidth)
{
logicalNode = lNode;
realNode = 0;
logicalNodeWidth = nodeWidth;
}
LRNode::operator==(const LRNode &node) const
{
return(logicalNode == node.logicalNode);
}
108
RealNodeInfo::RealNodeInfo(nid_t rNode, int nodeStatus)
{
node = rNode;
status = nodeStatus;
}
RealNodeInfo::operator==(const RealNodeInfo &rNode) const
{
return(node == rNode.node);
}
NodesInfo::NodesInfo(WCPtrSList <RealNodeInfo> nodes)
{
cout << "debug: NodesInfo::NodesInfo" << endl;
int nodeNum = nodes.entries();
int nodeCount = 0;
while(nodeCount < nodeNum)
{
RealNodeInfo * node = nodes.find(nodeCount);
RealNodeInfo * rNode;
RealNodeInfo * rNodeStartState;
if(node->node == getnid())
{
rNode = new RealNodeInfo(node->node, NS_PASSIVE);
rNodeStartState
=
new
RealNodeInfo(node->node,
NS_PASSIVE);
}
else
{
rNode = new RealNodeInfo(node->node, NS_UNAVAILIBLE);
rNodeStartState
=
NS_UNAVAILIBLE);
}
rNodes.append(rNode);
109
new
RealNodeInfo(node->node,
rNodesStartState.append(rNodeStartState);
++nodeCount;
}
}
void NodesInfo::reset()
{
cout << "debug: NodesInfo::reset" << endl;
int nodesTotal = rNodes.entries();
int nodesCounter = 0;
rNodes.clearAndDestroy();
while(nodesCounter < nodesTotal)
{
RealNodeInfo
rNodesStartState.find(nodesCounter);
RealNodeInfo
*
*
rNode
=
rNodeSS
new
RealNodeInfo(rNodeSS->node,
rNodeSS->status);
rNodes.append(rNode);
++nodesCounter;
}
}
void NodesInfo::setRealNodeToLogicalNode(int lNode, int rNode)
{
LRNode node(lNode, 0);
int nodeIndex = lrNodes.index(&node);
assert(nodeIndex != -1);
LRNode * currentNode = lrNodes.find(nodeIndex);
currentNode->realNode = rNode;
}
int NodesInfo::getRealNodeFromLogicalNode(int lNode)
{
LRNode node(lNode, 0);
110
=
int nodeIndex = lrNodes.index(&node);
assert(nodeIndex != -1);
return lrNodes.find(nodeIndex)->realNode;
}
int NodesInfo::realNodeState(int node)
{
RealNodeInfo rNode(node, 0);
int nodeIndex = rNodes.index(&rNode);
cout << "debug: realNodeState :node " << node;
if(rNodes.find(nodeIndex)->status == NS_ACTIVE)
cout << " NS_ACTIVE" << endl;
else if(rNodes.find(nodeIndex)->status == NS_PASSIVE)
cout << " NS_PASSIVE" << endl;
else
cout << " NS_UNKNOWN" << endl;
assert(nodeIndex != -1);
return rNodes.find(nodeIndex)->status;
}
int NodesInfo::isRealNodeAvailible(int rNode)
{
RealNodeInfo realNode(rNode, NS_PASSIVE);
int nodeIndex;
if((nodeIndex = rNodes.index(&realNode)) == -1)
{
return 0;
}
else
{
if(rNodes.find(nodeIndex)->status == NS_UNAVAILIBLE)
{
return 0;
111
}
return 1;
}
}
int NodesInfo::haveActiveNode()
{
int nodeCount = 0;
int totalNodes = rNodes.entries();
while(nodeCount < totalNodes)
{
RealNodeInfo * realNode = rNodes.find(nodeCount);
if(realNode->status == NS_ACTIVE)
{
return 1;
}
++nodeCount;
}
return 0;
}
int NodesInfo::findMinWidthRealNode()
{
int minNodeNum = getnid();
int minNodeWidth = -1;
int nodeCount = 0;
int totalNodes = rNodes.entries();
while(nodeCount < totalNodes)
{
RealNodeInfo * realNode = rNodes.find(nodeCount);
if(realNode->status != NS_UNAVAILIBLE)
{
int nodeNum = realNode->node;
112
int nodeWidth = getWidthOnNode(nodeNum);
if((minNodeWidth == -1) || (nodeWidth < minNodeWidth))
{
minNodeNum = nodeNum;
minNodeWidth = nodeWidth;
}
}
++nodeCount;
}
return minNodeNum;
}
RealNodeInfo * NodesInfo::getNode(int node)
{
cout << "debug: NodesInfo::getNode(" << node << ")" << endl;
RealNodeInfo realNode(node, NS_PASSIVE);
int nodeIndex = rNodes.index(&realNode);
assert(nodeIndex != -1);
return rNodes.find(nodeIndex);
}
ClusterState::ClusterState()
{
currentRev = 0;
newRev = 0;
remainingUpdates = 0;
cycle = 0;
cycleOfLastMessage = MESSAGE_NEVER_GET;
}
int main()
{
113
Config conf("/home/dmitry/test/main.txt");
int status = conf.checkConfig();
log = new LogEvents();
log->startEvent("PARSE-CONFIG :file /home/dmitry/test/main.txt");
if(status != PARSE_ERROR)
{
log->endEvent("OK");
ProcWatch watcher(conf.getNodeList(), conf.getProcList());
watcher.startMonitoring();
}
else
{
log->endEvent("ERROR");
}
return 0;
}
114
Download