设为首页 加入收藏

TOP

kmalloc分配物理内存与高端内存映射--Linux内存管理(十八)(一)
2019-09-01 23:08:54 】 浏览:118
Tags:kmalloc 分配 物理 内存 高端 映射 --Linux 管理 十八

1 前景回顾

1.1 内核映射区

尽管vmalloc函数族可用于从高端内存域向内核映射页帧(这些在内核空间中通常是无法直接看到的), 但这并不是这些函数的实际用途.

重要的是强调以下事实 : 内核提供了其他函数用于将ZONE_HIGHMEM页帧显式映射到内核空间, 这些函数与vmalloc机制无关. 因此, 这就造成了混乱.

而在高端内存的页不能永久地映射到内核地址空间. 因此, 通过alloc_pages()函数以__GFP_HIGHMEM标志获得的内存页就不可能有逻辑地址.

在x86_32体系结构总, 高于896MB的所有物理内存的范围大都是高端内存, 它并不会永久地或自动映射到内核地址空间, 尽管X86处理器能够寻址物理RAM的范围达到4GB(启用PAE可以寻址64GB), 一旦这些页被分配, 就必须映射到内核的逻辑地址空间上. 在x86_32上, 高端地址的页被映射到内核地址空间(即虚拟地址空间的3GB~4GB)

内核地址空间的最后128 MiB用于何种用途呢?

该部分有3个用途。

  1. 虚拟内存中连续、但物理内存中不连续的内存区,可以在vmalloc区域分配. 该机制通常用于用户过程, 内核自身会试图尽力避免非连续的物理地址。内核通常会成功,因为大部分大的内存块都在启动时分配给内核,那时内存的碎片尚不严重。但在已经运行了很长时间的系统上, 在内核需要物理内存时, 就可能出现可用空间不连续的情况. 此类情况, 主要出现在动态加载模块时.
  2. 持久映射用于将高端内存域中的非持久页映射到内核中
  3. 固定映射是与物理地址空间中的固定页关联的虚拟地址空间项,但具体关联的页帧可以自由选择. 它与通过固定公式与物理内存关联的直接映射页相反,虚拟固定映射地址与物理内存位置之间的关联可以自行定义,关联建立后内核总是会注意到的.

在这里有两个预处理器符号很重要 __VMALLOC_RESERVE设置了vmalloc区域的长度, 而MAXMEM则表示内核可以直接寻址的物理内存的最大可能数量.

  1. 直接映射区

线性空间中从3G开始最大896M的区间, 为直接内存映射区,该区域的线性地址和物理地址存在线性转换关系:线性地址=3G+物理地址。

  1. 动态内存映射区

该区域由内核函数vmalloc来分配, 特点是 : 线性空间连续, 但是对应的物理空间不一定连续. vmalloc分配的线性地址所对应的物理页可能处于低端内存, 也可能处于高端内存.

  1. 永久内存映射区

该区域可访问高端内存. 访问方法是使用alloc_page(_GFP_HIGHMEM)分配高端内存页或者使用kmap函数将分配到的高端内存映射到该区域.

  1. 固定映射区

该区域和4G的顶端只有4k的隔离带,其每个地址项都服务于特定的用途,如ACPI_BASE等。

说明

注意用户空间当然可以使用高端内存,而且是正常的使用,内核在分配那些不经常使用的内存时,都用高端内存空间(如果有),所谓不经常使用是相对来说的,比如内核的一些数据结构就属于经常使用的,而用户的一些数据就属于不经常使用的。用户在启动一个应用程序时,是需要内存的,而每个应用程序都有3G的线性地址,给这些地址映射页表时就可以直接使用高端内存。

而且还要纠正一点的是:那128M线性地址不仅仅是用在这些地方的,如果你要加载一个设备,而这个设备需要映射其内存到内核中,它也需要使用这段线性地址空间来完成,否则内核就不能访问设备上的内存空间了.

2 kmallc & kfree分配释放连续的物理内存

kmalloc和kzalloc

kmalloc函数与用户空间的malloc一族函数非常类似, 只不过它多了一个flags参数, kmalloc函数是一个简单的接口, 用它可以获取以字节为单位的一块内核内存.

如果你需要整个页, 那么前面讨论的页分配接口是更好的选择. 但是, 对大多数内核分配来说, kmalloc接口用的更多, 同时内核也提供了kzalloc该接口在kmalloc的基础上会将分配的内存清0. 他们定义在tools/virtio/linux/kernel.h?v=4.7, line 46

这两个函数返回一个指向内存块的指针, 其内存块至少要有size大小. 所分配的内存区在物理上是连续的. 在出错时, 它返回NULL. 除非没有足够的内存可用, 否则内核总能分配成功. 在对kmalloc调用之后, 你必须检查返回的是不是NULL, 如果是, 要适当处理错误.

kfree释放内存

kmalloc的另一端就是kfree, 用于释放分配的内存, kfree声明与定义

kmalloc定义 kzalloc定义 kfree定义
tools/virtio/linux/kernel.h?v=4.7, line 46 tools/virtio/linux/kernel.h?v=4.7, line 52 tools/virtio/linux/kernel.h?v=4.7, line 60
include/linux/slab.h, line 466 include/linux/slab.h?v=4.7, line 620 mm/slob.c?v=4.7, line 484
mm/slub.c?v=4.7, line 3645
mm/slab.c?v=4.7, line 3853

3 分配掩码(gfp_mask标志)

3.1 分配掩码

前述所有函数中强制使用的mask参数,到底是什么语义?

我们知道Linux将内存划分为内存域. 内核提供了所谓的内存域修饰符(zone modifier)(在掩码的最低4个比特位定义), 来指定从哪个内存域分配所需的页.

内核使用宏的方式定义了这些掩码, 一个掩码的定义被划分为3个部分进行定义, 我们会逐步展开来讲解, 参见include/linux/gfp.h?v=4.7, line 12~374, 共计26个掩码信息, 因此后面__GFP_BITS_SHIFT = 26.

3.2 掩码分类

Linux中这些掩码标志gfp_mask分为3种类型 :

类型 描述
区描述都符 内核把物理内存分为多个区, 每个区用于不同的目的, 区描述符指明到底从这些区中的哪一区进行分配
行为修饰符 表示内核应该如何分配所需的内存. 在某些特定情况下, 只能使用某些特定的方法分配内存
类型标志 组合了行为修饰符和区描述符, 将这些可能用到的组合归纳为不同类型

3.3 内核中掩码的定义

3.3.1 内核中的定义方式

//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7

/*  line 12 ~ line 44  第一部分
 *  定义可掩码所在位的信息, 每个掩码对应一位为1
 *  定义形式为  #define  ___GFP_XXX      0x01u
 */
/* Plain integer GFP bitmasks. Do not use this directly. */
#define ___GFP_DMA              0x01u
#define ___GFP_HIGHMEM          0x02u
#define ___GFP_DMA32            0x04u
#define ___GFP_MOVABLE          0x08u
/*  ......  */

/*  line 46 ~ line 192  第二部分
 *  定义掩码和MASK信息, 第二部分的某些宏可能是第一部分一个或者几个的组合
 *  定义形式为  #
首页 上一页 1 2 3 4 5 6 下一页 尾页 1/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇System.map文件的作用 下一篇痞子衡嵌入式:开启NXP-MCUBootUt..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目