dpvs源代码分析——dpvs和ipvsadm通信流程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haolipengzhanshen/article/details/82432168

今天我们来讲讲dpvs的控制平面的代码

在dpvs和ipvsadm的通信中dpvs作为服务端,ipvsadm作为客户端,传送的媒介是套接字

一、套接字资源的初始化

在sockopt_init函数中对创建套接字,设置套接字属性,绑定套接字,在套接字上侦听数据

static inline int sockopt_init(void)
{
    struct sockaddr_un srv_addr;
    int srv_fd_flags = 0;

    INIT_LIST_HEAD(&sockopt_list);

	//进程间通信采用unix domain方式
    memset(ipc_unix_domain, 0, sizeof(ipc_unix_domain));
    strncpy(ipc_unix_domain, UNIX_DOMAIN_DEF, sizeof(ipc_unix_domain) - 1);

	//创建套接字
    srv_fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if (srv_fd < 0) {
        RTE_LOG(ERR, MSGMGR, "%s: Fail to create server socket\n", __func__);
        return EDPVS_IO;
    }

	//设置监听套接字的非阻塞属性
    srv_fd_flags = fcntl(srv_fd, F_GETFL, 0);
    srv_fd_flags |= O_NONBLOCK;
    if (-1 == fcntl(srv_fd, F_SETFL, srv_fd_flags)) {
        RTE_LOG(ERR, MSGMGR, "%s: Fail to set server socket NONBLOCK\n", __func__);
        return EDPVS_IO;
    }

    memset(&srv_addr, 0, sizeof(struct sockaddr_un));
    srv_addr.sun_family = AF_UNIX;
    strncpy(srv_addr.sun_path, ipc_unix_domain, sizeof(srv_addr.sun_path) - 1);
    unlink(ipc_unix_domain);

	//绑定套接字到网络地址
    if (-1 == bind(srv_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr))) {
        RTE_LOG(ERR, MSGMGR, "%s: Fail to bind server socket\n", __func__);
        close(srv_fd);
        unlink(ipc_unix_domain);
        return EDPVS_IO;
    }

	//侦听套接字
    if (-1 == listen(srv_fd, 1)) {
        RTE_LOG(ERR, MSGMGR, "%s: Server socket listen failed\n", __func__);
        close(srv_fd);
        unlink(ipc_unix_domain);
        return EDPVS_IO;
    }

    return EDPVS_OK;
}

二、接受连接,并收发数据

在ctrl.c文件的sockopt_ctl中调用accept接受tcp连接。

int sockopt_ctl(__rte_unused void *arg)
{
    int clt_fd;
    int ret;
    socklen_t clt_len;
    struct sockaddr_un clt_addr;
    struct dpvs_sockopts *skopt;
    struct dpvs_sock_msg *msg;
    struct dpvs_sock_msg_reply reply_hdr;
    void *reply_data = NULL;
    size_t reply_data_len = 0;

    memset(&clt_addr, 0, sizeof(struct sockaddr_un));
    clt_len = sizeof(clt_addr);

    /* Note: srv_fd is nonblock */
    /*接受新连接*/
    clt_fd = accept(srv_fd, (struct sockaddr*)&clt_addr, &clt_len);
    if (clt_fd < 0) {
        //非阻塞套接字,判断超时情况
        if (EWOULDBLOCK != errno) {
            RTE_LOG(WARNING, MSGMGR, "%s: Fail to accept client request\n", __func__);
        }
        return EDPVS_IO;
    }

    /* Note: clt_fd is block */
    //阻塞方式接收客户端的消息,先接收消息头,再接收消息体
    ret = sockopt_msg_recv(clt_fd, &msg);
    if (unlikely(EDPVS_OK != ret)) {
        close(clt_fd);
        return ret;
    }

    skopt = sockopts_get(msg);
    if (skopt) {
        if (msg->type == SOCKOPT_GET)//SOCKOPT_GET消息类型,则调用get回调函数
            ret = skopt->get(msg->id, msg->data, msg->len, &reply_data, &reply_data_len);
        else if (msg->type == SOCKOPT_SET)SOCKOPT_SET消息类型,则调用set回调函数
            ret = skopt->set(msg->id, msg->data, msg->len);
        if (ret < 0) {
            /* assume that reply_data is freed by user when callback fails */
            reply_data = NULL;
            reply_data_len = 0;
            RTE_LOG(INFO, MSGMGR, "%s: socket msg<type=%s, id=%d> callback failed\n",
                    __func__, msg->type == SOCKOPT_GET ? "GET" : "SET", msg->id);
        }

        //响应头的初始化
        memset(&reply_hdr, 0, sizeof(reply_hdr));
        reply_hdr.version = SOCKOPT_VERSION;
        reply_hdr.id = msg->id;
        reply_hdr.type = msg->type;
        reply_hdr.errcode = ret;//使用get和set函数的返回值,赋值给响应头的errcode
        strncpy(reply_hdr.errstr, dpvs_strerror(ret), SOCKOPT_ERRSTR_LEN - 1);//出错信息
        reply_hdr.len = reply_data_len;

        /* send response */
        //发送响应到对端
        ret = sockopt_msg_send(clt_fd, &reply_hdr, reply_data, reply_data_len);

        if (reply_data)
            rte_free(reply_data);

        if (EDPVS_OK != ret) {
            sockopt_msg_free(msg);
            close(clt_fd);
            return ret;
        }
    }

    sockopt_msg_free(msg);
    close(clt_fd);

    return EDPVS_OK;
}

流程是:

接收新连接 -> 接收消息 -> 判断消息类型,调用相应get/set回调函数 -> 发送响应对对端

我们来看下sockopts_get函数在什么?

static struct dpvs_sockopts* sockopts_get(struct dpvs_sock_msg *msg)
{
    struct dpvs_sockopts *skopt;
    if (unlikely(NULL == msg))
        return NULL;

    switch (msg->type) {
        case SOCKOPT_GET:
            list_for_each_entry(skopt, &sockopt_list, list) {
                if (judge_id_betw(msg->id, skopt->get_opt_min, skopt->get_opt_max)) {
                    if (unlikely(skopt->version != msg->version)) {
                        RTE_LOG(WARNING, MSGMGR, "%s: socket msg version not match\n", __func__);
                        return NULL;
                    }
                    return skopt;
                }
            }
            return NULL;
            break;
        case SOCKOPT_SET:
            list_for_each_entry(skopt, &sockopt_list, list) {
                if (judge_id_betw(msg->id, skopt->set_opt_min, skopt->set_opt_max)) {
                    if (unlikely(skopt->version != msg->version)) {
                        RTE_LOG(WARNING, MSGMGR, "%s: socket msg version not match\n", __func__);
                        return NULL;
                    }
                    return skopt;
                }
            }
            return NULL;
            break;
        default:
            RTE_LOG(WARNING, MSGMGR, "%s: unkown sock msg type: %d\n", __func__, msg->type);
    }
    return NULL;
}

使用list_for_each_entry遍历sockopt_list链表,

调用judge_id_betw函数判断msg->id的值处于skopt->set_opt_min和skopt->set_opt_max之间

调用judge_id_betw函数判断msg->id的值处于skopt->get_opt_min和skopt->get_opt_max之间

各位心里是不是有个疑问,sockopt_list链表中的元素是何时添加的呢?

啊哈,找到了,是sockopt_register函数

int sockopt_register(struct dpvs_sockopts *sockopts)
{
    if (unlikely(NULL == sockopts)) {
        RTE_LOG(WARNING, MSGMGR, "%s: invalid socket msg type\n", __func__);
        return EDPVS_INVAL;
    }

    if (sockopts_exist(sockopts)) {
        RTE_LOG(WARNING, MSGMGR, "%s: socket msg type already exist\n", __func__);
        rte_exit(EXIT_FAILURE, "sockopt type already exist ->\n"
                "\t\tget: %d - %d\n\t\tset: %d - %d\n",
                sockopts->get_opt_min, sockopts->get_opt_max,
                sockopts->set_opt_min, sockopts->set_opt_max);

        return EDPVS_EXIST;
    }

    //将sockopts元素添加到sockopt_list链表中
    list_add_tail(&sockopts->list, &sockopt_list);

    return EDPVS_OK;
}

其中末尾的list_add_tail函数将sockopts元素添加到sockopt_list链表中。

在插入之前,先调用sockopt_exist函数判断下是否存在

如netif.c模块,此模块想接收ctrl控制面的控制指令,就需要调用sockopt_register函数进行注册

int netif_ctrl_init(void)
{
    int err;

    g_master_lcore_id = rte_get_master_lcore();
    netif_get_slave_lcores(&g_slave_lcore_num, &g_slave_lcore_mask);
    netif_get_isol_rx_lcores(&g_isol_rx_lcore_num, &g_isol_rx_lcore_mask);

    if ((err = sockopt_register(&netif_sockopt)) != EDPVS_OK)
        return err;

    if ((err = lcore_stats_msg_init()) != EDPVS_OK)
        return err;

    return EDPVS_OK;
}

注册的dpvs_sockopts结构体如下

struct dpvs_sockopts netif_sockopt = {
    .version = SOCKOPT_VERSION,
    .get_opt_min = SOCKOPT_NETIF_GET_LCORE_MASK,
    .get_opt_max = SOCKOPT_NETIF_GET_MAX,
    .get = netif_sockopt_get,
    .set_opt_min = SOCKOPT_NETIF_SET_LCORE,
    .set_opt_max = SOCKOPT_NETIF_SET_MAX,
    .set = netif_sockopt_set,
};

如果在程序中,你添加了新的控制语句,

要确保下msg id的范围是在设置的get_opt_min和get_opt_max之间

要确保下msg id的范围是在设置的set_opt_min和set_opt_max之间

再细说下dpvs_sockopts结构体中的get回调函数指针类型和set回调函数指针类型

struct dpvs_sockopts {
    uint32_t version;
    struct list_head list;
    sockoptid_t set_opt_min;
    sockoptid_t set_opt_max;
    int (*set)(sockoptid_t opt, const void *in, size_t inlen);
    sockoptid_t get_opt_min;
    sockoptid_t get_opt_max;
    int (*get)(sockoptid_t opt, const void *in, size_t inlen, void **out, size_t *outlen);
};

set函数指针类型指向的函数形参

in:输入缓冲区的指针

inlen:输入缓冲区的长度

get函数指针类型指向的函数形参

in:输入缓冲区的指针

inlen:输入缓冲区的长度

out:输出缓冲区的指针

outlen:输出缓冲区的长度

相信大家对这块已经掌握的可以了,每天进步一点点,心里更踏实。

猜你喜欢

转载自blog.csdn.net/haolipengzhanshen/article/details/82432168