воскресенье, 10 апреля 2011 г.

8.3. get_free_page и друзья

get_free_page и друзья
Если модулю необходимо выделять большие блоки памяти, как правило, лучше использовать странично-ориентированные техники. Запрос целых страниц также имеет и другие преимущества, которые представлены в Главе 15.
Чтобы выделить страницы, доступны следующие функции:
get_zeroed_page(unsigned int flags);
Возвращает указатель на новую страницу и заполняет страницу нулями.

__get_free_page(unsigned int flags);
Подобно get_zeroed_page, но страницу не очищает.

__get_free_pages(unsigned int flags, unsigned int order);
Выделяет память и возвращает указатель на первый байт области памяти, которая потенциально размером с несколько (физически непрерывных) страниц, но не обнуляет область.

Аргумент flags работает так же, как с kmalloc; как правило, используется GFP_KERNEL или GFP_ATOMIC, возможно, с добавлением флага __GFP_DMA (для памяти, которая может быть использована для ISA-операций прямого доступа к памяти) или __GFP_HIGHMEM, когда может быть использована верхняя область памяти. (* Хотя на самом деле для выделения страниц верхней области памяти в действительности должна быть использована alloc_pages (описываемая в ближайшее время) по причинам, которые мы не можем объяснить до Главы 15.) order (порядок) является результатом логарифма с основанием двойки числа запрошенных или освобождаемых страниц (то есть, log2N). Например, order равен 0, если вы хотите одну страницу и 3, если вы запрашиваете восемь страниц. Если order является слишком большим (нет такой непрерывной области такого размера), выделение страниц не удаётся. Функция get_order, которая принимает целочисленный аргумент, может быть использована для получения order из размера (который должен быть степенью двойки) на данной платформе. Максимально допустимое значение для order составляет 10 или 11 (соответствующее 1024 или 2048 страниц) в зависимости от архитектуры. Как бы то ни было, шансы успешного выделения при порядке 10 иначе, чем на только что загруженной системе с большим количеством памяти, малы.

Если вам интересно, /proc/buddyinfo расскажет вам, как много блоков каждого порядка доступно для каждой зоны памяти в системе.

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

void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);

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

Стоит подчеркнуть, что __get_free_pages и другие функции могут быть вызваны в любой время и подчиняются тем же правилам, которые мы видели для kmalloc. В определённых обстоятельствах функции могут не выделить память, особенно когда используется GFP_ATOMIC. Поэтому программа, вызывающая эти функции выделения, должна быть готова обработать ошибку выделения.

Хотя kmalloc(GFP_KERNEL) иногда заканчивается неудачей, когда нет доступной памяти, ядро делает всё возможное, чтобы выполнить запросы на выделение памяти. Таким образом, можно легко затруднить реагирование системы, запрашивая слишком много памяти. Например, вы можете свалить компьютер, поместив слишком много данных в устройство scull; система начинает сканирование в попытке выгрузить как можно больше для того, чтобы выполнить запрос kmalloc. Так как каждый ресурс поглощается растущим устройством, компьютер скоро оказывается непригодным для использования; в этот момент вы больше не можете даже запустить новый процесс, чтобы попытаться решить эту проблему. Мы не будем решать этот вопрос в scull, так как это просто пример модуля, а не реальный инструмент для размещения в многопользовательской системе. Как программист, вы должны быть, тем не менее, осторожными, поскольку модуль является привилегированным кодом и может открыть новые дыры в безопасности системы (наиболее вероятной, как только что говорилось, является дыра отказа службы).
scull, использующий целые страницы: scullp
В целях проверки выделения страниц по-настоящему, вместе с другим примером кода мы выпустили модуль scullp. Это сокращённый scull, как и scullc, с которым мы познакомились ранее. Память кванта, выделяемая scullp, является целыми страницами или наборами страниц: значение для scullp_order по умолчанию 0, но может быть изменено во время компиляции или загрузки. Следующие строки показывают, как он выделяет память:

/* Здесь выделяется память для одного кванта */
if (!dptr->data[s_pos]) {
    dptr->data[s_pos] = (void *)__get_free_pages(GFP_KERNEL, dptr->order);
    if (!dptr->data[s_pos])
        goto nomem;
    memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order);
}

Код для освобождения памяти в scullp выглядит следующим образом:

/* This code frees a whole quantum-set */
for (i = 0; i < qset; i++)
    if (dptr->data[i])
        free_pages((unsigned long)(dptr->data[i]), dptr->order);

На уровне пользователя, разница воспринимается в первую очередь как улучшение скорости и лучшее использование памяти, потому что нет внутренней фрагментации памяти. Мы запускали тесты, копируя 4 Мб из scull0 к scull1, а затем из scullp0 к scullp1; результаты показали некоторое улучшение в использовании пространства ядра процессора.

Увеличение производительности не слишком большое, потому что kmalloc создана быть быстрой. Основным преимуществом выделения страниц на самом деле является не скорость, а более эффективное использование памяти. Выделение по страницам не создаёт отходов памяти, в то время как при использовании kmalloc теряется непредсказуемый объём памяти, из-за зернистости распределения. Но самое большое преимущество функции __get_free_page то, что полученная страницы полностью ваша, и теоретически можно собрать страницы в линейной области соответствующими настройками таблиц страниц. Например, можно разрешить пользовательскому процессу сделать mmap области памяти, полученной как целые несвязанные страницы. Мы обсуждаем такую операцию в Главе 15, где мы покажем, как scullp предлагает связывать память, то, что scull предложить не может.
Интерфейс alloc_pages
Для полноты картины мы вводим ещё один интерфейс для выделения памяти, хотя мы ещё не будем готовы его использовать, пока не закончим Главу 15. В настоящее время достаточно сказать, что структура page является внутренней структурой ядра, которая описывает страницу памяти. Как мы увидим, есть много мест в ядре, где необходимо работать со структурами страниц; они особенно полезны в любой ситуации, где вы могли бы иметь дело с верхней областью памяти, которая не имеет постоянного адреса в пространстве ядра.

Настоящим ядром распределителя страниц Linux является функция, названная alloc_pages_node:

struct page *alloc_pages_node(int nid, unsigned int flags, unsigned int order);

Эта функция также имеет два варианта (которые просто макросы); эти версии то, что вы, скорее всего, будете использовать:

struct page *alloc_pages(unsigned int flags, unsigned int order);
struct page *alloc_page(unsigned int flags);

Основная функция, alloc_pages_node, принимает три аргумента. nid является ID узла NUMA (* Компьютеры и многопроцессорные системы с NUMA (неоднородным доступом к памяти), где память является "локальной" для определённых групп процессоров ("узлов"). Доступ к локальной памяти происходит быстрее, чем доступ к не локальной памяти. В таких системах выделение памяти на правильном узле является важным. Однако, авторы драйверов обычно не должны беспокоиться о проблемах NUMA.), чья память должна быть выделена, flags является обычными GFP_ флагами выделения и order определяет размер выделяемой памяти. Возвращаемое значение является указателем на первую из (возможно, нескольких) структур страниц, описывающих выделенную память, или, как обычно, NULL в случае неудачи. alloc_pages упрощает ситуацию путём выделения памяти на текущем узле NUMA (она вызывает alloc_pages_node с возвращением значения из numa_node_id как параметр nid). И, конечно, alloc_page опускает параметр order и выделяет одну страницу.

Чтобы освободить страницы, выделенные таким образом, вы должны использовать одно из следующих действий:

void __free_page(struct page *page);
void __free_pages(struct page *page, unsigned int order);
void free_hot_page(struct page *page);
void free_cold_page(struct page *page);

Если у вас есть особые знания о том, вероятно ли, что содержимое одной страницы постоянно находится в кэше процессора или нет, вы должны проинформировать ядро с помощью free_hot_page (для страниц, постоянно находящихся в кэше) или free_cold_page. Эта информация помогает распределителю памяти оптимизировать использование памяти в системе.

Комментариев нет:

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

Примечание. Отправлять комментарии могут только участники этого блога.