Основы управления ресурсами в Apache: APR Пулы (pools)


APR пулы (pools) - это основные строительные блоки в APR и Apache, а также основа для всего управления ресурсами. Они служат для выделения памяти, либо напрямую (на malloc- подобный манер), либо косвенно (как, например, в операциях со строками), и гарантируют полное освобождение памяти после использования. Также они используются для выделения памяти для таких ресурсов как, например, файлы или сокеты. Кроме того, пулы APR могут работать с ресурсами, управляемыми сторонними библиотеками.
В этой статье мы расскажем о APR пулах и опишем их возможности по предоставлению гарантии того, что все Ваши динамические ресурсы будут полностью управляемы, и будут иметь ровно то время жизни, в котором нуждаются.

Проблемы управления ресурсами

Основная проблема управления ресурсами конечно известна всем программистам. Когда Вы выделяете память под ресурс, Вы должны гарантированно ее высвободить по завершению работы. Например:

char* buf = malloc(n);
… выделяем память для buf…
… делаем что-либо с buf …
free(buf);
или
FILE* f = fopen(path, “r”);
… выделяем память под f…
… читаем из f …
fclose(f);
Несомненно, некорректное освобождение buf или закрытие f - это ошибка, и при постоянной работе сервера Apache это может привести к серьезным последствиям вплоть до аварийного завершения работы системы. Следовательно, мы не должны допустить такие ошибки!
В простых ситуациях, как рассмотрено выше, все очень просто. Но в большинстве более сложных ситуаций с многочисленными ветвлениями сложно определить точное время освобождения ресурса, что может привести к серьезным проблемам при освобождении занимаемой памяти при каждом возможном исходе программы. Таким образом, нам необходим более удобный механизм управления ресурсами.

Модель конструктора / деструктора

Одним из методов управления ресурсами может служить концепция объекта С++, имеющего конструктор и деструктор. Этот метод выбирается многими (но не всеми) С++ программистами, и делает деструктор ответственным за очистку всех ресурсов, полученных объектом. Этот метод хорошо работает, предоставляя любые динамические ресурсы и возлагая ответственность за их освобождение на объект. Но, как и с подходом, рассмотренным выше, этот метод требует тщательного внимания от программиста. Например, там, где ресурс получен условно или разделен между разными объектами, есть возможность совершения ошибок.

Модель уборки мусора

Более высокоуровневый метод управления ресурсами, типичный для Lisp и Java, это уборка мусора. Данный метод имеет преимущество в том, что решение проблемы перекладывается с программиста на язык, на котором он пишет, таким образом, опасность ошибок программиста просто исчезает. Недостатком такого подхода является дополнительный расход памяти там, где в этом нет необходимости, а также это понижает степень контроля программиста над памятью, как и возможность контроля времени жизни ресурса. Также данная модель требует, чтобы все компоненты программы, включая сторонние библиотеки, были созданы в тех же системах, что практически невозможно в открытых системах, написанных на С.

APR пулы

APR пулы предлагают альтернативную модель управления ресурсами. Подобно уборке мусора, они во всех случаях освобождают программиста от необходимости освобождения памяти. Но также они предлагают несколько дополнительных преимуществ, включая полный контроль над временем жизни ресурса и возможность управления разнородными ресурсами.
Основа концепции заключается в том, что любой ресурс Вы регистрируете в пуле. При этом пул становится ответственным за освобождение этого ресурса, которое произойдет тогда, когда пул очистится сам. Это означает, что вся проблема управления ресурсами уменьшается до единственного получения и очищения одного ресурса - самого пула. А так как пулы Apache управляются самим сервером, то эта проблема удаляется из области прикладного программирования. Программистам же остается только выбирать пул, подходящий под время жизни конкретного ресурса.

Основы управления памятью

Основное использование пулов происходит при работе с памятью. Вместо:
mytype* myvar = malloc(sizeof(mytype));
/*Необходимо убедиться, что myvar вернет память в каждом пути исполнения*/
мы используем
mytype* myvar = apr_palloc(pool, sizeof(mytype));
и пул автоматически начинает отвечать за освобождение myvar.
В APR и Apache память может выделяеться также внутри других функции. Например, в функциях обработки строк и ведения логов мы можем использовать конструкцию подобную sprintf без знания размера строки:
char* result = apr_psprintf(pool, fmt, …);
APR также предоставляет высокий уровень абстракции пула памяти, например в контейнерах (bucket), используемых для передачи данных вниз по цепочке фильтров. Но мы оставим это для другой статьи.

Общее управление памятью

APR предоставляет собственные функции для управления памятью и некоторых других основных ресурсов, таких как: файлы, сокеты и семафоры. Но особой потребности в их использовании нет. Альтернативой этому подходу является использование родных функций ресурса и последующая явная регистрация ресурса в пуле:
mytype* myvar = malloc(sizeof(mytype));
apr_pool_cleanup_register(pool, myvar, free, apr_pool_cleanup_null);
или
FILE* f = fopen(filename, “r”) ;
apr_pool_cleanup_register(pool, f, fclose, apr_pool_cleanup_null) ;
Такой подход передает ответственность за освобождение памяти пулу. Но использование родных функции может быть менее пригодно для других платформ, чем использование функций APR.
Данный метод обобщается на любые ресурсы. Вот, например, подключение к базе данных и гарантированное отключение от нее после использования:
MYSQL* sql = NULL;
sql = mysql_init(sql);
if ( sql == NULL ) { возврат; }
apr_pool_cleanup_register(pool, sql, mysql_close, apr_pool_cleanup_null);
sql= mysql_real_connect(sql, host, user, pass, dbname, port, sock, 0);
if ( sql == NULL ) { возврат; }
Заметьте, что APR предлагает в целом лучший метод управления подключением к базе данных, но это уже будет предметом другой статьи.
Во втором примере рассмотрим обработку XML:
xmlDocPtr doc = xmlReadFile(filename);
apr_pool_cleanup_register(pool, doc, xmlFreeDoc, apr_pool_cleanup_null);
/*теперь начинаем работать с doc, что может потребовать выделения дополнительной
памяти для XML библиотеки, но она все равно будет очищена*/
Другой пример. Код очистки интегрированный в С деструктор: Предположим у нас есть:
class myclass {
public:
virtual ~myclass() { очистка ; }
// …
};
Мы определяем С обертку:
void myclassCleanup(void* ptr) { delete (myclass*)ptr; }
и регистрируем ее в пуле при выделении памяти под myclass:
myclass* myobj = new myclass(…);
apr_pool_cleanup_register(pool, (void*)myobj, myclassCleanup, apr_pool_cleanup_null);
//теперь у нас есть возможность управления ресурсами С++
//в Apache и больше нет нужды удалять myobj.

Неявная и явная очистка

Теперь допустим, мы хотим освободить наши явно заданные ресурсы до завершения обработки запроса, например, потому что мы делаем что-либо расходующее много памяти, но при этом есть объекты, которые мы можем освободить. Функция пула apr_pool_cleanup_kill обеспечивает решение этой задачи. Для явного освобождения памяти мы должны открепить ресурс от пула, что и делается функцией apr_pool_cleanup_kill. Или мы можем поступить элегантнее. Ниже представлен С++ класс, управляемый с помощью пулов:
class poolclass {
private:
apr_pool_t* pool;
public:
poolclass(apr_pool_t* p) : pool(p) {
apr_pool_cleanup_register(pool, (void*)this, myclassCleanup,
apr_pool_cleanup_null);
}
virtual ~poolclass() {
apr_pool_cleanup_kill(pool, (void*)this, myclassCleanup);
}
};
Если Вы используете С++ в Apache (или APR), Вы можете наследовать любые классы от класса poolclass. Большинство APR функции делают что-то подобное этому, используя регистрацию и открепление всякий раз при выделении или освобождении ресурсов.

Время жизни ресурса

Когда мы выделяем ресурс в пуле, мы гарантируем его очистку в некоторый момент времени. Но когда? Мы должны удостовериться, что очистка произойдет в нужное время.

Пулы Apache

К счастью Apache упрощает эту задачу для нас, предоставляя различные пулы для различных типов ресурсов. Эти пулы связаны со структурами сервера и имеют время жизни соответствующее этим структурам. Вот четыре основных пула всегда доступных в Apache:
  • Пул запроса. Связан с временем жизни НТТР запроса.
  • Пул процесса. Связан с временем жизни процесса сервера.
  • Пул соединения. Связан с временем жизни ТСР соединения.
  • И пул конфигурации.
Первые три пула ассоциируются с соответствующими структурами Apache и доступны как request->pool, connection->pool и process->pool соответственно. Четвертый, process->pconf, также ассоциируется с процессом, но отличается от пула процесса, потому что он очищается всякий раз, когда Apache перечитывает свою конфигурацию.
Пул процесса подходит под долгоживущий ресурс, например тот, который инициализируется при запуске сервера, или для ресурса, используемого для обработки составных запросов. Пул запроса подходит для временного ресурса и используется для обработки одного запроса.
Третий основной пул - это пул соединения, который имеет время жизни соединения. Он подходит для временного ресурса, который не может быть связан с запросом: особенно в фильтрах уровня соединения, где структура request_rec (структура запроса) не определена.
В дополнение к этому создается специальный пул для других целей, например, для конфигурирования и ведения логов. Также пулы могут быть созданы модулями для собственного использования.

Использование пулов в Apache: Обработка запроса

Вся обработка запросов имеет форму:
int my_func(request_rec* r) {
/* реализация обработки запроса */
}
Такая форма предоставляет в Ваше распоряжение пул запроса r->pool. Как обсуждалось выше, пул запроса подходит для большинства действий возникающих при обработке. Именно его Вы передаете Apache и APR функциям, которым необходим пул.
Пул процесса доступен через r->server->process->pool для операций, которые нуждаются в выделении долгоживущего ресурса; например, кэшированный ресурс может быть вычислен только один раз и, в последствии, использоваться неоднократно в других запросах. Пул соединения доступен через r->connection->pool.

Использование пулов в Apache: Инициализация и конфигурирование.

Внутренний механизм инициализации Apache является очень сложным. Но чем более связанными становятся модули, тем легче становится инициализация: Вам только остается установить собственные настройки. Apache упрощает это - большинство хуков (hooks) имеют прототипы функций, которые передают пул в качестве первого аргумента.

Конфигурация обработчиков

static const char* my_cfg(cmd_parms* cmd, void* cfg, /*args*/). Использование пула конфигурации cmd->pool дает Вам пул с временем жизни директивы.

Pre- и Post- хуки конфигурации

Эти хуки передают несколько пулов, что не совсем обычно: static int my_pre_config(apr_pool_t* pool, apr_pool_t* plog, apr_pool_t* ptemp). Для большинства задач используется первый пул, но если Ваши функции использует пул для временного ресурса, то используйте ptemp.

Использование пулов в Apache: Другие случаи

Как мы уже обсудили, большинство модулей Apache используют инициализацию и обрабатывают запросы. Но есть два других случая:

Функции соединения

Хуки pre_connection и process_connection уровня соединения передают conn_rec как первый аргумент, по аналогии с функциями запроса. Хук create_connection уровня соединения-инициализации передает пул как первый аргумент: любые модули могут использовать его для установки соединения.

Функции фильтра

Функции фильтра получают ap_filter_t в качестве первого аргумента. Эта структура содержит оба пула: и request_rec, и conn_rec, невзирая на тип фильтра. Фильтрам уровня запроса (AP_FTYPE_RESOURCE или AP_FTYPE_CONTENT) нужно использовать пул запроса. Фильтры уровня соединения получают пустой указатель в f->r и поэтому должны использовать пул соединения.
Автор: Ник Кью (Nick Kew)
Перевод: Сипягин Максим
Оригинал документа (en)

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
Опубликовано в: Разработка модулей Февраль 4, 2006

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

  1. блин как я рад что всеже нашел єто!!! спасиба!!! РЕСПЕКТ!!!
    _____________
    www.dorus.ru

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

  2. низач0т

    Комментарий от Юлия — Октябрь 18, 2008 @ 5:41 pm

  3. +1

    Комментарий от Ираклий — Октябрь 18, 2008 @ 5:41 pm

  4. буду неоригинален:
    +2 =)

    Комментарий от Оскар — Октябрь 18, 2008 @ 5:42 pm

  5. Спасибо. Давно исал.Digitsy.com/russia

    Комментарий от Сергей — Декабрь 10, 2008 @ 2:18 am

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

You must be logged in to post a comment.

Работает на WordPress