计算机组成 -- DMA
背景
- 无论IO速度如何提升,比起CPU,还是太慢,SSD的IOPS可以达到2W,但CPU的主频有2GHz
- 对于IO操作,都是由CPU发出对应的指令,然后等待IO设备完成操作后返回,CPU有大量的时间都是在等待IO设备完成操作
- 在很多时候,CPU的等待是没有太多的实际意义的
- 对于IO设备的大量操作,其实都只是把内存里面的数据,传输到IO设备而已,此时CPU只是在傻等
- 当传输的数据量比较大的时候,如大文件复制,如果所有数据都要经过CPU,实在有点太浪费时间
- 因此发明了DMA技术,即直接内存访问(Direct Memory Access),来减少CPU等待的时间
协处理器
- 本质上,DMA技术就是在主板上一块独立的芯片
- 在进行内存和IO设备的数据传输的时候,不再通过CPU来传输数据
- 而直接通过DMA控制器(DMA Controller,DMAC),其实是一个协处理器(Co-Processor)
- DMAC最有价值的地方:当要传输的数据特别大,速度特别快,或者传输的数据特别小、速度特别慢的时候
- 用千兆网卡或者硬盘传输大量数据的时候,如果都用CPU来搬运的话,肯定忙不过来,可以选择DMAC
- 当数据传输很慢的时候,DMAC可以等数据到齐后,再发送信号,给到CPU去处理,而不是让CPU忙等待
- DMAC是在协助CPU,完成对应的数据传输工作,在DMAC控制数据传输的过程中,还是需要CPU介入的
主设备 + 从设备
- DMAC其实是一个特殊的IO设备,通过连接到总线上来进行实际的数据传输
- 总线上的设备,有两种类型,一种称之为主设备(Master),另一种,称之为从设备(Slave)
- 想要主动发起数据传输,必须是一个主设备才可以,CPU是一个主设备
- 从设备(如硬盘)只能接受数据传输
- 因此,如果通过CPU来传输数据,要么CPU从IO设备读数据,要么是CPU向IO设备写数据
- IO设备只能向CPU发送控制信号,告诉CPU有数据要传输给他,实际数据是CPU去读取的,而不是IO设备推给CPU的
- DMAC即是一个主设备,也是一个从设备
- 对于CPU来说,它是一个从设备
- 对于硬盘来说,它是一个主设备
数据传输过程
- CPU作为一个主设备,通过总线,向DMAC设备(此时作为一个从设备)发起请求
- 该请求的作用:在DMAC里面修改配置寄存器
- CPU修改DMAC配置
- 源地址的初始值 + 传输时的地址递减方式
- 源地址是数据要从哪里传输过来
- 如果要从内存里面写入数据到硬盘上,那就是要读取的数据在内存里面的地址
- 如果要从硬盘读取数据到内存里,那就是硬盘的IO接口的地址
- IO的地址可以是一个内存地址,也可以是一个端口地址
- 地址递减方式:数据是从大的地址向小的地址传输,还是从小的地址向大的地址传输
- 目标地址初始值 + 传输时的地址递减方式
- 与源地址对应
- 要传输的数据长度
- 源地址的初始值 + 传输时的地址递减方式
- CPU设置完这些信息后,DMAC就会变成一个空闲(Idle)的状态
- 如果要从硬盘上往内存里面加载数据,此时,硬盘就会向DMAC发起一个数据传输请求
- 该请求并不是通过总线,而是通过一个额外的连线
- DMAC需要再通过一个额外的连线来响应这个申请
- DMAC(此时是主设备)向硬盘接口(从设备)发起要总线读的传输请求
- 数据就从硬盘里面读到DMAC的控制器里面
- 然后,DMAC(主设备)再向内存(从设备)发起总线写的数据传输请求,把数据写入到内存里
- DMAC会重复6、7步,直到DMAC的寄存器里面设置的数据长度传输完成
- 数据传输完成后,DMAC重新回到空闲状态
设备独立的DMAC
- 最早的计算机里面是没有DMAC的,所有数据都是由CPU来搬运的
- 随着对于数据传输的需求越来越多,先是出现在主板上独立的DMAC控制器
- 现在各个设备里面都有自己独立的的DMAC芯片了
Kafka的实现原理
- Kafka是一个用来处理实时数据的管道,常用来做一个消息队列,或者用来收集和落地海量日志
- 作为一个处理实时数据和日志的管道,瓶颈自然在IO层面
- Kafka里面会有两种常见的海量数据传输的情况
- 一种是从网络中接收上游的数据,然后需要落地到本地的磁盘上,确保数据不丢失
- 一种是从本地磁盘读取出来,通过网络发送出去
4次传输
1 | File.read(fileDesc, buf, len); |
- 第一次传输
- 通过DMA搬运:硬盘 -> 操作系统内核的读缓冲区
- 第二次传输
- 通过CPU搬运:内核的读缓冲区 -> 应用的内存
- 第三次传输
- 通过CPU搬运:应用的内存 -> 操作系统内核的Socket缓冲区
- 第四次传输
- 通过DMA搬运:内核的Socket缓冲区 -> 网卡的缓冲区
2次传输
1 |
|
- Kafka的代码调用了Java NIO库(
FileChannel#transferTo
) - 数据并没有复制到中间的应用内存里面,而是直接通过**
Channel
,写入到对应的网络设备**里 - 对于Socket的操作,也不是写入到Socket缓冲区里面,而是直接根据Socket描述符(Descriptor)写入到网卡的缓冲区里
- 具体过程
- 通过DMA搬运:硬盘 -> 操作系统内核的读缓冲区
- 通过DMA搬运:根据Socket的描述符信息,直接从内核的读缓冲区里面,写入到网卡的缓冲区里面
- 只有两次传输,只有DMA来进行数据搬运,并不需要CPU
- 没有在内存层面进行数据复制 -> 零拷贝
- 吞吐率提升:300%
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.