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

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

2015-10-04 14:10 1241 查看
高性能网络I/O框架-netmap源码分析(6) 2013-07-14
22:43:01

分类: LINUX

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

作者:gfree.wind@gmail.com

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

微博:weibo.com/glinuxer

QQ技术群:4367710

因为最近比较忙,很久没有更新博客了,但是netmap源码分析还没有写完,今天继续分析。这里就显示出了写博客的好处。如果不是写博客,可能自己私下看代码,看了一半,就半途而废了。而写了博客,每次打开自己的主页,看到还未完成的系列,就会坚持把这件事情做完。

唠了一点闲话,也是鼓励大家多写文章,给ChinaUnix贡献一些文字吧。

上次已经把netmap_ioctl分析完了,根据netmap的示例,下面该分析netmap的mmap的实现了。

定位netmap的mmap

前文提到过netmap会创建一个设备
static struct miscdevice netmap_cdevsw = {  /* same name as FreeBSD */
MISC_DYNAMIC_MINOR,
"netmap",
&netmap_fops,
};


netmap_fops定义了netmap设备支持的操作
static struct file_operations netmap_fops = {
.mmap = linux_netmap_mmap,
LIN_IOCTL_NAME = linux_netmap_ioctl,
.poll = linux_netmap_poll,
.release = netmap_release,
};


OK,现在我们找到了mmap的入口,linuxnetmapmmap。

linux_netmap_mmap分析

现在直接进入linux_netmap_mmap的代码
static int
linux_netmap_mmap(struct file *f, struct vm_area_struct *vma)
{
int lut_skip, i, j;
int user_skip = 0;
struct lut_entry *l_entry;
const struct netmap_obj_pool *p[] = {
nm_mem->nm_if_pool,
nm_mem->nm_ring_pool,
nm_mem->nm_buf_pool };
/*
* vma->vm_start: start of mapping user address space
* vma->vm_end: end of the mapping user address space
*/

/*
这里又是一个编程技巧,使用(void)f既不会产生任何真正的代码,又可以消除变量f没有使用的warning。
为什么f不使用,还会出现在参数列表中呢?没办法啊,只是Linux框架决定的。linux_netmap_mmap只是一个注册回调,自然要遵从linux的框架了。
*/
(void)f;    /* UNUSED */
// XXX security checks

for (i = 0; i < 3; i++) {  /* loop through obj_pools */
/*
* In each pool memory is allocated in clusters
* of size _clustsize , each containing clustentries
* entries. For each object k we already store the
* vtophys malling in lut[k] so we use that, scanning
* the lut[] array in steps of clustentries,
* and we map each cluster (not individual pages,
* it would be overkill).
*/
/*
上面的注释说的很明白。
每个pool里的object都是由_clustsize组成的,每一个都包含clustertries个基础内存块。 一个pool公有_numclusters个基础内存块。
所以,在进行内存映射的时候,user_skip表示已经映射的内存大小,vma->start+user_skip也就是当前未映射内存的起始地址,lut_skip表示当前待映射的物理内存池的块索引
*/
for (lut_skip = 0, j = 0; j < p[i]->_numclusters; j++) {
l_entry = &p[i]->lut[lut_skip];
if (remap_pfn_range(vma, vma->vm_start + user_skip,
l_entry->paddr >> PAGE_SHIFT, p[i]->_clustsize,
vma->vm_page_prot))
return -EAGAIN; // XXX check return value
lut_skip += p[i]->clustentries;
user_skip += p[i]->_clustsize;
}
}

/*
循环执行完毕后,netmap在内核中的3个对象池已经完全映射到用户空间
真正执行映射的函数是remap_pfn_range,这是内核函数,用于将内核空间映射到用户空间
这个函数超出了本文的主题范围了,我们只需要知道它是做什么的就行了。
*/

return 0;
}

用户态得到对应网卡的netmap结构

在将netmap内核态的内存映射到用户空间以后,netmap的示例通过offset来得到对应网卡的netmap结构。
fd = open("/dev/netmap", 0);
strcpy(req.nr_name, "ix0"); // register the interface
ioctl(fd, NIOCREG, &req); // offset of the structure
mem = mmap(NULL, req.nr_memsize, PROT_READ|PROT_WRITE, 0, fd, 0);
nifp = NETMAP_IF(mem, req.nr_offset);


在此例中,使用ioctl,得到req.nroffset是ix0网卡的netmap结构的偏移——准确的说是netmap管理网卡结构内存池的偏移。mmap后,mem是netmap内存的映射,而网卡结构内存是内存中的第一项,那么mem同样可以视为netmap管理网卡结构的内存池的起始地址。因此,利用前面的req.nroffset,就得到了ix0的netmap结构,即struct
netmap_if。

走读netmap的示例中工作代码

按照netmap示例,马上就要进入netmap真正工作的代码了。
for (;;) {
struct pollfd x[1];
/*
根据netmap的代码,NETMAP_RXRING的定义如下
#define NETMAP_RXRING(nifp, index)          \
((struct netmap_ring *)((char *)(nifp) +    \
(nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] ) )
得到该网卡的接收ring buffer。

吐个槽,为什么英文接收Receive要缩写为RX呢。。。我在别的地方也见过。
*/
struct netmap_ring *ring = NETMAP_RX_RING(nifp, 0);
x[0].fd = fd;
x[0].events = POLLIN;
/* 超时1秒等接收事件发生 */
poll(x, 1, 1000);
/* 收到ring->avail个包 */
for ( ; ring->avail > 0 ; ring->avail--) {
/* 得到当前包索引 */
i = ring->cur;
/* 得到对应的数据包 */
buf = NETMAP_BUF(ring, i);
/* 用户态处理该数据包 */
use_data(buf, ring->slot[i].len);
/* 移到下一个待处理数据包 */
ring->cur = NETMAP_NEXT(ring, i);
}
}


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