首页 科技 军事 财经 教育 体育 房产 健康 汽车 安全 热点 人才 推选

热点

旗下栏目:

零复制(zero copy)技术

发布时间:2019-08-12 来源:原创/投稿/转载 作者:admin 人气:

  网名骏马金龙,钟情于IT世界里的各种原理和实现机制,强迫症重症患者。爱研究、爱翻译、爱分享。特借此一亩三分田记录自己成长点滴!!!

  当httpd服务收到浏览器发送的index.html文件的请求时,负责处理请求的httpd子进程/线程总是会先发起系统调用,让内核将index.html从存储设备中加载出来。但是加载到的位置是内核空间的缓冲区kernel buffer,而不是直接给进程/线程的内存区。由于是内存设备和存储设备之间的数据传输,没有CPU的参与,所以这次是DMA操作。

  当数据准备好后,内核唤醒httpd子进程/线程,让它使用read()函数把数据复制到它自己的缓冲区,也就是图中的app buffer。到了app buffer中的数据,已经独属于进程/线程,也就可以对它做读取、修改等等操作。由于这次是使用CPU来复制的,所以会消耗CPU资源。由于这个阶段从内核空间切换到用户空间,所以进行了上下文切换。

  当数据修改完成(也可能没做任何操作)后,按我们所想的,需要把它响应给浏览器,也就是说要通过TCP连接传输出去。但TCP协议栈有自己的缓冲区,要通过它发送数据,必须将数据写到它的buffer中,对于发送者就是send buffer,对于接受者就是recv buffer。于是,通过write()函数将数据再次从app buffer复制到send buffer。这次也是CPU参与进行的复制,所以会消耗CPU。同样也会进行上下文切换。

  非本机数据最终还是会通过网卡传输出去的,所以再使用send()函数就可以将send buffer中的数据交给网卡并通过网卡传输出去。由于这次是内存和设备之间的数据传输,没有CPU的参与,所以这次也是DMA操作。

  当浏览器所在主机的网卡收到响应数据后(当然,数据是源源不断传输的),将它传输到TCP的recv buffer。这次是DMA操作。

  数据源源不断地填充到recv buffer中,但是浏览器却不一定会去读取,而是需要通知浏览器进程使用recv()函数将数据从read buffer中取走。这次是CPU操作(图中忘记标注了)。

  需要注意,对于httpd端来说,如果网速很慢,而httpd子进程/线程需要响应出去的数据又足够大(比send buffer还大),很可能会导致socket buffer填满的情况,这时write()函数会返回EWOULDBLOCK或EAGAIN,子进程/线程会进入等待状态。

  每次进程/线程需要一段数据时,总是先拷贝到kernel buffer,再拷贝到app buffer,再拷贝到socket buffer,最后再拷贝到网卡上。也就是说,总是会经过4段拷贝经历。

  但想想,正常情况下,数据从存储设备到kernel buffer是必须的,从socket buffer到NIC也是必须的,但是从kernel buffer到app buffer是必须的吗?进程一定需要访问、修改这些数据吗?不一定,甚至对于web服务来说,如果不是要修改http响应报文,数据完全可以不用经过用户空间。也就是不用再从kernel buffer拷贝到app buffer,这就是零复制的概念。

  零复制的概念是避免将数据在内核空间和用户空间进行拷贝。主要目的是减少不必要的拷贝,避免让CPU做大量的数据拷贝任务。

  注:上面只是说正常情况下,例如某些硬件可以完成TCP/IP协议栈的工作,数据可以不经过socket buffer,直接在app buffer和硬件之间传输数据,RDMA技术就是在此基础上实现的。

  mmap()函数将文件直接映射到用户程序的内存中,映射成功时返回指向目标区域的指针。这段内存空间可以用作进程间的共享内存空间,内核也可以直接操作这段空间。

  在映射文件之后,暂时不会拷贝任何数据到内存中,只有当访问这段内存时,发现没有数据,于是产生缺页访问,使用DMA操作将数据拷贝到这段空间中。可以直接将这段空间的数据拷贝到socket buffer中。所以也算是零复制技术。如图:

责任编辑:admin