高性能网络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);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
netmap是一个基于零拷贝思想的高速网络I/O架构,它通过在网卡运行在netmap模式下与主机协议栈断开连接,并创建一个netmap环来实现高效的数据包处理。\[2\]netmap的架构包括了网卡环、netmap环和用于与主机协议栈交互的环。网卡环是网卡直接将数据包存入的缓存,而netmap环是应用程序可以通过调用netmap API访问的缓存。这些缓存位于共享空间,应用程序可以直接访问数据包内容,实现了网络数据包的零拷贝。\[2\] netmap的数据结构包括了netmap_if、nmreq、netmap_ring等。netmap_if是一个结构体,用于表示一个netmap接口,其中包含了与接口相关的信息。nmreq是一个结构体,用于向内核注册一个netmap接口。netmap_ring是一个结构体,用于表示一个netmap环,其中包含了环的相关信息,如可用的数据包数量、当前处理的数据包索引等。\[3\] 在使用netmap时,可以通过打开字符设备"/dev/netmap"来获取一个文件描述符,然后使用ioctl函数来注册网卡。接下来,可以使用mmap函数将共享内存映射到用户空间,从而可以访问netmap环中的数据包内容。最后,可以使用poll函数来等待数据包的到达,并通过遍历netmap环中的数据包来处理数据。\[3\] 需要注意的是,上述提供的代码示例是一个官方的例子,可能已经过时,不能直接使用。但是它可以大致说明netmap的使用过程。\[3\] #### 引用[.reference_title] - *1* *3* [netmap 介绍](https://blog.csdn.net/fengfengdiandia/article/details/52869290)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Netmap分析(一)](https://blog.csdn.net/superbfly/article/details/51224920)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值