linux调用ocx插件_vpp plugins插件相关介绍

微信公众号:DPDK VPP源码分析
关注公众号可了解更多的学习资源,请留言:资料。问题或建议,请公众号留言;如果你觉得内容对你有帮助,欢迎赞赏[1]

   前面文章有介绍过通过make-plugin.sh命令行自动生成plugins的架构,vpp 软件架构介绍;本文就来讲解一下plugins插件的加载和使用流程。

plugins加载流程

plugins插件加载是比较靠前的,应为plugins里面会有node节点的注册,需要在生成node节点注册及node图初始化完成之前。下面是阅读源码后总结的流程:

 1int main (int argc, char *argv[]) /*main函数位置src\vpp\vnet\main.c*/ 2|   |--解析配置文件是否配置plugin_path路径,如果配置存储到全局变量vlib_plugin_path  3|   |   (!strncmp (argv[i], "plugin_path", 11)) vlib_plugin_path = argv[++i];
4|   |--vpe_main_init (vm)/*如果没由配置plugin_path路径,如果动态查询*/
5|      |--vpp_find_plugin_path()/*查询plugin目录,配置到全局变量vlib_plugin_path */
6|   |--vlib_unix_main() 
7|      |--vlib_plugin_config() /*解析配置文件中plugins配置信息*/
8|      |--vlib_plugin_early_init()加载插件
9|      |--vlib_main()
10|          |-- vlib_register_all_static_nodes (vm)/*node节点注册*/
11|          |--vlib_node_main_init (vm) /*node 图初始化*/

插件必须在vlib_main前执行的原因:
plugin库中有很多attribute__((__constructor))类型的构造函数是在dlopen()装载时被调用。
比如下面的宏定义完成注册,都是串联到全局的链表上,
VLIB_CONFIG_FUNCTION (acl_plugin_config, "acl-plugin");
VLIB_REGISTER_NODE (acl_in_l2_ip6_node)

通过命令行show plugins可以查询当前环境vpp加载插件信息及插件描述信息(版本号,名称,描述)。

 1DBGvpp# show plugins
2 Plugin path is: /usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
3
4     Plugin                                   Version                          Description
5  1. ioam_plugin.so                           20.09-rc0~472-g4ee78fbcc         Inbound Operations, Administration, and Maintenance (OAM)
6  2. perfmon_plugin.so                        20.09-rc0~472-g4ee78fbcc         Performance Monitor
7  3. tracedump_plugin.so                      20.09-rc0~472-g4ee78fbcc         Streaming packet trace dump plugin
8  4. urpf_plugin.so                           20.09-rc0~472-g4ee78fbcc         Unicast Reverse Path Forwarding (uRPF)
9  5. tlspicotls_plugin.so                     20.09-rc0~472-g4ee78fbcc         Transport Layer Security (TLS) Engine, Picotls Based
10  6. l3xc_plugin.so                           20.09-rc0~472-g4ee78fbcc         L3 Cross-Connect (L3XC)
11  7. mdata_plugin.so                          20.09-rc0~472-g4ee78fbcc         Buffer metadata change tracker.

plugins路径查询

上面讲到如果配置文件中没有设置plugin_path插件路径时,会动态查询,下面时函数vpp_find_plugin_path 查询插件路径的部分代码如下:

 1static void vpp_find_plugin_path () 2{
3  extern char *vat_plugin_path;
4  char *p, path[PATH_MAX];
5  int rv;
6  u8 *s;
7  /* find executable path */
8  if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
9    return;
10  /* readlink doesn't provide null termination */
11  path[rv] = 0;
12  /* strip filename strip bin 跳过执行文件名*/
13  ...
14  s = format (0, "%s/lib/" CLIB_TARGET_TRIPLET "/vpp_plugins:"
15          "%s/lib/vpp_plugins", path, path);
16  vec_add1 (s, 0);
17 /*保存插件路径*/
18  vlib_plugin_path = (char *) s;
19....
20}

上一节通过show plugins命令行查询到当前环境plugins目录:

1/usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins

这里由2点疑问:
1、readlink函数作用?
 readlink()会将参数path的符号链接内容存储到参数buf所指的内存空间,返回的内容不是以‘\0’作字符串结尾,但会将字符串的字符数返回,这使得添加‘\0’变得简单。若参数bufsiz小于符号连接的内容长度,过长的内容会被截断;如果 readlink 第一个参数指向一个文件而不是符号链接时,readlink 设 置errno 为 EINVAL 并返回 -1。readlink()函数组合了open()、read()和close()的所有操作。
path是一个存在的软连接。
path="/proc/self/exe"标识获取当前执行程序的绝对路径。

在github上写了readlink测测试程序,你可以在你环境上去执行看一下:https://github.com/jin13417/dpdk-vpp-learning/test/readlink.c

2、CLIB_TARGET_TRIPLET 这个宏应该就是x86_64-linux-gnu,怎么来的。
这里是使用了cmake的configure_file来设置的CLIB_TARGET_TRIPLET。你可以通过下面链接来学习cmake的configure_file使用。

https://www.cnblogs.com/gaox97329498/p/10952732.html

1、在源码路径上通过grep -rn CLIB_TARGET_TRIPLET 查询宏定义的位置

1/mnt/f/workspce/vpp/src$ grep -rn CLIB_TARGET_TRIPLET
2vppinfra/config.h.in:23:#define CLIB_TARGET_TRIPLET "@CMAKE_C_COMPILER_TARGET@"

2、再次查询CMAKE_C_COMPILER_TARGET,如下

1:/mnt/f/workspce/vpp/src$ grep -rn CMAKE_C_COMPILER_TARGET
2CMakeLists.txt:46:set(CMAKE_C_COMPILER_TARGET ${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)

CMAKE_SYSTEM_PROCESSOR是cmake环境变量,我们可以通过下面命令来查询设置。

1$ cmake --system-information | grep CMAKE_SYSTEM_PROCESSOR
2CMAKE_SYSTEM_PROCESSOR "x86_64"

3、configure_file设置时在vppinfra/CMakeLists.txt文件中。

1###vppinfra/CMakeLists.txt cmake编译文件生成config.h 定义了CLIB_TARGET_TRIPLET 内容
2configure_file(
3  ${CMAKE_SOURCE_DIR}/vppinfra/config.h.in
4  ${CMAKE_BINARY_DIR}/vppinfra/config.h
5)

4、下面时我编译完成后再vppinfra目录下config.h的内容

 1 cat build-root/install-vpp-native/vpp/include/vppinfra/config.h
2...
3#ifndef included_clib_config_h
4#define included_clib_config_h
5#ifndef CLIB_LOG2_CACHE_LINE_BYTES
6#define CLIB_LOG2_CACHE_LINE_BYTES 6
7#endif
8#define CLIB_TARGET_TRIPLET "x86_64-linux-gnu"
9#define CLIB_VECTOR_GROW_BY_ONE 0
10#endif

plugins配置解析

vlib_plugin_config函数完成plugins相关配置信息的解析,代码比较简单这里就不说了。下面是vpp默认statup.conf文件中plugins字段的信息内容

 1plugins {
2   ## Adjusting the plugin path depending on where the VPP plugins are
3   #path /ws/vpp/build-root/install-vpp-native/vpp/lib/vpp_plugins
4   ## Disable all plugins by default and then selectively enable specific plugins 
5   #plugin default { disable } 
6   plugin dpdk_plugin.so { enable }
7   plugin acl_plugin.so { enable }
8   ## Enable all plugins by default and then selectively disable specific plugins
9   # plugin dpdk_plugin.so { disable }
10   # plugin acl_plugin.so { disable }
11}

除了默认配置文件中写的,还又下面不经常使用的参数:
name-filter :设置过滤的插件名称
vat-path    :vat插件路径
vat-name-filter:设置vat过滤插件名称

plugin acl_plugin.so skip-version-check :跳过版本的检查。在注册插件的时候执行依赖vpp的版本时,才会检查。

1、上述配置中,设置了加载dpdk、acl查询但是命令行查询的时候,还是全部加载上了,这个因为插件默认是全部加载的,只能先disable所有的插件,再设置需要加载的插件才能生效。
2、path、plugin_path设置有什么区别?两个都设置后,那个生效?
这个需要阅读代码的实现了,默认是path的优先级更高。

plugins加载

注册插件

下面是单元测试插件的注册函数,默认指定是不需要加载的。

1VLIB_PLUGIN_REGISTER () =
2{
3  .version = VPP_BUILD_VER,
4  .description = "C unit tests",
5  .default_disabled = 1,
6};

对应相关结构体字段的描述信息。

 1/* *INDENT-OFF 注册插件结构体字段描述* */
2typedef CLIB_PACKED(struct { 3  u8 default_disabled; /*是否需要加载*/ 4  const char version[32];  /*插件的版本信息*/ 5  const char version_required[32];/*设置查询依赖的vpp版本信息*/ 6  const char overrides[256];/*不是太理解*/ 7  const char *early_init; /*指定early_init函数名称,在加载so时进行初始化*/ 8  const char *description;/*插件描述信息*/ 9}) vlib_plugin_registration_t;
10/* *INDENT-ON 插件信息结构体* */
11typedef struct12{
13  u8 *name; /*plugin插件名称libcpudef_cplist_plugin.so*/
14  u8 *filename;/*plugin so文件名称libcpudef_cplist_plugin.so.0.0.0*/
15  struct stat file_info;/*文件详细信息*/
16  void *handle;/*dlopen返回的so句柄*/
17  /* plugin registration 插件注册信息*/
18  vlib_plugin_registration_t *reg;
19  char *version;/*版本信息*/
20} plugin_info_t;

平时不太关注插件注册的一些信息,但是可能在某些场景中会使用到,不如下面提供了2个比较好的参数
1、version_required:设置当前so依赖的vpp版本信息,如果和vpp版本不一会会报错。
但是也可以在配置文件中设置跳过检查,
2、early_init:指定early_init函数名称,在加载so时执行初始化
使用dlsym函数得到函数的地址,执行初始化动作。

插件加载

插件加载的流程大概意义就是读取插件目录下的文件,判断是否是.so格式后,逐个调用load_one_plugin函数来加载动态so。代码也比较简单,自己看下吧。

1vlib_plugin_early_init()
2   |---vlib_load_new_plugins()
3   |---load_one_plugin()

plugins之间互相访问

vpp本身并不支持插件之间互相访问,但是我们可以通过一些方法来解决。我工作中用的方法就行是src目录下添加一个test目录,参考src/vet/CMakeLists.txt的方式来定义自己的so库。

 1/*CMakeLists.txt如下*/
2##############################################################################
3# test Library
4##############################################################################
5add_vpp_library(test
6  SOURCES ${TEST_SOURCES}
7  MULTIARCH_SOURCES ${TEST_MULTIARCH_SOURCES}
8  INSTALL_HEADERS ${TEST_HEADERS}
9  API_FILES ${TEST_API_FILES}
10  LINK_LIBRARIES vppinfra svm vlib ${OPENSSL_LIBRARIES}
11  DEPENDS vpp_version_h api_headers
12)

把2个插件都需要调用函数,或者全局变量放在这里面来声明和定义(当然你也可以直接放在vnet下)。

总结

本文简单描述了vpp插件的加载流程及代码逻辑的一些解读,另外前段时间在vpp群里面有同学问到两个插件之间怎么互相访问。上面也说了大概实现思路。你们有更好的方法,欢迎公众号留言讨论。

8640c885317da4c9fc5caec9277040a1.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值