netmap是一个高效的收发报文的 I/O 框架,已经集成在 FreeBSD 的内部了。当然,也可以在 Linux 下编译使用 。

一、架构

现在的网卡都使用多个 buffer 来发送和接收 packet,并有一个叫NIC ring的环形数组。

NIC ring 是静态分配的,它的槽指向mbufs链的部分缓冲区。

netmap 内存映射网卡的packet buffer到用户态,实现了自己的发送和接收报文的circular ring来对应网卡的 ring,使用 netmap 时,程序运行在用户态,即使出了问题也不会 crash 操作系统。

下图显示了一个接口可以有多个 netmap ring。

将文件描述符绑定到 NIC 时,应用程序可以选择将所有 ring或仅一个 ring附加到文件描述符。

  • 使用所有 ring,相同的代码可以用于单队列或多队列 NIC。

  • 使用一个 ring,可以通过每个 ring 一个进程/CPU core 来构建高性能系统,从而在系统中并行。

netmap 使用poll等待网卡的文件描述符可接收或可发送。

netmap 会建立一个字符设备/dev/netmap,然后通过nm_open来注册网卡为 netmap 模式。

注意:这里顺便提一下,网卡进入 netmap 模式后,ifconfig 是看不到网卡统计信息变化的,wireshark 也抓不到报文,因为协议栈被旁路了。

内存映射的区域里面,有网卡的收发队列,这样可以通过将接收缓冲区的地址写在发送的 ring 里面实现零拷贝(Zero-copy)。

二、性能

netmap 官网说在 10GigE 上测试,发包速率可以达到 14.88Mpps,收包的速率和发包相近。同时还支持多网卡队列。

我在网络设备 Intel 82599EB 10-Gigabit 上测试,发包速率和 netmap 官网的速度差不多,可以达到 1400W pps,不过收包要比这个低很多。。。不知道是不是我测的方式有问题。

三、例子

这是官方的例子,不过已经很老了(也就是不能直接用了^_^),但是还是可以大概说明使用过程的:

struct netmap_if *nifp;
struct nmreq req;
int i, len;
char *buf;


fd = open("/dev/netmap", 0);  // 打开字符设备
strcpy(req.nr_name, "eth0");
ioctl(fd, NIOCREG, &req);  // 注册网卡
mem = mmap(NULL, req.nr_memsize, PROT_READ|PROT_WRITE, 0, fd, 0);
nifp = NETMAP_IF(mem, req.nr_offset);


for (;;) {
    struct pollfd x[1];
    struct netmap_ring *ring = NETMAP_RX_RING(nifp, 0);


    x[0].fd = fd;
    x[0].events = POLLIN;
    poll(x, 1, 1000);
    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 官网地址

Revisiting Network I/O APIs: The netmap Framework

netmap: a novel framework for fast packet I/O

————————————————

版权声明:本文为CSDN博主「疯疯癫癫」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/fengfengdiandia/article/details/52869290

(END)

Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注

别忘了点一下“在看”哦~

Logo

更多推荐