零拷贝
操作系统分为用户空间和内核空间。程序处于用户空间,而磁盘属于硬件,操作系统本质上是程序和硬件设备的一个中间层。程序需要通过操作系统去调用硬件能力。
如果用户想要将数据从磁盘发送到网络。那么就会发生下面这几件事:程序会发起系统调用
read()
,尝试读取磁盘数据,- 磁盘数据从设备拷贝到内核空间的缓冲区。
- 再从内核空间的缓冲区拷贝到用户空间。
程序再发起系统调用
write()
,将读到的数据发到网络:- 数据从用户空间拷贝到 socket 发送缓冲区
- 再从 socket 发送缓冲区拷贝到网卡。
最终数据就会经过网络到达消费者。
整个过程,本机内发生了
2
次系统调用,对应 4
次用户空间和内核空间的切换,以及 4
次数据拷贝。一顿操作猛如虎,结果就是同样一份数据来回拷贝。有没有办法优化呢?有,它就是零拷贝技术,常见的方案有两种,分别是
mmap
和 sendfile
。我们来看下它们是什么。mmap 是什么
mmap
是操作系统内核提供的一个方法,可以将内核空间的缓冲区映射到用户空间。用了它,整个发送流程就有了一些变化。程序发起系统调用
mmap()
,尝试读取磁盘数据,具体情况如下:- 磁盘数据从设备拷贝到内核空间的缓冲区。
- 内核空间的缓冲区映射到用户空间,这里不需要拷贝。
程序再发起系统调用
write()
,将读到的数据发到网络:- 数据从内核空间缓冲区拷贝到 socket 发送缓冲区。
- 再从 socket 发送缓冲区拷贝到网卡。
整个过程,发生了
2
次系统调用,对应 4
次用户空间和内核空间的切换,以及 3
次数据拷贝,对比之前,省下一次内核空间到用户空间的拷贝。看到这里大家估计也蒙了,不是说零拷贝吗?怎么还有 3 次拷贝。mmap 作为一种零拷贝技术,指的是用户空间到内核空间这个过程不需要拷贝,而不是指数据从磁盘到发送到网卡这个过程零拷贝。
确实省了一点,但不多。有没有更彻底的零拷贝?有,用
sendfile
.sendfile 是什么
sendfile
,也是内核提供的一个方法,从名字可以看出,就是用来发送文件数据的。程序发起系统调用sendfile()
,内核会尝试读取磁盘数据然后发送,具体情况如下:- 磁盘数据从设备拷贝到内核空间的缓冲区。
- 内核空间缓冲区里的数据可以直接拷贝到网卡。
整个过程,发生了
1
次系统调用,对应 2
次用户空间和内核空间的切换,以及 2
次数据拷贝。这时候问题很多的小明就有意见了,说好的零拷贝怎么还有 2
次拷贝?其实,这里的零拷贝指的是零 CPU拷贝。也就是说 sendfile 场景下,需要的两次拷贝,都不是 CPU 直接参与的拷贝,而是其他硬件设备技术做的拷贝,不耽误我们 CPU 跑程序。
Loading...