决战圣地玛丽乔亚Day12--零拷贝02

对于上次提到的mmap+write做一个总结

mmap用到的特性是虚拟内存。

 

 

 

操作:

1.DMA把磁盘内容copy到内核缓冲区。

2.通过虚拟空间映射的方式,用户缓冲区和内核缓冲区共享,减少了一次内核缓冲区到用户缓冲区的cpu拷贝。直接把内核缓冲区数据拷贝到socket缓冲区中,这个过程发生在内核态。

但是由于mmap和write是两个系统调用,还是四次上下文切换,三次拷贝。

分别是:

1.用户调用mmap函数,用户态-->内核态(第一次切换

DMA控制器把磁盘数据拷贝到内核缓冲区(第一次拷贝

2.mmap函数返回,内核态—>用户态(第二次切换)、

3.用户发起write函数,用户态-->内核态(第三次切换

(省略:内核态--->用户态)

CPU把内核缓冲区的数据copy到socket缓冲区(第二次拷贝

4.DMA控制器把socket缓冲区数据拷贝到网卡(第三次拷贝

5.write函数调用返回,内核态-->用户态(第四次切换

 

相比传统模式,仅仅是通过虚拟内存的应用省了一步把数据从内核态copy到用户态的cpu拷贝步骤。但是仍然不够,进一步优化!

sendfile:

Linux 2.1

sendfile简单来说就是对mmap+wirte进行了综合,两个系统调用变成了一个,上下文切换自然少了两次。我们直接用sendfile函数就可以代替mmap+write

sendfile方法IO数据对用户空间完全不可见,所以只能适用于完全不需要用户空间处理的情况,比如静态文件服务器。

 

Linux 2.4

在sendfile的基础上进行了优化操作,DMA Scatter/Gather 分散/收集功能。

直接把读缓存中的内存地址和偏移量交给socket,DMA直接根据socket记录的数值读取到网卡中。少了一次操作。

优化成两次上下文切换和两次拷贝操作。

 

Linux 2.6.17

splice

splice调用在两个文件描述符之间移动数据,而不需要数据在内核空间和用户空间来回拷贝

他从fd_in拷贝len长度的数据到fd_out,但是有一方必须是管道设备,这也是目前splice的一些局限性。splice调用利用了Linux提出的管道缓冲区机制, 所以至少一个描述符要为管道。

 

关于MQ和零拷贝:

消息队列使用零拷贝的场景就是发送和接受消息。生产者发送消息到MQ,然后持久化到磁盘。消费者从MQ读取消息。

RocketMQ:生产和消费都用的mmap+write

Kafka:生产数据持久化到磁盘用的mmap+write,发送数据用sendfile

 

总结:

由于CPU和IO速度的差异问题,产生了DMA技术,通过DMA搬运来减少CPU的等待时间。

传统的IO read+write方式会产生2次DMA拷贝+2次CPU拷贝,同时有4次上下文切换。

而通过mmap+write方式则产生2次DMA拷贝+1次CPU拷贝,4次上下文切换,通过内存映射减少了一次CPU拷贝,可以减少内存使用,适合大文件的传输。

sendfile方式是新增的一个系统调用函数,产生2次DMA拷贝+1次CPU拷贝,但是只有2次上下文切换。因为只有一次调用,减少了上下文的切换,但是用户空间对IO数据不可见,适用于静态文件服务器。

sendfile+DMA gather方式产生2次DMA拷贝,没有CPU拷贝,而且也只有2次上下文切换。虽然极大地提升了性能,但是需要依赖新的硬件设备支持。

 

posted @   NobodyHero  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示