技术解析

如何将同一片物理内存映射到两个不同的地方
0
2021-06-19 00:57:00
idczone

最近想在 linux 上使用一个循环 buffer,希望将同一片物理内存连着映射两次,例如同一片 0x1000 的物理内存映射到 0 和 0x1000 的位置。不知道这个在 linux 上怎么实现?(用户态程序)


先配合驱动挖内存,然后随便 mmap 就行了

看不明白映射两个地方(两次)是什么意思,比如某一程序通过 mmap 映射物理内存 0x1000-0x2000 算一次,那么第二次或第二个地方是什么呢?
要映射物理内存直接 mmap 映射 /dev/mem

假设程序里有一个循环,循环读进了 500 字节的数据到 0 位置,但只处理了 100 的数据,比较 low 的做法是把剩下 400 字节拷贝到 0,然后可以读 0x1000-400 个字节,但是如果 0-0x1000 和 0x1000-0x2000 映射的是同一片物理内存,就能省下那次拷贝,直接在 500 的位置写 0x1000-400 个字节了

你是不是想多了,就算能映射,你在程序里使用的不还是逻辑地址,你访问完 0x1000 接着就是 0x1001,难不成就能自动回到 0x0000 了?循环映射?似乎 cpu 就没这功能吧

指针你可以判断一下,超过 0x1000 的时候减 0x1000 就行,减指针比拷贝要省得多,功能肯定是有的,内核模块可以做,只是想用户态程序可不可以

既然能改指针,那么分离读写指针完全就可以不需要拷贝了啊
逻辑地址映射是按页完成的,而且 64 位系统下逻辑地址空间那么大,循环映射,没可能


好混乱,我大概理解就是想实现类似 ring buffer 的结构吧。你还是需要定义 start,end,length 等信息的数据结构共享出来。读取和写入都要根据这个数据结构信息判断动作和操作结束后更改数据结构对应数据。这样就不需要拷贝数据了,当你移动数据就会增加复杂度。怎么简单怎么来。
参考: https://zhuanlan.zhihu.com/p/28929614


用户态肯定可以,直接 sudo 把 /dev/mem 映射就可以了。就算额外的硬件核心访问内存控制器,可以从同一物理地址读到相同的数据。之前遇到需求就是固件写入某个物理地址,协处理器读取改地址的数据然后运行固件。再从另外一段物理地址做数据交换。也是 ring buffer 结构。

感谢回复,以之前说的例子为例,在 500 位置写了 0x1000-400 个字节后,你能直接用逻辑地址 100 访问 0x1000 大小的内存,例如传给 memcpy 之类的函数。当然也可以申请两倍的空间,或者写一个中间层来处理读写,但是会造成空间浪费或效率降低,我在一个微软的驱动里看过这种用法,只是想能不能学习一下而已。

勉强看懂了你是想要一个 ring buffer。 不要直接用地址加减,用 ring buffer 提供的读写接口来写,ring buffer 在内部通过指针处理地址循环使用的问题。典型的 ring-buffer 实现是实现成把一段内存分为很多个槽位,内部有指针记录已经用到第几个槽了。当最后一个 slot 被使用的时候,指针会重新回到第一个槽位开始的地方。


我觉得这个想法好像和现有用 ring buffer 的处理方法不一样。可用网卡 ring bffer 是分槽的,也就是每写一次分一次槽,如果单次写入刚好超过槽大小,那么就继续写到下一个,但 ring buffer 剩余可用空间都用完了还没写完的话,应该堵塞写入端或者要求写入端缓存数据并通知用户暂时不可写入。就如 所说那样。

其实你就是把简单的问题复杂化了
用户态,由于无法一次处理完数据,基于性能考虑(避免不必要拷贝)。其实,你需要做的不就是要记录读写两个位置的地址而已
比较通用的,就是环形缓存,数据结构选择你喜欢的都可以,比如数组

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服