内核空间非连续内存区的分配之vmalloc

我们知道CPU所访问的都是虚拟内存地址。那么平时我们自己在编写的内核模块时,linux到底分配的是什么样的

内存空间呢?要解答这个问题,首先就要看看内核非连续内存。

在linux的内存管理中,用户使用0~3GB的地址空间,而内核只是用了3GB~4GB区间的地址空间,共1GB;非连

续空间的物理映射就位于3GB~4GB之间,如下图示

0GB                                                          3GB                   4GB

而关于内核空间中这1GB是如何分配的呢,具体请看下图:

通常会把内核空间中大于896M的空间称作内核空间中的高端内存。内核可以用三种不同的机制将页框映射到高端

内存:永久内核映射、临时内核映射和非连续内存分配。本文中将要谈论的是非连续内存分配。

从上图可以知道,在物理内存的末尾和非连续内存区之间插入了一个大小为8MB的区间,这是一个安全区,

目的是“捕获”对非连续区的非法访问。出于同样的理由,在其它非连续区间也插入了大小为4KB的安全区。而每个

非连续区的大小都是4KB的倍数。如下图:

非连续内存的线性地址空间是从VMALLOC_START~VMALLOC_END,共128MB大小。当内核需要用vmalloc类的函数

进行非连续内存分配时,就会申请一个vm_struct结构来描述对应的vmalloc区,若分配多个vmalloc的内存区,那

么相邻两个vmalloc区之间的间隔大小至少为4KB,即至少是一个页框大小PAGE——SIZE。如上图。

内核中描述非连续区的数据结构是struct vm_struct:

在3.7的内核版本中(include/linux/vmalloc.h)是这样描述该结构的:

struct vm_struct {

struct vm_struct * next ;        //指向下一个vm_struct区,所有非连续区组成一个单链表

void * addr ;               //代表每个内存区的起始地址,即指向申请的内存区的第一个内存单元(线性地址)unsigned long size ;            //当前所申请的内存区大小加4KB(安全区)

unsigned long flags ;           //标识内存区类型

struct page ** pages ;       //指向nr_pages页描述符指针数组的指针

unsigned int nr_pages ;     //所申请的内存区大小对应的页框数

phys_addr_t phys_addr ;   //该字段一般为0,除非内存已经被申请用作映射一个硬件设备的I/O共享内存

const void * caller ;         //当前调用vmalloc类的函数的返回地址

};

内核中用

我们知道CPU所访问的都是虚拟内存地址。那么平时我们自己在编写的内核模块时,linux到底分配的是什么样的

内存空间呢?要解答这个问题,首先就要看看内核非连续内存。

在linux的内存管理中,用户使用0~3GB的地址空间,而内核只是用了3GB~4GB区间的地址空间,共1GB;非连

续空间的物理映射就位于3GB~4GB之间,如下图示

0GB                                                          3GB                   4GB

而关于内核空间中这1GB是如何分配的呢,具体请看下图:

通常会把内核空间中大于896M的空间称作内核空间中的高端内存。内核可以用三种不同的机制将页框映射到高端

内存:永久内核映射、临时内核映射和非连续内存分配。本文中将要谈论的是非连续内存分配。

从上图可以知道,在物理内存的末尾和非连续内存区之间插入了一个大小为8MB的区间,这是一个安全区,

目的是“捕获”对非连续区的非法访问。出于同样的理由,在其它非连续区间也插入了大小为4KB的安全区。而每个

非连续区的大小都是4KB的倍数。如下图:

非连续内存的线性地址空间是从VMALLOC_START~VMALLOC_END,共128MB大小。当内核需要用vmalloc类的函数

进行非连续内存分配时,就会申请一个vm_struct结构来描述对应的vmalloc区,若分配多个vmalloc的内存区,那

么相邻两个vmalloc区之间的间隔大小至少为4KB,即至少是一个页框大小PAGE——SIZE。如上图。

内核中描述非连续区的数据结构是struct vm_struct:

在3.7的内核版本中(include/linux/vmalloc.h)是这样描述该结构的:

struct vm_struct {

struct vm_struct * next ;        //指向下一个vm_struct区,所有非连续区组成一个单链表

void * addr ;               //代表每个内存区的起始地址,即指向申请的内存区的第一个内存单元(线性地址)unsigned long size ;            //当前所申请的内存区大小加4KB(安全区)

unsigned long flags ;           //标识内存区类型

struct page ** pages ;       //指向nr_pages页描述符指针数组的指针

unsigned int nr_pages ;     //所申请的内存区大小对应的页框数

phys_addr_t phys_addr ;   //该字段一般为0,除非内存已经被申请用作映射一个硬件设备的I/O共享内存

const void * caller ;         //当前调用vmalloc类的函数的返回地址

};

内核中用get_vm_area函数来创建一个新的非连续区结构,在该函数的实现中又会调用kmallloc函数和kfree函数

分别为vm_struct结构分配和释放所需的内存。

vmalloc给内核分配一个非连续的内存区,其原型为:

void * vmalloc (unsigned long size )

函数首先把size参数取为页面的大小(即4KB)的倍数,然后进行有效性检查,若有大小适合的可用内存,就调用

get_vm_area()获得一个内存区的结构,最后会调用vmalloc_area_pages()进行真正的的非连续内存的分配,该函数

实际上建立了非连续内存到物理页面的映射

函数来创建一个新的非连续区结构,在该函数的实现中又会调用kmallloc函数和kfree函数

分别为vm_struct结构分配和释放所需的内存。

vmalloc给内核分配一个非连续的内存区,其原型为:

void * vmalloc (unsigned long size )

函数首先把size参数取为页面的大小(即4KB)的倍数,然后进行有效性检查,若有大小适合的可用内存,就调用

get_vm_area()获得一个内存区的结构,最后会调用vmalloc_area_pages()进行真正的的非连续内存的分配,该函数

实际上建立了非连续内存到物理页面的映射

https://www.tuicool.com/articles/emMVJnV

猜你喜欢

转载自blog.csdn.net/woainilixuhao/article/details/81283552