Дескрипторы несмежных областей памяти
Каждая несмежная область памяти ассоциирована с дескриптором типа vm struct
Эти дескрипторы организованы в простой список при помощи ПОЛЯ next. Адрес первого элемента списка хранится в переменной vmiist. Обращения к этому списку регулируются спин-бллокировкой чтения/записи vmlist_lock. Поле flags идентифицирует тип памяти, отображаемый областью: VM ALLOC ДЛЯ страниц, получаемых С ПОМОЩЬЮ функции vmalloc , vm map для уже выделенных страниц, отображенных функцией vmapo, и vm ioremap для встроенной памяти аппаратных устройств, отображенных при помощи функции ioremapO.
Функция get vm area ищет свободный интервал линейных адресов между vmalloc_start и vmMjLOC_end. Эта функция принимает два параметра:
- size — размер создаваемой области в байтах;
- flag — флаг, задающий тип памяти.
Функция выполняет следующие действия:
1. Вызывает функцию kmalloc , чтобы получить область памяти для нового дескриптора типа vm struct.
2. Получает блокировку vmlist lock и просматривает список дескрипторов типа vm_struct в поисках свободного интервала линейных адресов, включающего, как минимум, size + 4096 адресов (4096 — это размер интервала безопасности между областями памяти).
3. Если такой интервал существует, функция инициализирует поля дескриптора, освобождает блокировку vmlist lock и завершает работу, возвращая начальный адрес несмежной области памяти.
4. В противном случае функция get vm area освобождает ранее полученный дескриптор, освобождает блокировку vmlist lock И ВОЗВращает NULL.
Выделение несмежной области памяти
Функция vmaiioc выделяет ядру несмежную область памяти. Параметр size задает размер запрошенной области. Если функция в состоянии удовлетворить запрос, она возвращает начальный линейный адрес новой области. В противном случае функция возвращает null:
void vmalloc(unsigned long size)
{struct vm_struct area;struct page pages;
unsigned int array_size, i;size = (size + PAGE_SIZE - 1) & PAGE_MASK;area = get_vm_area(size, VM_ALLOC);
if (!area)return NULL; area->nr_pages = size » PAGE_SHIFT;
array_size = (area->nr_pages sizeof(struct page )); area->pages = pages = kmalloc(array_size, GFP_KERNEL); if (!area_pages) {remove_vm_area(area->addr); kfree(area); return NULL;}
memset(area->pages, 0, array_size); for (i=0; inr_pages; i++) {
area->pages[i] = alloc_page(GFP_KERNEL| GFP_HIGHMEM);
if (!area->pages[i]) { area->nr_pages = i; fail: vfree(area->addr); return NULL;}}
if (map_vm_area(area, pgprot(0x63), &pages))
goto fail; return area->addr;}
Функция начинает с округления значения параметра size до числа, кратного 4096 (размер страничного кадра). Затем функция vmaiioco вызывает функцию get vm area , которая создает новый дескриптор и возвращает линейный адрес, присвоенный области памяти. Поле flags дескриптора инициализируется флагом vm alloc, и это означает, что несмежные области памяти будут отображены в интервал линейных адресов с помощью функции vmalloc. Затем функция vmalloc вызывает функцию kmaiioc , чтобы сделать запрос на группу смежных страничных кадров, достаточно большую для хранения массива указателей на дескрипторы страниц. Функция memset вызывается для сброса всех этих указателей в значение null. После этого многократно вызывается фуНКЦИЯ alloc_page, ПО разу на каждую ИЗ nrjpages страниц области, для выделения страничного кадра и сохранения адреса соответствующего дескриптора страницы в массиве area->pages. Обратите внимание, что применение массива area->pages необходимо, потому что страничные кадры могут принадлежать зоне памяти zone highmem, т. е. в данный момент они необязательно отображены в линейный адрес.
А теперь — самое интересное. К этому моменту получен "свежий" интервал смежных линейных адресов, и выделена группа несмежных страничных кадров для отображения этих адресов. Последний, и самый важный, шаг включает в себя манипуляции с записями таблиц страниц, используемых ядром, для обозначения того факта, что каждый страничный кадр, выделенный несмежной области памяти, теперь ассоциирован с линейным адресом, входящим в интервал смежных линейных адресов, который был получен от функции vmalloc . Именно ЭТИМ И занимается функция map_vm_area .
Функция map vm area принимает три параметра:
- area — указатель на дескриптор vm struct данной области;
- prot— биты защиты выделенных страничных кадров. Параметр всегда равен ОхбЗ, ЧТО соответствует битам Present, Accessed, Read/Write И Dirty;
- pages — адрес переменной, указывающей на массив указателей на дескрипторы страниц (то есть как тип данных используется struct page ).
Функция начинает с того, что присваивает линейные адреса начала и конца области памяти переменным address и end соответственно:
address = area->addr;
end = address + (area->size — PAGE_SIZE);
Вспомним, что поле area->size хранит фактический размер области плюс 4 Кбайт (размер интервала безопасности между областями). Функция применяет макрос pgd offset k, чтобы извлечь запись из главного глобального каталога страниц ядра, относящуюся к начальному линейному адресу области, а затем получает спин-блокировку Таблицы Страниц:
pgd = pgd_offset_k(address); spin_lock(&init_mm.page_table_lock);
Затем функция входит в следующий цикл:
int ret = 0;
for (i = pgd_index(address); i addr, end); return ret;
На каждом шаге цикла функция вначале вызывает функцию pud aiioc , чтобы создать верхний каталог страниц для новой области, и заносит его физический адрес в соответствующую запись глобального каталога страниц ядра. Затем она вызывает функцию map area pud , чтобы выделить все таблицы страниц, ассоциированные с новым верхним каталогом страниц. Она складывает размер диапазона линейных адресов, охватываемого одним верхним каталогом страниц (это константа 230, если механизм РАЕ включен, и 222 в противном случае), с текущим значением переменной address и продвигает указатель pgd на глобальный каталог страниц.
Цикл повторяется, пока не будут заполнены все записи Таблицы Страниц, ссылающиеся на несмежную область памяти.
Функция map area pud выполняет аналогичный цикл для всех таблиц страниц, на которые указывает верхний каталог страниц:
do {pmd_t pmd = pmd_alloc(&init_mm, pud, address); if (!pmd)
return -ENOMEM; if (map_area_pmd(pmd, address, end-address, prot, pages)) return -ENOMEM; address = (address + PUD_SIZE) & PUD_MASK; pud++;} while (address next) { if (tmp->addr == addr) { unmap_vm_area (tmp) /p = tmp->next; break;}}
write_unlock(&vmlist_lock) ; return tmp;
Сама область освобождается при помощи функции unmap vm area . Эта функция принимает единственный параметр, указатель area на дескриптор области vmstruct. Она выполняет следующий цикл, чтобы совершить действия, противоположные действиям функции map_vm_area :
address = area->addr;
end = address + area->size;
pgd = pgd_offset_k(address);
for (i = pgd_index(address); i vm_start mm_rb.rb_node; vma = NULL; while (rb_node) {
vma_tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb); if (vma_tmp->vm_end > addr) { vma = vma_tmp ;
if (vma_tmp->vm_start rb_left;
} elserb_node = rb_node->rb_right;}if (vma)
mm->mmap_cache = vma; return vma;
Функция вызывает макрос rb entry, который по указателю на узел красночерного дерева вычисляет адрес соответствующего дескриптора области памяти.
Функция f ind vma prev аналогична функции f ind vma с той разницей, что она записывает в дополнительный параметр pprev указатель на дескриптор области памяти, предшествующей той, которую нашла функция.
Наконец, функция f ind vma prepare находит позицию нового листа в крас- но-черном дереве, который соответствует данному линейному адресу, и возвращает адрес предшествующей области памяти и адрес родителя вставляемого узла.
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница