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