百度Apollo Cyber Component 源码解读(二)

367 阅读6分钟

Apollo (阿波罗)是一个开放的、完整的、安全的平台,将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统,快速搭建一套 属于自己的自动驾驶系统。

Cyber RT是一个高性能、高吞吐、低延时的计算运行框架,其中,动态加载技术和有向无环图(DAG)是其实现高性能重要途径之一。

Cyber RT采用了基于Component模块和有向无环图(DAG)的动态加载配置的工程框架。即将相关算法模块通过Component创建,并通过DAG拓扑定义对各Component依赖关系进行动态加载和配置,从而实现对算法进行统一调度,对资源进行统一分配。采用这个工程框架可以使算法与工程解耦,达到工程更专注工程,算法更专注算法的目的。

本文主要是接着上一篇文章讲一下ClassLoader下层的utility主要做了什么工作。 直接进入代码解析环节。

代码解析

从上一篇文章最后讲解的ClassLoader的createClassObj以及LoadLibrary函数中可以看到,他们最终都会调用utility内部的函数。

bool ClassLoader::LoadLibrary() {
  std::lock_guard<std::mutex> lck(loadlib_ref_count_mutex_);
  ++loadlib_ref_count_;
  AINFO << "Begin LoadLibrary: " << library_path_;
  // 调用utility
  return utility::LoadLibrary(library_path_, this);
}

template <typename Base>
std::shared_ptr<Base> ClassLoader::CreateClassObj(
    const std::string& class_name) {
  if (!IsLibraryLoaded()) {
    LoadLibrary();
  }

  // 调用utility
  Base* class_object = utility::CreateClassObj<Base>(class_name, this);
  if (class_object == nullptr) {
    AWARN << "CreateClassObj failed, ensure class has been registered. "
          << "classname: " << class_name << ",lib: " << GetLibraryPath();
    return std::shared_ptr<Base>();
  }

  std::lock_guard<std::mutex> lck(classobj_ref_count_mutex_);
  classobj_ref_count_ = classobj_ref_count_ + 1;
  std::shared_ptr<Base> classObjSharePtr(
      class_object, std::bind(&ClassLoader::OnClassObjDeleter<Base>, this,
                              std::placeholders::_1));
  return classObjSharePtr;
}

我们可以大概的认为ClassLoader负责提供友好的用户接口,管理引用计数;utility 实现底层功能,管理全局状态。

// 全局状态管理
BaseToClassFactoryMapMap& GetClassFactoryMapMap();      // 存储所有类工厂
LibPathSharedLibVector& GetLibPathSharedLibVector();    // 存储已加载的库

// 核心功能实现
bool LoadLibrary(const std::string& library_path, ClassLoader* loader);
void UnloadLibrary(const std::string& library_path, ClassLoader* loader);
bool IsLibraryLoaded(const std::string& library_path, ClassLoader* loader);

我们来看下utility::LoadLibrary具体的实现:

bool LoadLibrary(const std::string& library_path, ClassLoader* loader) {
  // 如果库已经被其他加载器加载,只需要将当前加载器关联到现有的类工厂对象
  if (IsLibraryLoadedByAnybody(library_path)) {
    AINFO << "lib has been loaded by others,only attach to class factory obj."
          << library_path;
    ClassFactoryVector lib_class_factory_objs =
        GetAllClassFactoryObjectsOfLibrary(library_path);
    for (auto& class_factory_obj : lib_class_factory_objs) {
      class_factory_obj->AddOwnedClassLoader(loader);
    }
    return true;
  }

  SharedLibraryPtr shared_library = nullptr;
  static std::recursive_mutex loader_mutex;
  {
    std::lock_guard<std::recursive_mutex> lck(loader_mutex);

    ...
      SetCurActiveClassLoader(loader);
      SetCurLoadingLibraryName(library_path);
      // 创建共享库实例
        // 这一步会实际加载动态库(.so文件)
        // 加载过程中会触发库中的静态初始化代码
        // 包括通过CLASS_LOADER_REGISTER_CLASS宏注册的类
      shared_library = SharedLibraryPtr(new SharedLibrary(library_path));
    ...
  if (shared_library == nullptr) {
    AERROR << "shared library failed: " << library_path;
    return false;
  }

    // 检查是否有类被成功注册
    auto num_lib_objs = GetAllClassFactoryObjectsOfLibrary(library_path).size();
    if (num_lib_objs == 0) {
        AWARN << "Class factory objs counts is 0, maybe registerclass failed.";
    }
  // 记录已加载的库,全局变量
  std::lock_guard<std::recursive_mutex> lck(GetLibPathSharedLibMutex());
  LibPathSharedLibVector& opened_libraries = GetLibPathSharedLibVector();
  opened_libraries.emplace_back(
      std::pair<std::string, SharedLibraryPtr>(library_path, shared_library));
  return true;
}

上面主要的步骤已经给出详细的注释,在这个函数中会最终去加载库,创建SharedLibrary类。SharedLibrary的Load函数会调用dlopen加载对应的库

SharedLibrary::SharedLibrary(const std::string& path) { Load(path, 0); }

void SharedLibrary::Load(const std::string& path, int flags) {
  std::lock_guard<std::mutex> lock(mutex_);
  if (handle_) throw LibraryAlreadyLoadedException(path);

  int real_flag = RTLD_LAZY;
  if (flags & SHLIB_LOCAL) {
    real_flag |= RTLD_LOCAL;
  } else {
    real_flag |= RTLD_GLOBAL;
  }
  // 加载库
  handle_ = dlopen(path.c_str(), real_flag);
  if (!handle_) {
    const char* err = dlerror();
    throw LibraryLoadException(err ? std::string(err) : path);
  }

  path_ = path;
}

上述提到的两个全局变量: BaseToClassFactoryMapMap& GetClassFactoryMapMap(); // 存储所有类工厂 LibPathSharedLibVector& GetLibPathSharedLibVector(); // 存储已加载的库

在上面的例子中也有体现。GetAllClassFactoryObjectsOfLibrary函数会通过library_path来确定是否类被成功的注册;GetLibPathSharedLibVector 函数主要是将加载库的类进行保存。

LibPathSharedLibVector 记录物理库文件的加载状态,BaseToClassFactoryMapMap 记录库中类的注册状态。 在进行库加载时,先加载物理文件,再注册类工厂;库卸载时,先清理类工厂,再卸载物理文件。

这里,大家应该会有疑问,LibPathSharedLibVector中的对象是从LoadLibrary中来,那么BaseToClassFactoryMapMap中的对象是从哪边来的呢? 从utility系列函数中我们看到有一个RegisterClass函数。

void RegisterClass(const std::string& class_name,
                   const std::string& base_class_name) {
  AINFO << "registerclass:" << class_name << "," << base_class_name << ","
        << GetCurLoadingLibraryName();

  utility::AbstractClassFactory<Base>* new_class_factory_obj =
      new utility::ClassFactory<Derived, Base>(class_name, base_class_name);
  // 添加当前激活的ClassLoader
  new_class_factory_obj->AddOwnedClassLoader(GetCurActiveClassLoader());
  new_class_factory_obj->SetRelativeLibraryPath(GetCurLoadingLibraryName());

  GetClassFactoryMapMapMutex().lock();
  ClassClassFactoryMap& factory_map =
      GetClassFactoryMapByBaseClass(typeid(Base).name());
  // 将创建的工厂类对象放入map
  factory_map[class_name] = new_class_factory_obj;
  GetClassFactoryMapMapMutex().unlock();
}

RegisterClass函数在一个定义的宏里面

#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_

通过全局搜索可以看到在插件中会调用这个函数来注册类,每个都会注册。这些宏定义会在库加载的时候初始化时被调用,也就是LoadLibrary函数中创建SharedLibrary类时候dlopen库时调用。

LoadLibrary() -> 加载.so文件 -> 执行静态初始化 -> 构造 ProxyTypeX 对象 -> RegisterClass<Derived, Base>() -> 创建 ClassFactory 对象 -> 添加到全局类工厂映射

image.png

总结

utility下面的函数可以认为是ClassLoader的底层实现,他在内部维护了一系列的函数,包括库管理函数 (LoadLibrary、UnloadLibrary),对象创建(CreateClassObj),全局状态管理(GetClassFactoryMapMap、GetLibPathSharedLibVector),类注册机制(RegisterClass)。

主要的作用如下:

a) 资源管理:

  • 管理动态库的加载和卸载

  • 管理类工厂对象的生命周期

  • 确保线程安全的资源访问

b) 类注册和创建:

  • 提供类注册机制

  • 支持运行时动态创建对象

  • 维护类型信息和工厂映射

c) 多加载器支持:

  • 允许多个类加载器共享同一个库

  • 跟踪类工厂和加载器的关系

  • 安全处理库的引用计数

d) 错误处理:

  • 处理库加载异常

  • 处理符号查找异常

  • 验证类注册状态

5. 设计特点:

  • 分层设计:

  • AbstractClassFactoryBase 提供基础功能

  • AbstractClassFactory 添加类型安全

  • ClassFactory 实现具体创建逻辑

  • 状态管理:

  • 使用全局单例管理共享状态

  • 使用互斥锁确保线程安全

  • 清晰的资源生命周期管理

  • 扩展性:

  • 支持动态加载任意类型

  • 支持多个类加载器

  • 灵活的类工厂机制