Библиотека сайта rus-linux.net
Open MPI
Глава 15 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: Open MPI,
глава из книги "The Architecture of Open Source Applications" том 2.
Автор: Jeffrey M. Squyres
Перевод: Н.Ромоданов
Компоненты плагинов
Плагины Open MPI состоят из двух частей: структуры компонента и структуры модуля. Структура компонента и функций, к которым он обращается, как правило, вместе именуются как «компонент». Аналогичным образом собирательное понятие «модуль» относится к структуре модуля и к его функциям. Деление немного напоминает деление на классы и объекты в языке C++. В каждом процессе есть только один компонент; в нем описывается общий плагин с некоторыми полями, которые являются общими для всех компонентов (независимо от фреймворка). Если компонент выбирается для запуска, то он используется для создания одного или нескольких модулей, которые обычно выполняют основную часть функций, необходимых фреймворку.
На протяжении следующих нескольких разделов мы создадим структуры, необходимые для компонента TCP в фреймворке BTL (слой побайтовой передачи данных). Фреймворк BTL используется при передаче сообщений типа «точка-точка»; компонент TCP, что очевидно, использует TCP в качестве основного транспорта для передачи сообщений.
Структура компонента
Независимо от фреймворка в каждом компоненте есть всем известная
статически выделяемая и инициализируемая структура компонента.
Структура должна называться согласно шаблону как
mca_<framework>_<component>_component
. Например, структура драйвера сети TCP во фреймворке BTL называется mca_btl_tcp_component
.
Наличие символов компонентов, созданных по шаблону, гарантирует, что между именами компонентов не будет никаких конфликтов, и позволяет ядру MCA искать структуру произвольного компонента при помощи dlsym(2)
(или соответствующего эквивалента в каждой поддерживаемой операционной системе).
Структура базового компонента содержит некоторую информацию об используемых ресурсах, например, официальное название компонента, версия, принадлежность версии фреймворка и т.д. Эти данные используются для отладки, учета и во время выполнения для проверки соблюдения совместимости.
struct mca_base_component_2_0_0_t { /* Номер версии структуры компонента */ int mca_major_version, mca_minor_version, mca_release_version; /* Строка с именем фреймворка, к которому принадлежит компонент, и версия API фреймворка, к которому принадлежит данный компонент */ char mca_type_name[MCA_BASE_MAX_TYPE_NAME_LEN + 1]; int mca_type_major_version, mca_type_minor_version, mca_type_release_version; /* Имя и номер версии компонента */ char mca_component_name[MCA_BASE_MAX_COMPONENT_NAME_LEN + 1]; int mca_component_major_version, mca_component_minor_version, mca_component_release_version; /* Указатели на функции */ mca_base_open_component_1_0_0_fn_t mca_open_component; mca_base_close_component_1_0_0_fn_t mca_close_component; mca_base_query_component_2_0_0_fn_t mca_query_component; mca_base_register_component_params_2_0_0_fn_t mca_register_component_params; };
Структура базового компонента является основой компонента TCP BTL; она содержит указатели на следующие функции:
- Open. Вызов функции open является инициализирующей функцией, к которой обращается компонент. Она позволяет компоненту инициализировать состояние самого компонента, проанализировать систему, в которой он работает, и определить, должен ли он выполняться. Если компонент должен выполняться всегда, он может в качестве указателя на функцию open передавать значение NULL.
Функция open компонента TCP BTL, как правило, инициализирует некоторые структуры данных и обеспечивает, чтобы пользователь не смог установить недопустимые параметры.
- Close. Когда фреймворк решает, что компонент не больше не нужен, он вызывает функцию close, которая позволяет компоненту освободить все ресурсы, которые для него были выделены. Когда процессы останавливаются, то для всех остальных компонентов также вызывается функция close. Однако, функция close также может быть вызвана для компонентов, запуск которых был отклонен во время выполнения, так что они могут быть закрыты и игнорироваться в течение всего процесса.
Функция close компонента TCP BTL закрывает прослушиваемые сокеты и освобождает ресурсы (например, приемные буферы).
- Query. Этот вызов является обобщением функции, запрашивающей необходимость запуска компонента. Этот специальный вызов используется не во всех фреймворках — в некоторых требуется более специализированная функция запроса.
Во фреймворке BTL обобщенная функция query не используется (в нем определяется своя собственная функция; смотрите ниже), поэтому TCP BTL ее не заполняет.
- Регистрация параметров. Эта функция, как правило, является первой функцией, которая вызывается в компоненте. Она позволяет компоненту зарегистрировать соответствующие параметры времени выполнения, которые можете задавать пользователь. Параметры времени выполнения будут рассмотрены ниже.
Функция register компонента TCP BTL создает различные параметры времени выполнения, которые может устанавливать пользователь, например, один из них позволяет пользователю указывать, какой интерфейс IP будет использоваться.
Компонентная структура также может быть расширена в каждом конкретном фреймворке и/или в каждом конкретном базисном коде. Во фреймворке обычно создают новую структуру компонента с базовой структурой компонента в качестве первого элемента. Такая вложенность позволяет фреймворкам добавлять свои собственные атрибуты и указателей на функции. Например, для фреймворка, для которого требуется более специализированная функция запроса (в сравнении с функцией query, которая реализована в базовом компоненте), можно добавить указатель на функцию внутри структуры, специализированной под конкретный фреймворк.
Эта методика используется во фреймворке btl
в MPI, в котором реализуются функции MPI для передачи сообщений типа «точка-точка».
struct mca_btl_base_component_2_0_0_t { /* Структура базового компонента */ mca_base_component_t btl_version; /* Блок данных базового компонента */ mca_base_component_data_t btl_data; /* Функции query, специальные для фреймворка btl */ mca_btl_base_component_init_fn_t btl_init; mca_btl_base_component_progress_fn_t btl_progress; };
Например, функции query фреймворка TCP BTL и функция btl_init
компонента TCP BTL выполняют следующее:
- Создается прослушиваемый сокет для каждого интерфейса IPv4 и IPv6, идущего вверх.
- Создается модуль для каждого интерфейса IP, идущего вверх.
- В центральном репозитарии регистрируется кортеж
(IP address, port)
, т. е. IP адрес и порт для каждого интерфейса IP, идущего вверх, с тем, чтобы в других процессах MPI было известно, как связаться с данным процессом.
Аналогичным образом плагины могут расширять структуру компонента конкретного фреймворка, добавляя в нее свои собственные элементы. Это делает компонент tcp
во фреймворке btl
; он кэширует многие члены-данные в своей собственной структуре компонента.
struct mca_btl_tcp_component_t { /* Структура компонента, специальная для фреймворка btl */ mca_btl_base_component_2_0_0_t super; /* Некоторые данные-члены, специальные для компонента TCP BTL */ /* Количество интерфейсов TCP на данном сервере */ uint32_t tcp_addr_count; /* Дескриптор сокета, слушающего IPv4 */ int tcp_listen_sd; /* ... и многое другое, что здесь не показано */ };
Такая методика вложенных структур является эффектной и простой имитацией одиночного наследования языка C++: указатель на экземпляр структуры struct mca_btl_tcp_component_t
может быть приведен к любому из трех типов, т. е. он может использоваться на уровне абстракции, на котором непонятны «производные» типы.
Надо сказать, что такое приведение типов, как правило, не одобряется в Open MPI, поскольку оно может привести к невероятно тонким, трудно обнаруживаемым ошибкам. Исключение может быть сделано для этого варианта эмуляции C++, поскольку в этом случае задается строго определенное поведение, которое помогает соблюдать границы абстракций.
Далее Структура модуля