IA-32环境下的Linux对用户进程分配4G的虚拟地址空间,低3G是用户独立的空间,高1G是内核空间。用户空间的动态内存分配用malloc,而在设备驱动程序或者内核模块中动态开辟内存,不是用malloc,而是kmalloc ,vmalloc。释放内存用的是kfree,vfree,或free_pages. kmalloc函数返回的是虚拟地址(线性地址).

kmalloc – 内核分配内存

void * kmalloc (size_t size, int flags);
size 要分配内存的大小. 以字节为单位.
flags 要分配内存的类型.

kmalloc特殊之处在于它分配的内存是物理上连续的,这对于要进行DMA的设备十分重要.

注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。

kmalloc() 函数本身是基于 slab 实现的。slab 是为分配小内存提供的一种高效机制。但 slab 这种分配机制又不是独立的,它本身也是在页分配器的基础上来划分更细粒度的内存供调用者使用。也就是说系统先用页分配器分配以页为最小单位的连续物理地址,然后 kmalloc() 再在这上面根据调用者的需要进行切分。

下面看看kmalloc的调用过程
kmalloc()函数的实现是在 __do_kmalloc() 中,可以看到在 __do_kmalloc()代码里通过__find_general_cachep来在malloc_size结构体数组中查找已经缓存的slab内存块,这里的查找是找到第一个大于用户传进来的大小。如果找到了就调用了 kmem_cache_alloc()来分配一个 slab,把这块内存的没有分配的slab分配出去。如果没有找到,通过kmem_cache_create()函数建立专用缓冲区(新的slab),再分配。其实 kmem_cache_alloc() 等函数的实现也是调用了_cache_alloc()这个函数来分配新的 slab。我们按照 __cache_alloc()函数的调用路径一直跟踪下去会发现在 cache_grow() 函数中使用了 kmem_getpages()函数来分配一个物理页面,kmem_getpages() 函数中调用的alloc_pages_node() 最终是使用 __alloc_pages() 来返回一个struct page 结构,而这个结构正是系统用来描述物理页面的。

需要注意的是大家都知道kmalloc都是在slab机制上实现的,但是有没有发现slab是对不同对象建立专用kmem_cache_t的,那么kmalloc是如何通过大小来确定某一个对象的,如果有的对象大小一样怎么办,不同的对象初始化也不一样。其实这样理解是不对的,Linux下的slab有两种功能,一种是建立普通的缓存,一种是建立对象的缓存。kmalloc就是建立普通的缓存。大家可以看这张图中的最后一句话。《Linux内核设计与实现》和《Linux内核情景分析》

vmalloc – 内核分配内存

说vmalloc前先来看看这张图

由图可见,内核线性地址空间部分从PAGE_OFFSET(通常定义为3G)开始,为了将内核装入内存,从PAGE_OFFSET开始8M线性地址用来映射内核所在的物理内存地址(也可以说是内核所在虚拟地址是从PAGE_OFFSET开始的);接下来是mem_map数组,mem_map的起始线性地址与体系结构相关,比如对于UMA结构,由于从PAGE_OFFSET开始16M线性地址空间对应的16M物理地址空间是DMA区,mem_map数组通常开始于PAGE_OFFSET+16M的线性地址;从PAGE_OFFSET开始到VMALLOC_START – VMALLOC_OFFSET的线性地址空间直接映射到物理内存空间(一一对应影射,物理地址<==>线性地址-PAGE_OFFSET),这段区域的大小和机器实际拥有的物理内存大小有关,这儿VMALLOC_OFFSET在X86上为8M,主要用来防止越界错误;在内存比较小的系统上,余下的线性地址空间(还要再减去空白区即VMALLOC_OFFSET)被vmalloc()函数用来把不连续的物理地址空间映射到连续的线性地址空间上,在内存比较大的系统上,vmalloc()使用从VMALLOC_START到VMALLOC_END(也即PKMAP_BASE减去2页的空白页大小PAGE_SIZE(解释VMALLOC_END))的线性地址空间,此时余下的线性地址空间(还要再减去2页的空白区即VMALLOC_OFFSET)又可以分成2部分:第一部分从PKMAP_BASE到FIXADDR_START用来由kmap()函数来建立永久映射高端内存;第二部分,从FIXADDR_START到FIXADDR_TOP,这是一个固定大小的临时映射线性地址空间,(引用:Fixed virtual addresses are needed for subsystems that need to know the virtual address at compile time such as the APIC),在X86体系结构上,FIXADDR_TOP被静态定义为0xFFFFE000,此时这个固定大小空间结束于整个线性地址空间最后4K前面,该固定大小空间大小是在编译时计算出来并存储在__FIXADDR_SIZE变量中。

正是由于vmalloc()使用区、kmap()使用区及固定大小区(kmap_atomic()使用区)的存在才使ZONE_NORMAL区大小受到限制,由于内核在运行时需要这些函数,因此在线性地址空间中至少要VMALLOC_RESERVE大小的空间。VMALLOC_RESERVE的大小与体系结构相关,在X86上,VMALLOC_RESERVE定义为128M,这就是为什么ZONE_NORMAL大小通常是16M到896M的原因。

现在来仔细说说vmalloc,内核的线性地址空间把大于 896M 的内存称为高端内存区。对于这样的内存,无法在“内核直接映射空间”进行映射。因为“内核直接映射空间”最多只能从 3G 到 4G,只能直接映射 1G 物理内存,对于大于 1G 的物理内存,无能为力。实际上,“内核直接映射空间”也达不到 1G, 还得留点线性空间给“内核动态映射空间” 呢。因此,Linux 规定“内核直接映射空间” 最多映射 896M 物理内存。  
对于高端内存,可以通过 alloc_pages() 或者其它函数获得对应的 page,但是要想访问实际物理内存,还得把 page 转为线性地址才行(为什么?想想 MMU 是如何访问物理内存的),也就是说,我们需要为高端内存对应的 page 找一个线性空间,这个过程称为高端内存映射。

高端内存映射有三种 ,其中一种就是vmalloc(非连续物理内存分配区),还有一种就是永久映射区(通过 kmap(), 可以把一个 page 映射到这个空间来)。如果这块知识不知道的,可以看我这篇博文:Linux内存管理

内核给vmalloc维护了vm_struct这个结构体来实现映射

需要注意的是不要把vm_struct和用户空间的vm_area_struct搞混了

vmalloc的数据结构

描述非连续区的数据结构为struct vm_struct,定义于include/linux/vmalloc.h中:

struct vm_struct {

          unsigned long flags;void * addr;unsigned long size;struct vm_struct * next;

};

struct vm_struct * vmlist;

非连续区组成一个单链表,链表第一个元素的地址存放在变量vmlist中。Addr域是内存区的起始地址;size是内存区的大小加4096(安全区的大小)。

2.创建一个非连续区的结构

函数get_vm_area()创建一个新的非连续区结构。

这个函数比较简单,就是在单链表中插入一个元素。其中调用了kmalloc()和kfree()函数,分别用来为vm_struct结构分配内存和释放所分配的内存。只是为vm_struct这个结构来分配释放内存,而不是对vm_struct描述的内存区域来分配和释放内存。别搞混了!

vmalloc和kmalloc、malloc的区别

1、kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存
2、kmalloc保证分配的内存在物理上是连续的,内存只有在要被DMA访问的时候才需要物理上连续,malloc和vmalloc保证的是在虚拟地址空间上的连续

3、kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相对较大

4、vmalloc比kmalloc要慢。 尽管在某些情况下才需要物理上连续的内存块,但是很多内核代码都用kmalloc来获得内存,而不是vmalloc。这主要是出于性能的考虑。vmalloc函数为了把物理内存上不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项。糟糕的是,通过vmalloc获得的页必须一个个地进行映射,因为它们物理上是不连续的,这就会导致比直接内存映射大得多的TLB抖动,vmalloc仅在不得已时才会用–典型的就是为了获得大块内存时。

上面几点都是网上总结的我贴过来的,大家也都知道没什么意思。kmallo和vmalloc有我觉的特别重要的一点就是,kmalloc分配的内存是物理地址和线性地址直接转换的,而vmalloc就要像用户空间一样在该进程的页目录和页表中增加映射关系来转换物理地址和线性地址。这样就造成了一个问题kmalloc分配的都是直接映射区,这个区间是不能产生页面异常的,所以也就不存在物理页面的延迟分配原则了。

下面还有几个问题我自己也没有解决,大家可以一起讨论下。
1.kmalloc真的只能分配直接映射区内存吗?不可以分配高端内存区吗?
2.kmalloc是slab层上实现,那么可不可以用vmalloc分配一块高端内存区,缓存在slab中

详解kmalloc、vmalloc相关推荐

  1. 内核中的kmalloc函数详解

    一.kmalloc函数详解 #include <linux/slab.h> void *kmalloc(size_t size, int flags); 给 kmalloc 的第一个参数是 ...

  2. Kmalloc参数详解

    Kmalloc参数详解 Void *kmalloc(size_t size, int flags); 第一个参数是要分配的块的大小,第二个参数是分配标志(flags),他提供了多种kmalloc的行为 ...

  3. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入Lin ...

  4. Android 8.0学习(32)---Android 8.0源码目录结构详解

    Android 8.0源码目录结构详解 android的移植按如下流程:     (1)android linux 内核的普通驱动移植,让内核可以在目标平台上运行起来.     (2)正确挂载文件系统 ...

  5. 15.linux-LCD层次分析(详解)

    如果我们的系统要用GUI(图形界面接口),这时LCD设备驱动程序就应该编写成frambuffer接口,而不是像之前那样只编写操作底层的LCD控制器接口. 什么是frambuffer设备? frambu ...

  6. 详解Linux-I2C驱动

    目录 一.LinuxI2C驱动--概述 1.1 写在前面 1.2 I2C 1.3 硬件 1.4 软件 1.5 参考 二.LinuxI2C驱动--I2C总线 2.1 I2C总线物理结构 2.2 I2C总 ...

  7. cat /proc/meminfo 各字段详解

    一.cat /proc/meminfo 各字段详解 / $ cat /proc/meminfo MemTotal: 877368 kB :所有可用RAM大小(即物理内存减去一些预留位和内核的二进制代码 ...

  8. linux usb声卡 submit urb,linux usb urb详解

    linux usb urb详解 谨以此文纪念过往的岁月 一.前言 在前文中看过了hub的驱动以及host的驱动还有usb设备的驱动,在把这些东西关联起来的东东中,一个很重要的urb(usb reque ...

  9. ioctl 函数 参数 详解

    2019独角兽企业重金招聘Python工程师标准>>> ioctl 函数 参数 详解 2009-04-24 11:55 ioctl函数 本函数影响由fd参数引用的一个打开的文件. # ...

最新文章

  1. 如何在CSDN MarkDown中居中显示并设置本地图像大小
  2. 4.11 日期/时间的程序
  3. C语言的补码表示和unsigned及signed的转换
  4. 文巾解题 13. 罗马数字转整数
  5. jquery ajax设置头部,jQuery Ajax 设置请求头
  6. 江翰服务器保护系统(JH-Data Backup server)
  7. ECMAScript 5 新增 Object 接口
  8. mysql的exp平方_MySQL EXP()用法及代码示例
  9. 中有atoi函数吗_C++ 多态的实现及原理,深挖vptr指针,手动调用虚函数
  10. 文本分类模型_多标签文本分类、情感倾向分析、文本实体抽取模型如何定制?...
  11. JAVA 测试日期的不同显示格式
  12. 事务隔离级别和传播行为_.spring的事务有几种方式?spring事务的隔离级别和传播行为是什么?...
  13. 因果推断——借微软EconML测试用DML和deepIV进行反事实预测实验(二十五)
  14. Jsoup进阶之获取指定数据
  15. 基于python的学生信息管理系统毕业论文_学生信息管理系统毕业论文
  16. 微信模板消息发送实例
  17. Mybatis笔记(狂神)
  18. 【解决方案】SkeyeARS及SkeyeIVMS技术助力地铁安防视频监控系统建设
  19. 征途2s 服务器文件,征途2 部分服务器更新内容介绍!
  20. 【Soul】用户运营策略分析报告

热门文章

  1. oracle04098错误,ORA-04098错误解决方法
  2. android 投屏开发框架,Android DLNA投屏-基于CyberGarage开发投屏功能
  3. 2010 CJ Cosplay嘉年华北京赛区决战
  4. 车辆攻防研究模型,车联网VANET术语
  5. 【GoLang】《GORM实战》第三篇:关联与预加载
  6. ubuntu镜像的选择:i386还是amd64?
  7. 万象助手For流星无语
  8. 2020网鼎杯玄武组部分题writeup(签到/vulcrack/java/js_on)
  9. OpenShift 4 - 用 OpenShift Serverless Functions 为 Knative 开发 FaaS
  10. Web流程、Web服务器软件、Servlet