FreeBSD下的工具(sysctl、netstat等)如何移植到F-Stack

F-Stack基于DPDK,绕过内核的协议栈,移植了FreeBSD协议栈到用户态,在大幅提高性能的同时,常规网络设置分析工具(如sysctl、netstat、ifconfig、route等)都无法直接使用。但是由于在用户态运行了FreeBSD的协议栈,我们可以移植FreeBSD下的这些工具到F-Stack。

移植的关键是这些工具要能与F-Stack进程通信,在之前的文章中,我们介绍了如何使用DPDK rte_ring来进行多进程的通信,tools/ipc目录就是基于rte_ring实现了一个简单的ipc框架。下面以sysctl为例,介绍一下如何移植到F-Stack。

查看FreeBSD 11.0.1 /sbin/sysctl的源码,可以发现是通过系统调用sysctl来与内核进行通信的,我们需要替换掉的就是这个函数。

首先在lib/ff_msg.h中,定义了用于通信的结构体struct ff_msg,暂时只实现了sysctl,后续会加上sysctlbyname、ioctl等其他工具移植需要的系统调用。

/* MSG TYPE: sysctl, sysctlbyname, etc.. */
enum FF_MSG_TYPE {
    FF_UNKNOWN = 0,
    FF_SYSCTL,
};

struct ff_sysctl_args {
    int *name;
    unsigned namelen;
    void *old;
    size_t *oldlenp;
    void *new;
    size_t newlen;
};

#define MAX_MSG_BUF_SIZE 10240

/* structure of ipc msg */
struct ff_msg {
    enum FF_MSG_TYPE msg_type;
    /* Result of msg processing */
    int result;
    /* Length of segment buffer. */
    uint16_t buf_len;
    /* Address of segment buffer. */
    char *buf_addr;

    union {
        struct ff_sysctl_args sysctl;
    };
} __attribute__((packed)) __rte_cache_aligned;

lib/ff_dpdk_if.c中,F-Stack初始化时会创建单个元素长度为MAX_MSG_BUF_SIZE的内存池message_pool,通信时从message_pool里取出元素,转换成struct ff_msg,这里有个要注意的地方,ff_msg.sysctl里的指针成员(name、old等)必须指向ff_msg.buf_addr到ff_msg.buf_addr+ff.msg_buf_len之间的地址(ff_msg.buf_len=MAX_MSG_BUF_SIZE-sizeof(struct ff_msg)),不能使用自己申请的内存地址,这是因为用于通信的数据必须使用rte_mempool中的共享内存,否则另一端会出现未知的错误。

处理流程:从ring中出队列,取出msg,判断是FF_SYSCTL类型,然后执行ff_sysctl函数获取或设置FreeBSD内核的状态参数,最后再把msg入队列。这里出入的ring是单生产者单消费者模式的,使用了两个,一个用于F-Stack出,工具入,一个用于F-Stack入,工具出。

static inline void
handle_sysctl_msg(struct ff_msg *msg, uint16_t proc_id)
{
    int ret = ff_sysctl(msg->sysctl.name, msg->sysctl.namelen,
        msg->sysctl.old, msg->sysctl.oldlenp, msg->sysctl.new,
        msg->sysctl.newlen);

    if (ret < 0) {
        msg->result = errno;
    } else {
        msg->result = 0;
    }

    rte_ring_enqueue(msg_ring[proc_id].ring[1], msg);
}

static inline void
handle_default_msg(struct ff_msg *msg, uint16_t proc_id)
{
    msg->result = EINVAL;
    rte_ring_enqueue(msg_ring[proc_id].ring[1], msg);
}

static inline void
handle_msg(struct ff_msg *msg, uint16_t proc_id)
{
    switch (msg->msg_type) {
        case FF_SYSCTL:
            handle_sysctl_msg(msg, proc_id);
            break;
        default:
            handle_default_msg(msg, proc_id);
            break;
    }
}

static inline int
process_msg_ring(uint16_t proc_id)
{
    void *msg;
    int ret = rte_ring_dequeue(msg_ring[proc_id].ring[0], &msg);

    if (unlikely(ret == 0)) {
        handle_msg((struct ff_msg *)msg, proc_id);
    }

    return 0;
}

然后看下sysctl中的处理,这里我们实现了一个新的函数sysctl_ipc用来替换原来的系统调用sysctl:

int sysctl_ipc(uint16_t proc_id, int *name, unsigned namelen, void *old,
    size_t *oldlenp, const void *new, size_t newlen);

因为F-Stack是多进程架构,并且每个进程都有一个独立的FreeBSD栈,所以新增了一个参数proc_id,用于指定与哪个F-Stack进程通信,这个算是一个不方便的地方。其他参数都与原生sysctl的一样。

sysctl_ipc的实现流程:从mempool中获取ff_msg对象,设置参数,入ring队列,出ring队列,输出返回信息。

struct ff_msg *msg = ff_ipc_msg_alloc();

char *buf_addr = msg->buf_addr;
msg->msg_type = FF_SYSCTL;
msg->sysctl.name = (int *)buf_addr;
msg->sysctl.namelen = namelen;
memcpy(msg->sysctl.name, name, namelen*sizeof(int));
buf_addr += namelen*sizeof(int);

........

ff_ipc_send(msg, proc_id);

ff_ipc_recv(&retmsg, proc_id);

.....

ff_ipc_msg_free(msg);

另外由于是移植的FreeBSD下的sysctl,所以它的头文件、结构体在Linux下可能没有,需要根据实际情况进行增删。 具体的代码可以查看tools/ipctools/sysctl目录。

运行效果:

除了新增加了一个-p参数用于指定与哪个F-Stack进程通信外,其他参数与原生FreeBSD一致,具体可参考man page。

根据这个例子,我们可以对其他工具进行移植,如ifconfig、route、netstat等。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端知识分享

第108天:Ajax中XMLHttpRequest详解

   在Ajax应用程序中,XmlHttpRequest对象负责将用户信息以异步通信地发送到服务器端,并接收服务器返回的响应信息和数据。

611
来自专栏大学生计算机视觉学习DeepLearning

c++ 网络编程(五)TCP/IP LINUX下 socket编程 多种I/O函数 -以及readv和writev函数用法

原文链接:https://www.cnblogs.com/DOMLX/p/9614056.html

755
来自专栏码洞

Spark通信原理之Python与JVM的交互

我们知道Spark平台是用Scala进行开发的,但是使用Spark的时候最流行的语言却不是Java和Scala,而是Python。原因当然是因为Python写代...

691
来自专栏架构之路

【网络编程系列】二:socket通信原理及实践

我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或...

4556
来自专栏F-Stack的专栏

FreeBSD下的工具(sysctl、netstat等)如何移植到F-Stack

F-Stack基于DPDK,绕过内核的协议栈,移植了FreeBSD协议栈到用户态,在大幅提高性能的同时,常规网络设置分析工具(如sysctl、netstat、i...

2698
来自专栏DOTNET

【翻译】MongoDB指南/CRUD操作(三)

【原文地址】https://docs.mongodb.com/manual/ CRUD操作(三) 主要内容: 原子性和事务(Atomicity and Tran...

2559
来自专栏Golang语言社区

Golang通用连接池

连接池在编程中并不少见,链接数据库,redis等操作都需要连接池,否则就会出现并发问题,如果每次操作都建立一条新的链接将会大大消耗资源,笔者也是在使用thrif...

1054
来自专栏PHP技术

Redis 和 Memcached 的区别

说到redis就会联想到memcached,反之亦然。了解过两者的同学有那么个大致的印象:redis与memcached相比,比仅支持简单的key-value数...

3266
来自专栏企鹅号快讯

一文学会Python协程

Python圣诞学习狂欢夜 距离开始还有3天 . . . 详情 . . . 生成器和协程的介绍 生成器(Generator)的本质和特点 生成器 是 可以生成一...

21710
来自专栏前端知识分享

第109天:Ajax请求GET和POST的区别

  用get方式可传送简单数据,但大小一般限制在1KB下,数据追加到url中发送(http的header传送),也就是说,浏览器将各个表单字段元素及其数据按照U...

602

扫码关注云+社区