Linux 的零拷贝技术

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Jmilk/article/details/98949655

目录

Linux I/O 缓存背景

当请求文件服务器的下载功能时,服务端程序所做的事情是:将服务器磁盘中的文件不做修改地从已连接的 Socket 发送到客户端,通常使用下面的代码完成:

while ((n = read(diskfd, buf, BUF_SIZE)) > 0)
    write(sockfd, buf , n);

该程序的基本操作就是循环的从磁盘读入文件内容到缓冲区,再将缓冲区的内容发送到 Socket。Linux I/O 操作默认是缓冲 I/O,主要使用了 read()write() 这两个系统调用。当应用程序访问某块数据时,操作系统首先会检查最近是否访问过此系统,文件内容是否缓存在了内核缓冲区。如果是,操作系统则直接根据 read() 传入的 buf 地址,将内核缓冲区中的内容拷贝到 buf 地址所指向的用户空间缓冲区中;如果不是,操作系统则首先将磁盘上的数据拷贝的内核缓冲区,这一步主要依靠 DMA(直接存储器存取)来传输,继而再把内核缓冲区上的内容拷贝到用户空间缓冲区。接下来,write() 再把用户缓冲区的内容拷贝到网络堆栈相关的内核缓冲区中,最后 Socket 再把内核缓冲区的内容发送到网卡上。

可见,上述简单的 I/O 操作,实际上发生了多次的数据拷贝。与此同时,在用户态、内核态运行模式的切换也会产生多次 CPU 上下文切换,无疑也加重了 CPU 的性能损耗。
在这里插入图片描述
问题的关键在于,在文件下载的过程中,我们并不需要对文件的内容做任何修改,那么这种缓存式的 I/O 操作就显得非常的无谓了。零拷贝技术主要就是为了解决这种低效性。

零拷贝技术(Zero-Copy)

零拷贝是一种数据传输的优化思想,主要任务是避免进行不必要的数据拷贝动作。主要的优化思路有两类:

  • 针对特定应用场景,去除完成不必要的拷贝。
  • 优化拷贝过程,例如:让别的组件来承接这一类简单的数据传输任务,继而释放 CPU,让系统资源的利用更加有效。

参考文章

https://mp.weixin.qq.com/s/3H26Yqa8pp5g3BYEGgaAXw
https://mp.weixin.qq.com/s/GtNrVAvqsnzYSoGiqoI-0A

展开阅读全文

linux 内核开发(零拷贝

10-23

我正在写一个linux网卡零拷贝的驱动,是在intel e1000驱动基础之上改动。rnrn查了查资料已经搞清楚DMA是如何运作,而且也知道如何使DMA将数据直接写入自己申请的内存空间。已经有了一个大致的解决方案,但有以下下几个细节搞不定,请同学们帮帮。rnrn我先说下我的思路:rn首先从用户太说起,数据包从经数据线至网卡驱动至协议栈最后至用户太要经过至少两次拷贝才能完成,期间cpu参与过多,造成cpu负担过重,效率低下。rnrn从e1000的驱动看到:pci_map_single()这个函数完成了将内核内存映射到DMA设备的过程。我所做的就是将pci_mag_single()映射的内存替换成我申请的内存。这样DMA设备就把数据直接写到指定的内存了,用户台进程读这块内存就OK了。其中没有cpu的参与。rnrn如何替换呢?写过内核的同学们都知道skb buffer这个东西。当网卡驱动被初始化的时候,会netdev_alloc_skb这个系统函数,预先申请一堆的skb buffer。rnrn从源码中看出,申请过程分两个过程,首先申请一个skb buffer结构。skb buffer中有一个指针类型的成员变量data,此时的data是没有指向任何有效数据块的。然后在用kmalloc()申请一块内存,赋给skb->data。网卡驱动就是用pci_map_single()将skb->data映射入DMA设备。然后DMA设备会将收到的数据直接写入skb->data指向的内存区。rnrn所以我们只需增加一个内存分配模块,替换网卡驱动skb buffer的分配函数即可。那是DMA设备会自动的将收到的数据写入我们指定的内存中。rn以上是我的思路。不对的地方请指正。rnrn下面是我的问题。rnDMA将数据写入我们分配的内存后,就需要通知用户态的进程来读取了。问题如下:rnrn1.用户态如何读取这部分内存。rn我查了查资料,有两种方式可以完成。a.使用proc的方式。b.使用ioctrl的方式。由于数据量过大,我希望采用proc的方式。但是对于proc的方式我很不理解。从资料中看到的是在驱动初始化时使用create_proc_entry这个函数,在/proc/目录下建立一个虚拟文件。用户态进程读这个虚拟文件就行了。rnrnstruct proc_dir_entry *create_proc_entry(constchar*name, mode_t mode,struct proc_dir_entry *parent)rnrn这个函数会在/proc目录下建立一个文件,OK,没有问题。然后呢,用户态的程序去读该文件,读什么呢,文件建立后里面并没有写入任何有用的东西。我就猜也许是要我自己把我申请的物理内存首地址以及内存大小写入到这个文件中吧。用户态读取文件中的物理地址,使用mmap将物理内存地址映射入自己的进程空间。rnrn这个想发不知对不对。如果不对,请同学给我指明一个思路,谢谢。rnrn2.将内核空间成功映射入用户态进程空间后,网卡驱动如何同用户态的进程同步。rn 我是这样实现的,我在内核申请了块内存,作为队列。接收过来的数据会被放入队列中。驱动和用户态进程肯定要竞争这个队列,如何同步呢。rnrn3.内核中的内存形式。rn 用户态进程都有自己的虚拟空间,用户态进程是否有类似的概念呢。还是说内核空间就是被所有内核模块,内核进程所共享的。我看得书中并没有类似概念的说明,请同学们给我扫扫盲。rnrn4.一台机器上会有多块型号相同的网卡,那么这些网卡会使用同样的驱动。rn比如有a,b,c三块网卡,型号相同。rna网卡首先被激活,申请一块内存。然后b,c网卡也被激活。a网卡申请的内存会被b.c网卡使用吗。rnrnrn同学们一起交流,共同提高。rnrn由于本人积分不多,所以希望大家谅解。功能完成后,分享期间我所遇到的困难以及解决问题的思路。rn谢谢各位。 论坛

没有更多推荐了,返回首页