文章目录

  • 0x01 内核态mmap
  • 0x02 用户态mmap
  • 0x03 映射I/O内存关闭Cache
  • 0x04 利用/dev/mem进行mmap

0x01 内核态mmap

内核态mmap函数如下,vm_area_struct 结构体记录一片虚存区域,在一定范围内的页将被映射至该区域内。

static int rdma_mmap(struct file *fp, struct vm_area_struct *vma)

在驱动中只需要为用户空间虚拟地址和物理地址建立新的页表即可完成映射。
remap_pfn_range()函数可以一次全部建立页表。

int remap_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot);

virt_addr 是用户虚拟地址的起始地址,即vma->vm_start;

pfn 是虚拟地址将映射到的物理地址的页面号,即物理地址右移PAGE_SHIFT位;

size 是虚存大小,即vma->vm_end - vma->vm_start;

prot 为新页要求的保护属性;

vma结构体中的 vma->vm_pgoff 是物理地址偏移,单位页数,可以通过这个参数编写驱动达到映射地址偏移效果。

mmap必须以PAGE_SIZE为单位进行映射,而内存也只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行内存对齐,强行以PAGE_SIZE的倍数大小进行映射。
一般在内核中申请物理地址(kmalloc/kzalloc)或进行I/O内存映射(如BAR空间、外设寄存器)时,首地址都是按页对齐的,无需担忧,若映射大小不足一页大小,则强制映射为1页,但当改写未被分配的物理内存时可能会导致系统崩溃,比如申请了2KB物理内存,并使用mmap进行2KB的映射,实际映射了4KB,但后2KB物理内存并不是事先申请到的,不清楚到底是什么,改写可能导致死机;而申请了4KB物理内存,映射2KB是没事的。

0x02 用户态mmap

系统调用原型:

void * mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)

start 是用户虚存首地址,必须按页对齐,通常填写0或NULL,表示虚存由Linux内核分配;
length 是要进行映射的用户虚存大小,单位字节,可以不是页的整数倍,但会被强制按页的整数倍映射;
prot 是期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过或运算合理地组合在一起;

1. PROT_EXEC //页内容可以被执行
2. PROT_READ //页内容可以被读取
3. PROT_WRITE //页可以被写入
4. PROT_NONE //页不可访问

flags 是指定映射对象的类型,映射选项和映射页是否可以共享,也可以通过或运算组合;

1. MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
2. MAP_SHARED //对映射区域的写入数据会复制回文件内, 而且允许其他映射该文件的进程共享。
3. MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
4. MAP_DENYWRITE //这个标志被忽略。
5. MAP_EXECUTABLE //同上
6. MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
7. MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
8. MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
9. MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
10. MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
11. MAP_FILE //兼容标志,被忽略。
12. MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
13. MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
14. MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

fd 即文件描述符;

offset 是被映射的对象开始映射的位置,值必须是PAGE_SIZE的整数倍,通常在嵌入式开发中已经知晓外设寄存器在MMIO空间中的物理地址(一定是按页对齐的),则可以使用/dev/mem搭配offset来定位到寄存器首地址处并进行读写。而在PC机的PCIe驱动开发中,BAR空间的物理地址由BIOS扫描得到(一定是按页对齐的,但每次开机可能地址不固定),需要自己在驱动中编写mmap接口,将BAR空间物理首地址映射到用户空间,并将offset设置为0,即不相对于映射的物理首地址偏移,驱动中也需要判断vma->vm_pgoff等于0。

 int fd;int ret;unsigned char *bar_reg_char;unsigned int *bar_reg;unsigned int reg_data = 0;// mmap测试fd = open("/dev/rdma", O_RDWR);bar_reg_char = (unsigned char *)mmap(NULL, 16384, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 8192);bar_reg = (unsigned int *)bar_reg_char;for(unsigned long i=0; i<(16384/4); i++) {*(bar_reg + i) = i;}for(int i=0; i<(16384/4); i++) {reg_data = *(bar_reg + i);printf("%lu, 0x%x\r\n", i, reg_data);}printf("%p\r\n", bar_reg);munmap(bar_reg_char, 16384);close(fd);return 0;

0x03 映射I/O内存关闭Cache

Cache和write buffer都是内置于CPU内部的一小段高速存储器,Cache中保存着最近一段时间被CPU使用过的内存数据,而write buffer则是用来应对内存的写操作的,将原本要写向内存的数据暂写到write buffer中,等到CPU空闲的时候,数据才会慢慢地被搬移到内存里。
写缓冲器是一个非常小的高速存储缓冲器,用来临时存放处理器将要写入到主存中的数据,在没有写缓冲器的系统中,处理器直接写数据到主存中。在带有写缓冲器的系统中,cache直接将数据先写到写缓冲器中,然后写缓冲器再以低速写入主存中。这就将高速的CPU和cache从对主存的低速读写中脱离了出来。
写缓冲器同时还改善了cache的性能,这体现在cache行被替换时。当cache控制器要替换出一个脏的cache行时,它只将该cache行中的数据放入写缓冲器中,而不写入主存。这样就可以快速填充新的cache行数据,处理器就可以继续从cache存储器中读/写数据。
写缓冲器中的数据在没有被写入主存之前,是不能被读取的。同样,被替换的cache行在写缓冲器中时也不能进行读操作。这也是为什么写缓冲器的深度通常比较小的原因之一,一般只有几个cache行的深度。
内核中的配置:

 /* non-cached, non-buffered 无cache无写缓冲 */#define pgprot_noncached(prot) \__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)/* non-cached, buffered 无cache有写缓冲 */#define pgprot_writecombine(prot) \__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)

IO内存被映射时需要是nocache的,这个时候应该对vma->vm_page_prot设置nocache标志。
为什么对外设地址访问时,这些地址是不允许被D-cache缓存的?因为如果被缓存了,那么这些读写将只会在D-cache中起作用,并不会传递到外设寄存器中而真正对外设模块进行操作。

1.   vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);   //赋nocache标志

dma_alloc_coherent()里就使用了pgprot_nocached()这个宏关闭对应页面的Cache功能。

0x04 利用/dev/mem进行mmap

“/dev/mem”是linux系统的一个虚拟字符设备,无论是标准linux系统还是嵌入式linux系统,都支持该设备。
“/dev/mem”设备是内核所有物理地址空间的全映像,这些地址包括:
● 物理内存(RAM)空间;
● 物理存储(ROM)空间;
● cpu总线地址;
● cpu寄存器地址;
● 外设寄存器地址,GPIO、定时器、ADC等;
“/dev/mem”设备通常与“mmap”结合使用,将该设备的物理内存映射到用户态,这样用户空间可以直接访问内存态。
因为涉及访问内核空间,因此只有root用户才有访问“/dev/mem”设备的权限。
“/dev/mem”设备通常与“mmap”结合使用,将该设备的物理内存映射到用户态,在用户态直接访问内核态物理内存。

 // mmap测试
fd = open("/dev/mem", O_RDWR);
ret = write(fd, &dat, sizeof(dat));bar_reg_char = (unsigned char *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 40);
bar_reg = (unsigned int *)bar_reg_char;for(unsigned long i=0; i<(4096/4); i++) {*(bar_reg + i) = *(bar_reg + i) + 1;
}for(int i=0; i<(4096/4); i++) {reg_data = *(bar_reg + i);printf("0x%x\r\n", reg_data);
}munmap(bar_reg_char, 4096);
close(fd);

解读Linux零拷贝之mmap相关推荐

  1. Linux 零拷贝方案

    一 什么是零拷贝(zero copy)? 零拷贝有什么优点? 1.1 什么是零拷贝? 零拷贝: 指的是在I/O过程中,用户空间和内核空间不需要进行CPU数据拷贝.零拷贝并不是指I/O过程中一次拷贝都没 ...

  2. Linux - 零拷贝技术

    Linux - 零拷贝技术 前言 一. 相关概念 1.1 缓冲区 1.1.1 内核缓冲区 1.1.2 用户缓冲区 1.2 DMA技术 1.3 虚拟内存 二. 零拷贝 2.1 传统文件传输流程 2.2 ...

  3. Java 两种zero-copy零拷贝技术mmap和sendfile的介绍

    详细介绍了两种zero-copy零拷贝技术mmap和sendfile的概念和基本原理. 文章目录 1 标准IO 2 零拷贝 2.1 sendfile调用 2.1 mmap调用 2.2 MQ中的应用 1 ...

  4. Linux 零拷贝技术

    文章目录 使用标准I/O的痛点 零拷贝技术介绍 直接I/O操作 mmap内存映射 sendfile 在文件描述符之间传递数据 使用标准I/O的痛点 在Linux中 标准I/O操作都是基于数据拷贝的缓冲 ...

  5. 什么是零拷贝?mmap与sendFile的区别是什么?

    零拷贝这三个字,一直是服务器网络编程的关键字,任何性能优化都离不开.在 Java 程序员的世界,常用的零拷贝有 mmap 和 sendFile.那么,他们在 OS 里,到底是怎么样的一个的设计?本文将 ...

  6. 消息中间件零拷贝?mmap与sendFile的区别是什么?

    什么是零拷贝? 在这个方法里面,我们没有在内存层面去"复制(Copy)"数据,所以这个方法,也被称之为零拷贝(Zero-Copy).IBM Developer Works里面有一篇 ...

  7. Linux网络处理“零拷贝”技术mmap()内核进程间通信设计8086分页管理——摆在一起来谈谈...

    Jack:最近听说了网络处理的"零拷贝"技术,觉得非常神奇,在网上查阅了很多资料.不过,并不是太明白--知其然,而不知其所以然.你能通俗地解释一下吗? 我:这是一个相对比较复杂的话 ...

  8. 讲透彻什么是Linux零拷贝?

    本文探讨 Linux 中主要的几种零拷贝技术以及零拷贝技术的适用场景.为了迅速建立起零拷贝的概念,我们拿一个常用的场景进行引入.在写一个服务端程序时(Web Server或者文件服务器),文件下载是一 ...

  9. 可算是有文章,把Linux零拷贝讲透彻了!

    击上方"朱小厮的博客",选择"设为星标" 后台回复"加群",加入组织 来源:22j.co/brVn 本文探讨 Linux 中主要的几种零拷贝 ...

最新文章

  1. Elasticsearch-04 ES中的术语和基本用法
  2. 每个前端开发者必会的二十个JavaScript面试题
  3. eclipse报错资料备份
  4. MFC 单选按钮Radio使用注意
  5. 【layui】【laydate】设置可以选择相同的年份范围
  6. vb屏蔽文本框点右键时的弹出菜单
  7. day12 生成器和各种推导式
  8. 如今前端程序员还有前途吗?
  9. 最精确的噪音测试软件,关于噪音测试App的选择与使用
  10. 端口tagged和untagged详解
  11. FPGA时钟电路PCBlayout设计原则
  12. 新广告法违规词、敏感词在线检测工具 淘宝违规词检测、查询
  13. CSS 的 hsl() 和 hsla() 函数(设置颜色的方式之一)
  14. 《区块链编程》第七章
  15. 怎样控制竞价点击价格
  16. windows xp sp3
  17. 中石油大学22春季《大学英语(四)#》第一阶段在线作业
  18. 极米h3s和坚果j10、当贝f3三款投影实测对比来了!
  19. 你妈给你介绍对象,你说自己new一个 | 程序员母亲节快乐
  20. SpringSecurity基础:自定义登录和登出

热门文章

  1. Linux 安装sbt
  2. 互联网人出游必备清单
  3. 本周 GitHub 速览:自动化当道,破密、Python爬虫各凭本事
  4. CSS实现PC端简单的聊天消息气泡样式
  5. 随笔:做一个平庸程序员,are you scared?
  6. 傻瓜式免费自助建站系统,菜鸟建站理想工具
  7. C++ getline():从文件中读取一行字符串
  8. Docker概述与安装
  9. Linux 7 (RedHatCentOS)静默安装 Oracle11.2.0.4
  10. 第一章 认识Java 2019-09-28