Architecture: i386 32bit Machine Ubuntu 16.04

Linux version: 4.15.0-39-generic

目录

DMA 虚拟内存区

在 IA32 体系结构中,由于 CPU 的地址总线只有 32 位,在不开启 PAE 的情况下,CPU

可以访问 4G 的线性地址空间。Linux 采用了 3:1 的策略,即内核占用 1G 的线性地址

空间,用户占用 3G 的线性地址空间。如果 Linux 物理内存小于 1G 的空间,通常将线

性地址空间和物理空间一一映射,这样可以提供访问速度。但是,当 Linux 物理内存超

过 1G,内核的线性地址就不够用,所以,为了解决这个问题,Linux 把内核的虚拟地址

空间分作线性区和非线性区两个部分,线性区规定最大为 896M,剩下的 128M 为非线性

区。从而,线性区映射的物理内存都是低端内存,剩下的物理内存作为高端内存。

在低端内存被分作两部分:

ZONE_DMA

ZONE_NORMAL

Linux 内核空间的分布图如下:

4G +----------------+

| |

+----------------+-- FIXADDR_TOP

| |

| | FIX_KMAP_END

| Fixmap |

| | FIX_KMAP_BEGIN

| |

+----------------+-- FIXADDR_START

| |

| |

+----------------+--

| | A

| | |

| Persistent | | LAST_PKMAP * PAGE_SIZE

| Mappings | |

| | V

+----------------+-- PKMAP_BASE

| |

+----------------+-- VMALLOC_END / MODULE_END

| |

| |

| VMALLOC |

| |

| |

+----------------+-- VMALLOC_START / MODULE_VADDR

| | A

| | |

| | | VMALLOC_OFFSET

| | |

| | V

+----------------+-- high_memory

| |

| |

| |

| Mapping of all |

| physical page |

| frames |

| (Normal) |

| |

| |

+----------------+-- MAX_DMA_ADDRESS

| |

| DMA |

| |

+----------------+

| .bss |

+----------------+

| .data |

+----------------+

| 8k thread size |

+----------------+

| .init |

+----------------+

| .text |

+----------------+ 0xC0008000

| swapper_pg_dir |

+----------------+ 0xC0004000

| |

3G +----------------+-- TASK_SIZE / PAGE_OFFSET

| |

| |

| |

0G +----------------+

对于整个 1G 内核虚拟地址空间,通常把空间开头的 16MB 称为 DMA 区,即 ZONE_DMA

区,因为以往硬件将 DMA 空间固定在了物理内存的低 16MB 空间,这段物理内存一一映

射到内核开头的虚拟内存区,所以这段虚拟内存区域就称为 DMA 虚拟内存区。

特别值得注意的是在低端虚拟内存中,Linux 内核将虚拟地址与物理地址采用一一对

应的线性方式进行固定映射,但这种映射只是内核虚拟地址和物理页框的一种“预定”,并

不是“霸占”或“独占”了这些物理页,只有低端内核虚拟地址真正使用了这些物理页框时,

内核低端虚拟地址才和物理页框一一对应的线性映射。而在平时,这个页框没被低端虚拟

地址使用的时候,该页框完全可以被用户空间以及 kmalloc 分配使用。

DMA 虚拟内存区中分配内存

IA32 体系结构中,Linux 4.x 可以使用 kmalloc() 函数和 GFP_DMA 标志从 DMA 虚拟内

存中获得虚拟内存。函数使用方法如下:

#ifdef CONFIG_DEBUG_VA_KERNEL_DMA

/*

* DMA Virtual Space

* 0 3G 4G

* +----+---------------------------------+-----------------+

* | | | |

* | | DMA Virtual Space | |

* | | | |

* +----+---------------------------------+-----------------+

* A A

* | |

* | |

* | |

* o o

* PAGE_OFFSET MAX_DMA_ADDRESS

*/

unsigned int *DMA_int = NULL;

DMA_int = (unsigned int *)kmalloc(sizeof(unsigned int), GFP_DMA);

printk("[*]unsigned int *DMA_int: Address: %#08x\n",

(unsigned int)(unsigned long)DMA_int);

if (DMA_int)

kfree(DMA_int);

#endif

kmalloc() 用于分配一段可用的虚拟内存,标志 GFP_DMA 指定这块虚拟内存从 DMA 虚拟

区获得。从上面的代码可以看出,DMA 虚拟内存的范围由 PAGE_OFFSET 开始,到

MAX_DMA_ADDRESS 结束。

DMA 虚拟内存实践

BiscuitOS 提供了相关的实例代码,开发者可以使用如下命令:

首先,开发者先准备 BiscuitOS 系统,内核版本 linux 1.0.1.2。开发可以参照文档构

建 BiscuitOS 调试环境:

由于代码需要运行在 IA32 系统上,如果开发者的系统不是 IA32,那么可以按照下面的

教程搭建一个 IA32 虚拟机:

接着,开发者配置内核,使用如下命令:

cd BiscuitOS

make clean

make update

make linux_1_0_1_2_ext2_defconfig

make

cd BiscuitOS/kernel/linux_1.0.1.2/

make clean

make menuconfig

由于 BiscuitOS 的内核使用 Kbuild 构建起来的,在执行完 make menuconfig 之后,系

统会弹出内核配置的界面,开发者根据如下步骤进行配置:

选择 kernel hacking,回车

选择 Demo Code for variable subsystem mechanism, 回车

选择 MMU(Memory Manager Unit) on X86 Architecture, 回车

选择 Debug MMU(Memory Manager Unit) mechanism on X86 Architecture 之后选择

Addressing Mechanism 回车

选择 Virtual address, 回车

选择 Virtual address and Virtual space 之后,接着选择 Choice Kernel/User

Virtual Address Space, 回车。

该选项用于选择程序运行在用户空间还是内核空间,这里选择内核空间。选择 Kernel

Virtual Address Space. 回车之后按 Esc 退出。

最后开发者选择 .data segment,下拉菜单打开后,选择 DMA Virtual Space (kmalloc:

GFP_DMA) 选项,回车保存并退出。

运行实例代码,使用如下代码:

cd BiscuitOS/kernel/linux_1.0.1.2/

make

cd tools/demo/mmu/addressing/virtual_address/data/kernel/

如果开发者的主机本身即是 IA32,那么使用如下命令安装并运行模块程序

sudo insmod kern_data.ko

dmesg | tail -n 20

如果开发者的主机不是 IA32,那么使用如下命令运行并安装模块

cp kern_data.c /虚拟机指定目录

cp .tmp/Makefile /虚拟机指定目录

开发者可以通过多种方式将上面两个文件拷贝到虚拟机指定目录下,然后在虚拟机上执行

如下命令

make

make install

源码如下:

Makefile

# Module on higher linux version

obj-m += kern_data.o

kern_dataDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

ROOT := $(dir $(M))

DEMOINCLUDE := -I$(ROOT)../include -I$(ROOT)/include

all:$(MAKE) -C $(kern_dataDIR) M=$(PWD) modules

install:@sudo insmod kern_data.ko

@dmesg | tail -n 20

@sudo rmmod kern_data

clean:@rm -rf *.o *.o.d *~ core .depend .*.cmd *.ko *.ko.unsigned *.mod.c .tmp_ \

.cache.mk *.save *.bak Modules.* modules.order Module.* *.b

CFLAGS_kern_data.o := -Wall $(DEMOINCLUDE)

CFLAGS_kern_data.o += -DCONFIG_DEBUG_VA_KERNEL_DMA -fno-common

运行结果如图:

从上面数据可知,kmalloc() 函数使用 GFP_DMA 标志之后,所分配的内存位于

0xC0000000 到 0xC1000000 之间,所以分配成功。

总结

DMA 虚拟内存区域位于 Linux 内核虚拟区域开始的位置,其与物理内存开始的区域一

一对应,并进行一一映射,这里的映射就是虚拟地址到物理地址的转换是一个线性公式,

也就是虚拟地址做一个简单的线性计算就可以获得物理地址,这样大大加速的地址转换

的效率。DMA 内存区域有特定的作用,开发者应该根据体系相关的文档进行使用。

通过上面的实践可知,当开发者需要使用到 DMA 虚拟地址,可以使用 kmalloc() 函数和

GFP_DMA 标志进行分配使用,使用之后,可以使用 kfree() 进行释放。

附录

赞赏一下吧

linux内核dma内存分配,Linux 4.x 内核空间 DMA 虚拟内存地址相关推荐

  1. linux双系统内存分配,Linux和Windows硬盘分区设置(二)

    三.关于硬盘分区划分标准及合理分区结构 1.硬盘分区划分标准 硬盘的分区由主分区.扩展分区和逻辑分区组成:所以我们在对硬盘分区时要遵循这个标准:主分区(包括扩展分区)的最大个数是四个,主分区(包含扩展 ...

  2. Linux Tomcat安装,Linux配置Tomcat,Linux Tomcat修改内存,Linux tomcat修改端口

    Linux Tomcat安装,Linux配置Tomcat,Linux Tomcat修改内存,Linux tomcat修改端口 >>>>>>>>>& ...

  3. Linux内核中内存分配函数

    1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...

  4. linux 进城 io字节,(2)linux内核之内存分配与IO口操作

    /***************************************************分配内存******************************************** ...

  5. 深入分析linux内核的内存分配函数devm_kzalloc

    在分析驱动代码的时候,经常会遇到使用devm_kzalloc()为一个设备分配一片内存的情况.devm_kzalloc()是内核用来分配内存的函数,同样可以分配内存的内核函数还有devm_kmallo ...

  6. 显卡 内存分配 linux,【原创】Linux环境下的图形系统和AMD R600显卡编程(4)——AMD显卡显存管理机制...

    显卡使用的内存分为两部分,一部分是显卡自带的显存称为VRAM内存,另外一部分是系统主存称为GTT内存(graphics translation table和后面的GART含义相同,都是指显卡的页表,G ...

  7. linux 优化内存分配,Linux性能优化 第三章 性能工具:系统内存

    3.1内存性能统计信息 3.1.1内存子系统和性能 和CPU相比,内存的读写速度都大大落后于CPU.为了弥补这个差距,通常CPU会采用高速缓存的机制(高cache). 3.1.2内存子系统(虚拟存储器 ...

  8. 读取当前linux进程内存_(笔记)Linux上的内存分配

    作者: LemonNan 原文: https://juejin.im/post/5ee3c34a518825430c3ad31d 前言 本篇是对Linux内存分配的一个学习笔记. 程序内存结构 下面是 ...

  9. linux 内核申请内存大小,linux内核常用的内存申请函数

    在读驱动程序时,常遇到内存申请函数.驱动程序属于内核空间,和用户空间用到的内存申请函数不同. 内核空间最常用到的内存申请函数为kmalloc()和kzalloc(). kmalloc()是申请一段内存 ...

最新文章

  1. mysql5.5数据库操作_命令行下mysql数据库基本操作
  2. ef 单表一对多集合查询_FILTER函数你用过吗?一对多查询与自动筛选,用它都能轻松搞定...
  3. iOS Icon Size 快速得到三种大小的图标
  4. MM--发票校验 及基于采购订单的MIRO发票校验过程
  5. 汇编语言--微机CPU的指令系统(五)(字符串操作指令)
  6. C++之类与对象(2)
  7. Linux CentOS6离线安装Jupyter notebook
  8. Linux内核分析 - 网络[四补]:路由表补充
  9. lua系列之 lua-cjson模块安装报错问题解决
  10. Flink + Hudi 在 Linkflow 构建实时数据湖的生产实践
  11. 卫生纸玫瑰花折法5步_手工教程:做一个漂亮的玫瑰花捧花,用折纸表达我喜欢你...
  12. HTTP发包工具 -HTTPie
  13. 内网穿透外网访问内网 MySQL 数据库教程
  14. 宏碁传奇14 Swift 指纹模块失效解决
  15. Java使用纯真IP库获取IP对应省份和城市
  16. idea的代码文本距离左边很远问题解决
  17. Excel 相同名称或ID的 内容 合并起来 同列不同内容剃加
  18. 用SNMP协议实现系统监控
  19. python需要cpu还是显卡问题_买新电脑是cpu重要还是显卡重要?该怎么选择?
  20. 图形化编程语言的设计

热门文章

  1. 方舟服务器显示队友位置,方舟如何看队友在哪 | 手游网游页游攻略大全
  2. 怎样测试运算放大器的输入失调电压?
  3. 安卓和ios抓包神器
  4. 活动目录之用户配置文件(转载)
  5. 综合应用WPF/WCF/WF/LINQ之三:采用用代码创建的方式实现CheckListBox的CustomControl
  6. css实现一个写信的格式
  7. mysql 1005 错误
  8. 高清视频实时对讲SDK源码
  9. Call From hadoop102/192.168.121.102 to hadoop102:9000 failed on connection exception
  10. 文本分类实战(三)—— charCNN模型