我们在那些需要和用户空间交互大量数据的子系统(例如MMC[1]、Video、Audio等)中,经常看到scatterlist的影子。对我们这些“非英语母语”的人来说,初见这个词汇,脑袋瞬间就蒙圈了。scatter可翻译成“散开、分手”,list是“列表”的意思,因而scatterlist可翻译为“散列表”。“散列表”又是什么?太抽象了!
之所以抽象,是因为这个词省略了主语----物理内存(Physical memory),加上后,就好理解了多了,既:物理内存的散列表。再通俗一些,就是把一些分手的物理内存,以列表的形式组织起来。那么,也许你会问,有什么用处呢?
固然有用,具体可参考本文后续的介绍。
2. scatterlist孕育产生的配景我没有去考究scatterlist API是在哪个kernel版本中引入的(年代太长远了),凭猜度,我感受应该和MMU有关。因为在引入MMU之后,linux系统中的软件将不得不面对一个困扰(下文将以图片1中所示的系统架构为例进行说明):
假设在一个系统中(参考下面图片1)有三个模块可以访谒memory:CPU、DMA控制器和某个外设。CPU通过MMU以虚拟地点(VA)的形式访谒memory;DMA直接以物理地点(PA)的形式访谒memory;Device通过本身的IOMMU以设备地点(DA)的形式访谒memory。
然后,某个“软件实体”分配并使用了一片存储空间(参考下面图片2)。该存储空间在CPU视角上(虚拟空间)是持续的,起始地点是va1(实际上,它映射到了3块不持续的物理内存上,我们以pa1,pa2,pa3暗示)。
那么,如果该软件纯挚的以CPU视角访谒这块空间(操纵va1),则完全没有问题,因为MMU实现了持续VA到非持续PA的映射。
不过,如果软件颠末一系列操纵后,要把该存储空间交给DMA控制器,最终由DMA控制器将此中的数据搬移给某个外设的时候,由于DMA控制器只能访谒物理地点,只能以“不持续的物理内存块”为单位递交(而不是我们所熟悉的虚拟地点)。
此时,scatterlist就诞生了:为了便利,我们需要使用一个数据布局来描述这一个个“不持续的物理内存块”(起始地点、长度等信息),这个数据布局就是scatterlist(具体可参考下面第3章的说明)。而多个scatterlist组合在一起形成一个表(可以是一个struct scatterlist类型的数组,也可以是kernel资助抽象出来的struct sg_table),就可以完整的描述这个虚拟地点了。
最后,从素质上说:scatterlist(数组)是各类差别地点映射空间(PA、VA、DA、等等)的媒介(因为物理地点是真实的、实在的存在,因而可以作为通用语言),借助它,这些映射空间才华彼此转换(例如从VA转到DA)。
图片1 cpu_dma_device_memory
图片2 cpu_view_memory
3. scatterlist API介绍 3.1 struct scatterliststruct scatterlist用于描述一个在物理地点上持续的内存块(以page为单位),它的界说位于“include/linux/scatterlist.h”中,如下:
struct scatterlist {
#ifdef CONFIG_DEBUG_SG
unsigned long sg_magic;
#endif
unsigned long page_link;
unsigned int offset;
unsigned int length;
dma_addr_t dma_address;
#ifdef CONFIG_NEED_SG_DMA_LENGTH
unsigned int dma_length;
#endif
};
page_link,指示该内存块地址的页面。bit0和bit1有特殊用途(可参考后面的介绍),因此要求page最低4字节对齐。
offset,指示该内存块在页面中的偏移(起始位置)。
length,该内存块的长度。
dma_address,该内存块实际的起始地点(PA,对比page更接近我们人类的语言)。
dma_length,相应的长度信息。
在实际的应用场景中,单个的scatterlist是没有几多意义的,我们需要多个scatterlist构成一个数组,以暗示在物理上不持续的虚拟地点空间。凡是情况下,使用scatterlist成果的模块,会自行维护这个数组(指针和长度),例如[2]中所提到的struct mmc_data:
struct mmc_data {…