012.对netmap API的解读

一.简要说明:

    1.netmap API主要为两个头文件netmap.h 和netmap_user.h ,当解压下载好的netmap程序后,在./netmap/sys/net/目录下,本文主要对这两个头文件进行分析。

    2.我们从netmap_user.h头文件开始看起。

 

二.likely()和unlikely()

    这两个宏定义是对编译器做优化的,并不会对变量做什么改变。后面看到这两个宏的调用自动忽略就好了。

#ifndef likely
#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)
#endif /* likely and unlikely */

 

三.netmap.h头文件

    1.netmap.h被netmap_user.h调用,里面定义了一些宏和几个主要的结构体,如nmreq{}, netmap_if{}, netmap_ring{},  netmap_slot{}。

    2.一个网卡(或者网络接口)只有一个netmap_if{}结构,在使用mmap()申请的共享内存中,通过netmap_if{}结构可以访问到任何一个发送/接收环(也就是netmap_ring{}结构,一个netmap_if{}可以对应多发送/接收环,这应该和物理硬件有关 ,我在虚拟机下只有一对环,在真实主机上有两队环)。

    3.找到netmap_ring{}的地址后,我们就可以找到环中每一个buffer的地址(buffer里面存储的是将要发送/接收的数据包)。后面会讲解这是如何实现的。

    4.通过一个nifp是如何访问到多个收/发环的,通过一个ring如何找到多个不同的buffer地址的,其实都是通过存储这些结构体相邻的后面一部分空间实现。(申请共享内存的时候,这些均已被设计好)

 

四.几个重要的宏定义

1._NETMAP_OFFSET

#define _NETMAP_OFFSET(type, ptr, offset) \((type)(void *)((char *)(ptr) + (offset)))

解释:该宏定义的作用是将ptr指针(强转成char *类型)向右偏移offset个字节,再将其转化为指定的类型type。

2.NETMAP_IF

#define NETMAP_IF(_base, _ofs)    _NETMAP_OFFSET(struct netmap_if *, _base, _ofs)

解释:该宏定义将_base指针向右偏移_ofs个字节后,强转为netmap_if *类型返回。在nemap中通过此宏得到d->nifp的地址。

3.NETMAP_TXRING

#define NETMAP_TXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \nifp, (nifp)->ring_ofs[index] )

解释:1.通过该宏定义,可以找到nifp的第index个发送环的地址(index是从0开始的),ring_ofs[index]为偏移量,由内核生成。   

        2.其中,我们注意到struct netmap_if{}最后面只定义了const ssize_t ring_ofs[0],实际上其它的netmap环的偏移量都写在了该结构体后面的内存地址里面,直接访问就可以了。

4.NETMAP_RXRING

#define NETMAP_RXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *,    \nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] )

解释:通过该宏定义,可以找到nifp的第index个接收环的地址,其中(nifp)->ring_ofs[]里面的下标为index+(nifp)->ni_tx_rings+1,正好与发送环的偏移量区间隔开1个。(我想这应该是作者特意设计的)

5.NETMAP_BUF

#define NETMAP_BUF(ring, index)                \((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size))

解释:1.通过该宏定义,可以找到ring这个环的第index个buffer的地址(buffer里面存的就是我们接收/将发送的完整数据包),每个buffer占的长度是2048字节(在(ring)->nr_buf_size也给出了)。

        2.其中(ring) ->buf_ofs是固定的偏移量,不同的环这个值不相同,但所有的(char *)(ring)+(ring)->buf_ofs会指向同一个地址,也就是存放buffer的连续内存的开始地址(d->buf_start会指向该地址)。

6.NETMAP_BUF_IDX

#define NETMAP_BUF_IDX(ring, buf)            \( ((char *)(buf) - ((char *)(ring) + (ring)->buf_ofs) ) / \(ring)->nr_buf_size )

解释:在讲NETMAP_BUF的时候我们说(char *)(ring) + (ring)->buf_ofs)总会指向存放buffer的起始位置(无论是哪一个环),在这段内存中将第一个buffer下标标记为0的话,NETMAP_BUF_IDX计算的恰好是指针buf所指buffer的下标。

上面几个宏一时没弄懂也没关系,下面调用的时候还会提的。

 

五.nm_open函数

    1.调用nm_open函数时,如:nmr = nm_open("netmap:eth0", NULL, 0, NULL); nm_open()会对传递的ifname指针里面的字符串进行分析,提取出网络接口名。

    2.nm_open()会对struct nm_desc *d申请内存空间,并 通过d->fd = open(NETMAP_DEVICE_NAME, O_RDWR);打开一个特殊的设备/dev/netmap来创建文件描述符d->fd。

    3.通过ioctl(d->fd, NIOCREGIF, &d->req)语句,将d->fd绑定到一个特殊的接口,并对d->req结构体里面的成员做初始化,包括a.在共享内存区域中 nifp 的偏移,b.共享区域的大小nr_memsize,c.tx/rx环的大小nr_tx_slots/nr_rx_slots(大小为256),d.tx/rx环的数量nr_tx_rings、nr_rx_rings(视硬件性能而定)等。

    4.接着在if ((!(new_flags & NM_OPEN_NO_MMAP) || parent) && nm_mmap(d, parent))语句中调用nm_mmap函数,继续给d指针指向的内存赋值。

 

六.nm_mmap函数

nm_mmap()源码:

static int nm_mmap(struct nm_desc *d, const struct nm_desc *parent)
{
//XXX TODO: check if mmap is already doneif (IS_NETMAP_DESC(parent) && parent->mem && parent->req.nr_arg2 == d->req.nr_arg2){/* do not mmap, inherit from parent */D("do not mmap, inherit from parent");d->memsize = parent->memsize;d->mem = parent->mem;} else{/* XXX TODO: 检查如果想申请的内存太大 (or there is overflow) */d->memsize = d->req.nr_memsize;       /* 将需要申请的内存大小赋值给d->memsize */d->mem = mmap(0, d->memsize, PROT_WRITE | PROT_READ, MAP_SHARED, d->fd, 0);  /* 申请共享内存 */if (d->mem == MAP_FAILED){goto fail;}d->done_mmap = 1;}{struct netmap_if *nifp = NETMAP_IF(d->mem, d->req.nr_offset);  /*通过d->req.nr_offset这个偏移量的到nifp的地址,NETMAP_IF前面说过*/int i;/**for(i=0; i<=2; i++)*    printf("ring_ofs[%d]:0x%x\n",i,nifp->ring_ofs[i]);   // 这里是我自己加的,为了手动计算收/发环的偏移量*/struct netmap_ring *r = NETMAP_RXRING(nifp,); //对nifp,找接收包的环r,因为index为0,所以省略了*(struct netmap_if **) (uintptr_t) &(d->nifp) = nifp;   //对d->nifp赋值,虽然d->nifp使用const定义的,但对其取地址再强值类型转换后,依然可以对其指向的空间进行操作*(struct netmap_ring **) (uintptr_t) &d->some_ring = r; //同理,对d->some_ring进行赋值,此处指向了第一个接受(rx)环。//printf("buf_ofs:0x%x\n", (u_int)r->buf_ofs);*(void **) (uintptr_t) &d->buf_start = NETMAP_BUF(r, 0);//计算第一个buffer的地址,并存入d->buf_start指针中*(void **) (uintptr_t) &d->buf_end = (char *) d->mem + d->memsize; //计算共享区间的最后一个地址,赋值给d->buf_end
    }return 0;fail: return EINVAL;
}

其中:

    1.nifp为申请的共享内存首地址d->mem向右偏移d->req.nr_offset(该值在调用前面的ioctl()时得到)得到。并且一个网络接口(网卡)只对应一个nifp。(使用宏NETMAP_IF计算)

    2.得到的nifp的地址,nifp结构体里最后定义的ring_ofs[0]以及接下来内存中的ring_ofs[1],ring_ofs[2]...,这些内存中存储的是访问每一个环(tx or rx ring)的偏移量,通过这个偏移量我们可以得到每一个环的地址(使用宏NETMAP_RXRING/NETMAP_TXRING进行计算)。

    3.得到每个收/发环的地址了,netmap_ring结构体最后面有一个struct netmap_slot slot[0];,通过slot[0],后面内存的slot[1],slot[2],slot[3]...,取出里面的偏移量就可以得到每一个buffer(也叫数据包槽)的地址了(使用宏NETMAP_BUF计算得到)。    到这里,netmap如何访问到内存槽中的每一个buffer的,我们都知道了。实际上netmap运行的数据结构就和下图描述的一样:

    4.在struct nm_desc中,nifp,some_ring,buf_start,buf_end等指针都定义为const的,但我们通过对其取地址再强转指针的方式去往这些指针指向的内存中赋值。

    注:在nm_mmap()中使用mmap()申请共享的时候,这些数据结构里数据的设计是内核模块就已写好了的,我们在这里其实是在做验证。

七.nm_nextpkt函数

    1.nm_nextpkt()是用来接收网卡上到来的数据包的函数。

    2.nm_nextpkt()会将所有rx环都检查一遍,当发现有一个rx环有需要接收的数据包时,得到这个数据包的地址,并返回。所以nm_nextpkt()每次只能取一个数据包。

nm_nextpkt()源代码:

static u_char *nm_nextpkt(struct nm_desc *d, struct nm_pkthdr *hdr)
{int ri = d->cur_rx_ring;  //当前的接收环的编号do{/* compute current ring to use */struct netmap_ring *ring = NETMAP_RXRING(d->nifp, ri);  //得到当前rx环的地址if (!nm_ring_empty(ring))  //判断环里是否有新到的包
        {u_int i = ring->cur; //当前该访问哪个槽(buffer)了u_int idx = ring->slot[i].buf_idx; //得到第i个buffer的下标//printf("%d\n", idx);u_char *buf = (u_char *) NETMAP_BUF(ring, idx);  //得到存有到来数据包的地址// __builtin_prefetch(buf);hdr->ts = ring->ts;hdr->len = hdr->caplen = ring->slot[i].len;ring->cur = nm_ring_next(ring, i);  //ring->cur向后移动一位/* we could postpone advancing head if we want* to hold the buffer. This can be supported in* the future.*/ring->head = ring->cur;d->cur_rx_ring = ri; //将当前环(d->cur_rx_ring)指向第ri个(因为可能有多个环)。return buf;  //将数据包地址返回
        }ri++;if (ri > d->last_rx_ring)  //如果ri超过了rx环的数量,则再从第一个rx环开始检测是否有包到来。ri = d->first_rx_ring;} while (ri != d->cur_rx_ring);return NULL; /* 什么也没发现 */
}

 

八.nm_inject函数

    1.nm_inject()是用来往共享内存中写入待发送的数据包数据的。数据包经共享内存拷贝到网卡,然后发送出去。所以nm_inject()是用来发包的。

    2.nm_inject()也会查找所有的发送环(tx环),找到一个可以发送的槽,就将数据包写入并返回,所以每次函数调用也只能发送一个包。

源代码:

static int nm_inject(struct nm_desc *d, const void *buf, size_t size)
{u_int c, n = d->last_tx_ring - d->first_tx_ring + 1;for (c = 0; c < n; c++){/*   计算当前的环去使用(compute current ring to use) */struct netmap_ring *ring;uint32_t i, idx;uint32_t ri = d->cur_tx_ring + c;  //该访问第几个tx环了if (ri > d->last_tx_ring)  //当超过访问的tx环的下标范围时,从头开始访问ri = d->first_tx_ring;ring = NETMAP_TXRING(d->nifp, ri);  //得到当前tx环的地址if (nm_ring_empty(ring))       //如果当前tx环是满的(ring->cur=ring->tail表示没地方存数据包了),就跳过
        {continue;}i = ring->cur; //当前要往哪个槽(槽指向buffer)中写入数据idx = ring->slot[i].buf_idx; //得到这个槽相对于buffer起始地址(d->buf_start)的下标编号ring->slot[i].len = size;  //size为待发送数据包的长度nm_pkt_copy(buf, NETMAP_BUF(ring, idx), size); //将buf里存的数据包拷贝给ring这个环的第i个槽d->cur_tx_ring = ri;ring->head = ring->cur = nm_ring_next(ring, i); //将head和cur指向下一个槽return size;}return 0; /* 失败 */
}

 

九.nm_close函数

    1.nm_close函数就是回收动态内存,回收共享内存,关闭文件描述符什么的了。

源代码:

static int nm_close(struct nm_desc *d)
{/** ugly trick to avoid unused warnings*/static void *__xxzt[] __attribute__ ((unused)) ={ (void *) nm_open, (void *) nm_inject, (void *) nm_dispatch, (void *) nm_nextpkt };if (d == NULL || d->self != d)return EINVAL;if (d->done_mmap && d->mem)munmap(d->mem, d->memsize);  //释放申请的共享内存if (d->fd != -1){close(d->fd); //关闭文件描述符
    }bzero(d, sizeof(*d));  //将d指向的空间全部置0free(d);   //释放指针d指向的空间return 0;
}

 

转载于:https://www.cnblogs.com/ruo-yu/p/5106213.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/491370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学术笔记】探索大脑静息态活动中的动态信息

来源&#xff1a;脑科学2019年6月18日下午&#xff0c;来自加州大学河滨分校(The University of California, Riverside) Bourns工程学院的生物工程系主任Xiaoping Hu (胡小平)教授应北京大学麦戈文脑研究所方方老师的邀请来到北京大学&#xff0c;在王克桢楼1113室为老师和同学…

OpenCV中的函数子

随着OpenCV的发展&#xff0c;封装了越来越多的功能&#xff0c;而往往这些功能不是一个函数就能完成的&#xff0c;实现为一组函数又会导致整个库的函数变得杂乱无章&#xff0c;因此常常使用一个新的对象类型来实现这个新功能。通过重载operator()来生成对象或函数子。下面主…

查找会议论文的会议地址

有时候会议论文conference proceedings引用格式中要求出现会议地址&#xff0c;如下所示 查找会议地址的方法为直接搜索该会议论文&#xff0c;以ieee为例&#xff0c;会议地址信息在该论文的首页信息中&#xff1a; Conference Location: Arlington, VA, USA中的三项就分别对应…

OpenCV可移植图形工具HighGUI实现图像和视频操作

OpenCV把用于操作系统、文件系统以及摄像机等硬件设备交换的函数纳入了HighGUI&#xff08;High-level Graphical User Interface&#xff09;模块中。有了HighGUI模块&#xff0c;我们可以方便地打开窗口、显示图像、读出或写入图像相关的文件、鼠标事件和键盘事件。下面将对三…

华为内部深度解读,关于5G发展的28个核心问题

来源 | 腾讯深网关于5G技术动态与商用进展业界最关心的核心问题&#xff0c;华为5G产品线相关负责人近日对《深网》等进行了详细解读&#xff0c;以下是《深网》整理的问答实录&#xff1a;一、5G先进性与行业应用1. 5G到底是什么&#xff1f;和4G比有什么不一样&#xff1f;从…

OpenCV鼠标事件和滑动条事件

鼠标事件 ① 鼠标事件是通过传统的回调函数机制来完成。 void your_mouse_callback(int event, int x, int y, int flags, void* param) 其中&#xff0c;第一个参数要指明事件&#xff0c;第二个和第三个参数是鼠标事件的位置&#xff0c;第四个参数是标志位&#xff0c;第…

GSMA:中国有望成为全球领先的5G市场之一

来源&#xff1a;GSMA移动智库近日&#xff0c;GSMA&#xff08;全球移动通信协会&#xff09;发布首个《中国移动经济发展报告2019》。报告称&#xff0c;中国的移动生态系统在2018年为中国经济创造了5.2万亿元 (7,500亿美元) 的附加值&#xff0c;相当于2018年中国GDP的5.5%。…

canal —— 阿里巴巴mysql数据库binlog的增量订阅消费组件

阿里巴巴mysql数据库binlog的增量订阅&消费组件canal &#xff0c;转载自 https://github.com/alibaba/canal 最新更新 canal QQ讨论群已经建立&#xff0c;群号&#xff1a;161559791 &#xff0c;欢迎加入进行技术讨论。canal消费端项目开源: Otter(分布式数据库同步系统…

OpenCV的滤波与卷积

目录 预备知识 滤波、核和卷积 边界外推和边界处理 阈值化操作 Otsu算法 自适应阈值 平滑 简单模糊和方框型滤波器 中值滤波器 高斯滤波器 双边滤波器 导数和梯度 索贝尔导数 Scharr滤波器 拉普拉斯变换 图像形态学 膨胀和腐蚀 通用形态学函数 开操作和闭操…

中国科协发布20个重大科学问题和工程技术难题

来源&#xff1a;晓艳的科技坊6月30日&#xff0c;中国科协在第二十一届中国科协年会闭幕式上发布了2019年20个对科学发展具有导向作用、对技术和产业创新具有关键作用的前沿科学问题和工程技术难题。   中国科学院院士、中国科协名誉主席韩启德表示&#xff0c;中国科协重大…

OpenCV常见的图像变换

拉伸、收缩、扭曲和旋转 最简单的图像变换是调整图像大小&#xff0c;使其变大或变小。但实际操作时要比想象的复杂一些&#xff0c;因为调整大小带来了像素如何插值&#xff08;放大&#xff09;或合并&#xff08;减少&#xff09;的问题。 均匀调整 cv::resize() void c…

边缘计算对于工业物联网的重要性

来源&#xff1a;物联网世界随着物联网的发展&#xff0c;工业制造设备所产生的数据量将越来越多。如果这些数据都要放到云端处理&#xff0c;就需要无穷无尽的频谱资源、传输带宽和数据处理能力&#xff0c;“云”难免不堪重负&#xff0c;此时就需要边缘计算来分担云计算的压…

全新算法助机器学习抵抗干扰

来源&#xff1a;科技日报 机器学习模型受到攻击将产生严重的后果&#xff0c;但如果对这一情形提前预防呢&#xff1f;就像人类针对即将到来的病毒去接种疫苗一样。据澳大利亚联邦科学与工业研究组织&#xff08;CSIRO&#xff09;官方网站消息&#xff0c;该机构的一个研究团…

磁共振成像技术发展简史丨科学史

来源&#xff1a;中国科学院苏州生物医学工程技术研究所编者按&#xff1a;核磁共振是一种常见的影像检查方式。全球首次为病人做核磁共振检查是在1977年7月3日。该技术是基于物理学家拉比有关测量原子核在磁场中性质的研究发展而来的。拉比在1988年去世前不久&#xff0c;也使…

最先进的AI还不如动物聪明?首届AI-动物奥运会英国开赛!

来源&#xff1a;animalaiolympics,新智元【导读】目前&#xff0c;即使是最先进的AI智能体&#xff0c;在适应环境变化的能力方面&#xff0c;还无法与动物相提并论。听上去很不可思议对不对&#xff0c;不服来比比看&#xff1f;日前&#xff0c;动物-AI奥运会正式拉开帷幕&a…

background-size 兼容ie8以下浏览器的方法

filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(srcimgs/section-1.png, sizingMethodscale); -ms-filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader(srcimgs/section-1.png, sizingMethodscale)";转载于:https://www.cnblogs.com/wend…

一文尽览5G全产业链及新机遇

来源&#xff1a;5G产业圈5G牌照的发放&#xff0c;对通信产业发展具有重要的战略意义。这不仅仅是为了5G商用&#xff0c;更赋予了多重目的。比如为了利用5G技术推动经济结构创新、促进经济增长、帮助华为中兴、结交爱立信诺基亚&#xff0c;分化高通英特尔……甚至可能是为了…

Windows10+Ubuntu 18.04.2+ROS 安装笔记(SSD单硬盘)上

Windows10Ubuntu 18.04.2ROS 安装笔记&#xff08;SSD单硬盘&#xff09;上一、前言二、化繁为简三、补充说明小结一、前言 最近需要学习ROS和python,于是便需要装Linux系统&#xff0c;于是选则了主流的 Ubuntu 18.04系统&#xff0c;同时与ROS 也是兼容性最好的系统。 之前…

实验室蒋田仔研究员:脑网络组图谱近10年研究详解【附PPT】

来源&#xff1a;模式识别国家重点实验室【导读】脑是人体最为复杂的器官&#xff0c;脑图谱是理解脑的结构和功能的基石。它为探索复杂的脑部结构和异常变化进行"导航"。因此&#xff0c;脑图谱绘制一直以来都是神经科学、认知科学、心理学和脑疾病研究的共同前沿领…

5G最完整的PPT

来源&#xff1a;云数据中心5G未来智能实验室是人工智能学家与科学院相关机构联合成立的人工智能&#xff0c;互联网和脑科学交叉研究机构。未来智能实验室的主要工作包括&#xff1a;建立AI智能系统智商评测体系&#xff0c;开展世界人工智能智商评测&#xff1b;开展互联网&a…