netmap源码分析(四)用户态收包过程

来源:互联网 发布:淘宝swatch官方旗舰店 编辑:程序博客网 时间:2024/06/05 20:51

上篇文章 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_RXRINGNETMAP_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_opennm_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;}
0 0
原创粉丝点击