当前位置:首页>Linux>DMA 技术专题:从概念到 Linux 内核实现

DMA 技术专题:从概念到 Linux 内核实现

  • 2026-08-19 03:08:37
DMA 技术专题:从概念到 Linux 内核实现

基于 Linux 7.1 源码


目录

  1. 什么是 DMA
  2. 为什么需要 DMA
  3. DMA 的地址问题:物理地址、总线地址与 dma_addr_t
  4. 内核实现之一:DMA Mapping API
  5. 内核实现之二:DMAEngine 框架
  6. 实战:网络驱动中的 DMA 使用模式
  7. 使用方法速查与示例代码
  8. 总结

1. 什么是 DMA

DMA(Direct Memory Access,直接内存访问) 是一种让外设不经过 CPU、直接读写主内存的硬件机制。

1.1 没有 DMA 的世界:PIO 模式

在没有 DMA 的情况下(PIO, Programmed I/O),设备与内存之间的每一个字节都要 CPU 亲自搬运:

  设备 ──> CPU 寄存器 ──> 内存        (每个字至少两条 CPU 指令)
  内存 ──> CPU 寄存器 ──> 设备

以 10 Gbps 网卡为例,每秒约 12.5 亿字节,若按 4 字节一次 MMIO 计算,CPU 需要执行超过 6 亿次访存指令——一个核全部时间都在搬数据,什么也干不了

1.2 有 DMA 的世界

  CPU:配置好 "源地址、目的地址、长度" → 告诉 DMA 引擎 → 去做别的事
  DMA 引擎:设备 <──── 直接搬运 ────> 内存
  搬完后:中断通知 CPU "干完了"

CPU 的角色从搬运工变成了调度员:只负责描述"搬什么、搬到哪",实际传输由 DMA 硬件完成。这个"描述"就是后面反复出现的描述符(descriptor)

1.3 两类 DMA 硬件

类型
别名
位置
典型用户
内核框架
Bus Master DMA
主控 DMA
集成在设备内部(PCIe 网卡、NVMe、GPU)
设备自己发起总线事务
DMA Mapping API
System DMA / Slave DMA
第三方 DMA 控制器
SoC 内独立的 DMA 引擎(如 ARM PL330)
UART/SPI/I2S 等小外设借用
DMAEngine 框架

这个区分是理解内核 DMA 体系的钥匙:内核用两套 API 分别服务这两类硬件,后文第 4、5 节分别展开。


2. 为什么需要 DMA

  1. 解放 CPU:搬运数据是纯粹的机械劳动,交给专用硬件后 CPU 可以跑协议栈、跑应用。NAPI 收包、零拷贝(zero-copy)、XDP 高速转发全部建立在 DMA 之上。
  2. 带宽匹配:CPU 的 load/store 流水线不是为外设突发流量设计的;DMA 引擎支持 burst 传输、scatter-gather 聚合,能吃满 PCIe / 内存总线带宽。
  3. 降低功耗与延迟:拷贝路径变短,设备到内存一跳直达;CPU 可以进入深度睡眠,只在完成中断时被唤醒。
  4. 并发性:CPU 提交多个 DMA 描述符后继续工作,传输在后台并行进行,通过完成中断/回调回收结果。

但 DMA 引入了三个内核必须解决的问题,这也正是内核 DMA 子系统存在的意义:

问题
成因
内核对策
Cache 一致性
CPU 读的是 cache,DMA 改的是内存,两者可能不一致
方向标志驱动的 cache flush/invalidate
地址转换
设备看到的总线地址 ≠ CPU 物理地址(IOMMU、偏移、加密位)
dma_addr_t
 抽象 + 三条映射路径
寻址范围
老设备只能寻址低 32 位,内存可能在高地址
DMA mask + SWIOTLB bounce buffer

3. DMA 的地址问题

3.1 三种地址空间

一次 DMA 传输涉及三个"地址",初学者最容易在这里翻车:

  CPU 虚拟地址 (void *)          —— 驱动代码里用的指针
       │  virt_to_phys()
  CPU 物理地址 (phys_addr_t)     —— 内存的真实物理编号
       │  phys_to_dma() / IOMMU 页表
  DMA/总线地址 (dma_addr_t)      —— 设备在总线上看到的地址 ← 填给硬件的只能是它

官方文档的定义非常直白(Documentation/core-api/dma-api.rst L22-25):

"A dma_addr_t can hold any valid DMA address for the platform... A CPU cannot reference a dma_addr_t directly because there may be translation between its physical address space and the DMA address space."

工程铁律

  1. 交给硬件寄存器/描述符的地址,必须是 dma_alloc_coherent() 或 dma_map_*() 返回的 dma_addr_t,绝不能用 virt_to_phys() 手工换算(没有 IOMMU 的平台上看似能跑,上了 IOMMU 立刻数据损坏)。
  2. dma_map_*() 可能失败(IOMMU 映射资源耗尽、SWIOTLB 池满),必须用 dma_mapping_error() 检查:
/* include/linux/dma-mapping.h L152-159 */
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
  debug_dma_mapping_error(dev, dma_addr);
if (unlikely(dma_addr == DMA_MAPPING_ERROR))   /* ~(dma_addr_t)0 */
return -ENOMEM;
return 0;
}

3.2 DMA mask:告诉内核设备能"看到"多大内存

设备寻址能力有限(老式 PCI 设备只有 32 位),驱动在 probe 阶段必须声明:

/* drivers/net/ethernet/intel/e1000/e1000_main.c  e1000_probe() L994-1004 */
pci_using_dac = 0;
if ((hw->bus_type == e1000_bus_type_pcix) &&
    !dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) {
  pci_using_dac = 1;              /* 优先 64 位(DAC) */
else {
  err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
    pr_err("No usable DMA config, aborting\n");
goto err_dma;
  }
}

dma_set_mask_and_coherent() 一次性设置流式映射与一致性映射两个掩码(include/linux/dma-mapping.h L641-647)。内核随后为每次映射/分配检查 DMA 地址是否落在 mask 内,够不到就走第 4.4 节的 SWIOTLB 兜底。


4. 内核实现之 DMA Mapping API

这是服务 Bus Master 设备的通用层,核心文件:

文件
职责
include/linux/dma-mapping.h
API 声明与内联包装
kernel/dma/mapping.c分发核心
:direct / IOMMU / SWIOTLB 三路选择
kernel/dma/direct.c
direct.h
直接映射实现 + SWIOTLB 回退
kernel/dma/swiotlb.c
低地址反弹缓冲池
kernel/dma/coherent.c
pool.c
设备私有一致性池、原子上下文池
mm/dmapool.c
dma_pool(小块一致性内存 slab)

4.1 两类映射:一致性 vs 流式

DMA Mapping API 按生命周期和 cache 策略分为两类,这是使用时最重要的决策点:

维度
一致性映射 dma_alloc_coherent
流式映射 dma_map_single / dma_map_sg
内存来源
内核分配(direct/CMA/SWIOTLB/atomic pool)
驱动已有的缓冲区(kmalloc、页)
Cache 处理
返回即一致(不可缓存或架构保证)
map/unmap 时按方向 flush/invalidate
生命周期
长期持有,驱动退出才释放
单次传输,用完立即 unmap
典型场景
描述符环
、doorbell 等小而长期共享结构
数据 payload
(skb、bio)
成本
分配贵,使用中零开销
映射便宜,但每方向都有 cache 同步成本

一致性映射入口(include/linux/dma-mapping.h L614-625):

static inline void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t gfp)

{
return dma_alloc_attrs(dev, size, dma_handle, gfp,
      (gfp & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0);
}

流式映射入口(L519-529,注意它拒绝 vmalloc 地址——vmalloc 内存物理不连续,DMA 无法直接使用):

if (dev_WARN_ONCE(dev, is_vmalloc_addr(ptr),
"rejecting DMA map of vmalloc memory\n"))
return DMA_MAPPING_ERROR;
return dma_map_page_attrs(dev, virt_to_page(ptr), offset_in_page(ptr),
      size, dir, attrs);

4.2 方向标志:cache 一致性的开关

流式映射必须声明方向(dma-api.rst L218-223),它直接决定 map/unmap 时内核做哪种 cache 维护:

方向
语义
map 时
unmap 时
典型场景
DMA_TO_DEVICE
CPU 写、设备读
flush(把 cache 脏行写回内存)
无操作
网卡发包
DMA_FROM_DEVICE
设备写、CPU 读
invalidate(丢弃陈旧 cache 行)
invalidate(再次防预取污染)
网卡收包
DMA_BIDIRECTIONAL
方向不确定
flush + invalidate
flush + invalidate
双向共享 buffer(性能差,避免用)
DMA_NONE
仅调试用
使用即触发警告

方向写错的后果是隐蔽的数据损坏:该 flush 没 flush,设备读到旧数据;该 invalidate 没 invalidate,CPU 读到 DMA 前的旧 cache 行。这类 bug 在 x86(硬件一致性)上不暴露,到 ARM/RISC-V 上才炸——这是驱动可移植性最经典的坑。

4.3 分发核心:mapping.c 的三分支骨架

DMA Mapping API 最精妙的设计在 kernel/dma/mapping.c所有映射请求在这里被分发到三条路径之一

判定函数 dma_go_direct()(L120-153):

static bool dma_go_direct(struct device *dev, dma_addr_t mask,
const struct dma_map_ops *ops)

{
if (use_dma_iommu(dev))
return false;
if (likely(!ops))
return true;
#ifdef CONFIG_DMA_OPS_BYPASS
if (dev->dma_ops_bypass)
return min_not_zero(mask, dev->bus_dma_limit) >=
          dma_direct_get_required_mask(dev);
#endif
return false;
}

流式映射的实际分发(dma_map_phys(),L155-180)——dma_map_single/dma_map_page 的共同底层:

if (dma_map_direct(dev, ops) ||
      (!is_mmio && !is_cc_shared &&
       arch_dma_map_phys_direct(dev, phys + size)))
    addr = dma_direct_map_phys(dev, phys, size, dir, attrs, true);
else if (is_cc_shared)
return DMA_MAPPING_ERROR;
else if (use_dma_iommu(dev))
    addr = iommu_dma_map_phys(dev, phys, size, dir, attrs);
else if (ops->map_phys)
    addr = ops->map_phys(dev, phys, size, dir, attrs);

一致性分配 dma_alloc_attrs()(L643-662)结构完全对称:先查设备私有 coherent 池,再 direct / iommu / ops 三分支。

三条路径对比

路径
触发条件
地址生成
开销
Direct
无 IOMMU(!ops && !dev->dma_iommu
phys_to_dma()
 恒等或加固定偏移
SWIOTLB
direct 内兜底:地址超 mask 或强制 bounce
低地址预留池内地址
一次内存拷贝
IOMMUdev->dma_iommu = true
(Intel VT-d / AMD-Vi / SMMU)
IOMMU 驱动分配的 IOVA
页表建立 + TLB 失效

x86 上 IOMMU 的探测在 arch/x86/kernel/pci-dma.c 的 pci_iommu_alloc()(L98-107):依次 pci_swiotlb_detect → gart_iommu_detect → amd_iommu_detect → detect_intel_iommu。现代内核检测到 IOMMU 后不再替换全局 dma_ops,而是设置 dev->dma_iommu = true,让 mapping.c 走 drivers/iommu/dma-iommu.c 的 iommu_dma_* 路径。

4.4 SWIOTLB:设备够不到内存时的救生艇

当 32 位设备要 DMA 到高于 4GB 的内存时,direct 路径里的兜底逻辑(kernel/dma/direct.hdma_direct_map_phys() L85-136):

  dma_addr = phys_to_dma(dev, phys);
if (unlikely(!dma_capable(dev, dma_addr, size, true)) ||
      dma_kmalloc_needs_bounce(dev, size, dir)) {
if (is_swiotlb_active(dev) &&
        !(attrs & DMA_ATTR_REQUIRE_COHERENT))
return swiotlb_map(dev, phys, size, dir, attrs);
goto err_overflow;
  }

swiotlb_map()kernel/dma/swiotlb.c L1594-1623)从系统启动时预留的低地址 IO TLB 池中分配槽位,把数据拷贝进去,返回池内地址;unmap 时反向拷贝回来(bounce buffering 一词的来源)。它保证了任何设备都能 DMA 到任意内存,代价是一次额外拷贝——所以它是兜底而不是常态。机密计算(TDX/SEV)下 is_swiotlb_force_bounce() 强制所有 DMA 走 bounce,因为设备不能直接访问加密内存。

4.5 dma_pool:小块一致性内存的 slab

USB 驱动等需要频繁分配小块一致性内存(如几十字节的 URB 控制块),dma_alloc_coherent 按页分配太浪费。mm/dmapool.c 提供 slab 化的 dma_pool

  • dma_pool_create_node()(L226):创建池,底层页仍由 dma_alloc_coherent 供应;
  • dma_pool_alloc()(L407-442):池内切分,内部持 spin_lock_irqsave可在中断上下文使用
  • 一致性分配的另一来源是 kernel/dma/pool.c 的三个全局 atomic pool(atomic_pool_dma/dma32/kernel,L16-20),为不可阻塞的原子上下文兜底。

5. 内核实现之 DMAEngine 框架

服务 Slave DMA(SoC 系统 DMA 控制器) 的框架,三层结构:

  客户端 API(include/linux/dmaengine.h 内联函数)   ← UART/SPI/I2S/音频驱动调用
        │ 纯转发
  核心层(drivers/dma/dmaengine.c + dmaengine.h)     ← 通道注册、cookie 管理
        │ device_* 虚函数表
  Provider 驱动(drivers/dma/pl330.c 等)             ← 具体 DMA 控制器硬件

5.1 关键数据结构(include/linux/dmaengine.h

enum dma_status {/* L37 */
  DMA_COMPLETE, DMA_IN_PROGRESS, DMA_PAUSED, DMA_ERROR, DMA_OUT_OF_ORDER,
};
enum dma_transaction_type {/* L51 */
  DMA_MEMCPY, ..., DMA_SLAVE, DMA_CYCLIC, DMA_INTERLEAVE, ...,
};
enum dma_transfer_direction { /* L79 */
  DMA_MEM_TO_MEM, DMA_MEM_TO_DEV, DMA_DEV_TO_MEM, DMA_DEV_TO_DEV,
};

事务描述符(L614-632)——一次 DMA 传输的句柄:

struct dma_async_tx_descriptor {
dma_cookie_t cookie;              /* 事务序号,见 5.3 */
dma_addr_t phys;
struct dma_chan *chan;
dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
  dma_async_tx_callback callback;   /* 完成回调 */
void *callback_param;
  ...
};

Provider 虚函数表struct dma_device(L868-960)是客户端 API 的最终落点:device_alloc_chan_resourcesdevice_prep_slave_sgdevice_configdevice_issue_pendingdevice_tx_status 等。

5.2 客户端六步流程

官方文档 Documentation/driver-api/dmaengine/client.rst L17-27 定义的标准流程:

步骤
API
源码位置
说明
1. 申请通道
dma_request_chan(dev, name)
dmaengine.c L815
DT/ACPI/map 三路匹配并独占
2. 配置参数
dmaengine_slave_config(chan, &cfg)
dmaengine.h L962
外设地址、位宽、burst
3. 准备描述符
dmaengine_prep_slave_single/sg/cyclic
dmaengine.h L977/1013/1039
此前客户端须自行 dma_map
4. 提交
dmaengine_submit(desc)
dmaengine.h L1248
返回 cookie,入 pending 队列
5. 触发
dma_async_issue_pending(chan)
dmaengine.h L1454
队列刷入硬件
6. 完成
callback 或 dma_async_is_tx_complete() 轮询
dmaengine.h L1470
回调在 tasklet 中执行,非硬中断上下文

职责划分的关键点:slave DMA 场景下,框架不做 DMA 映射,客户端必须自己映射且用 DMA 控制器的 device(client.rst L122-138):

struct device *dma_dev = dmaengine_get_dma_device(chan);
nr_sg = dma_map_sg(dma_dev, sgl, sg_len);            /* 客户端自己映射 */
desc = dmaengine_prep_slave_sg(chan, sgl, nr_sg, direction, flags);

证据在 provider 侧:pl330 只消费已映射的地址(drivers/dma/pl330.cpl330_prep_slave_sg() L2882-2888 直接取 sg_dma_address(sg)/sg_dma_len(sg))。

5.3 Cookie 机制:轻量级事务跟踪

dmaengine_submit() 不返回描述符指针的所有权,而是返回一个单调递增的序号(drivers/dma/dmaengine.h L29):

static inline dma_cookie_t dma_cookie_assign(struct dma_async_tx_descriptor *tx)
{
struct dma_chan *chan = tx->chan;
dma_cookie_t cookie = chan->cookie + 1;
if (cookie < DMA_MIN_COOKIE)
    cookie = DMA_MIN_COOKIE;
  tx->cookie = chan->cookie = cookie;   /* 发号 */
return cookie;
}

完成侧推进水位线 chan->completed_cookiedma_cookie_complete() L52),客户端用 dma_cookie_status()/dma_async_is_complete() 通过区间比较判定自己的事务是否完成(处理了序号回绕)。这个设计让客户端无需持有描述符引用即可跟踪进度。

5.4 Provider 侧:以 ARM PL330 为例

drivers/dma/pl330.c 的 probe 填充虚函数表并注册(L3125-3141):

  pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
  pd->device_prep_dma_cyclic      = pl330_prep_dma_cyclic;
  pd->device_tx_status            = pl330_tx_status;
  pd->device_prep_slave_sg        = pl330_prep_slave_sg;
  pd->device_config               = pl330_config;
  pd->device_issue_pending        = pl330_issue_pending;
  ret = dma_async_device_register(pd);

完成路径:pl330_irq_handler(L2901)→ 标记 DONE 并 tasklet_schedule → pl330_tasklet(L2068-2137)中 dma_cookie_complete() 推进水位,然后先释放自旋锁再调回调(L2126-2130)——防止客户端回调里再次提交描述符造成死锁:

if (dmaengine_desc_callback_valid(&cb)) {
    spin_unlock_irqrestore(&pch->lock, flags);
    dmaengine_desc_callback_invoke(&cb, NULL);
    spin_lock_irqsave(&pch->lock, flags);
  }

5.5 dmatest:最好的客户端参考实现

drivers/dma/dmatest.c 浓缩了完整流程(dmatest_thread):dma_request_channel 按能力请通道 → dmaengine_get_dma_device → dma_map_page(L764/782)→ device_prep_dma_memcpy → 设置 tx->callback → tx->tx_submit(tx) → dma_async_issue_pending(L842)→ 回调或轮询等待 → dmaengine_terminate_sync 收尾。写 DMAEngine 客户端代码前值得通读一遍。


6. 实战:网络驱动中的 DMA 使用模式

以 Intel e1000 为例,它把两类映射用得泾渭分明,堪称教科书。

6.1 描述符环:一致性映射(长期持有)

e1000_setup_tx_resources()e1000_main.c L1515):

txdr->size = txdr->count * sizeof(struct e1000_tx_desc);
txdr->size = ALIGN(txdr->size, 4096);

txdr->desc = dma_alloc_coherent(&pdev->dev, txdr->size, &txdr->dma,
        GFP_KERNEL);

返回的虚拟地址txdr->desc 给 CPU 读写描述符,总线地址txdr->dma 写入网卡寄存器 TDBAL/TDBAH 供硬件 DMA 访问。这对指针在 struct e1000_tx_ringe1000.h L143-162)中配对保存:

struct e1000_tx_ring {
void *desc;          /* CPU 虚拟地址 */
dma_addr_t dma;      /* 总线地址,写给硬件 */
unsigned int size;
struct e1000_tx_buffer *buffer_info;
  ...
};

6.2 数据缓冲区:流式映射(用完即解除)

发包路径 e1000_tx_map()(L2877),方向 DMA_TO_DEVICE(CPU 写、设备读):

buffer_info->dma = dma_map_single(&pdev->dev, skb->data + offset,
          size, DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, buffer_info->dma))
goto dma_error;

发送完成回收时对称解除(e1000_unmap_and_free_tx_resource() L1956-1968),注意按映射方式区分 dma_unmap_single/dma_unmap_page

收包路径方向相反(DMA_FROM_DEVICE):e1000_alloc_rx_buffers()(L4621)map,e1000_clean_rx_irq()(L4400)在 skb 上送协议栈前 unmap。小包走 copybreak 优化时只做同步不解除(L4341):

dma_sync_single_for_cpu(&pdev->dev, buffer_info->dma, length,
      DMA_FROM_DEVICE);

6.3 演进:page_pool 的"长期持有 + 按需 sync"

现代高性能驱动(尤其 XDP 场景)发现:收包方向每次都 map/unmap,cache 操作开销巨大。net/core/page_pool.c 把流式映射固化——映射在页的整个池化生命周期内保持(page_pool_dma_map() L530-558):

dma = dma_map_page_attrs(pool->p.dev, netmem_to_page(netmem), 0,
       (PAGE_SIZE << pool->p.order), pool->p.dma_dir,
       DMA_ATTR_SKIP_CPU_SYNC |
         DMA_ATTR_WEAK_ORDERING);

两个属性的工程含义:

  • DMA_ATTR_SKIP_CPU_SYNC:map/unmap 时跳过全页 cache 同步;同步推迟到每次收发报文前后,由 page_pool_dma_sync_for_device/cpu() 按实际报文长度精确执行——把 O(页大小) 的 cache 操作降为 O(报文大小)。
  • DMA_ATTR_WEAK_ORDERING:允许设备弱序访问(ARM 等弱内存序架构省去昂贵的一致性屏障),可见性由显式 sync 点保证。

页真正离开池时才 unmap(__page_pool_release_netmem_dma() L730-751)。这套"映射常驻 + 细粒度 sync"的组合是 page_pool 在高 PPS 收包路径上性能的关键,也是流式映射 API 的最高阶用法。


7. 使用方法速查与示例代码

7.1 决策树

要给设备 DMA 一块内存?
├─ 设备自带 bus-master 能力(PCIe 网卡/NVMe)→ DMA Mapping API
│   ├─ 长期共享、双向访问(描述符环) → dma_alloc_coherent
│   ├─ 小块高频(USB 控制块)        → dma_pool
│   └─ 单次传输(skb/bio)           → dma_map_single / dma_map_sg
│        └─ 高频复用收包 buffer       → page_pool(SKIP_CPU_SYNC)
└─ 借用 SoC 系统 DMA 控制器(UART/SPI/I2S)→ DMAEngine 六步流程

7.2 一致性映射示例(描述符环模式)

struct my_ring {
void *desc;
dma_addr_t dma;
size_t size;
};

static int my_ring_alloc(struct device *dev, struct my_ring *ring, int count)
{
  ring->size = ALIGN(count * sizeof(struct my_desc), 4096);
  ring->desc = dma_alloc_coherent(dev, ring->size, &ring->dma,
          GFP_KERNEL);
if (!ring->desc)
return -ENOMEM;
/* ring->dma 写入硬件寄存器;ring->desc 由 CPU 访问 */
return 0;
}

static void my_ring_free(struct device *dev, struct my_ring *ring)
{
  dma_free_coherent(dev, ring->size, ring->desc, ring->dma);
}

7.3 流式映射示例(skb 发包模式)

static int my_tx_map(struct device *dev, struct sk_buff *skb,
dma_addr_t *out)

{
dma_addr_t dma = dma_map_single(dev, skb->data, skb->len,
          DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma))
return -ENOMEM;
  *out = dma;
return 0;
}

static void my_tx_unmap(struct device *dev, dma_addr_t dma, unsigned int len)
{
  dma_unmap_single(dev, dma, len, DMA_TO_DEVICE);   /* 与 map 参数严格对称 */
}

7.4 DMAEngine slave 六步完整示例

#include <linux/dmaengine.h>

static int my_slave_xfer(struct device *dev, void *buf, size_t len)
{
struct dma_chan *chan;
struct dma_slave_config cfg = {0};
struct dma_async_tx_descriptor *desc;
struct device *dma_dev;
dma_addr_t dma;
dma_cookie_t cookie;

/* 1. 申请通道(DT 中 dmas = <&dmac 0>; dma-names = "rx";) */
  chan = dma_request_chan(dev, "rx");
if (IS_ERR(chan))
return PTR_ERR(chan);

/* 2. 配置外设参数 */
  cfg.direction     = DMA_DEV_TO_MEM;
  cfg.src_addr      = MY_FIFO_PHYS;      /* 外设 FIFO 物理地址 */
  cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
  cfg.src_maxburst  = 8;
  dmaengine_slave_config(chan, &cfg);

/* 3a. 客户端自行映射(用 DMA 控制器的 device!) */
  dma_dev = dmaengine_get_dma_device(chan);
  dma = dma_map_single(dma_dev, buf, len, DMA_FROM_DEVICE);
if (dma_mapping_error(dma_dev, dma))
return -ENOMEM;

/* 3b. 准备描述符 */
  desc = dmaengine_prep_slave_single(chan, dma, len,
             DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (!desc)
return -ENOMEM;
  desc->callback = my_dma_done;          /* tasklet 上下文执行 */
  desc->callback_param = dev;

/* 4. 提交(此后描述符归引擎所有,不得再访问) */
  cookie = dmaengine_submit(desc);

/* 5. 触发硬件 */
  dma_async_issue_pending(chan);

/* 6. 等回调,或轮询: */
/* while (dma_async_is_tx_complete(chan, cookie, NULL, NULL)
   *          != DMA_COMPLETE) cpu_relax(); */

return 0;
}

7.5 常见错误清单

错误
后果
正确做法
给硬件填 virt_to_phys() 结果
有 IOMMU 时数据损坏
只用 dma_map_*() 返回的 dma_addr_t
不检查 dma_mapping_error()
映射失败被当成地址 0xFFFF... 使用
每次 map 后必查
方向标志写反
cache 同步缺失,隐蔽数据损坏(ARM 上才暴露)
TO_DEVICE=发包,FROM_DEVICE=收包
map/unmap 的 size/direction 不对称
DMA API debug 报错、IOMMU 页表泄漏
保存 dma+length+方向,严格对称解除
流式映射后忘记 unmap
IOMMU 映射耗尽/SWIOTLB 池泄漏
传输完成立即 unmap
用 dma_map_single 映射 vmalloc 内存
WARN_ONCE
 直接拒绝
映射物理连续内存;vmalloc 需逐页处理
DMAEngine 回调里直接提交新描述符
死锁
provider 已先解锁再回调,但客户端也应避免持锁回调

8. 总结

  1. DMA 的本质是把数据搬运从 CPU 卸载到专用硬件,CPU 从搬运工变成调度员——描述符 + 完成中断是它的基本工作模式。
  2. 内核 DMA 子系统解决三件事:cache 一致性(方向标志驱动)、地址转换(dma_addr_t 抽象)、寻址限制(DMA mask + SWIOTLB)。
  3. 两套 API 对应两类硬件:Bus Master 设备用 DMA Mapping API(一致性映射管描述符环,流式映射管数据);Slave DMA 用 DMAEngine 六步流程(request → config → prep → submit → issue → callback)。
  4. kernel/dma/mapping.c 的三分支分发(direct / SWIOTLB / IOMMU)是理解整个体系的核心骨架——同一个 dma_map_single(),在无 IOMMU 平台上是零开销的地址换算,在有 IOMMU 平台上是 IOVA 分配 + 页表建立。
  5. 性能工程的最高阶形态在 page_pool:把流式映射固化到页的生命周期,用 SKIP_CPU_SYNC + 细粒度 sync 把 cache 维护成本压到最低——这正是 e1000 经典模式在 10G/100G 时代的演进方向。

参考源码索引

主题
文件
Mapping API 文档
Documentation/core-api/dma-api.rst
dma-api-howto.rst
Mapping API 入口
include/linux/dma-mapping.h
分发核心
kernel/dma/mapping.c
dma_map_phys L155、dma_alloc_attrs L643)
直接映射 + SWIOTLB 回退
kernel/dma/direct.h
 L85、kernel/dma/swiotlb.c L1594
dma_pool
mm/dmapool.c
kernel/dma/pool.c
DMAEngine 文档
Documentation/driver-api/dmaengine/client.rst
DMAEngine 头文件
include/linux/dmaengine.h
(1681 行)
DMAEngine 核心
drivers/dma/dmaengine.c
、内部头 drivers/dma/dmaengine.h
Provider 示例
drivers/dma/pl330.c
客户端参考
drivers/dma/dmatest.c
网卡经典用法
drivers/net/ethernet/intel/e1000/e1000_main.c
e1000.h L143-162
page_pool 高阶用法
net/core/page_pool.c
 L530-558、L730-751

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 20:54:30 HTTP/2.0 GET : https://f.mffb.com.cn/a/507066.html
  2. 运行时间 : 0.186095s [ 吞吐率:5.37req/s ] 内存消耗:4,688.40kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=122351f1cd630864d4d09b9f6d2df53f
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000840s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000934s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000449s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000432s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000768s ]
  6. SELECT * FROM `set` [ RunTime:0.000314s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000964s ]
  8. SELECT * FROM `article` WHERE `id` = 507066 LIMIT 1 [ RunTime:0.007483s ]
  9. UPDATE `article` SET `lasttime` = 1787316870 WHERE `id` = 507066 [ RunTime:0.004227s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000376s ]
  11. SELECT * FROM `article` WHERE `id` < 507066 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000726s ]
  12. SELECT * FROM `article` WHERE `id` > 507066 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000688s ]
  13. SELECT * FROM `article` WHERE `id` < 507066 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.013726s ]
  14. SELECT * FROM `article` WHERE `id` < 507066 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001370s ]
  15. SELECT * FROM `article` WHERE `id` < 507066 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002564s ]
0.188502s