Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Функции для освобождения фреймов страниц
Существует много функций для освобождения фреймов страниц: два макроса и две функ
Рис. 4.4. Иерархия вызова *free_page*0 Макросы __ f ree_page () и f ree_page () освобождают одну страницу. Они передают 0 в качестве порядка освобождаемой страницы в функцию, выполняющую ос include/linux/gfp.h 94 #define _ free_page(page) _ free_pages((page), 0) 95 tdefine free_page(addr) free_pages((addr), 0) 4.3 Фреймы страниц В конце концов f ree_page () вызывает______ free_pages_bulk(), являющуюся функцией реализации в Linux системы близнецов (buddy system). Мы рассмотрим совместную систему более подробно в следующем подразделе. Система близнецов (buddy system) При выделении и освобождении фреймов страниц, система сталкивается с проблемой фрагментации памяти, называемой внешней фрагментацией (external fragmentation). Это происходит, когда доступные фреймы страниц оказываются разбросаны по памяти таким образом, что невозможно выделить непрерывную последовательность страниц достаточной длины для удовлетворения запроса программы. При этом доступные фреймы страниц перемежаются одной или несколькими занятыми фреймами страниц, которые их разрывают. Уменьшить внешнюю фрагментацию можно несколькими способами. Linux использует реализацию алгоритма менеджера памяти, называемую системой близнецов (buddy system). Система близнецов содержит список доступных блоков памяти. Каждый список указывает на блок памяти разного размера, каждый из которых равен степени двойки. Количество списков зависит от реализации. Фреймы страниц выделяются из списка свободных блоков наименьшего доступного размера. Система придерживает наибольшие доступные блоки для обслуживания больших запросов. Когда возвращаются выделенные блоки, система близнецов выполняет поиск свободных списков доступных блоков памяти, имеющих тот же размер, который имеет и возвращаемый блок. Если любой из доступных блоков прилегает к возвращаемому блоку, они объединяются блок в 2 раза большего размера. Эти блоки (возвращаемый и следующий за ним доступный) называются близнецами, откуда и происходит название «система близнецов». При этом ядро проверяет, чтобы блоки большего размера стали доступными сразу после освобождения фрейма страницы. Теперь посмотрим на функции, реализующие систему близнецов в Linux. Функция mm/page_alloc.с 585 struct page * fastcall 586 __ alloc_pages(unsigned int gfp_mask, unsigned int order, 587 struct zonelist *zonelist) 588 { 589 const int wait = gfp_mask & _ GFP__WAIT; 590 unsigned long min; 591 struct zone **zones; 592 struct page *page; 593 struct reclaim_state reclaim_state; 594 struct task_struct *p = current; 595 int i; Глава 4 • Управление памятью 596 int alloc__type; 597 int do_retry; 598 599 might_sleep_if(wait); 601 zones = zonelist-> zones; 602 if (zones[0] == NULL) /* В списке зон нет зон */ 603 return NULL; 605 alloc_type = zone_idx(zones[0]); 608 for (i = 0; zones [i]! = NULL; i++) { 609 struct zone *z = zones[i]; 611 min = (l«order) + z-> protection[alloc_type]; 617 if (rt_task(p)) 618 min -= z-> pages_low » 1; 620 if (z-> free_pages > = min || 621 (! wait & & z-> free_pages > = z-> pages__high) ) { 622 page = buffered_rmqueue(z, order, gfp_mask); 623 if (page) { 624 zone_statistics(zonelist, z); 625 goto got_pg; 626 } 627 } 628 } 63 0 /* у нас мало памяти, и мы не смогли найти то, что нам нужно */ 631 for (i = 0; zones[i]! = NULL; i++) { 632 wakeup_kswapd(zones [i]); 634 /* Снова проходим по списку зон, с учетом _ GFP_HIGH */ 635 for (i = 0; zones[i]! = NULL; i++) { 636 struct zone *z = zones[i]; 638 min = (l«order) + z-> protection[alloc_type]; 640 if (gfp_mask & _ GFP_HIGH) 641 min -= z-> pages_low » 2; 642 if (rt_task(p)) 643 min -= z-> pages_low » 1; 645 if (z-> free_pages > = min || 646 (! wait & & z-> free_pages > = z-> pages_high)) { Фреймы страниц 647 page = buffered_rmqueue(z, order, gfp_mask); 648 if (page) {
649 zone_statistics(zonelist, z); 650 goto got_pg; 651 } 652 } 653 } 72 0 nopage: 721 if (! (gfp_mask & _ GFP_NOWARN) & & printk_ratelimit () ) { 722 printk(KEKN_WAKNING " %s: page allocation failure." 723 * ordered, mode: 0x%x\n" , 724 p-> comm, order, gfp_mask); 725 dump_s tack(); 726 } 727 return NULL; 728 got_pg: 729 kernel_map_pages(page, 1 « order, 1); 73 0 return page; Система близнецов в Linux разделена на зоны, что означает, что список поддерживаемых доступных фреймов разделен на зоны. При этом каждый поиск свободного фрейма страниц может выполняться в одной из трех возможных зон, из которых можно получить фреймы страниц. Строка 586 Целочисленное значение gf p_mask позволяет вызывающему__ alloc_pages () коду определять способ поиска фреймов страниц (модификаторов действия). Возможные значения определены в include/linux/gfp.h и перечислены в табл. 4.2. Таблица 4.2. Модификаторы действия для gfpjnask при выделении страниц памяти Флаг Описание __ GFP_WAIT Позволяет ядру блокировать процесс, ожидающий фрейм страницы. Пример его использования можно увидеть в строке 537 page_alloc. с __ GFP_COLD Требуется кеширование холодных страниц __ GFP_HIGH Фрейм страницы можно найти в экстренном пуле памяти __ GFP_IO Возможно выполнение передачи ввода-вывода __ GFP_FS Позволяет вызвать низкоуровневые операции файловой системы Глава 4 • Управление памятью Таблица 4.2. Модификаторы действия для gfpjnask при выделении страниц памяти (Окончание) GFP_NOWARN _GFP_REPEAT _GFP_NORETRY _GFP_DMA _GFP_HIGHMEM При ошибке выделения фрейма страницы функция выделения посылает предупреждение об ошибке. Если выбран этот модификатор, сообщение не выводится. Пример применения этого флага можно увидеть в строках 665-666 page_alloc. с Повторная попытка выделения Запрос не требуется повторять из-за возможности ошибки Фрейм страницы находится в ZONE_DMA Фрейм страницы находится в ZONE_HIGHMEM В табл. 4.3 представлены указатели на список зон, соответствующих модификаторам из gf p_mask. Таблица 4.3. Список зон Флаг Описание GFP__USER Означает, что память выделяется не в пространстве ядра GFP_KERNEL Означает, что память выделяется в пространстве ядра GFP_ATOMIC Используется в обработчиках прерываний с помощью вызова kmalloc, так как предполагается, что выделитель памяти не засыпает GFP_DMA Означает, что память выделяется из ZONE_DMA Строка 599 Функция might_sleep_if () получает значение переменной wait, хранящей ло wait равно 0, если_______ GFP_WAIT не установлено, и 1, если установлено. Если при конфигурации ядра включена проверка сна при циклической блокировке (в меню Kernel Hacking), эта функция позволяет ядру блокировать текущий процесс на значение времени задержки. Строки 608-628 В этом блоке мы проходим список описателей зон и ищем зону с достаточным для удовлетворения запроса количеством свободных страниц. Если количество свободных страниц удовлетворяет требуемому или если процессу разрешены ожидания, а количество свободных страниц больше либо равно верхнему пороговому значению зоны, вызывается функция buf f ered_rmqueue (). Фреймы страниц Функция buf f ered__rmqueue () получает три аргумента: описатель зоны с доступными фреймами страниц, порядок количества требуемых фреймов страниц и температуру требуемых страниц. Строки 631-632 Если мы попали в этот блок, мы не можем выделить страницу, потому что у нас слишком мало доступных фреймов страниц. Здесь предпринимается попытка вернуть фреймы страниц для удовлетворения запроса. Функция wakeup_ kswapd () выполняет эти действия и корректирует зоны с соответствующими фреймами страниц. При этом обновляются описатели зон. Строки 635-653 После попытки возвращения фреймов страниц в предыдущем блоке кода мы снова проходим по зонам и ищем свободные фреймы страниц. Строки 720-727 В этот блок кода мы попадаем, когда понимаем, что доступных фреймов страниц нет. Если выбран модификатор GFP_NOWARN, функция выводит сообщение об ошибке выделения страницы, включающее имя команды, вызванной для текущего процесса, порядок требуемых фреймов страниц и применяемую к запросу gf p_mask. Эта функция возвращает NULL. Строки 728-730 Переход в этот блок кода выполняется после того, как требуемые страницы обнаружены. Функция возвращает адрес описателя страницы. Если требуется более одного фрейма страницы, она возвращает адрес описателя страницы для первого выделенного фрейма страницы. При возвращении блока памяти система близнецов старается объединить их в блок mm/page_alloc.с 178 static inline void _ free_pages_bulk (struct page *page, struct page *base, 179 struct zone *zone, struct free_area *area, unsigned long mask, 180 unsigned int order) 181 { 182 unsigned long page_idx, index; 184 if (order) 185 destroy_compound_page(page/ order); 186 page_idx = page - base; Глава 4 • Управление памятью 187 if (page_idx & -mask) 188 BUG (); 189 index = page_idx » (1 + order); 191 zone-> free_j? ages -= mask; 192 while (mask + (1 « (MAX_ORDER-l))) { 193 struct page *buddyl/ *buddy2; 195 BUG_ON(area > = zone-> free_area + MAX_ORDER); 196 if (! _ test_and_change_bit(index, area-> map)) 206 buddyl = base + (page_idx Л -mask); 2 07 buddy2 = base + page_idx; 208 BUG__ON(bad_range(z one, buddyl)); 209 BUG_ON(bad_range(z one, buddy2)); 210 list_del(& buddyl-> lru); 211 mask «= 1; 212 area++; 213 index »= 1; 214 page_idx & = mask; 215 } 216 list_add(& (base + page_idx)-> lru, & area-> free_list); 217 } Строки 184-215 Функция___ free_pages_bulk() перебирает размеры блоков, соответствующих каждому из списков свободных блоков. (MAX_ORDER - это порядок блока наибольшего размера.) Для каждого порядка, и пока не будет достигнут максимальный порядок или найден наименьший близнец, она вызывает ___ test_and_ change_bit (). Эта функция проверяет, выделена ли страница близнеца для возвращаемого блока. Если это так, мы прерываем цикл. Если нет, она смотрит, можно ли найти близнеца с большим порядком, с которым можно объединить наш освобождаемый блок фреймов страниц. Строка 216 Свободный блок вставляется в соответствующий список свободных фреймов страниц. Выделение секций Мы говорили, что страницы являются для менеджера памяти базовой единицей памяти. Тем не менее обычно процесс требует память указанного порядка байтов, а не порядка страниц. Для удовлетворения запросов по выделению меньших объемов памяти исполь- Выделение секций зуется вызов функции kmalloc (), ядро реализует выделитель секций (slab allocator), являющийся слоем менеджера памяти, работающего с затребованными страницами. Выделитель секций пытается уменьшить затраты на выделение, инициализацию, уничтожение и освобождение областей памяти, поддерживая кеш недавно использованных областей памяти. Этот кеш хранит выделенные и инициализированные области памяти, готовые к размещению. Когда пославший требование процесс больше не нуждается в области памяти, эта область возвращается в кеш. На практике выделитель секций содержит несколько кешей, каждый из которых хранит области памяти разного размера. Кеши могут быть специализированными (specialized) или общего назначения (general purpose). Например, описатель процесса task_struct хранится в кеше, поддерживаемом выделителем секций. Область памяти, занимаемая этим кешем равна sizeof (task_struct). Аналогично хранятся в кеше структуры данных inode и dentry. Кеши общего назначения создаются из областей памяти предопределенного размера. Области памяти могут иметь размер 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65535 и 131072 байта1. Если мы запустим команду cat /proc/stabinf о, будет выведен список существующих кешей выделителя. В первой колонке вывода мы можем увидеть имена структур данных и группу элементов в формате size-*. Первый набор соответствует специализированным объектам кеша; набор букв соответствует кешам, хранящим объекты общего назначения указанного размера. Кроме этого, вы можете заметить, что кеши общего назначения имеют два вхождения одинакового размера, у одного из которых в конце стоит DMA. Это происходит потому, что области памяти могут быть затребованы как из обычной зоны, так и из зоны DMA. Выделитель секций поддерживает кеши обеих типов памяти для удовлетворения всех запросов. Рис. 4.5 демонстрирует вывод /proc/slabinf о, где видны кеши обеих типов памяти. В свою очередь, кеш делится на контейнеры, называемые секциями (slabs). Каждая секция составляется из одного или более прилегающих фреймов страниц, из которых выделяются области памяти меньшего размера. Поэтому мы будем говорить, что секции содержат объекты. Сами объекты представляют собой интервалы адресов предопределенного размера внутри фрейма страницы, который принадлежит к данной секции. Рис. 4.6 демонстрирует анатомию выделителя секций. Выделитель секций использует три главные структуры для поддержания информации об объекте: описатель кеша, называемый kmem_cache; общий описатель кеша, называемый cache_size, и выделитель секции, называемый slab. Рис. 4.7 показывает общую картину связей между всеми описателями. 1 Все кеши общего назначения для повышения производительности выравниваются по L1. Глава 4 • Управление памятью
Рис. 4.5. cat /proc/slabinfo Секция Объекты Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 913; Нарушение авторского права страницы