Apollo:源码分析之cyber/mainboard启动入口介绍

软件结构图

在这里插入图片描述

cyber入口

cyber的入口在"cyber/mainboard"目录中:

├── mainboard.cc           // 主函数
├── module_argument.cc     // 模块输入参数
├── module_argument.h
├── module_controller.cc   // 模块加载,卸载
└── module_controller.h

mainboard中的文件比较少,也很好理解,我们先从"mainboard.cc"中开始分析:

int main(int argc, char** argv) {
 // 解析参数 
  ModuleArgument module_args;
  module_args.ParseArgument(argc, argv);

 // 初始化cyber
  apollo::cyber::Init(argv[0]);

// 加载模块
  ModuleController controller(module_args);
  if (!controller.Init()) {
    controller.Clear();
    AERROR << "module start error.";
    return -1;
  }

  // 等待cyber关闭
  apollo::cyber::WaitForShutdown();
    // 卸载模块
  controller.Clear();
  AINFO << "exit mainboard.";

  return 0;
}

mainboard 是 cyberRT 的入口,init 方法都在这里触发。Module 也在这里启动。

解析参数ModuleArgument

解析参数是在"ModuleArgument"类中实现的,主要是解析加载DAG文件时候带的参数

void ModuleArgument::ParseArgument(const int argc, char* const argv[]) {
  // 二进制模块名称
  binary_name_ = std::string(basename(argv[0]));
    // 解析参数
  GetOptions(argc, argv);

  // 如果没有process_group_和sched_name_,则赋值为默认值
  if (process_group_.empty()) {
    process_group_ = DEFAULT_process_group_;
  }

  if (sched_name_.empty()) {
    sched_name_ = DEFAULT_sched_name_;
  }

  // 如果有,则设置对应的参数
  GlobalData::Instance()->SetProcessGroup(process_group_);
  GlobalData::Instance()->SetSchedName(sched_name_);

 // 打印dag_conf配置
  AINFO << "binary_name_ is " << binary_name_ << ", process_group_ is "
        << process_group_ << ", has " << dag_conf_list_.size() << " dag conf";
  for (std::string& dag : dag_conf_list_) {
    AINFO << "dag_conf: " << dag;
  }
}

模块加载ModuleController

在"ModuleController"实现cyber模块的加载,在"ModuleController::Init()"中调用"LoadAll()"来加载所有模块
在这里插入图片描述

我们接着看cyber是如何加载模块。

  1. 首先是找到模块的路径
   if (module_config.module_library().front() == '/') {
      load_path = module_config.module_library();
    } else {
      load_path =
          common::GetAbsolutePath(work_root, module_config.module_library());
    }
  1. 调用LoadModule载入模块。
   if (!LoadModule(module_path)) {
   
    }

在这里面,是通过class_loader_manager_加载模块

(1) 然后调用类加载器class_loader_manager_加载好对应的类(通过load_path)
在这里插入图片描述
(2)加载好对应的类之后在创建对应的对象,并且初始化对象(调用对象的Initialize()方法,也就是说所有的cyber模块都是通过Initialize()方法启动的)

    // 加载模块
    for (auto& component : module_config.components()) {
      const std::string& class_name = component.class_name();
      // 创建对象
      std::shared_ptr<ComponentBase> base =
          class_loader_manager_.CreateClassObj<ComponentBase>(class_name);
      // 调用对象的Initialize方法
      if (base == nullptr || !base->Initialize(component.config())) {
        return false;
      }
      component_list_.emplace_back(std::move(base));
    }

    // 加载定时器模块
    for (auto& component : module_config.timer_components()) {
      const std::string& class_name = component.class_name();
      std::shared_ptr<ComponentBase> base =
          class_loader_manager_.CreateClassObj<ComponentBase>(class_name);
      if (base == nullptr || !base->Initialize(component.config())) {
        return false;
      }
      component_list_.emplace_back(std::move(base));
    }

cyber中实现了类型通过动态加载并且实例化类的功能,好处是可以动态加载和关闭单个cyber模块(定位,感知,规划等),也就是在dreamview中的模块开关按钮,实际上就是动态的加载和卸载对应的模块。

小结

cyber mainboard总流程如下

  • cyber main函数中先解析dag参数
  • 然后根据解析的参数,通过类加载器动态的加载对应的模块
  • 最后调用Initialize方法初始化模块

utility

ClassFactory

可以看到有如下继承关系"ClassFactory -> AbstractClassFactory -> AbstractClassFactoryBase",其中"ClassFactory"和"AbstractClassFactory"为模板类,主要的实现在"AbstractClassFactoryBase"中,我们逐个分析:
首先是类初始化,指定了"relative_library_path_", “base_class_name_”, “class_name_”

AbstractClassFactoryBase::AbstractClassFactoryBase(
    const std::string& class_name, const std::string& base_class_name)
    : relative_library_path_(""),
      base_class_name_(base_class_name),
      class_name_(class_name) {}

设置OwnedClassLoader,而"RemoveOwnedClassLoader"同理。

void AbstractClassFactoryBase::AddOwnedClassLoader(ClassLoader* loader) {
  if (std::find(relative_class_loaders_.begin(), relative_class_loaders_.end(),
                loader) == relative_class_loaders_.end()) {
    relative_class_loaders_.emplace_back(loader);
  }
}

classloader是否属于该classFactory

bool AbstractClassFactoryBase::IsOwnedBy(const ClassLoader* loader) {
  std::vector<ClassLoader*>::iterator itr = std::find(
      relative_class_loaders_.begin(), relative_class_loaders_.end(), loader);
  return itr != relative_class_loaders_.end();
}

也就是说一个ClassFactory能够生产一个路径下的所有类,一个ClassFactory可能会有好几个ClassLoader,分为base_class_name和class_name。

工具函数

接下来我们看"class_loader_utility.cc"的实现,文件中实现了很多函数,这个分析如下:

创建对象(CreateClassObj)的具体实现如下,先找到类对应的factory,然后通过factory创建对象。

template <typename Base>
Base* CreateClassObj(const std::string& class_name, ClassLoader* loader) {
  GetClassFactoryMapMapMutex().lock();
  ClassClassFactoryMap& factoryMap =
      GetClassFactoryMapByBaseClass(typeid(Base).name());
  AbstractClassFactory<Base>* factory = nullptr;
  if (factoryMap.find(class_name) != factoryMap.end()) {
    factory = dynamic_cast<utility::AbstractClassFactory<Base>*>(
        factoryMap[class_name]);
  }
  GetClassFactoryMapMapMutex().unlock();

  Base* classobj = nullptr;
  if (factory && factory->IsOwnedBy(loader)) {
    classobj = factory->CreateObj();
  }

  return classobj;
}

注册类到factory

template <typename Derived, typename Base>
void RegisterClass(const std::string& class_name,
                   const std::string& base_class_name) 

查找classloader中所有类的名称

template <typename Base>
std::vector<std::string> GetValidClassNames(ClassLoader* loader)

加载类,通过指定的classloader加载指定路径下的库。

bool LoadLibrary(const std::string& library_path, ClassLoader* loader) {
  // 类是否已经被加载,如果被加载则对应的class_factory加上依赖的class_loader  
  if (IsLibraryLoadedByAnybody(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;
  }

  PocoLibraryPtr poco_library = nullptr;
  static std::recursive_mutex loader_mutex;
  {
    std::lock_guard<std::recursive_mutex> lck(loader_mutex);

    try {
      // 设置当前激活的classloader, 当前加载库路径
      SetCurActiveClassLoader(loader);
      SetCurLoadingLibraryName(library_path);
      // 创建poco_library
      poco_library = PocoLibraryPtr(new Poco::SharedLibrary(library_path));
    } catch (const Poco::LibraryLoadException& e) {
      SetCurLoadingLibraryName("");
      SetCurActiveClassLoader(nullptr);
      AERROR << "poco LibraryLoadException: " << e.message();
    } catch (const Poco::LibraryAlreadyLoadedException& e) {
      SetCurLoadingLibraryName("");
      SetCurActiveClassLoader(nullptr);
      AERROR << "poco LibraryAlreadyLoadedException: " << e.message();
    } catch (const Poco::NotFoundException& e) {
      SetCurLoadingLibraryName("");
      SetCurActiveClassLoader(nullptr);
      AERROR << "poco NotFoundException: " << e.message();
    }

    SetCurLoadingLibraryName("");
    SetCurActiveClassLoader(nullptr);
  }

  if (poco_library == nullptr) {
    AERROR << "poco 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(GetLibPathPocoShareLibMutex());
  LibpathPocolibVector& opened_libraries = GetLibPathPocoShareLibVector();
  // 保存加载路径和对应的poco_library
  opened_libraries.emplace_back(
      std::pair<std::string, PocoLibraryPtr>(library_path, poco_library));
  return true;
}

Component

  • Component实际上是cyber为了帮助我们特意实现的对象,component加载的时候会自动帮助我们创建一个Node,通过Node来订阅和发布对象,每个Component只能有一个Node
  • component对用户提供2个接口"Init()“和"Proc()”,用户在Init中初始化,在Proc中执行具体的算法。对用户隐藏的部分包括component的"Initialize()"的初始化,以及"Process()"调用执行
  • component还可以动态的加载和卸载,这也可以对应到在dreamviewer上动态的打开关系模块。

component的工作流程大致如下:

  • 通过继承"cyber::Component",用户自定义一个模块,并且实现"Init()"和"Proc()“函数。编译生成”.so"文件。
  • 通过classloader加载component模块到内存,创建component对象,调用"Initialize()"初始化。(Initialize中会调用Init)
  • 创建协程任务,并且注册"Process()"回调,当数据到来的时候,唤醒对象的协程任务执行"Process()"处理数据。(Process会调用Proc)

综上所述,component帮助用户把初始化和数据收发的流程进行了封装,减少了用户的工作量,component封装了整个数据的收发流程,component本身并不是单独的一个线程执行,模块的初始化都在主线程中执行,而具体的任务则是在协程池中执行。

目录结构

我们先看下component的目录结构。

.
├── BUILD
├── component_base.h             // 基础组件
├── component.h                  // 组件
├── component_test.cc
├── timer_component.cc           // 定时组件  
├── timer_component.h
└── timer_component_test.cc

https://github.com/daohu527/dig-into-apollo/tree/main/cyber/source

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值