Проблемы при создании модуля для Apache 2


Проблема 1: Освобождение ресурсов в момент выгрузки модуля

Можно указать сравнительно большое число прикладных задач, при решении которых разработчик модулей сервера Apache так или иначе сталкивается с необходимостью выделения ресурсов системы и их использования на протяжении всего цикла работы модуля (примером такой задачи вполне может стать необходимость установить соединение с СУБД). В общем случае, при реализации, разработчику может потребоваться способ, позволяющий:
  1. Провести выделение ресурсов и инициализацию данных в момент загрузки модуля;
  2. Иметь возможность получать доступ к данным из процедур и функций модуля;
  3. Корректно провести освобождение ресурсов в момент выгрузки модуля.

В целом, такой способ оказывается сравнительно простым в понимании, но отдельные моменты, связанные с его реализацией, требуют особого внимания.

Выбор пула памяти


Согласно документации, рекомендуемым способом хранения данных и доступа к ним является механизм пулов памяти, поддерживаемый сервером Apache, поэтому из всех возможных вариантов предпочтительно использовать именно этот механизм. Идея, которой следует реализация, весьма проста и заключается в том, чтобы в момент загрузки модуля выделить системные ресурсы и область памяти в пуле, разместить в этой области все необходимые данные, а в момент выгрузки модуля освободить все ресурсы и область памяти пула. При этом нет необходимости создавать дополнительные пулы памяти, поскольку в рамках сервера Apache уже имеется несколько пулов, среди них может быть выбран пул, подходящий для размещения данных модуля с точки зрения следующих очевидных условий:
  1. Пул обязательно должен существовать в момент загрузки модуля;
  2. Пул не должен освобождаться во время работы модуля, так как это неизбежно приведет к потере данных;
  3. Освобождение пула должно производиться до момента выгрузки модуля, в противном случае процедура освобождения ресурсов, являющаяся одной из процедур модуля, к моменту освобождения пула окажется выгруженной из памяти вместе с остальными процедурами и функциями модуля и освобождение ресурсов системы не произойдет (более того, попытка передачи управления по “не действительному” адресу процедуры освобождения ресурсов приведет к некорректному завершению всего процесса сервера Apache).
Вполне возможно, что выбор подходящего пула может оказаться не однозначным в существующих и последующих версиях сервера Apache, поэтому вопрос выбора остается на усмотрение разработчика.

Проблема 2: Двукратная загрузка модулей Apache

Существенной особенностью работы сервера Apache с модулями является их двукратная загрузка. При первоначальном запуске, сервер Apache загружает все модули (имеются в виду модули, указанные в файле конфигурации с помощью соответствующей директивы LoadModule), при этом производятся все необходимые вызовы функций и процедур, начиная с процедуры регистрации хуков вплоть до вызова самих функций хуков. Далее следует выгрузка всех успешно загруженных модулей, опять же сопровождаемая вызовами всех необходимых процедур и функций. Именно таким образом сервер Apache осуществляет проверку того, что все модули корректно выполняют загрузку и выгрузку, после чего повторно выполняет загрузку всех модулей и только после этого продолжает работу.
Очевидным является то, что выделение ресурсов во время первой загрузки модуля лишено всякого смысла, поскольку в скором времени произойдет выгрузка модуля. В момент первой загрузки следует лишь сохранить информацию о том, что она имела место, с тем, чтобы выделение ресурсов и инициализацию данных провести при второй загрузке. Реализовать сохранение информации о первой загрузке можно различными способами, в том числе и с помощью пулов памяти следующим образом: при первой загрузке модуля в одном из пулов памяти сервера Apache разместить некоторый флаг инициализации, наличие которого при второй загрузке будет свидетельствовать о том, первая загрузка уже имела место и данная загрузка является второй по счету. При этом следует быть аккуратным в выборе пула для размещения флага, и, прежде всего, убедиться в том, что он не освобождается вплоть до момента второй загрузки модуля, в противном случае, флаг инициализации будет утерян. Следует так же отметить, пул размещения флага, не обязательно должен совпадает с пулом, который будет использоваться для хранения данных модуля.

Решение. Модуль mod_time

В качестве реализации, рассмотрим простейший модуль (исходный код модуля), представляющий собой выходной фильтр, на примере которого проиллюстрируем приемы, изложенные в двух предыдущих разделах. Из соображений простоты изложения максимально упростим работу модуля, единственной задачей которого станет определение системных даты и времени в момент загрузки и вывод некоторого информационного сообщения, содержащего полученные дату и время, в конце всех html-страниц.
Объявление структуры модуля является стандартным, причем существенной в данном случае является только поле функции регистрации хуков.
// объявляем модуль
module AP_MODULE_DECLARE_DATA time_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* создание конфигурации для директории */
NULL, /* слияние конфигурации для директорий */
NULL, /* создание конфигурации сервера */
NULL, /* слияние конфигураций сервера */
NULL, /* cmds */
hook_registration /* функция регистрации hook’ов */
};
В рамках функции регистрации хуков hook_registration, выполняется регистрация выходного фильтра, который будет осуществлять вывод информационного сообщения,
// регистрация выходного фильтра
ap_register_output_filter(“time_output_filter”,
time_output_filter, NULL,
AP_FTYPE_RESOURCE);
и регистрация хука, выполняемого после завершения обработки файла конфигурации
// регистрация post config hook
ap_hook_post_config(post_config_hook, NULL, NULL, APR_HOOK_LAST);
Предположим, что объявлены следующие константы
// ключ для флага инициализации
#define MODULE_INITIALIZATION_FLAG_KEY “mod_time:initialization_flag_key”
// указатель для ассоциации с ключом флага
#define MODULE_INITIALIZATION_POINTER 0xFFFFFFFF
Особо отметим две функции работы с пулами памяти, которые будут использоваться далее и представляют особый интерес:
  1. apr_pool_userdata_set - позволяет ассоциировать строку (ключ) с указателем;
  2. apr_pool_userdata_get - позволяет получить указатель по заданной строке (ключу).
Фактически, совокупность этих двух функций реализует функциональность хеша.
Идея использования этих функций такова: при каждой загрузке модуля, с помощью функции apr_pool_userdata_get производиться попытка получить указатель для ключа MODULE_INITIALIZATION_FLAG_KEY, далее
  1. Если такого ключа не существует - данная загрузка является первой по счету, и необходимо с помощью функции apr_pool_userdata_set произвести ассоциацию ключа MODULE_INITIALIZATION_FLAG_KEY с указателем MODULE_INITALIZATION_POINTER, значение которого не существенно и может быть выбрано произвольным образом;
  2. Если такой ключ существует - ассоциация была произведена и данная загрузка является второй по счету.
В данном случае, наличие и отсутствие ключа MODULE_INITIALIZATION_FLAG_KEY позволяет отличить первую загрузку модуля от второй (ассоциация этого ключа выступает в роли флага первой загрузки, о котором упоминалось в предыдущем разделе).
Таким образом, в функции post_config_hook, которая выполняется при каждой загрузке модуля, прежде всего, необходимо проверить наличие ключа флага
// извлечение указателя для ключа флага инициализации
apr_pool_userdata_get(&pData, MODULE_INITIALIZATION_FLAG_KEY,
s->process->pool);
Если ключа не существует - необходимо произвести ассоциацию ключа
// ассоциация указателя с ключом флага инициализации
apr_pool_userdata_set((LPVOID)MODULE_INITIALIZATION_POINTER,
MODULE_INITIALIZATION_FLAG_KEY,
apr_pool_cleanup_null,
s->process->pool);
(в данной реализации для хранения флага первой загрузки используется пул s->process->pool).
Если ключ существует - необходимо выделить память в подходящем пуле
// выделение памяти под структуру данных модуля
pData = apr_palloc(s->process->pconf, sizeof(_MODULE_INTERNAL_DATA));
и произвести ассоциацию ключа MODULE_INTERNAL_DATA_KEY
// ключ для данных
#define MODULE_INTERNAL_DATA_KEY “mod_time:internal_data_key”
c указателем выделенной памяти
// ассоциация указателя с ключом данных
apr_pool_userdata_set(pData, MODULE_INTERNAL_DATA_KEY,
module_internal_data_cleanup,
s->process->pconf);
В данном случае для хранения данных модуля (структуры типа _MODULE_INTERNAL_DATA) выбран пул s->process->pconf и он отличен от пула хранения флага первой загрузки. Если ассоциация выполнена успешно, при необходимости можно так же зарегистрировать функцию освобождения ресурсов
// регистрация функции освобождения ресурсов
apr_pool_cleanup_register(s->process->pconf, (LPVOID)pData,
module_internal_data_cleanup,
apr_pool_cleanup_null);
которая будет вызвана при освобождении пула s->process->pconf с указанным при регистрации аргументом pData
apr_status_t module_internal_data_cleanup(void* pData)
{

return APR_SUCCESS;
}
Последним шагом в приготовлении к работе модуля является выполнение функции инициализации данных системными датой и временем в процедуре инициализации
initialize_module_internal_data((_MODULE_INTERNAL_DATA*) pData);
Во время работы модуля, периодически будут происходить обращения к зарегистрированной ранее функции фильтра time_output_filter. Для того чтобы иметь возможность получить доступ к данным модуля из функции time_output_filter, следует воспользоваться упомянутой ранее функцией apr_pool_userdata_get:
apr_status_t time_output_filter(ap_filter_t *filter,
apr_bucket_brigade *brigade_of_buckets)
{

apr_pool_userdata_get(&pData, MODULE_INTERNAL_DATA_KEY,
filter->r->server->process->pconf);

}
Автор: Давид Тигетов
Источник: www.apachedev.ru

These icons link to social bookmarking sites where readers can share and discover new web pages.
  • News2.ru
  • NewsLand.ru
  • del.icio.us
  • BobrDobr.ru
  • Ma.gnolia
  • Digg
  • Reddit
  • Technorati
  • Slashdot
  • Netscape
  • DZone
  • ThisNext
  • Furl
  • YahooMyWeb
Опубликовано в: Разработка модулей Март 13, 2006

11 Комментариев »

  1. Хорошая статья, спасибо огромное :)

    Комментарий от Akiv — Март 24, 2006 @ 11:07 am

  2. Для меня все выше сказанное - загадка - зачем все эти “хитросплетения”, если есть hook child_init. Данный вопрос хорошо рассмотрен в книге “Writing Apache Modules with Perl and C”. Наверняка, многие и без этой книги “дошли” до решения использовать hook child_init. Причем, в этом случае возможностей больше: хочешь - создавай глобальные данные модуля, хочешь - создавай данные per server, хочешь - регистрируй cleanup в pchild, который будет вызываться при его разрушении и сигнализировать модулю о shutdown/restart

    Комментарий от Marat — Ноябрь 23, 2007 @ 5:25 pm

  3. как у вас всё складно получается, блин

    Комментарий от организация праздника — Май 3, 2008 @ 1:28 am

  4. ДА классная сатья! Респект!!!
    __________________
    http://footballnotes.ru/

    Комментарий от андрюха — Август 29, 2008 @ 1:45 pm

  5. one

    Комментарий от Тимур — Октябрь 21, 2008 @ 3:39 pm

  6. two

    Комментарий от Тимур — Октябрь 21, 2008 @ 3:40 pm

  7. three

    Комментарий от Мария — Октябрь 21, 2008 @ 3:40 pm

  8. four

    Комментарий от Мартин — Октябрь 21, 2008 @ 3:41 pm

  9. oneoneone

    Комментарий от http://danetnavern0.narod.ru — Октябрь 21, 2008 @ 4:14 pm

  10. Все получилось!

    Комментарий от kaa — Ноябрь 28, 2008 @ 7:42 pm

  11. Да хорошая статья

    Комментарий от Heap — Январь 17, 2009 @ 3:08 am

Оставить комментарий

You must be logged in to post a comment.

Работает на WordPress