场景:文件读出数据,传到另一台服务器上
伪代码 File.read(file, buf, len); Socket.send(socket, buf, len); 4次数据拷贝,用户态和内核态区别
1、read()一次上下文切换(用户态->内核态),底层用DMA(direct memory access)读磁盘文件,存储到内核地址空间的读取缓存区
2、程序无法访问内核地址空间数据,从读取拷贝到用户缓冲区。第二次切换(内核态->用户态),可修改数据。
3、终目通过Socket传到另一个服务,send()第三次切换(用户态->内核态),缓冲区与目标套接字相关联,与读取缓冲区无关。
4、send()返回,第四次切换,DMA把数据从目标套接字相关的缓存区传到协议引擎进行发送。
1和4是由DMA负责,不会消耗CPU,只有过程2和3的拷贝需要CPU参与
好几次拷贝多余,影响性能
一、零拷贝优化
不需要操作内容,2和3多余,直接把内核态读取缓存冲区数据直接拷贝到套接字相关的缓存区
切换四次减到两次,拷贝四次减到三(其中DMA copy 2次,CPU copy 1次)
FileChannel的transferTo() 可以实现,数据从文件通道传输到给定可写字节通道, file.read()和socket.send()替换为transferTo()调用"
public void transferTo(long position, long count, WritableByteChannel target);
但不是零拷贝(一次cpu)
二、零拷贝实现
1、transferTo() 用 DMA 将文件拷贝到内核读取缓冲区。
2、避免内容整体拷贝,只位置和长度追加到套接字缓冲区,DMA 引擎直接把数据从内核缓冲区传到协议引擎,消除最后一次 CPU拷贝
ps:面试从用户态谈到内核态,socket谈到FileChannel,从NIO谈到Netty,从直接内存到CompositeBy
网友评论