1. 为什么需要scatterlist?
想象一下你正在搬家,所有物品都堆放在不同房间的角落。如果必须把所有东西先搬到客厅集中,再装车运输,效率会非常低。scatterlist解决的正是类似问题——当物理内存碎片化时,如何让DMA控制器高效搬运分散在各处的数据块。
在Linux内核中,物理内存经过长时间运行后往往呈现碎片化状态。此时若需要为NVMe SSD或网卡等高速设备分配大块连续内存,就像要求搬家时必须找到能放下所有家具的巨型空房间一样困难。scatterlist通过"物理地址离散,逻辑地址连续"的设计,完美解决了这个矛盾。
我曾调试过一个视频采集卡驱动,当用户传递1080P视频帧时,经常遇到内存分配失败。改用scatterlist后,驱动程序将视频帧的YUV分量分别存放在三个非连续区域,DMA控制器却能将其视为连续数据流处理,传输效率提升40%。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. scatterlist核心数据结构解析
2.1 链式管理结构
scatterlist的精妙之处在于其链式管理。就像用绳子串起散落的珍珠,内核通过sg_table结构管理离散内存块:
c复制struct sg_table {
struct scatterlist *sgl; // 链表头
unsigned int nents; // 有效条目数
unsigned int orig_nents; // 原始条目数
};
每个scatterlist节点记录着内存块的物理地址信息:
c复制struct scatterlist {
unsigned long page_link; // 内存页指针+链标记
unsigned int offset; // 页内偏移
unsigned int length; // 数据长度
dma_addr_t dma_address; // DMA映射地址
};
特别要注意page_link的妙用——低两位作为标志位:
- bit0=1:当前是链节点(chain sg)
- bit1=1:当前是终止节点(end sg)
- 其余位存储物理页地址
这种设计使得32字节的结构体既能存储内存信息,又能实现链表功能。我在开发RAID
