您的位置:首页 > 理论基础 > 计算机网络

高性能网络I/O框架-netmap源码分析(3) http://blog.chinaunix.net/uid-23629988-id-3614187.html

2015-10-04 14:08 776 查看
分类: LINUX

高性能网络I/O框架-netmap源码分析(3)

作者:gfree.wind@gmail.com

博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net

微博:weibo.com/glinuxer

QQ技术群:4367710

前面学习了netmap对e1000网卡驱动的修改,今天开始进入netmap的核心代码。一切从init开始。。。

netmap_init

Linux环境下,netmap使用动态模块加载,由linuxnetmapinit调用netmap_init。
static int
netmap_init(void)
{
int error;

/*
申请netmap的各个内存池,包括netmap_if,netmap_ring,netmap_buf以及内存池的管理结构
*/
error = netmap_memory_init();
if (error != 0) {
printf("netmap: unable to initialize the memory allocator.\n");
return (error);
}
printf("netmap: loaded module with %d Mbytes\n",
(int)(nm_mem->nm_totalsize >> 20));

/*
在Linux上,调用的实际上是misc_register。make_dev为一共宏定义。
创建一个名为netmap的misc设备,作为userspace和kernel的接口
*/
netmap_dev = make_dev(&netmap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0660,
"netmap");

#ifdef NM_BRIDGE
{
int i;
for (i = 0; i < NM_BRIDGES; i++)
mtx_init(&nm_bridges[i].bdg_lock, "bdg lock", "bdg_lock", MTX_DEF);
}
#endif
return (error);
}

netmapmemoryinit

netmap目前有两套内存分配管理代码,一个是netmapmem1.c,另一个是netmapmem2.c。默认使用的是后者。
static int
netmap_memory_init(void)
{
struct netmap_obj_pool *p;

/* 先申请netmap内存管理结构 */
nm_mem = malloc(sizeof(struct netmap_mem_d), M_NETMAP,
M_WAITOK | M_ZERO);
if (nm_mem == NULL)
goto clean;

/* netmap_if的内存池 */
p = netmap_new_obj_allocator("netmap_if",
NETMAP_IF_MAX_NUM, NETMAP_IF_MAX_SIZE);
if (p == NULL)
goto clean;
nm_mem->nm_if_pool = p;

/* netmap_ring的内存池 */
p = netmap_new_obj_allocator("netmap_ring",
NETMAP_RING_MAX_NUM, NETMAP_RING_MAX_SIZE);
if (p == NULL)
goto clean;
nm_mem->nm_ring_pool = p;

/* netmap_buf的内存池 */
p = netmap_new_obj_allocator("netmap_buf",
NETMAP_BUF_MAX_NUM, NETMAP_BUF_SIZE);
if (p == NULL)
goto clean;

/* 对于netmap_buf,为了以后的使用方便,将其中的一些信息保存到其它明确的全局变量中 */
netmap_total_buffers = p->objtotal;
netmap_buffer_lut = p->lut;
nm_mem->nm_buf_pool = p;
netmap_buffer_base = p->lut[0].vaddr;

mtx_init(&nm_mem->nm_mtx, "netmap memory allocator lock", NULL,
MTX_DEF);

nm_mem->nm_totalsize =
nm_mem->nm_if_pool->_memtotal +
nm_mem->nm_ring_pool->_memtotal +
nm_mem->nm_buf_pool->_memtotal;

D("Have %d KB for interfaces, %d KB for rings and %d MB for buffers",
nm_mem->nm_if_pool->_memtotal >> 10,
nm_mem->nm_ring_pool->_memtotal >> 10,
nm_mem->nm_buf_pool->_memtotal >> 20);
return 0;

clean:
if (nm_mem) {
netmap_destroy_obj_allocator(nm_mem->nm_ring_pool);
netmap_destroy_obj_allocator(nm_mem->nm_if_pool);
free(nm_mem, M_NETMAP);
}
return ENOMEM;
}

netmapnewobj_allocator

进入内存池的申请函数——这是netmap中比较长的函数了。
static struct netmap_obj_pool *
netmap_new_obj_allocator(const char *name, u_int objtotal, u_int objsize)
{
struct netmap_obj_pool *p;
int i, n;
u_int clustsize;    /* the cluster size, multiple of page size */
u_int clustentries; /* how many objects per entry */

#define MAX_CLUSTSIZE   (1<<17)
#define LINE_ROUND  64
/* 这个检查应该是netmap不允许申请过于大的结构的内存池 */
if (objsize >= MAX_CLUSTSIZE) {
/* we could do it but there is no point */
D("unsupported allocation for %d bytes", objsize);
return NULL;
}

/*
让obj的size取整到64字节。为啥呢?
因为CPU的cache line大小一般是64字节。所以object的size如果和cache line对齐,可以获得更好的性能。
关于cache line对性能的影响,可以看一下我以前写得一篇博文《多核编程:选择合适的结构体大小,提高多核并发性能》
*/
/* make sure objsize is a multiple of LINE_ROUND */
i = (objsize & (LINE_ROUND - 1));
if (i) {
D("XXX aligning object by %d bytes", LINE_ROUND - i);
objsize += LINE_ROUND - i;
}
/*
* Compute number of objects using a brute-force approach:
* given a max cluster size,
* we try to fill it with objects keeping track of the
* wasted space to the next page boundary.
*/
/*
这里有一个概念:cluster。
暂时没有找到相关的文档介绍这里的cluster的概念。
这里,我只能凭借下面的代码来说一下我的理解:
cluster是一组内存池分配对象object的集合。为什么要有这么一个集合呢?
众所周知,Linux的内存管理是基于页的。而object的大小或小于一个页,或大于一个页。如果基于object本身进行内存分配,会造成内存的浪费。
所以这里引入了cluster的概念,它占用一个或多个连续页。这些页的内存大小或为object大小的整数倍,或者是浪费空间最小。
下面的方法是一个比较激进的计算cluster的方法,它尽可能的追求上面的目标直到cluster的占用的大小超出设定的最大值——MAX_CLUSTSIZE。
*/
for (clustentries = 0, i = 1;; i++) {
u_int delta, used = i * objsize;
/* 不能一味的增长cluster,最大占用空间为MAX_CLUSTSIZE */
if (used > MAX_CLUSTSIZE)
break;
/* 最后页面占用的空间 */
delta = used % PAGE_SIZE;
if (delta == 0) { // exact solution
clustentries = i;
break;
}
/* 这次利用页面空间的效率比上次的高,所以更新当前的clustentries,即cluster的个数*/
if (delta > ( (clustentries*objsize) % PAGE_SIZE) )
clustentries = i;
}
// D("XXX --- ouch, delta %d (bad for buffers)", delta);
/* compute clustsize and round to the next page */
/* 得到cluster的大小,并将其与PAGE SIZE对齐 */
clustsize = clustentries * objsize;
i =  (clustsize & (PAGE_SIZE - 1));
if (i)
clustsize += PAGE_SIZE - i;
D("objsize %d clustsize %d objects %d",
objsize, clustsize, clustentries);

/* 申请内存池管理结构的内存 */
p = malloc(sizeof(struct netmap_obj_pool), M_NETMAP,
M_WAITOK | M_ZERO);
if (p == NULL) {
D("Unable to create '%s' allocator", name);
return NULL;
}
/*
* Allocate and initialize the lookup table.
*
* The number of clusters is n = ceil(objtotal/clustentries)
* objtotal' = n * clustentries
*/
/* 初始化内存池管理结构 */
strncpy(p->name, name, sizeof(p->name));
p->clustentries = clustentries;
p->_clustsize = clustsize;
/* 根据要设定的内存池object的数量,来调整cluster的个数 */
n = (objtotal + clustentries - 1) / clustentries;
p->_numclusters = n;
/* 这是真正的内存池中的object的数量,通常是比传入的参数objtotal要多 */
p->objtotal = n * clustentries;
/* 为什么0和1是reserved,暂时不明。搁置争议,留给后面解决吧。:) */
p->objfree = p->objtotal - 2; /* obj 0 and 1 are reserved */
p->_objsize = objsize;
p->_memtotal = p->_numclusters * p->_clustsize;

/* 物理地址与虚拟地址对应的查询表 */
p->lut = malloc(sizeof(struct lut_entry) * p->objtotal,
M_NETMAP, M_WAITOK | M_ZERO);
if (p->lut == NULL) {
D("Unable to create lookup table for '%s' allocator", name);
goto clean;
}

/* Allocate the bitmap */
/* 申请内存池位图,用于表示那个object被分配了 */
n = (p->objtotal + 31) / 32;
p->bitmap = malloc(sizeof(uint32_t) * n, M_NETMAP, M_WAITOK | M_ZERO);
if (p->bitmap == NULL) {
D("Unable to create bitmap (%d entries) for allocator '%s'", n,
name);
goto clean;
}
/*
* Allocate clusters, init pointers and bitmap
*/
for (i = 0; i < p->objtotal;) {
int lim = i + clustentries;
char *clust;

clust = contigmalloc(clustsize, M_NETMAP, M_WAITOK | M_ZERO,
0, -1UL, PAGE_SIZE, 0);
if (clust == NULL) {
/*
* If we get here, there is a severe memory shortage,
* so halve the allocated memory to reclaim some.
*/
D("Unable to create cluster at %d for '%s' allocator",
i, name);
lim = i / 2;
for (; i >= lim; i--) {
p->bitmap[ (i>>5) ] &=  ~( 1 << (i & 31) );
if (i % clustentries == 0 && p->lut[i].vaddr)
contigfree(p->lut[i].vaddr,
p->_clustsize, M_NETMAP);
}
p->objtotal = i;
p->objfree = p->objtotal - 2;
p->_numclusters = i / clustentries;
p->_memtotal = p->_numclusters * p->_clustsize;
break;
}
/* 初始化位图即虚拟地址和物理地址插叙表 */
for (; i < lim; i++, clust += objsize) {
/*
1. bitmap是32位,所以i >> 5;
2. 为什么(i&31),也是这个原因;—— 这就是代码的健壮性。
*/
p->bitmap[ (i>>5) ] |=  ( 1 << (i & 31) );
p->lut[i].vaddr = clust;
p->lut[i].paddr = vtophys(clust);
}
}

/* 与前面一样,保留第0位和第1位。再次搁置争议。。。 */
p->bitmap[0] = ~3; /* objs 0 and 1 is always busy */
D("Pre-allocated %d clusters (%d/%dKB) for '%s'",
p->_numclusters, p->_clustsize >> 10,
p->_memtotal >> 10, name);

return p;

clean:
netmap_destroy_obj_allocator(p);
return NULL;
}


netmapnewobj_allocator的分析结束。关于netmap的内存管理,依然按照事件的主线分析,而不是集中将一部分搞定。

以后带全部看完netmap的代码后,会写一些netmap的整体性和高层面的文章,不拘泥于代码细节。这就需要先把细节看懂才行。

(未完待续。。。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: