零拷贝
- 一次系统调用涉及两次上下文切换
- DMA 负责将数据从磁盘拷贝到 page Cache,不需要cup参与,此时cpu可以做别的事情
- 传统的拷贝调用read() write()方法,涉及4次上下文切换
- 传统的拷贝(数据从磁盘到网络) 数据流转:磁盘——>page Cache(内核空间缓存) ——>用户空间内存 ——> socket缓存 ——>网卡 一个4次数据拷贝
- 零拷贝有两种实现方式:
* mmap + write()
* sendfile()
- mmap 是减少一次数据从 page Cache到用户空间的复制
- sendfile() 把两次系统调用变成一次,数据复制从4次变成2次,具体数据拷贝路径为: 磁盘——>page Cache ——> 网卡,因此理论上零拷贝性能提升50%
- 大文件传输时,数据占用page Cache但是由于收益很低,因此性能不佳,可采用「异步 I/O + 直接 I/O」,
- 传输小文件的时候,则使用「零拷贝技术」
mmap
1、 进程在MMAP区域进行读写都是纯内存操作,所以无须进行系统调用(不需要进内核态);
2、当进程未载入MMAP相应的内存页时,则会引发缺页异常,从而将对应内存页面载入当前进程的页表中;
3、当该部分区域的内存页存在于物理内存时,两个进程的写操作都会直接体现在物理内存
mmap内存映射的实现过程,总的来说可以分为三个阶段:
- 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
- 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
- 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝
- open一个文件,然后调用mmap系统调用,将文件的内容的全部或一部分直接映射到进程的地址空间,映射完成后,进程可以像访问普通内存一样做其他的操作
- 内核将文件的这一页数据读入到内核高速缓冲区中,并更新进程的页表,使页表指向内核缓冲中的这一页。之后有其他的进程再次访问这一页的时候,该页已经在内存中了,内核只需要将进程的页表登记并且指向内核的页高速缓冲区即可