对于x86体系[luther.gliethttp] 高端物理地址的分配
采用
vmalloc/vfree
这组函数进行,什么是高端物理内存呢?我们知道
Linux
给内核预留了一部分虚拟地址空间,这部分虚拟地址如果能全部直接映射到物理地址空间就不存在高端内
存。如果这部分内存有一部分不能直接映射到地址空间,那么这部分虚拟地址空间称为高端内存。因此,高端内存是虚拟地址空间中的概念。举个例子:如果你的物
理内存为
512M
,
那么就不存在高端内存的分配,如果你的物理地址为
2G
,那么有
1G+128M
(预留给
VMALLOC
区)是属于高端内存的。高端内存的分配即便是逻辑上连续,也不要求物理上是连续的。
下面我们介绍一下高端内存的分配。分配高端内存是通过调用
vmalloc
函数。我们来解读一下该函数的
分配过程。
首先介绍一下这部分内存管理所需要
的数据结构:
struct vm_struct {
void
*addr; //
虚拟地址的开始
unsigned
long
size; //
分配大小
unsigned
long
flags; //
标志位
struct
page
**pages; //
对应的页面
unsigned
int
nr_pages; //
页面数量
unsigned
long
phys_addr; //
物理地址
struct
vm_struct
*next; //
单链表,指向下一个
vm
节点
};
1.
首先检查请求分配的内存大小有没有超过最大的物理页面数。如果超过返回
0
,表示分配失败。
size =
PAGE_ALIGN(size);
if (!size
|| (size >> PAGE_SHIFT) > num_physpages)
return
NULL;
2.
使用
kmalloc
在
slab
中,分配
vm_struct
数据结构。
area =
kmalloc_node(sizeof(*area), GFP_KERNEL, node);
3.
在单链表
vmlist
中查找适合的位置,并将新的
vm
节点插入到单链表中。
for (p = &vmlist; (tmp = *p) !=
NULL ;p = &tmp->next) {
if
((unsigned long)tmp->addr < addr) {
if((unsigned
long)tmp->addr + tmp->size >= addr)
addr = ALIGN(tmp->size +
(unsigned long)tmp->addr, align);
continue;
}
if
((size + addr) < addr)
goto
out; //
地址越界
if
(size + addr <= (unsigned long)tmp->addr)
goto
found; //
查找成功。进行插入操作
addr
= ALIGN(tmp->size + (unsigned long)tmp->addr, align);
if
(addr > end - size)
goto
out; //
地址越界
}
//
插入新的
vm
节点到
vmlist
中去。
area->next
= *p;
*p = area;
//
初始化新结点
area->flags
= flags;
area->addr
= (void *)addr;
area->size
= size;
//
物理内存还未分配。
area->pages
= NULL;
area->nr_pages
= 0;
area->phys_addr
= 0;
4.
接下来初始化
vm_struct
结构中的
pages
和
nr_pages
字段。
a)
初始化
nr_pages
字段
nr_pages = (area->size - PAGE_SIZE) >>
PAGE_SHIFT;
b)
初始化
pages
数组
计算数组大小
array_size
= (nr_pages * sizeof(struct page *));
如果数组大小大于
1
个页面,在非连续区进行分配,否则在连续区
进行分配
if
(array_size > PAGE_SIZE)
pages = __vmalloc_node(array_size, gfp_mask, PAGE_KERNEL, node);
else
pages = kmalloc_node(array_size, (gfp_mask &
~__GFP_HIGHMEM), node);
area->pages = pages;
5.
从伙伴系统中进行物理内存页面的分配
for (i = 0; i < area->nr_pages; i++) {
if (node < 0)
area->pages[i]
= alloc_page(gfp_mask); //
针对
UMA
else
area->pages[i]
= alloc_pages_node(node, gfp_mask, 0); //
针对
NUMA
if
(unlikely(!area->pages[i])) {
/*
Successfully allocated i pages, free them in __vunmap() */
area->nr_pages
= i;
goto
fail;
}
}
6. 将刚申请的页面映射到页表中。
if (map_vm_area(area, prot,
&pages))
释放内存部分很简单,就是从
vmlist
当中删除掉对应的节点。然后将
内存归还给伙伴系统
#define high_memory (-128UL << 20)等于0xf8000000
#define VMALLOC_OFFSET (8 * 1024 * 1024)
#define VMALLOC_START ((unsigned long)high_memory + VMALLOC_OFFSET)
#define VMALLOC_END (PKMAP_BASE - 2 * PAGE_SIZE)
#define PKMAP_BASE ((FIXADDR_BOOT_START - PAGE_SIZE * (LAST_PKMAP + 1)) /
& PMD_MASK)
即vmalloc的起始地址为0xf8800000
---------------------------------------------------------------
以下转自:http://www.360doc.com/content/09/0525/21/36491_3650302.shtml
vmalloc函数实现细节
最新推荐文章于 2023-05-25 14:08:36 发布