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