linux内核内存分配(三、虚拟内存管理)

在分析虚拟内存管理前要先看下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。如上图。

===============================================================================================================

这里强调下:上面的图示表示的仅仅是虚拟地址,而实际的物理地址是分DMA和常规地址及高端地址的;

linux内核内存大概的就是上面的图示了,其中8MB说是为了安全,防止越界访问(看了很多书,都这么说),就是这8MB虚拟地址不做任何映射(这样仅仅是虚拟地址,没有实际的物理地址浪费)

由上面的图示可以知道,前面896MB(其他架构可以能不是以896MB分割的)就是我们说的内核逻辑地址(记住是内核逻辑地址,如果就说逻辑地址的话应该是指x86架构中虚拟地址中不包括段地址部分,也就是段内偏移部分);这部分内存地址已经在系统初始化的时候和物理页做好了映射,而且是一一映射,我们一般使用的时候就是用该部分的内存地址(kmalloc函数使用就是该部分)。这段内存是非常高效的,因为不需要做其他的映射和修改页表就可以直接使用。本blog是分析下虚拟内存地址的映射,主要是vmalloc函数和ioremap函数;

vmalloc函数

vmalloc函数是驱动模块经常使用的内存分配函数,该函数返回的虚拟地址连续的(其实这也有疑问,因为上面vmalloc的虚拟地址区有4k分割地址,如果vmalloc分配的虚拟地址很大,那么中间是否有4kb的分割地址?),但是不保证所映射的物理地址也是连续的。它主要对上面的vmalloc_start到vmalloc_end这段内存操作,返回的虚拟地址就是这一部分的。

在大多数情况下,不鼓励使用vmalloc来申请内存,原因: 1、通过vmalloc函数获取的内存使用效率不高(因为要自己做映射,要判断哪些是空闲页等操作);2、有些架构上给vmalloc使用的内存地址非常小,对vmalloc调用可能会因为没有空闲地址而失败;3、不能保证物理地址是连续的,对一些驱动程序来说这是硬伤;综上所述,最好不要用包含vmalloc的代码作为内核的主线代码。

下面大概来说下vmalloc函数的原型:

void *vmalloc(unsigned long size);

该函数的实现有3个步骤:1、在vmalloc区域分配一段连续的虚拟内存地址;2、通过伙伴系统获取物理页;3、通过对页表的操作,把1中获取到的虚拟地址映射到2中分配到物理页上;

注意:

1、上面的图示我们可以看出每个vmalloc虚拟地址之间都有4kb的分割区域(其作用就是防止越界,形成一个空洞,越界时产生异常),所以vmalloc函数实现时,会在size对齐后再增加4kb大小(一个页的大小);

2、在分配物理页时,会从高端地址(上面的图示表示的仅仅是虚拟地址而已,物理内存分配可以看linux内核内存分配(一、基本概念)中物理页和虚拟地址的映射图)分配,gfp为:GFB_KERNEL | _GFP_HIGHMEM;表示该函数可能睡眠,分配的物理地址来自高端物理页。常规物理页给kmalloc使用;vmalloc函数分配高端物理页时使用alloc_page函数或者alloc_pages_node函数来分配一个整页,多次调用分配函数来完成所有的物理页的分配,这样就不能保证所有的物理页一定连续了。

3、对虚拟地址映射时不会对额外的4k的分割地址进行映射,第2步中也不会对这4k的虚拟分割地址进行分配映射的物理页。

下面是vmalloc的映射图,图来自《深入linux设备驱动程序内核机制》

上图中:从vmalloc区域分配的两个虚拟页地址映射到物理地址的高端页面,其中高端内存是不连续的,虚拟地址最后一个页没有进行映射,那就是额外的4k分割页面;

用vmalloc分配得到的地址是不能在微处理器之外使用的,因为它们只在处理器的内存管理单元上才有意义。使用vmalloc函数的正确场合是在分配一大块连续的、只在软件中存在的、用于缓冲的内存区域的时候。

ioremap函数

函数原型:void __iomem  *ioremap(unsigned long phys_addr,  size_t  size);此处的__iomem只是标识返回的地址是io类型的地址;该函数用来把vmalloc区域之间的内存映射到设备I/O地址空间,这个函数和vmalloc函数的实现非常相似,不同的地方就是vmalloc是通过伙伴系统分配到物理页,而ioremap函数却是利用设备的I/O空间,而不是系统物理页;至于其他操作可以看:访问I/O内存和I/O端口设备

ioremap函数更多用于映射(物理的)PCI缓冲区地址到(虚拟的)内核空间。ioremap函数映射的内存需要用iounmap函数来释放;

vmalloc和kmalloc比较

kmalloc函数:

1、得到的内存保留上次使用的数据,不对申请到的内存进行初始化(zmalloc函数会初始化申请到的内存);

2、返回的逻辑地址(其实也是虚拟地址)和映射的物理页都是连续的,调用该函数时可能会休眠;

3、kmalloc函数和__get_free_pages函数返回的内存地址都是虚拟地址,其实际的物理地址需要通过MMU转换后得到(其实MMU就是通过页表机制来转换的);

4、kmalloc函数和__get_free_pages函数使用的虚拟地址范围与物理内存是一一对应的,可能有个常量偏移,这两个函数不需要修改页表;

5、kmalloc函数申请的内存大小是有限制的,一般根据架构决定;

vmalloc函数和ioremap函数:

1、使用效率不高,物理页不保证连续,虚拟地址保证连续;

2、vmalloc函数和ioremap函数使用的地址范围完全是虚拟的,每次分配都要通过对页表的操作来建立映射关系;

3、vmalloc函数一般用来分配大块的内存,并且返回的地址不能在微处理器之外使用;

转载地址:http://blog.csdn.net/yuzhihui_no1/article/details/47429411

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 00:49:56

linux内核内存分配(三、虚拟内存管理)的相关文章

linux内核内存分配(二、struct slab和struct kmem_cache)

前一篇bloglinux内核内存分配(一.基本概念)主要是分析linux内核内存的分配和物理页分配函数接口.但是在实际的操作中,不一定所有内存申请都需要一个物理页,很多只是需要分配几K大小的内存就可以.所以就需要更小的内存分配函数.刚开始看这个有点不懂,不过懂了就很简单了.哈哈. slab思想 摘抄<深入linux设备驱动程序内核机制>的一段话:slab分配器的基本思想是,先利用页面分配器分配出单个或者一组连续的物理页面,然后在此基础上将整块页面分割成多个相等的小内存单元,以满足小内存空间分配

linux内核内存分配

实验要求: 1.编写一个内核模块,在模块中分配内存并访问 2.理解并验证kmalloc.vmalloc等函数的区别. 背景知识: 1.Linux内存页管理 Linux内核管理物理内存是通过分页机制实现的,它将整个内存划分成4K大小页,作为使分配和回收内存的基本单位.在分配内存时尽量分配连续内存,避免TLB的刷新率过高.故此Linux采用了“伙伴“关系来管理空闲页框.因此空闲页面分配时也需要遵循伙伴关系.最小单位是2的幂倍页面大小.内核中分配空闲页框的基本函数是get_free_page/get_

linux内核内存分配(一、基本概念)

内存分配是linux比较复杂也是比较重要的部分,这个和ssd驱动很类似:物理地址和虚拟地址的映射关系.下面总结下最近看到的有关内存分配的内容和自己的理解: 1.一致内存访问和非一致内存访问 上图来自<深入linux设备驱动程序内核机制> 简单的说明下,UMA(一致内存访问 uniform memory access)可以很好的看到所有cpu访问内存的距离都是一样的(其实就是通过总线到内存的速度和距离都是一样的)所以就叫一致内存访问: 很显然右边的NUMA就是非一致内存访问,内存节点0是CPU0

Linux内核分析(三)----初识linux内存管理子系统

Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linux的内存管理子系统,linux的内存管理子系统相当的庞大,所以我们今天只是初识,只要对其进行简单的了解就好了,不会去追究代码,但是在后面我们还会对内存管理子系统进行一次深度的分析. 在分析今天的内容之前,我们先来看出自http://bbs.chinaunix.net/thread-2018659-2-1.html的一位大神做的内存管理图,真心佩服大神.其实这张图可以

Linux内核——内存管理

内存管理 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址)通常以页为单位进行处理.MMU以页大小为单位来管理系统中的页表.从虚拟内存的角度看,页就是最小单位. 32位系统:页大小4KB 64位系统:页大小8KB 在支持4KB页大小并有1GB物理内存的机器上,物理内存会被划分为262144个页.内核用 struct page 结构表示系统中的每个物理页. struct page { page_flags_t flags;   /* 表示页的状态,每

Linux内核设计第三周——构造一个简单的Linux系统

Linux内核设计第三周 ——构造一个简单的Linux系统 一.知识点总结 计算机三个法宝: 存储程序计算机 函数调用堆栈 中断 操作系统两把宝剑: 中断上下文的切换 进程上下文的切换 linux内核源代码分析 arch/目录保存支持多种CPU类型的源代码 其中的关键目录包括:Documentation.drivers.firewall.fs(文件系统).include init目录:含有main.c,内核启动相关的代码基本都在init目录下 start_kernal()函数为启动函数,初始化内

20135327郭皓--Linux内核分析第三周 构造一个简单的Linux系统MenuOS

Linux内核分析第三周  构造一个简单的Linux系统MenuOS 前提回顾 1.计算机是如何工作的三个法宝 1.存储程序计算机 2.函数调用堆栈 3.中断 2.操作系统的两把宝剑 中断上下文的切换 进程上下文的切换 第一讲  Linux内核源代码介绍 arch目录包括了所有和体系结构相关的核心代码.它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录.PC机一般都基于此目录. init目录包含核心的初始化代码(不是系统的引导代

解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的朋友可以参考下 内核模块管理Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. Linux内核的整体结构非常庞大,其包含的组件非常多.我们把需要的功能都编译到linux内核,以模块方式扩展内核功能. 先来看下最简单的内核模块 ? 1 2

Linux内核-内存回收逻辑和算法(LRU)

Linux内核内存回收逻辑和算法(LRU) LRU 链表 在 Linux 中,操作系统对 LRU 的实现主要是基于一对双向链表:active 链表和 inactive 链表,这两个链表是 Linux 操作系统进行页面回收所依赖的关键数据结构,每个内存区域都存在一对这样的链表.顾名思义,那些经常被访问的处于活跃状态的页面会被放在 active 链表上,而那些虽然可能关联到一个或者多个进程,但是并不经常使用的页面则会被放到 inactive 链表上.页面会在这两个双向链表中移动,操作系统会根据页面的