高性能网络I/O框架-netmap源码分析
from:http://blog.chinaunix.net/uid-23629988-id-3594118.html
博主这篇文章写的很好 感觉很有借签意义 值得阅读
高性能网络I/O框架-netmap源码分析(1)
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net
微博:weibo.com/glinuxer
QQ技术群:4367710
前几天听一个朋友提到这个netmap,看了它的介绍和设计,确实是个好东西。其设计思想与业界不谋而合——因为为了提高性能,几个性能瓶颈放在那里,解决方法自然也是类似的。
由于保密性,我是不可能拿公司的设计来和大家讨论和分享的。而netmap的出现,提供了这么一个好机会。它既实现了一个高性能的网络I/O框架,代码量又不算大,非常适合学习和研究。
netmap简单介绍
首先要感谢netmap的作者,创造出了netmap并无私的分享了他的设计和代码。netmap的文档写得很不错,这里我简单说明一下为什么netmap可以达到高性能。
1. 利用mmap,将网卡驱动的ring内存空间映射到用户空间。这样用户态可以直接访问到原始的数据包,避免了内核和用户态的两次拷贝;——前两天我还想写这么一个东西呢。
2. 利用预先分配的固定大小的buff来保存数据包。这样减少了内核原有的动态分配;——对于网络设备来说,固定大小的内存池比buddy要有效的多。之前我跟Bean_lee也提过此事呵。
3. 批量处理数据包。这样就减少了系统调用;
更具体的内容,大家直接去netmap的官方网站上看吧,写得很详细。虽然英文,大家还是耐着性子好好看看,收获良多。
netmap的源码分析
从上面netmap的简单介绍中可以看到,netmap不可避免的要修改网卡驱动。不过这个修改量很小。
驱动的修改
下面我以e1000.c为例来分析。由于netmap最早是在FreeBSD上实现的,为了在linux达到最小的修改,使用了大量的宏,这给代码的阅读带来了一些困难。
e1000_probe的修改
俺不是写驱动的。。。e1000_probe里面很多代码看不明白,但是不影响我们对netmap的分析。通过netmap的patch,知道是在e1000完成一系列硬件初始化以后,并注册成功,这时调用e1000_netmap_attach
@@ -1175,6 +1183,10 @@ static int __devinit e1000_probe(struct
if (err)
goto err_register; +#ifdef DEV_NETMAP
+ e1000_netmap_attach(adapter);
+#endif /* DEV_NETMAP */
+ /* print bus type/speed/width info */
e_info(probe, "(PCI%s:%dMHz:%d-bit) %pM\n",
((hw->bus_type == e1000_bus_type_pcix) ? "-X" : ""),
下面是e1000_netmap_attach的代码
static void
e1000_netmap_attach(struct SOFTC_T *adapter)
{
struct netmap_adapter na;
bzero(&na, sizeof(na)); na.ifp = adapter->netdev;
na.separate_locks = 0;
na.num_tx_desc = adapter->tx_ring[0].count;
na.num_rx_desc = adapter->rx_ring[0].count;
na.nm_register = e1000_netmap_reg;
na.nm_txsync = e1000_netmap_txsync;
na.nm_rxsync = e1000_netmap_rxsync;
netmap_attach(&na, 1);
}
SOFTC_T是一个宏定义,对于e1000,实际上是e1000_adapter,即e1000网卡驱动对应的private data。 下面是struct netmap_adapter的定义
/*
* This struct extends the 'struct adapter' (or
* equivalent) device descriptor. It contains all fields needed to
* support netmap operation.
*/
struct netmap_adapter {
/*
* On linux we do not have a good way to tell if an interface
* is netmap-capable. So we use the following trick:
* NA(ifp) points here, and the first entry (which hopefully
* always exists and is at least 32 bits) contains a magic
* value which we can use to detect that the interface is good.
*/
uint32_t magic;
uint32_t na_flags; /* future place for IFCAP_NETMAP */
int refcount; /* number of user-space descriptors using this
interface, which is equal to the number of
struct netmap_if objs in the mapped region. */
/*
* The selwakeup in the interrupt thread can use per-ring
* and/or global wait queues. We track how many clients
* of each type we have so we can optimize the drivers,
* and especially avoid huge contention on the locks.
*/
int na_single; /* threads attached to a single hw queue */
int na_multi; /* threads attached to multiple hw queues */ int separate_locks; /* set if the interface suports different
locks for rx, tx and core. */ u_int num_rx_rings; /* number of adapter receive rings */
u_int num_tx_rings; /* number of adapter transmit rings */ u_int num_tx_desc; /* number of descriptor in each queue */
u_int num_rx_desc; /* tx_rings and rx_rings are private but allocated
* as a contiguous chunk of memory. Each array has
* N+1 entries, for the adapter queues and for the host queue.
*/
struct netmap_kring *tx_rings; /* array of TX rings. */
struct netmap_kring *rx_rings; /* array of RX rings. */ NM_SELINFO_T tx_si, rx_si; /* global wait queues */ /* copy of if_qflush and if_transmit pointers, to intercept
* packets from the network stack when netmap is active.
*/
int (*if_transmit)(struct ifnet *, struct mbuf *); /* references to the ifnet and device routines, used by
* the generic netmap functions.
*/
struct ifnet *ifp; /* adapter is ifp->if_softc */ NM_LOCK_T core_lock; /* used if no device lock available */ int (*nm_register)(struct ifnet *, int onoff);
void (*nm_lock)(struct ifnet *, int what, u_int ringid);
int (*nm_txsync)(struct ifnet *, u_int ring, int lock);
int (*nm_rxsync)(struct ifnet *, u_int ring, int lock); int bdg_port;
#ifdef linux
struct net_device_ops nm_ndo;
int if_refcount; // XXX additions for bridge
#endif /* linux */
};
从struct netmap_adapter可以看出,netmap的注释是相当详细。所以后面,我不再列出netmap的结构体定义,大家可以自己查看,免得满篇全是代码。————这样的注释,有几个公司能够做到?
e1000_netmap_attach完成简单的初始化工作以后,调用netmap_attach执行真正的attach工作。前者是完成与具体驱动相关的attach工作或者说是准备工作,而后者则是真正的attach。
int
netmap_attach(struct netmap_adapter *na, int num_queues)
{
int n, size;
void *buf;
/* 这里ifnet又是一个宏,linux下ifnet实际上是net_device */
struct ifnet *ifp = na->ifp; if (ifp == NULL) {
D("ifp not set, giving up");
return EINVAL;
}
/* clear other fields ? */
na->refcount = 0;
/* 初始化接收和发送ring */
if (na->num_tx_rings == 0)
na->num_tx_rings = num_queues;
na->num_rx_rings = num_queues;
/* on each direction we have N+1 resources
* 0..n-1 are the hardware rings
* n is the ring attached to the stack.
*/
/*
这么详细的注释。。。还用得着我说吗?
0到n-1的ring是用于转发的ring,而n是本机协议栈的队列
n+1为哨兵位置
*/
n = na->num_rx_rings + na->num_tx_rings + 2;
/* netmap_adapter与其ring统一申请内存 */
size = sizeof(*na) + n * sizeof(struct netmap_kring); /*
这里的malloc,实际上为kmalloc。
这里还有一个小trick。M_DEVBUF,M_NOWAIT和M_ZERO都是FreeBSD的定义。那么在linux下怎么使用呢?
我开始以为其被定义为linux对应的flag,如GFP_ATOMIC和__GFP_ZERO,于是grep了M_NOWAIT,也没有找到任何的宏定义。
正在奇怪的时候,想到一种情况。让我们看看malloc的宏定义 /* use volatile to fix a probable compiler error on 2.6.25 */
#define malloc(_size, type, flags) \
({ volatile int _v = _size; kmalloc(_v, GFP_ATOMIC | __GFP_ZERO); })
这里type和flags完全没有任何引用的地方。所以在linux下,上面的M_DEVBUG实际上直接被忽略掉了。
*/
buf = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
if (buf) {
/* Linux下重用了struct net_device->ax25_ptr,用其保存buf的地址 */
WNA(ifp) = buf;
/* 初始化tx_rings和rx_rings,tx_rings和rx_rings之间用了一个额外的ring分隔,目前不知道这个ring是哨兵呢,还是本主机的ring */
na->tx_rings = (void *)((char *)buf + sizeof(*na));
na->rx_rings = na->tx_rings + na->num_tx_rings + 1;
/* 复制netmap_device并设置对应的标志位,用于表示其为netmap_device*/
bcopy(na, buf, sizeof(*na));
NETMAP_SET_CAPABLE(ifp); na = buf;
/* Core lock initialized here. Others are initialized after
* netmap_if_new.
*/
mtx_init(&na->core_lock, "netmap core lock", MTX_NETWORK_LOCK,
MTX_DEF);
if (na->nm_lock == NULL) {
ND("using default locks for %s", ifp->if_xname);
na->nm_lock = netmap_lock_wrapper;
}
}
/* 这几行Linux才用的上的代码,是为linux网卡的驱动框架准备的。未来有用处 */
#ifdef linux
if (ifp->netdev_ops) {
D("netdev_ops %p", ifp->netdev_ops);
/* prepare a clone of the netdev ops */
na->nm_ndo = *ifp->netdev_ops;
}
na->nm_ndo.ndo_start_xmit = linux_netmap_start;
#endif
D("%s for %s", buf ? "ok" : "failed", ifp->if_xname); return (buf ? 0 : ENOMEM);
}
完成了netmap_attach,e1000的probe函数e1000_probe即执行完毕。
高性能网络I/O框架-netmap源码分析的更多相关文章
- 框架-spring源码分析(一)
框架-spring源码分析(一) 参考: https://www.cnblogs.com/heavenyes/p/3933642.html http://www.cnblogs.com/BINGJJF ...
- 框架-springmvc源码分析(二)
框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...
- 框架-springmvc源码分析(一)
框架-springmvc源码分析(一) 参考: http://www.cnblogs.com/heavenyes/p/3905844.html#a1 https://www.cnblogs.com/B ...
- Java8集合框架——LinkedList源码分析
java.util.LinkedList 本文的主要目录结构: 一.LinkedList的特点及与ArrayList的比较 二.LinkedList的内部实现 三.LinkedList添加元素 四.L ...
- 基于vue实现一个简单的MVVM框架(源码分析)
不知不觉接触前端的时间已经过去半年了,越来越发觉对知识的学习不应该只停留在会用的层面,这在我学jQuery的一段时间后便有这样的体会. 虽然jQuery只是一个JS的代码库,只要会一些JS的基本操作学 ...
- Django rest framework框架——APIview源码分析
一.什么是rest REST其实是一种组织Web服务的架构,而并不是我们想象的那样是实现Web服务的一种新的技术,更没有要求一定要使用HTTP.其目标是为了创建具有良好扩展性的分布式系统. 可用一句话 ...
- paypal之nodejs 框架 Kraken-js 源码分析
本文是基于 kraken-js 0.6.1 版本的 关于如何使用kraken-js 可以去看看官网的使用文档 点击这里 .kraken-js 是基于express之上的,目的在于让工程师更多的去关注代 ...
- Linux驱动修炼之道-RTC子系统框架与源码分析【转】
转自:http://helloyesyes.iteye.com/blog/1072433 努力成为linux kernel hacker的人李万鹏原创作品,为梦而战.转载请标明出处 http://bl ...
- Android 轻量级ORM数据库开源框架ActiveAndroid 源码分析
ActiveAndroid 项目地址在https://github.com/pardom/ActiveAndroid 关于他的详细介绍和使用步骤 可以看下面两篇文章: https://github.c ...
随机推荐
- Spring 3.0 注解注入详解
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- Android-怎样用命令行进行打包
转载请标明出处:http://blog.csdn.net/goldenfish1919/article/details/40978859 1.生成R文件 aapt package -f -m -J . ...
- Winform-CheckBox、ChecklistBox控件遍历代码
CheckBox,是各种开发语言环境和网页中常用的控件.下面,本文给大家讲解的是C#Winform版的CheckBox.控件遍历.全选.反选实例代码.①直接引用如果窗体form中存在CheckBox控 ...
- POJ 2488-A Knight's Journey(DFS)
A Knight's Journey Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 31702 Accepted: 10 ...
- WordPress公布新文章Email通知注冊用户
非常多WordPress博客都开放了用户注冊的功能,用户能够參与到博客的内容建设其中来.也就是一个博客由多个用户来写.如今有这种需求,怎样实如今某一个用户发表文章后.其它用户都能收到Email通知?以 ...
- c#问答篇:对象与引用变量-----初学者的困惑
转自:http://www.cnblogs.com/huangyu/archive/2004/08/02/29622.html 从宏观的角度来看,对象是类的实例.比如: //定义一个名为Someone ...
- C++ Code_combobox
主题 1. 代码设置组合框风格 2. 调整组合框列表部分大小 3. 代码设置组合框相关属性 4. 自绘组合框 5. 用代码让combobox的的列表弹出 6. 不添加重复项目 代码 ...
- 进程环境之main函数
C程序总是从main函数开始执行.main函数的原型是: int main( int argc, char *argv[] ); 其中,argc是命令行参数的数目,argv是指向参数的各个指针所构成的 ...
- 动态使用webservice,以及含有ref类型的参数的问题
public class WSHelper { /// < summary> /// 动态调用web服务 /// < /summary> /// < param name ...
- Maven笔记(一)
1. 坐标 Maven坐标为各种构件引入了秩序,任何一个构件都有必须明确定义自己的坐标,而一组Maven坐标是通过一些元素定义的,它们是groupId, artifactId, version, pa ...