linux 内核开发(零拷贝)

golliwog 2010-10-23 06:24:49
我正在写一个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网卡使用吗。


同学们一起交流,共同提高。

由于本人积分不多,所以希望大家谅解。功能完成后,分享期间我所遇到的困难以及解决问题的思路。
谢谢各位。
...全文
11236 193 1 打赏 收藏 举报
写回复
193 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
plauajoke 2013-04-30
楼主,请问你的这种方案实现了吗?
  • 打赏
  • 举报
回复
lf197804 2013-01-31
可以找www.greenet.net.cn,该公司提供了完整的高性能网络驱动解决方案,可以让程序员只专注于开发上层应用。而且该公司自己的产品在上海电信骨干网中实现了双万兆链路的串行部署。
  • 打赏
  • 举报
回复
tcprebuild 2012-08-10
这样的好贴一看就让人精神抖擞啊
  • 打赏
  • 举报
回复
arm123123 2012-06-08
http://linux.chinaunix.net/bbs/viewthread.php?tid=1161364&page=1
  • 打赏
  • 举报
回复
foiresdn 2011-08-30
学到不少东西,顶一下
  • 打赏
  • 举报
回复
liyj76 2011-01-20
学习了,支持楼主,希望有进一步的分享
  • 打赏
  • 举报
回复
flywitu 2010-11-18
学习了,最近搞linux驱动开发,很难啃啊
  • 打赏
  • 举报
回复
Jackword 2010-11-18
路过学习,顺便赚10 分
  • 打赏
  • 举报
回复
golliwog 2010-11-18
哈哈,搞定。

自己给自己设了个套。。这个先不解释,大家先自己琢磨一下。很简单。
  • 打赏
  • 举报
回复
手机写程序 2010-11-16
学习一下。
  • 打赏
  • 举报
回复
golliwog 2010-11-16
从e1000的寻址范围上看,不应该存在无法寻址的问题。会不会是地址对齐的原因呢?
  • 打赏
  • 举报
回复
golliwog 2010-11-16
呵呵,各位我回来了。又带了一个问题。关于内存预留的问题。

由于需要用到大量的缓存,如果使用kmalloc, vmalloc可能无法获得足够的内存。所以需要设定linux所能使用的内存,为我预留出足够的缓存给DMA使用。

内存预留很容易实现,首先修改linux 启动参数 vim /etc/grub.conf 咱kernel 一行后增加 mem=XXM。xxM就是linux要使用的内存大小。比如你的实际内存是1024M,你修改为mem=1000M。就是linux启动后只会使用1000M。剩下的24M就是给你预留的。

然后用ioremap函数,将这个预留出的内存映射到你的内核模块中就可以了。

我的问题是,这块预留出来的内存似乎无法被网卡设备使用。分析了一下,似乎是pci设备没有寻址到预留的内存。
然后看看e1000的驱动,发现e1000是支持64位寻址的。再查资料需要调用pci_alloc_consistent函数建立一致性映射。可是这个函数的原型是void *
pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp)
注意第3个参数。这个参数我改怎么获得。不知道怎么用。查到的资料都是零零散散,上下不一致。

预留出来的内存怎么能够被DMA设备识别,使用呢?
  • 打赏
  • 举报
回复
doc110 2010-11-13
都是高手,我得好好学习。
  • 打赏
  • 举报
回复
huangjiu1990 2010-10-31
每天回帖即可获得10分可用分!小技巧:教您如何更快获得可用分
  • 打赏
  • 举报
回复
伴儿 2010-10-31
学习
学习
  • 打赏
  • 举报
回复
scuzhangdi 2010-10-31
学习下。。。。
  • 打赏
  • 举报
回复
qiuyuloveyou 2010-10-31
学习了
  • 打赏
  • 举报
回复
lfwalkman 2010-10-30

1、内核在skb的设计上已经非常精巧高效,要相信内核;

不是不相信内核,而是有些情况下无法在内核下完成任务,尤其是7层处理的时候,处理策略太过灵活,内核模式难实现。
考虑稳定性,挂一个应用层进程总比挂了内核安全。
考虑团队成员,不是所有成员都会内核模式开发。

2、相信内核,并且用好内核;不要试图与内核抢skb;也不要以为设计个“缓存管理器”来代替skb;内核slab的管理考虑到了各种CPU的Cache特性并加以利用;
自己设计的缓存管理器能够针对自己的应用、数据、硬件做一些处理,效率比通用的slab高。

3、USER<->KERNEL的切换是非常费时的,并且是非常浪费CPU资源及内存带宽的(如果切换一次只是为了获取4KB的网络数据,就是拣了芝麻丢了西瓜的事情);
数据用零拷贝到达应用层,更是为了减少USER<->KERNEL 的切换。这个时候获取数据不需要context switch。

4、跨地址空间的共享内存,您是否需要使能Cache?不用的话简单一点但低效;用的话复杂且低效;同步一段内存的Cache也要花时间,如果是嵌入式CPU更是低效,甚至比失效全部cache慢几百倍;
这个在x86上没有问题,因为x86按照物理地址处理cache。

5、现代CPU上,拷贝真的很花时间么?尤其是这样的小块内存,运行时Cache命中率极高的;高级CPU上尤其如此;
在共享cache 之间cpu 核之间拷贝数据,效率很高。
但是如果系统中有2个以上的物理CPU,在这两个CPU之间拷贝,数据就非常慢了。

6、一定要优化,可以看内存拷贝部分是否可以优化,可以使用一些CPU相关的特殊指令以加速拷贝;部分嵌入式CPU拥有CPU DMA机制,但优化起来也有一定难度,需要考虑的状况较多;动了内核,测试起来也是一大难题;
不管是内核还是应用层的memcpy,基本很难优化了,都针对CPU做了处理。
  • 打赏
  • 举报
回复
liyutao_54 2010-10-30
。。。。。。。。。。。。。。。。。。。。。。。。。。
  • 打赏
  • 举报
回复
golliwog 2010-10-29
[Quote=引用 157 楼 lujing9101 的回复:]

没有楼主说的这么复杂,我做过1.5G的7层处理的防火墙,百万session的。
[/Quote]

要处理标准链路,2.5G的。
  • 打赏
  • 举报
回复
加载更多回复
相关推荐
发帖
Linux_Kernel
创建于2007-08-27

4156

社区成员

Linux/Unix社区 内核源代码研究区
申请成为版主
帖子事件
创建了帖子
2010-10-23 06:24
社区公告
暂无公告