当前位置:   article > 正文

百度自动驾驶apollo源码解读19:/cyber/class_loader 模块_apollo源码解析

apollo源码解析

简介

简单来讲这个模块是干什么用的:大型系统一般不是由单个可执行程序代码组成的,而是夹杂了很多动态库,linux下的就是so文件,如何把这些so里面的类导出来,可执行程序如何动态的加载他们,比如一共写了6个so,只想动态的加载其中3个,运行过程中甚至想卸载掉其中的几个用以释放相关的资源,这个模块就是干这个工作的。

1.类的导出

要想导出,必须得首先注册,来看怎么注册的,他提供了一个宏

#define CLASS_LOADER_REGISTER_CLASS(Derived, Base) \
  CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, __COUNTER__)
  • 1
  • 2

这个宏接收两个参数,即两个父子类的类型,看下这个宏的具体实现

#define CLASS_LOADER_REGISTER_CLASS_INTERNAL(Derived, Base, UniqueID)     \
  namespace {                                                             \
  struct ProxyType##UniqueID {                                            \
    ProxyType##UniqueID() {                                               \
      apollo::cyber::class_loader::utility::RegisterClass<Derived, Base>( \
          #Derived, #Base);                                               \
    }                                                                     \
  };                                                                      \
  static ProxyType##UniqueID g_register_class_##UniqueID;                 \
  }

#define CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, UniqueID) \
  CLASS_LOADER_REGISTER_CLASS_INTERNAL(Derived, Base, UniqueID)

// register class macro
#define CLASS_LOADER_REGISTER_CLASS(Derived, Base) \
  CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, __COUNTER__)

#endif  // CYBER_CLASS_LOADER_CLASS_LOADER_REGISTER_MACRO_H_
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

注意:__COUNTER__是编译器内置变量,编译器每次遇见会自动+1。
代码解释:声明一个类ProxyType132132,后面数字不会重复,依次递增,保证唯一,并静态实例化该类,作用是编译期间就会实例化,也就意味着他的构造函数早早的就被调用了(so加载期间),核心句就是调用命名空间utility下的模板函数RegisterClass对父子类型进行注册。这个注册是怎么理解的?说白了就是实例化一个模板类ClassFactory,这个ClassFactory其实就是so和可执行程序的桥梁,两边都会使用到,这个ClassFactory模板类实例里面包含了将来想实例化的父子类的信息。

2.类的导入

ClassLoaderManager里面有个模板函数CreateClassObj,举例说明:

std::shared_ptr<ComponentBase> base =
       class_loader_manager_.CreateClassObj<ComponentBase>(class_name);
  • 1
  • 2

3.阅读源代码的一些理解

  • 一个mainboad对应一个ClassLoaderManager和多个Component,这个多个Component类是根据dag文件里面写的类名进行实例化的。
  • 一个ClassLoaderManager对应多个ClassLoader,对应关系key为so的路径。
  • 一个so对应多个Component(一般只写一个)
  • 一个ClassLoader对应一个so,并且会为其完成加载工作(构造即完成加载)。
  • ClassLoader里面有个loadlib_ref_count_记录so被加载的次数,这个有什么用呢?可以被加载多次吗?(目前的我的总结是不会加载两次)
  • /cyber/class_loader/shared_library文件下的文件就是设计so的动态加载的,核心文件shared_library头文件和和实现文件。里面提供了一个sample和shared_library_test文件的例子,演示加载so库之后调用里面导出函数,如果想看演示如何使用导出类,请看
    /cyber/class_loader/test文件夹下的文件是写了例子,测试工程为class_loader_test.cc
    类SharedLibrary采用了系统函数dlopen真正的加载so,一个SharedLibrary加载一个so,
    ClassLoader的设计挺令人费解的,其核心实现大都托管给了utility命名空间下的全局函数,注册信息采用局部静态变量,这个设计方案和当初网狐设计方案差不多,导出了类吗?没有的,导出的函数,返回对应类的基类指针。
  • BaseToClassFactoryMapMap为双层map,第一层key为基类ComponentBase指针的字符串名字,value为map,第二层key为子类AbstractClassFactoryBase指针的字符串名字,value为指针类型。
  • 一个ClassLoader可以对应多个Component。
  • 关于工厂类,设计采用了三重继承,AbstractClassFactoryBase、AbstractClassFactory、ClassFactory。这个设计的目的是什么?没看懂,我的理解是继承就是为代码复用和多态,只有一个儿子,儿子只有一个孙子,这样做有意义吗?难道和其子类都是模板类有关系?因为模板类都是家族类,不是一个类,但是好像直接写一个模板类也能达到一样的目的吧,不懂。

4总结:

用简短话来说明so里面的类是怎么导出然后使用的:so里面的ClassFactory记录了父子类的信息,而且so都储存了这些ClassFactory的实例(导出的是全局函数,函数里面定义了局部静态变量);可执行程序通过导出模板函数utility::CreateClassObj创建具体的ClassFactory,利用这个ClassFactory来创建具体的类型。 动态库的动态调用,其实也没那么复杂和麻烦,原作者实现了父子类关系+模板实现,显得有些绕,好处是比较通用化。还有就是可以根据类的名字以字符串的形式实例化,这样就可以把需要实例化的类写在配置文件里面,然后加载。有一篇文章写的挺好的,可以去看看:

Cyber RT模块加载流程简介
如果想深入理解还是推荐阅读源代码。

5.代码例子

这么好的代码,能不能把他独立出来,将来自己写个项目的时候集成该功能,那不是美滋滋,说干就干,一开始我想着反正有C++源代码了,写个CMakelist.txt吧,后来想想直接用bazel不是更好,顺便学习了一下从0构建bazel。
免积分下载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/414081
推荐阅读
相关标签
  

闽ICP备14008679号