上篇文章 netmap源码分析(三)内核态收包过程 说了内核态收包过程以及如何映射到用户态,那么我们这次来看看用户态究竟如何使用。
netmap的用户态代码将打开和关闭/dev/netmap
过程都封装成函数并放在了netmap_user.h
下,简单的使用过程如下:
- nm_open( )
- 收包(这部分自己处理)
- nm_close( )
netmap 用户态的使用过程
下面通过我写的一个简单的小例子来大致感受一下 netmap 的使用过程:
#include <stdio.h>
#include <poll.h>
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
int main(void)
{
struct nm_desc *d;
struct pollfd fds;
struct netmap_ring *ring;
int i;
d = nm_open("netmap:eth0", NULL, 0, 0); // 注意格式,netmap:ehtX
// d 的返回值我这里就不判断了
fds.fd = d->fd;
fds.events = POLLIN;
while (1) {
if (poll(&fds, 1, 1) < 0) {
perror("poll()");
exit(1);
}
// 遍历所有的接收队列
for (i = d->first_rx_ring; i <= d->last_rx_ring; i++) {
ring = NETMAP_RXRING(d->nifp, i);
receive_packets(ring); // 处理 ring
}
}
}
static void receive_packets(struct netmap_ring *ring)
{
int i;
char *buf;
// 遍历所有的槽位
while (!nm_ring_empty(ring)) {
i = ring->cur;
buf = NETMAP_BUF(ring, ring->slot[i].buf_idx); // buf 就是收到的报文喽
pps++; // 统计收包个数
ring->head = ring->cur = nm_ring_next(ring, i); // 下一个槽位
}
}
还记得前几篇文章中的那个图么
|
USERSPACE | struct netmap_ring
+---->+---------------+
/ | head,cur,tail |
struct netmap_if (nifp, one per fd) / | buf_ofs |
+---------------+ / | other fields |
| ni_tx_rings | / +===============+
| ni_rx_rings | / | buf_idx, len | slot[0]
| | / | flags, ptr |
| | / +---------------+
+===============+ / | buf_idx, len | slot[1]
| txring_ofs[0] | (rel.to nifp)--' | flags, ptr |
| txring_ofs[1] | +---------------+
(tx+1 entries) (num_slots entries)
| txring_ofs[t] | | buf_idx, len | slot[n-1]
+---------------+ | flags, ptr |
| rxring_ofs[0] | +---------------+
| rxring_ofs[1] |
(rx+1 entries)
| rxring_ofs[r] |
+---------------+
NETMAP_RXRING
和NETMAP_BUF
如下:
// ring 起始位置 + offset 算出是哪个 ring
#define NETMAP_RXRING(nifp, index) _NETMAP_OFFSET(struct netmap_ring *, \
nifp, (nifp)->ring_ofs[index + (nifp)->ni_tx_rings + 1] )
// 通过偏移计算 buffer
#define NETMAP_BUF(ring, index) \
((char *)(ring) + (ring)->buf_ofs + ((index)*(ring)->nr_buf_size))
使用起来是不是很容易呢 ^_^
下面我们来看看nm_open
和nm_close
都做了些什么吧。
nm_open
static struct nm_desc *nm_open(const char *ifname, const struct nmreq *req,
uint64_t new_flags, const struct nm_desc *arg)
{
struct nm_desc *d = NULL;
if (strncmp(ifname, "netmap:", 7) && strncmp(ifname, "vale", 4)) { // 判断是否是 netmap 模式接口名
errno = 0;
return NULL;
}
nr_flags = NR_REG_ALL_NIC;
d = (struct nm_desc *)calloc(1, sizeof(*d));
d->self = d;
d->fd = open(NETMAP_DEVICE_NAME, O_RDWR); // NETMAP_DEVICE_NAME 是 /dev/netmap
d->req.nr_version = NETMAP_API;
d->req.nr_ringid &= ~NETMAP_RING_MASK;
d->req.nr_ringid |= nr_ringid;
d->req.nr_flags |= nr_flags;
memcpy(d->req.nr_name, ifname, namelen);
d->req.nr_name[namelen] = '\0';
if (ioctl(d->fd, NIOCREGIF, &d->req)) { // 注册命令
snprintf(errmsg, MAXERRMSG, "NIOCREGIF failed: %s", strerror(errno));
goto fail;
}
if ((!(new_flags & NM_OPEN_NO_MMAP) || parent) && nm_mmap(d, parent)) { // mmap 映射内存,下面分析细节过程
snprintf(errmsg, MAXERRMSG, "mmap failed: %s", strerror(errno));
goto fail;
}
return d;
fail:
nm_close(d);
if (errno == 0)
errno = EINVAL;
return NULL;
}
nm_close
nm_close( ) 代码比较少,关闭文件描述符和释放相关资源
static int nm_close(struct nm_desc *d)
{
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); /* 关闭 /dev/netmap 的文件描述符 */
}
bzero(d, sizeof(*d));
free(d); // 释放资源
return 0;
}
nm_mmap
static int nm_mmap(struct nm_desc *d, const struct nm_desc *parent)
{
d->memsize = d->req.nr_memsize; // 映射内存区域的大小
d->mem = mmap(0, d->memsize, PROT_WRITE | PROT_READ, MAP_SHARED, d->fd, 0);
// 通过 offset 得到对应的 netmap_if 结构
struct netmap_if *nifp = NETMAP_IF(d->mem, d->req.nr_offset);
struct netmap_ring *r = NETMAP_RXRING(nifp, );
*(struct netmap_if **)(uintptr_t)&(d->nifp) = nifp;
*(struct netmap_ring **)(uintptr_t)&d->some_ring = r;
// 区域的起始和结束位置
*(void **)(uintptr_t)&d->buf_start = NETMAP_BUF(r, 0);
*(void **)(uintptr_t)&d->buf_end = (char *)d->mem + d->memsize;
return 0;
}