汉川网站制作,销售型网站建设的基本要素,应用商城官网下载最新版,怎样收录网站一.简要说明#xff1a; 1.netmap API主要为两个头文件netmap.h 和netmap_user.h #xff0c;当解压下载好的netmap程序后#xff0c;在./netmap/sys/net/目录下#xff0c;本文主要对这两个头文件进行分析。 2.我们从netmap_user.h头文件开始看起。 二.likely()和unlikely…一.简要说明 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_rings1正好与发送环的偏移量区间隔开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_memsizec.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(i0; i2; 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中nifpsome_ringbuf_startbuf_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-curring-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