linux-零拷贝技术

content:

  • 1.0 用户态内核态之间切换
  • 2.0 文件发送所经历的文件拷贝
  • 3.0 文件发送经历的文件拷贝(优化)----零拷贝
  • 4.0 零拷贝使用场景----kafka              

用户态内核态之间切换

用户态切换至内核态出现场景:系统调用/中断处理/异常处理

用户态切换至内核态原因:用户态下无法访问或者处理内核地址空间下的数据,用户空间程序0-3G无法执行内核代码,不能直接调用内核函数(内核函数驻留在受保护的地址空间上)。

切换过程:

a) 触发int &0x80(int 软中断指令、0x80立即数参数)

b) 切换内核态并执行128号异常中断程序。

c) 128号异常终端程序用来执行系统调用,具体实现:system_call()使用具体的系统调用(利用系统调用号),通过eax寄存器传递系统调用号至内核,使用ebx、ecx、edx、esi、edi等寄存器传递参数,超过5个参数时创建堆栈,用独立的寄存器存储该堆栈地址,将参数传递至内核。

文件传输所经历的文件拷贝次数

                                                linux-零拷贝技术

第一次:系统调用read方法(用户态---->内核态),底层使用DMA读取磁盘文件并存储至内核地址空间读取缓冲区。

第二次:因应用程序无法读取受保护的内核地址空间数据,所以将该数据拷贝至用户地址空间(内核态---->用户态)缓冲区,read调用返回。

第三次:调用socket的send方法(用户态---->内核态),数据拷贝至内核地址空间(非第一次的读取缓冲区)。

第四次:send调用返回(内核态---->用户态),通过DMA把data从目标套接字对应的缓冲区传递至网卡接口。

文件发送经历的文件拷贝(优化)----零拷贝

优化2:以上为传统的文件传输的方式,第2、3步拷贝是不是可以考虑去除

linux-零拷贝技术
@author whiskycocktail

                                                        

图内的三次文件拷贝可以利用Java里的transferTo实现内核文件之间的拷贝。

优化3:去除第2步,改为以标记的形式告诉第三次拷贝时文件在内核地址空间read buffer里的数据位置、长度等信息而避免内核里的拷贝,即为零拷贝。

貌似没多大作用吧!秉持着存在即合理心态去看了看,果然。。。。

零拷贝使用场景----kafka

中间件了解下,Apache kafka为其中之一,生产者1秒钟生产100个鸡蛋,消费者1秒钟只能吃50个鸡蛋,那要不了一会,消费者就吃不消了(消息堵塞,最终导致系统超时),消费者拒绝再吃了,”鸡蛋“又丢失了,这个时候我们放个篮子在它们中间,生产出来的鸡蛋都放到篮子里,消费者去篮子里拿鸡蛋,这样鸡蛋就不会丢失了,都在篮子里,而这个篮子就是”kafka“。
鸡蛋其实就是“数据流”,系统之间的交互都是通过“数据流”来传输的(就是tcp、http什么的),也称为报文,也叫“消息”。
消息队列满了,其实就是篮子满了,”鸡蛋“ 放不下了,那赶紧多放几个篮子,其实就是kafka的扩容。

kafka中的消费者在读取服务端的数据时,需要将服务端的磁盘文件通过网络发送到消费者进程,网络发送需要经过几种网络节点。

linux-零拷贝技术

通常情况下,Kafka的消息会有多个订阅者,生产者发布的消息会被不同的消费者多次消费,为了优化这个流程,Kafka使用了“零拷贝技术”,如下图所示:

linux-零拷贝技术

“零拷贝技术”只用将磁盘文件的数据复制到内核缓存区中一次,然后将数据从内核缓冲区直接发送到网络中(发送给不同的订阅者时,都可以使用同一块内核缓存区),避免了重复复制操作。

如果有10个消费者,传统方式下,数据复制次数为4*10=40次,而使用“零拷贝技术”只需要1+10=11次,一次为从磁盘复制到内核缓存区,10次表示10个消费者各自读取一次页面缓存。