vmalloc 实现
来源:互联网 发布:misumi 选型软件 编辑:程序博客网 时间:2024/06/05 21:54
vmalloc 简介
。vmalloc区为非连续内存分区,首地址为 VMALLOC_START,结束地址为VMALLOC_END
。由若干vmalloc子区域组成,每个vmalloc子区域间隔4KB,作为安全隔离区防止非法访问
。用vm_struct表示每个vmalloc子区域,每次调用vmalloc()在内核成功申请一段连续虚拟内存后,都会对应一个vm_struct子区域
。所有的vmalloc子区域组成一个链表,表头指针为 vmlist.
vmalloc 基本流程。size 修正为PAGE_SIZE的整数倍,保证对齐
。在vmalloc内存范围内查找一块合适的虚拟地址子内存空间,存储到vm_struct结构中.
。为申请到的vm_struct 子内存空间分配不连续的物理页框(physical frame)。实现连续的vm_struct子内存空间到非连续的物理页框(physical frame)之间的映射.
vmalloc 区域描述图
vm_struct 结构体(kernel 3.10)
1
2
3
4
5
6
7
8
9
10
struct
vm_struct {
struct
vm_struct *next;
void
*addr;
unsigned
long
size;
unsigned
long
flags;
struct
page **pages;
unsigned
int
nr_pages;
phys_addr_t phys_addr;
const
void
*caller;
};
结构体解析
next
所有的vm_struct子区域组成一个vmlist链表,next指针指向下一个vm_struct 节点地址
addr
vmalloc() 最终在内核空间申请一个vm_struct 内存子区域,addr指向该内存子区域首地址
size
表示该vm_struct子区域的大小
flags
表示该非连续内存区的类型标记
pages
指针数组成员是struct page*类型指针,每个成员都关联一个映射到该虚拟内存区的物理页框
nr_pages
指针数组pages中page结构的总数
phys_addr
通常为0,当使用ioremap()映射一个硬件设备的物理内存时才填充此字段
caller
返回地址
vmalloc() 分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void
*
vmalloc
(unsigned
long
size)
{
return
__vmalloc_node_flags(size, NUMA_NO_NODE,
GFP_KERNEL | __GFP_HIGHMEM);
}
static
inline
void
*__vmalloc_node_flags(unsigned
long
size,
int
node, gfp_t flags)
{
return
__vmalloc_node(size, 1, flags, PAGE_KERNEL,
node, __builtin_return_address(0));
}
static
void
*__vmalloc_node(unsigned
long
size, unsigned
long
align,
gfp_t gfp_mask, pgprot_t prot,
int
node,
const
void
*caller)
{
return
__vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
gfp_mask, prot, node, caller);
}
void
*
__vmalloc_node_range
(unsigned
long
size, unsigned
long
align,
unsigned
long
start, unsigned
long
end, gfp_t gfp_mask,
pgprot_t prot,
int
node,
const
void
*caller)
{
...
}
vmalloc() 函数内部封装成了__vmalloc_node_rang ,相关参数解析如下:
size
需要申请子内存的大小,通过vmalloc()传过来
align
表示将所申请的内存区分为几个部分,1表示size大小的虚拟内存区作为一个整体
start
vmalloc区域范围从 VMALLOC_START 开始
end
vmalloc区域范围到 VMALLOC_END 结束
gfp_mask
页面分配标志,GFP_KERNEL | __GFP_HIGHMEM 表示将从内核高端内存区分配内存空间
prot
描述当前页的保护标志
node
表示在哪个节点上(struct pg_data_t)为这段子内存区分配空间,-1表示在当前节点分配
caller
返回地址
__vmalloc_node_range() 分析
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
void
*__vmalloc_node_range(unsigned
long
size, unsigned
long
align,
unsigned
long
start, unsigned
long
end, gfp_t gfp_mask,
pgprot_t prot,
int
node,
const
void
*caller)
{
struct
vm_struct *area;
void
*addr;
unsigned
long
real_size = size;
size = PAGE_ALIGN(size)
;
if
(!size || (size >> PAGE_SHIFT) > totalram_pages)
goto
fail;
area =
__get_vm_area_node
(size, align, VM_ALLOC | VM_UNLIST,
start, end, node, gfp_mask, caller);
if
(!area)
goto
fail;
addr =
__vmalloc_area_node
(area, gfp_mask, prot, node, caller);
if
(!addr)
return
NULL;
clear_vm_unlist
(
area);
kmemleak_alloc(addr, real_size, 3, gfp_mask);
return
addr;
fail:
warn_alloc_failed(gfp_mask, 0,
"vmalloc: allocation failure: %lu bytes\n"
,
real_size);
return
NULL;
}
__vmalloc_node_range() 函数主要干了以下事情:
LINE 9
修正获取内存的size,PAGE_ALIGN 将size的大小修改成PAGE_SIZE的整数倍,假设要申请1KB的内存区,那么实际上分配的是PAGE_SIZE(4KB)大小 的区域,然后进行size合法性检查,若不合法则返回NULL,申请内存失败
LINE 13
在vmalloc区域VMALLOC_START,VMALLOC_END 范围内申请一块合适大小的子内存区vm_struct,这部分由函数__get_vm_area_node()来实现
LINE 18
为申请到的子内存区vm_struct 分配物理页框(physical frame),将不连续的physical frame分别映射到连续的vm_struct子内存区中,这部分由函数__vmalloc_area_node()来实现
LINE 22
~
LINE 24
新分配的vm_struct区域都有VM_UNLIST标记表明未完全初始化,这里初始化完成后,清除掉此标记,由函数clear_vm_unlist()来实现,表明已经完成VA->PA的映射,kmemleak_alloc()函数是调试用,最后返回addr.
__get_vm_area_node()分析
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
static
struct
vm_struct *__get_vm_area_node(unsigned
long
size,
unsigned
long
align, unsigned
long
flags, unsigned
long
start,
unsigned
long
end,
int
node, gfp_t gfp_mask,
const
void
*caller)
{
struct
vmap_area *va;
struct
vm_struct *area;
BUG_ON(in_interrupt());
if
(flags & VM_IOREMAP) {
int
bit = fls(size);
if
(bit > IOREMAP_MAX_ORDER)
bit = IOREMAP_MAX_ORDER;
else
if
(bit < PAGE_SHIFT)
bit = PAGE_SHIFT;
align = 1ul << bit;
}
size =
PAGE_ALIGN
(size)
;
if
(unlikely(!size))
return
NULL;
area =
kzalloc_node
(
sizeof
(*area), gfp_mask & GFP_RECLAIM_MASK, node);
if
(unlikely(!area))
return
NULL;
size += PAGE_SIZE;
va =
alloc_vmap_area
(size, align, start, end, node, gfp_mask);
if
(IS_ERR(va)) {
kfree(area);
return
NULL;
}
if
(flags & VM_UNLIST)
setup_vmalloc_vm
(area, va, flags, caller);
else
insert_vmalloc_vm(area, va, flags, caller);
return
area;
}
__get_vm_area_node() 函数主要动作有:
LINE 20
修正获取内存的size,修正为PAGE_SIZE的整数倍大小,实现页对齐;
LINE 24
使用kmalloc分配一段连续内存,用于存储vm_struct 结构LINE 28
每个vm_struct子区域之间有4KB大小的安全间隙,所以需要加上PAGE_SIZE的偏移
LINE 30
LINE 37
将查找到的vmap_area 加载到vm_struct子内存中,然后将这个子vm_struct子内存区插入到整个vmlist链表中,由setup_vmalloc_vm()函数实现
alloc_vmap_area()分析
以下为查找va区域的关键比对代码.
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
struct
vmap_area *va;
struct
rb_node *n;
unsigned
long
addr;
int
purged = 0;
struct
vmap_area *first;
...
...
addr = ALIGN(vstart, align);
if
(addr + size < addr)
goto
overflow;
n =
vmap_area_root
.rb_node;
first = NULL;
while
(n) {
struct
vmap_area *tmp;
tmp = rb_entry(n,
struct
vmap_area, rb_node);
if
(tmp->
va_end
>= addr) {
first = tmp;
if
(tmp->
va_start
<= addr)
break
;
n = n->rb_left;
}
else
n = n->rb_right;
}
if
(!first)
goto
found;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct
vmap_area *tmp;
tmp = rb_entry(n,
struct
vmap_area, rb_node);
#define
rb_entry
(ptr, type, member)
container_of
(ptr, type, member)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define
container_of
(ptr, type, member) ({ \
const
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (
char
*)__mptr - offsetof(type,member) );})
LINE 17~
LINE 27上面代码的
tmp = rb_entry(n,
struct
vmap_area, rb_node) 只通过上下文明白大概意思,实现细节没看懂.
----就是树结构,需要复习 data struct 课程.
__vmalloc_area_node()实现
当__get_vm_area_node()创建完新的vm_struct子内存区后,需要通过__vmalloc_area_node()为这个字内存区域分配物理页.
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
static
void
*__vmalloc_area_node(
struct
vm_struct *area, gfp_t gfp_mask,
pgprot_t prot,
int
node,
const
void
*caller)
{
const
int
order = 0;
struct
page **pages;
unsigned
int
nr_pages, array_size, i;
gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
nr_pages
= (area->size - PAGE_SIZE) >> PAGE_SHIFT;
array_size
= (nr_pages *
sizeof
(
struct
page *));
area->nr_pages = nr_pages
;
/* Please note that the recursion is strictly bounded. */
if
(
array_size > PAGE_SIZE
) {
pages = __vmalloc_node
(array_size, 1, nested_gfp|__GFP_HIGHMEM,
PAGE_KERNEL, node, caller);
area->flags |= VM_VPAGES;
}
else
{
pages = kmalloc_node
(array_size, nested_gfp, node);
}
area->pages = pages;
area->caller = caller;
if
(!area->pages) {
remove_vm_area(area->addr);
kfree(area);
return
NULL;
}
for
(i = 0; i < area->nr_pages; i++) {
struct
page *page;
gfp_t tmp_mask = gfp_mask | __GFP_NOWARN;
if
(node < 0)
page = alloc_page
(tmp_mask);
else
page = alloc_pages_node
(node, tmp_mask, order);
if
(unlikely(!page)) {
/* Successfully allocated i pages, free them in __vunmap() */
area->nr_pages = i;
goto
fail;
}
area->pages[i] = page;
}
if
(
map_vm_area
(area, prot, &pages))
goto
fail;
return
area->addr;
fail:
warn_alloc_failed(gfp_mask, order,
"vmalloc: allocation failure, allocated %ld of %ld bytes\n"
,
(area->nr_pages*PAGE_SIZE), area->size);
vfree(area->addr);
return
NULL;
}
LINE 9
~
LINE 10
根据PAGE_SIZE计算所需要的内存映射页框(frame)数,保存在nr_pages中,根据nr_pages计算出pages指针数组的大小array_size,pages指针数组每个元素指向一个用于描述物理页框(frame)的page 结构.LINE 15~
LINE 19如果pages指针数组大小超过一个PAGE_SIZE的大小(4kB),那么将递归调用
__vmalloc_node()为其分配空间,否则通过kmalloc_node()为pages数组分配一段连续内存空间,此段内存空间位于kernel空间的物理内存线性映射区域.LINE 29
~
LINE 43通过一个循环,为pages指针数组中的每个page结构分配物理页框(frame),这里的page结构只是用于藐视物理页框结构,不是代表物理内存,如果node<0,说明未指定物理内存所在节点,使用alloc_page()分配一个页框,否则通过alloc_page_node()在指定的节点上分配物理页框,然后把刚刚分配的page装载到pages[i]中.LINE 46上面完成了分配所需要的物理页框(frame),然后由map_vm_area()完成pages数组中每个页框到连续vmalloc子区的映射关系.
0 0
- vmalloc 实现
- vmalloc 实现
- Linux vmalloc的实现
- vmalloc函数实现细节
- Vmalloc实现原理
- Vmalloc
- vmalloc
- vmalloc
- Linux vmalloc/vfree函数实现解读
- 3.5.7 vmalloc 实现原理------《深入Linux内核架构》笔记
- kmalloc vmalloc
- kmalloc vmalloc
- vmalloc代码
- vmalloc kmem_cache_create
- kmalloc和vmalloc区别
- vmalloc() vs vmap()
- kmalloc vmalloc malloc区别
- vmalloc设计的思考(?)
- poj_1019
- 源码安装zabbix2.4
- 关于c++提取符号“>>”的重载问题
- 创建SSH Key连接github或gitlab
- 操作系统---基础题目汇总十三
- vmalloc 实现
- [javase学习笔记]-4.4 函数的重载
- 文字检测
- c语言总结
- android:gravity 和 android:layout_gravity 区别
- 风雨程序路(二)
- 2015-12-13
- 杭电Red and Black。。。。水题
- C#——类和继承