浅谈scatter-gather DMA

本文深入探讨了DMA(直接内存访问)技术,解释了其如何在不依赖CPU的情况下进行数据传输,从而提升嵌入式系统和网络数据处理的效率。特别介绍了scatter-gather DMA的工作原理及优势,并通过dpdk实例展示了其在ip分片零拷贝技术中的应用。

在正文开始之前需要先搞明白以下几个问题:


1. 什么是DMA?

DMA的中文名称叫做 直接内存访问,是一种不需要CPU参与,就能实现数据搬移的技术(从一个地址空间到另一个地址空间)。

2. DMA有什么用?

一定程度上解放CPU,对于实现  高效嵌入式系统 与  加速网络数据处理  有极其重要的作用。

3. DMA的实现简述

在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束 4个步骤。

 

scatter-gather DMA 与 block DMA


传统的block DMA 一次只能传输物理上连续的一个块的数据 完成传输后发起中断。scatter-gather DMA允许一次传输多个物理上不连续的块,完成传输后只发起一次中断。 

传统的block DMA像这样:

先进的scatter-gather DMA像这样:

 

这样做的好处是直观的,大大减少了中断的次数,提高了数据传输的效率。

 

scatter-gather DMA的应用


dpdk在ip分片的实现中,采用了一种称作零拷贝的技术。而这种实现方式的底层,正是由scatter-gather DMA支撑的。dpdk的分片包采用了链式管理,同一个数据包的数据,分散存储在不连续的块中(mbuf结构)。这就要求DMA一次操作,需要从不连续的多个块中搬移数据。附上e1000驱动发包部分代码:

		
uint16_t
eth_em_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
		uint16_t nb_pkts)
{
    //e1000驱动部分代码
    ...
    m_seg = tx_pkt;
	do {
		txd = &txr[tx_id];
		txn = &sw_ring[txe->next_id];

		if (txe->mbuf != NULL)
			rte_pktmbuf_free_seg(txe->mbuf);
		    txe->mbuf = m_seg;

		/*
		* Set up Transmit Data Descriptor.
		*/
		slen = m_seg->data_len;
		buf_dma_addr = rte_mbuf_data_iova(m_seg);

		txd->buffer_addr = rte_cpu_to_le_64(buf_dma_addr);
		txd->lower.data = rte_cpu_to_le_32(cmd_type_len | slen);
		txd->upper.data = rte_cpu_to_le_32(popts_spec);

		txe->last_id = tx_last;
		tx_id = txe->next_id;
		txe = txn;
		m_seg = m_seg->next;
	} while (m_seg != NULL);

	/*
	* The last packet data descriptor needs End Of Packet (EOP)
	*/
	cmd_type_len |= E1000_TXD_CMD_EOP;
	txq->nb_tx_used = (uint16_t)(txq->nb_tx_used + nb_used);
	txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_used);
    ...
}

 

### Scatter-Gather DMA 的工作原理 Scatter-Gather DMA 是一种高级的直接内存访问技术,允许设备控制器一次性传输多个不连续的数据块到不同的内存位置或从不同内存位置读取数据[^1]。这种机制通过减少 CPU 和设备之间的交互次数来提高性能。 #### 基本概念 DMA 控制器通常会维护一个描述符表(Descriptor Table),其中包含了每个数据块的相关信息,例如起始地址和长度。这些描述符定义了哪些内存区域需要被访问以及如何处理它们。当启用 scatter-gather 功能时,DMA 控制器能够按照指定顺序依次完成对各个分散内存块的操作而无需中断主机处理器[^2]。 #### 实现细节 为了支持 scatter-gather 操作,在硬件层面需具备以下特性: - **描述符结构设计**: 描述符应至少包含目标缓冲区的基址(Base Address),传输字节数(Transfer Length)以及其他控制标志位如链式模式(Link Flag)[^1]。 - **链表管理**: 如果存在大量非连续的小型数据片段,则可以采用链接列表形式组织各条目;每项记录除了常规字段外还可能增加指向下一个节点指针或者偏移量的信息以便于继续执行后续任务直到整个过程结束为止[^2]。 以下是简化版伪代码展示如何初始化并启动基于scatter gather list(DMA SG List)架构下的事务流程: ```c struct dma_descriptor { uint32_t src_addr; /* Source Memory Address */ uint32_t dst_addr; /* Destination Memory Address */ uint16_t length; /* Transfer Byte Count */ uint8_t flags; /* Control Flags e.g., End Of Chain etc.*/ }; void setup_dma_transfer(struct dma_controller *controller, struct dma_descriptor descriptors[], int count){ controller->base_register = (uintptr_t)&descriptors[0]; // Set Base Pointer To First Descriptor for(int i=0;i<count;i++){ if(i != count -1 ){ descriptors[i].flags |= DESCRIPTOR_LINKED; } write_to_hardware(&descriptors[i]); // Write Each Entry Into Hardware Registers Or Memory Mapped Area. } trigger_dma(controller); // Start The Actual Data Movement Operation After All Setup Is Complete. } ``` 此函数接受一组 `dma_descriptor` 数组作为参数,并将其配置给特定的 DMA 控制器实例。它还会设置必要的寄存器以指示第一个描述符的位置,并逐个写入所有描述符至相应的硬件资源中去。最后调用触发方法正式启动实际的数据传送动作。 #### 散列收集的优势 相比传统单次大块拷贝的方式而言,利用scatter-gather 技术有诸多好处: - 减少了上下文中断开销因为不需要频繁切换当前活动进程状态; - 提高吞吐率由于允许多路并发操作从而充分利用带宽资源; - 更灵活适应复杂应用场景比如网络协议栈分片重组等问题解决起来更加简便高效[^1]. 然而值得注意的是实现该功能往往伴随着额外成本包括但不限于更复杂的驱动程序逻辑编写难度提升以及潜在的安全隐患如果未能妥善保护好敏感数据的话则可能导致泄露风险加剧等情况发生因此开发者应当权衡利弊合理选用合适的技术方案满足具体需求场景的要求.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值