vlambda博客
学习文章列表

Linux网络零拷贝技术之——netmap 介绍

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 也抓不到报文,因为协议栈被旁路了。



二、性能


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);    }}



参考资料

Revisiting Network I/O APIs: The netmap Framework

netmap: a novel framework for fast packet I/O