
- •Сегменты стека
- •Динамическая память (куча)
- •Объектно ориентированный подход к проектированию системных модулей на основе концепций интерфейсов и реализаций.
- •Родовые данные или данные класса, значение которых относится не к экземпляру, а к типу в целом.
- •Активные объекты
- •Активные объекты с множественными рабочими потоками
- •Посылка синхронных сообщений
- •Выборка сообщений
- •Пример полной и упрощенной выборки
- •Сообщения, определяемые приложением
- •Локальная память потока
- •Трансляторы, компиляторы и интерпретаторы.
- •Общая схема работы транслятора
- •Понятие прохода
- •Генерация кода
- •Многоадресный код с явно именуемым результатом
- •Алгоритм вычисления выражений в полной записи
- •Современные системы программирования
Пример полной и упрощенной выборки
MSG msg;
BOOL ret;
while ((ret = GetMessage (8 msg, 0, 0, 0)) != 0)
{
if (ret == -1)
{//обработать ошибку}
else
{
TranslateMessage (8 msg);
Dispash Message (8 msg);
}
}
while (GetMessage (…))
{
TranslateMessage (8 msg);
Dispash Message (8 msg);
}
Существует более гибкий вариант выборки, который не приводит к ожиданию поступления сообщений, а возвращает управление немедленно.
Цикл выборки, устроенный таким образом, позволяет утилизировать фоновое время работы пользовательского приложения, т.к. в промежутках между обработкой сообщений можно выполнять какую – либо вычислительную процедуру.
BOOL PeckMessage (MSG *msg,
HWND,
UINT filterMin,
UINT filterMax,
UINT removeFlag);
Если дескриптор окна равен NULL, то функция не выбирает сообщения, посланные через PostThreadMessage, а GetMessage выбирает. Для того чтобы сделать выборку таких сообщений плюс сообщения окнам, HWND должен быть равен INVALID_HANDLE_VALUE. Функция возвращает ненулевое сообщение, если в очереди есть сообщение, соответствующее аргументам, и ноль, если сообщений нет.
Поскольку PeckMessage возвращается немедленно, построение алгоритмов, циклически проверяющих очередь, должно содержать какие – то действия в промежутках между сообщениями интерфейса.
WaitMessage (void) – в простейшем случае.
Сообщения, определяемые приложением
Для кодов сообщений предусмотрена достаточно строгая классификация, удобная для организации диапазонной выборки. Часть кодов предусмотрена системой, а часть доступна для распределения приложений.
0 ÷ (WM_USER-1)
WM_USER ÷ (WM_APP-1)
WM_ APP ÷ 0xFFFF
Первый диапазон – системные сообщения, второй – сообщения для классов окон, не принадлежащих системным, третий – сообщения, которые приложения может использовать для собственных логических потребностей.
Сообщение с кодом WM_PAINT , обрабатывается не так, как остальные, поскольку выбирается функциями выборки в тех случаях, когда в очереди нет других сообщений. Если в очереди присутствует несколько сообщений WM_PAINT, они объединяются в одно. Такие особенности являются оптимизацией вычетов затрат интерфейса.
Локальная память потока
Часто необходимо ассоциировать некоторых пользователей информации с каждым экземпляром потока. Для этого система в служебной памяти организует массив «слотов», доступных для распределения по запросу. При этом одному и тому же индексу в массиве для разных потоков будут соответствовать разные адреса в памяти.
Если поток желает исполнять механизм tls, он должен зарезервировать для себя некоторый индекс от нуля до максимального значения номера слота (это делается функцией DWORD TlsAlloc ( );). Эта функция сканирует битовый массив, каждый бит которого соответствует отдельному слоту, находит свободный, помечает его как занятый и номер слота возвращает приложению. Далее приложение может использовать этот индекс для записи и удаления значения.
BOOL TlsSetValue (DWORD index,
void *value);
void * TlsSetValue (DWORD index);
Система инициализирует все слоты нулями, что позволяет идентифицировать ситуацию первого обращения к слоту. По окончанию исполнения механизма Tls слот надо освободить механизмом:
BOOL TlsFree (DWORD index);
Механизм Tls удобно использовать для организации отслеживания ошибок в приложении. Выделяется специальная информация, возвращающая информацию об ошибке. Такая функция должна отрабатывать для каждого потока по – разному, поэтому ей недостаточно использовать глобальную переменную.
С помощью Tls для глобальной переменной, соответствующей коду ошибки, выделяется индекс в массиве слотов, а запись в стеке этой переменной происходит косвенно через SetValue и GetValue.
Должен быть интерфейсный компонент, который обозначим IFrontEnd. Этот интерфейс должен позволять внешнему окружению вызывать и обслуживать главные семантические аспекты системы. Вся детализация должна предусматривать параметры методов, синхронных или асинхронных вызовов, которые определяются только их внутренним смыслом. Многопоточные обращения и внешний интерфейс IFrontEnd должны синхронизировать внутренние компоненты системы. Для организации повторного использования компонентной инфраструктуры реентерабельных вызовов удобно определить интерфейсы IActiveObject и ICallBack.
Интерфейс IAO специфицирует вызов какой – либо процедуры в отдельном потоке, однако не специфицирует сигнатуру этого вызова.
Задача вызова процедуры специфицировать интерфейс ICB, при инициализации которого должен быть так или иначе указан адрес кода (в том числе символический (имя dll), экспортирующий какую-либо функцию).
Другой аспект, а именно передача параметров процедуре, должна решаться связкой интерфейсов.
-
Пул доступных для использования пакетов.
-
Модуль маршалинга.
-
Модуль обработки ошибок и обратной связи.
IBlocksPool IBP
ICustomMarshaler ICM
IErrorHandler IEH
IErrorHandler IEM
При использовании пула удобно выделить специальный компонент для для инициализации пакетов (первичный и повторный) IBlocksConstructer.
Клиент, желающий вызвать какой-либо метод, предусмотренный интерфейсом системы, обращается к IFE. Реализация этого модуля выполняет двухэтапные действия: 1 – обращение к пулу пакетов (IBP), включая заполнение пакетов аргументами вызова; 2 – обращение к активному объекту (IAO). IAO, в свою очередь, обращается к компоненту ICB, передавая инициализированный пакет вызовов. Модуль ICB выполняет основную реализацию метода, распаковывая аргументы с помощью ICM, а при необходимости обрабатывая ошибки с помощью IEH. При этом для информации об ошибках используется та же инфраструктура пакетов из пула, но обрабатывается через интерфейс IEM. Пул управляет вызовами тех или иных конструкций блоков (IBC), через диспетчер перенаправляющий их либо на часть пользовательского маршалинга, либо на участок маршалинговых ошибок. Реализация модулей маршалинга, как правило, использует стекоподобные контейнеры, позволяющие иметь унифицированный доступ к переменному числу варьированных по размеру единиц информации. С точки зрения СПО в такой схеме наблюдается широкое повторное использование двоичных компонентов. Часть модулей не требует изменений и подготавливается однократно при разработке инфраструктуры или архитектуры каркаса framework. К ним относятся IAO, IBP, IBC, ICM и IEM. Они могут быть реализованы в виде dll, отлажены и готовы к исполнению при снабжении доступом к необходимым внешним интерфейсам.
Поскольку смысловые аспекты каждой системы сосредоточены в IFE, ICB и IFH, то настройка инфраструктуры для каждого нового проекта сводится к реализации этих модулей и связыванию их с инфраструктурой.
При работе над серией проектов использование одной и той же инфраструктуры рекомендуется проводить не на уровне повторных текстов, а на уровне отлаженных модулей, поскольку это изолирует их код от изменения, а неработоспособность системы свидетельствует о неверном проекте семантически зависимых участков.
Первоочередной целью для разработчиков в области СПО должна считаться организация удобных партонов, позволяющих формировать устойчивые инфраструктуры решения. Как правило, такие партоны используют активные объекты, пулы и контейнеры.
В качестве инструментария используются сформированные и устоявшиеся партоны, можно рассматривать в том числе средства разработки и генераторы программ по шаблону. Те области, для которых четкие партоны не выражены, подвергаются ручному кодированию.