Cyber RT 代码分析, test ok

Cyber RT 代码分析

cyber base

xz@xiaqiu:~/study/apollo/cyber/base$ tree
.
├── atomic_hash_map.h
├── atomic_hash_map_test.cc
├── atomic_rw_lock.h
├── atomic_rw_lock_test.cc
├── bounded_queue.h
├── bounded_queue_test.cc
├── build
├── BUILD
├── CMakeLists.txt
├── concurrent_object_pool.h
├── for_each.h
├── for_each_test.cc
├── macros.h
├── main_test.cc
├── object_pool.h
├── object_pool_test.cc
├── reentrant_rw_lock.h
├── rw_lock_guard.h
├── signal.h
├── signal_test.cc
├── thread_pool.h
├── thread_safe_queue.h
├── unbounded_queue.h
├── unbounded_queue_test.cc
└── wait_strategy.h

1 directory, 24 files

测试的CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(CyberBase)

set(CMAKE_CXX_STANDARD 17) 
set(PROJECT_SOURCE_DIR "../../")

INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR})

message(${PROJECT_SOURCE_DIR})

add_executable(atomic_hash_map_test main_test.cc atomic_hash_map_test.cc)
target_link_libraries(atomic_hash_map_test -lgtest -lpthread)
#target_link_libraries(signal_test Cyber::signal)

build目录为代码生成测试目录

cyber 入口

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

xz@xiaqiu:~/study/apollo/cyber/mainboard$ tree
.
├── BUILD
├── mainboard.cc //主函数
├── module_argument.cc //模块输入参数
├── module_argument.h
├── module_controller.cc //模块加载,卸载
└── module_controller.h

0 directories, 6 files

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

#include "cyber/common/global_data.h"
#include "cyber/common/log.h"
#include "cyber/init.h"
#include "cyber/mainboard/module_argument.h"
#include "cyber/mainboard/module_controller.h"
#include "cyber/state.h"

using apollo::cyber::mainboard::ModuleArgument;
using apollo::cyber::mainboard::ModuleController;

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

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

    // start module 加载模块
    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.cc”的主函数,下面我们重点介绍下具体的过程。

解析参数

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

void ModuleArgument::DisplayUsage()
{
    AINFO << "Usage: \n    " << binary_name_ << " [OPTION]...\n"
          << "Description: \n"
          << "    -h, --help : help information \n"
          << "    -d, --dag_conf=CONFIG_FILE : module dag config file\n"
          << "    -p, --process_group=process_group: the process "
          "namespace for running this module, default in manager process\n"
          << "    -s, --sched_name=sched_name: sched policy "
          "conf for hole process, sched_name should be conf in cyber.pb.conf\n"
          << "Example:\n"
          << "    " << binary_name_ << " -h\n"
          << "    " << binary_name_ << " -d dag_conf_file1 -d dag_conf_file2 "
          << "-p process_group -s sched_name\n";
}

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_);
    AINFO << "binary_name_ is " << binary_name_ << ", process_group_ is "
          << process_group_ << ", has " << dag_conf_list_.size() << " dag conf";
	//打印 dag_conf配置,这里的 dag 是否可以设置多个?
    for (std::string &dag : dag_conf_list_)
    {
        AINFO << "dag_conf: " << dag;
    }
}

模块加载

在“ModuleController”实现 cyber 模块的加载在“ModuleController:Init0”中调用“LoadAlI() ”来加载所有模块,我们接着看 cyber 是如何加载模块。

首先是找到模块的路径。

if (module_config.module_library().front() == '/')
{
    load_path = module_config.module_library();
}
else
{
    load_path =
        common::GetAbsolutePath(work_root, module_config.module_library());
}

if (!common::PathExists(load_path))
{
    AERROR << "Path does not exist: " << load_path;
    return false;
}

通过“class_loader_manager_ ”加载模块,后面我们会接着分析“ClassLoaderManager”的具体实现,加载好对应的类之后在创建对应的允象,并且初始化对象.〈调用对象的 Initialize(方法,也就是说所有的 cyber 模块都是通过 Initialize()方法启动的,后面们会接团分析 Initialize 具体干了什么) 。这里的“classloader” 其实类似 java 中的 classloader,即 java 虚拟机在运行时加载对应的类,并且实例化人_” 对象。cyber 中其实也是实现了类型通过动态加载并且实例化类的功能,好处是可以动态加载和关闭单个 cyber模块(定位,感知,规划等),也就是在 dreamview 中的模块开关按钮,实际上就是动态的加载和印载对应的模块。

//通过类加载器加载.1oad_path 下的模class_1oader_manager_.LoadLibrary(load path);
//加载模块
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 mainboard 的整个流程,cyber main 函数中先解析 dag 参数滞然后根据解析的参数,通过类加载器动态的加载对应的模块,然后调用 Initialize方法初始化模块。

动态加载对象

类加载器(class_loader)类加载器的作用就是动态的加载动态库然后实例化对象。我们先来解释下, 首先 apollo 中的各个 module都会编译为一个动态库,拿 planning 模块来举例子,在“planning/dag/planning.dag”中,会加载:

module_config {
  module_library : "/apollo/bazel-bin/modules/planning/libplanning_component.so"

也就是说,apollo 中的模块都会通过类加载器以动态库的方式加载,然后实例化,之后再调用 Initialize 方法初始化。也就是说,我们讲清楚下面 3 个问题,也就是讲清楚了类加载器的原理。

1、cyber 如何加载 apollo 模块?

2、如何实例化模块?

3、如何初始化模块?

类加载器的实现在“cyber/class_loader”目录中,通过“Poco/SharedLibraryh”库来实现动态库的加载,关于 Poco 动态库的加载可以[参考](Class Poco::SharedLibrary)

xz@xiaqiu:~/study/apollo/cyber/class_loader$ tree
.
├── BUILD //编译文件
├── class_loader.cc//类加载器
├── class_loader.h
├── class_loader_manager.cc//类加载器管理
├── class_loader_manager.h
├── class_loader_register_macro.h//类加载器注册宏定义
├── class_loader_test.cc
├── shared_library
│   ├── BUILD
│   ├── exceptions.h
│   ├── sample.cc
│   ├── sample.h
│   ├── shared_library.cc
│   ├── shared_library.h
│   └── shared_library_test.cc
├── test
│   ├── base.h
│   ├── BUILD
│   ├── plugin1.cc
│   └── plugin2.cc
└── utility
    ├── BUILD
    ├── class_factory.cc //类工厂
    ├── class_factory.h
    ├── class_loader_utility.cc//类加载器工具类
    └── class_loader_utility.h

3 directories, 23 files

我们先从“class_loaderh”开始看起,首先我们分析下“class_loader”实现的具体方法:

/** *  for library load,createclass object */class ClassLoader { public:  explicit ClassLoader(const std::string& library_path);  virtual ~ClassLoader();	  bool IsLibraryLoaded();//库是否已经加载  bool LoadLibrary();// 贡载库  int UnloadLibrary();// 卸载库  const std::string GetLibraryPath() const;// 获取库路径  template <typename Base>  std::vector<std::string> GetValidClassNames();// 获取类名称  template <typename Base>  std::shared_ptr<Base> CreateClassObj(const std::string& class_name);// 实例化类对象  template <typename Base>  bool IsClassValid(const std::string& class_name);//判断类是否有效 private:  template <typename Base>  void OnClassObjDeleter(Base* obj); private:     std::string library_path_;// 类路径  int loadlib_ref_count_;// 类加载引用次数  std::mutex loadlib_ref_count_mutex_;// 类加载引用次数锁  int classobj_ref_count_;// 类引用次数  std::mutex classobj_ref_count_mutex_;// 类引用次数锁};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小硕算法工程师

你的鼓励将是我创作的最大动力哈

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值