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

上篇文章 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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值