网络IO 零拷贝解析

本文深入探讨了传统网络IO和Zero-Copy网络IO的工作原理,分析了数据从磁盘传输到socket的不同方式,包括四次数据拷贝和四次上下文切换的传统方式,以及减少至两次上下文切换和两次数据拷贝的Zero-Copy方式。文章详细解释了Linux内核中sendfile函数的演进,从Linux2.1到Linux2.4版本,如何逐步优化数据传输流程,提高应用程序性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、传统网络IO(read/write方式)

场景:将一张本地图片展示给用户,首先将本地图片从磁盘中拷贝出来放到内存buffer中,然后将这个buffer通过socket传递给用户,这个过程抽象成下面的过程:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

首先调用read将本地图片,读取到jvm内存中,然后调用write将jvm内存中的数据写道socket中,如图:

在这里插入图片描述

kernel模式user模式上下文切换:四次

数据拷贝:四次

  1. 首先,调用read时,本地图片从DMA拷贝到了kernel模式的 kernel buffer中
  2. 然后,CPU控制将kernel buffer中的数据拷贝到了user模式的 user buffer中
  3. 之后,调用write时,将user buffer中的数据拷贝到kernel模式下的socket buffer中
  4. 最后,将socket buffer中的数据拷贝到网卡设备protocol engine中传输

数据从kernel模式拷贝到user模式又拷贝回kernel再拷贝到网卡设备传输,浪费了2次拷贝;

应用程序用Zero-Copy来请求kernel直接把disk的data传输给socket,而不是通过应用程序传输。Zero-Copy大大提高了应用程序的性能,并且减少了kernel和user模式上下文的切换。

二、Zero-Copy网络IO

Zero-Copy直接将kernel buffer拷贝到socket buffer,省去了将kernel buffer拷贝到user buffer然后从user buffer 拷贝到socket buffer的过程,将整个过程的数据拷贝变为三次,并且减少了上下文切换

Java NIO中通过FileChannel.transferTo()方法依赖于操作系统底层的sendFile()实现Zero-Copy

//Java NIO 实现 Zero-Copy 方法
public void transferTo(long position, long count, WritableByteChannel target);

//transferTo底层调用方法sendfile(),这个系统内核调用本身被设计出来是用来从磁盘到TCP协议栈拷贝数据用的,但也我们也是可以把它用来做两个文件之间的数据拷贝。
#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
//in_fd 被打开是等待读数据的fd.
//out_fd 被打开是等待写数据的fd.
//Offset 是在正式开始读取数据之前应该向前偏移的byte数.
//count 是需要在两个fd之间“搬移”的数据的byte数.

三、Linux2.1内核开始的sendfile函数

sendfile(socket, file, len);

在这里插入图片描述

kernel模式user模式上下文切换:二次

数据拷贝:三次

  1. 首先,将本地图片从DMA拷贝到了kernel模式的 kernel buffer中
  2. 然后,CPU控制将kernel buffer中的数据拷贝到kernel模式下的socket buffer中
  3. 最后,将socket buffer中的数据拷贝到网卡设备protocol engine中传输

sendFile和read/write方式相比,只需要两次上下文切换,并且不需要将kernel buffer的数据拷贝到user buffer减少了一次数据拷贝。

但是Zero-Copy更理想的状态是直接从kernel buffer中获取数据,而不是从socket buffer,这样可以再减少一次数据拷贝

四、Linux2.4内核开始的sendfile函数

Linux2.4内核对sendfile做了改进

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)

在这里插入图片描述

kernel模式user模式上下文切换:二次

数据拷贝:二次

  1. 首先,将本地图片从DMA拷贝到了kernel模式的 kernel buffer
  2. 然后,向socket buffer追加当前要使用的数据在kernel buffer中的位置偏移量
  3. 最后,根据socket buffer中的位置偏移量直接将kernel buffer中的数据拷贝到网卡设备protocol engine中传输

升级后,数据经过两次拷贝就从磁盘中传输出去了,这就是目前java nio中TransferTo()的实现原理;

Zero-Copy技术已经应用于kafka、netty中等;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值