c语言程序 控制驱动器_C语言程序中,DMA传输数据比CPU快吗?这其实是一个奇怪的问题...

在C语言程序开发中,常常需要将内存中的数据从地址A拷贝到地址B,虽说 CPU 访问内存的速度非常快,但是拷贝总归是要消耗 CPU 时间的。如果需要拷贝的数据比较长,那么拷贝动作将会消耗相当多的 CPU 时间,在此期间,CPU 不能做任何别的事情。

9ba96d8e32e95e877c685bc8c4a5d13a.png

,在此期间,CPU 不能做任何别的事情

粗略来看,数据的拷贝过程并不复杂,无非就是寻址+写入数据,CPU 把时间花在处理这样的“简单重复劳动”上就太浪费了,因此,DMA 就被设计出来了。

DMA 简介

DMA 的英文全称是 direct memory access,直译过来就是“直接存储器存取”的意思,是计算机系统的一个特性。正如前文所讨论的,DMA 允许某些硬件子系统读写系统存储器,而不依赖于 CPU。简单来说,就是可以在不占用 CPU 时间的情况下,实现数据在存储器间传输(拷贝,移动等)。

在没有 DMA 的情况下,CPU 在处理数据的输入/输出时,通常会在数据读写器件完全被占用,因而无法执行其他工作。相反,如果系统有 DMA,CPU 只需完成数据传输的初始化,接下来的数据传输完全由 DMAC(DMA controller,DMA 控制器)实现,CPU 可以在数据传输过程中执行其他操作。

4d76ad2e5d5c143e140217ea77e589ad.png

DMA 的英文全称是 direct memory access

如果计算量很大,CPU 可能无法跟上数据传输的速率,那么 DMA 的存在可以让 CPU 专心处理数据的计算,而无需再抽空执行数据传输操作,这无疑可以提升系统的效率。另一方面,对于相对较慢的 I/O 数据,CPU 也无需再花时间等待数据传输完成。

如今 DMA 已被广泛用于各种硬件系统,包括磁盘驱动器控制器、显卡、网卡和声卡。DMA 还用于多核 CPU 中的芯片内数据传输。有 DMA 通道的计算机在传输数据时,CPU 开销要小得多。类似地,多核 CPU 内的处理元件可以在不占用 CPU 时间的情况下向本地存储器传输数据,从而允许并行进行计算和数据传输。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,您需要指定DMA寄存器中的值来控制DMA传输。具体来说,您需要设置DMA控制器寄存器(如8257或8237A)的各个位,以指定传输模式、传输方向、内存地址和I/O端口地址等参数。 下面是一个示例代码,可以将数据从网卡接收到内存中: ```c #include <stdint.h> #include <stddef.h> #define DMA_CHANNEL 0 // DMA通道号 #define PAGE_SIZE 4096 // 内存页大小 typedef struct dma_desc { uint16_t count; uint16_t addr_low; uint8_t addr_mid; uint8_t addr_high; uint8_t reserved; uint8_t control; } dma_desc_t; void init_dma(uint32_t ioaddr, size_t size, dma_desc_t *desc) { uint16_t page_count = (size + PAGE_SIZE - 1) / PAGE_SIZE; // 计算需要的页数 uint16_t byte_count = size & (PAGE_SIZE - 1); // 计算最后一页的字节数 // 初始化DMA控制器 outb(0, ioaddr + 0x0A); // 禁止DMA传输 outb(DMA_CHANNEL, ioaddr + 0x0B); // 设置DMA通道号 outb((PAGE_SIZE >> 8) & 0xFF, ioaddr + 0x04); // 设置内存页大小(高8位) outb(PAGE_SIZE & 0xFF, ioaddr + 0x04); // 设置内存页大小(低8位) outb((size >> 16) & 0xFF, ioaddr + 0x05); // 设置传输字节数(高8位) outb((size >> 8) & 0xFF, ioaddr + 0x05); // 设置传输字节数(中8位) outb(size & 0xFF, ioaddr + 0x05); // 设置传输字节数(低8位) outb(0x48 | DMA_CHANNEL, ioaddr + 0x0A); // 设置DMA模式(读取、自动初始化、单字节传输、连续传输) outb((uint16_t)(desc) & 0xFF, ioaddr + 0x00); // 设置DMA描述符表地址(低8位) outb(((uint16_t)(desc) >> 8) & 0xFF, ioaddr + 0x00); // 设置DMA描述符表地址(高8位) // 初始化DMA描述符表 for (uint16_t i = 0; i < page_count; i++) { uint16_t size = (i == page_count - 1) ? byte_count : PAGE_SIZE; // 计算当前页的大小 desc[i].count = size - 1; // 设置传输字节数 desc[i].addr_low = (uint16_t)(i * PAGE_SIZE) & 0xFFFF; // 设置内存地址(低16位) desc[i].addr_mid = ((uint16_t)(i * PAGE_SIZE) >> 16) & 0xFF; // 设置内存地址(中8位) desc[i].addr_high = 0; // 设置内存地址(高8位) desc[i].reserved = 0; // 保留字段 desc[i].control = 0x49; // 设置传输方向(从设备到内存)、自动初始化、单字节传输、连续传输 } // 启动DMA传输 outb(0x01 | DMA_CHANNEL, ioaddr + 0x0A); // 允许DMA传输 } ``` 在上述代码中,`init_dma()`函数用于初始化DMA控制器和描述符表,并启动DMA传输。其中,`ioaddr`参数为DMA控制器的I/O端口地址,`size`参数为要传输的字节数,`desc`参数为DMA描述符表的首地址。在函数内部,先计算需要的内存页数和最后一页的字节数,然后依次设置DMA控制器的各个寄存器,并初始化DMA描述符表。最后,启动DMA传输,将数据从网卡接收到内存中。 需要注意的是,上述代码仅供参考,实际情况下需要根据具体的硬件和操作系统环境做出相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值