本文基于vsomeip 3.1.20.3总结而成
源码地址:https://github.com/GENIVI/vsomeip.git
vsomeip协议栈之初始化流程
application的创建
在之前的文章中有写过双机通讯的Demo来了解使用API,这篇文章就从API的入口开始,来分析一下整个协议栈的源码实现,application共享指针是通过Runtime来创建的
using namespace vsomeip;
std::shared_ptr<runtime> rtm_ = runtime::get();
std::shared_ptr<application> app_= rtm_->create_application("name")
对应的源码如下:
std::shared_ptr<application> runtime_impl::create_application(
const std::string &_name) {
static std::uint32_t postfix_id = 0;
std::lock_guard<std::mutex> its_lock(applications_mutex_);
std::string its_name_ = _name;
//从当前runtime的环境中查找是否存在相同名称的application引用,
//如果已经存在相同名称的app,则将当前的name加上后缀之后重命名
//然后创建一个application的共享指针,并保存到运行环境的缓存map中
auto found_application = applications_.find(_name);
if( found_application != applications_.end()) {
its_name_ += "_" + std::to_string(postfix_id++);
}
std::shared_ptr<application> application = std::make_shared<application_impl>(its_name_);
applications_[its_name_] = application;
return application;
}
从上面代码中我们可以看到,创建app的流程比较简单,就是实例化一个application类,并创建其共享指针,然后按照名称将对象的弱指针保存到运行时环境的map缓存中。
application.hpp的实现在vsomeip/implementation/runtime/application_impl.cpp中,类中定义了众多的属性跟方法,以及回调函数,这里我们只关心在初始化过程中的相关属性值
application_impl::application_impl(const std::string &_name)
: runtime_(runtime::get()),
client_(VSOMEIP_CLIENT_UNSET), //默认client id = 0xFFFF
session_(0), //初始化session id = 0
is_initialized_(false),
name_(_name), //应用名称
work_(std::make_shared<boost::asio::io_service::work>(io_)),
//route_manager指针, 有两种角色,一个是host,一个是proxy,实现不一样
routing_(0),
//初始化app状态为DEREGISTERRED状态
state_(state_type_e::ST_DEREGISTERED),
//安全模式
security_mode_(security_mode_e::SM_OFF),
#ifdef VSOMEIP_ENABLE_SIGNAL_HANDLING
signals_(io_, SIGINT, SIGTERM),
catched_signal_(false),
#endif
is_dispatching_(false),
max_dispatchers_(VSOMEIP_MAX_DISPATCHERS),
max_dispatch_time_(VSOMEIP_MAX_DISPATCH_TIME),
stopped_(false),
block_stopping_(false),
is_routing_manager_host_(false),
stopped_called_(false),
watchdog_timer_(io_),
client_side_logging_(false)
至此,application的创建流程完毕,主要是调用runtime的api,创建application对象,然后初始化application中的一些属性值。
init流程
顺着demo中的api调用往下走的话,就来到了application的init流程,这个流程中又包含了若干个子模块的初始化流程,比较复杂,按照阶段,将其归类为如下几个阶段:
- 配置模块初始化
- 路由模块初始化
流程图如下所示
下面开始手撕代码, 内容比较多,省略了中间client side logging的处理逻辑:
bool application_impl::init() {
//首先如果应用已经被初始化了,则直接不处理返回初始化成功
if(is_initialized_) {
VSOMEIP_WARNING << "Trying to initialize an already initialized application.";
return true;
}
//如果应用程序的名称为空,那么此处就从环境变量中取application名称
// Application name
if (name_ == "") {
const char *its_name = getenv(VSOMEIP_ENV_APPLICATION_NAME);
if (nullptr != its_name) {
name_ = its_name;
}
}
std::string configuration_path;
// load configuration from module
std::string config_module = "";
const char *its_config_module = getenv(VSOMEIP_ENV_CONFIGURATION_MODULE);
if (nullptr != its_config_module) {
// TODO: Add loading of custom configuration module
} else { // load default module
#ifndef VSOMEIP_ENABLE_MULTIPLE_ROUTING_MANAGERS
//如果协议栈不支持多路由管理,则配置信息模块的创建以及初始化走这里
//从插件管理器中查找配置信息模块。这个在下文中单独列出
auto its_plugin = plugin_manager::get()->get_plugin(
plugin_type_e::CONFIGURATION_PLUGIN, VSOMEIP_CFG_LIBRARY);
if (its_plugin) {
auto its_configuration_plugin
= std::dynamic_pointer_cast<configuration_plugin>(its_plugin);
if (its_configuration_plugin) {
//通过配置信息插件获取应用程序对应的配置信息
configuration_ = its_configuration_plugin->get_configuration(name_);
VSOMEIP_INFO << "Configuration module loaded.";
} else {
std::cerr << "Invalid configuration module!" << std::endl;
std::exit(EXIT_FAILURE);
}
} else {
std::cerr << "Configuration module could not be loaded!" << std::endl;
std::exit(EXIT_FAILURE);
}
#else
//如果协议栈定义了多路由管理,则配置信息模块的初始化走这里
configuration_ = std::dynamic_pointer_cast<configuration>(
std::make_shared<vsomeip_v3::cfg::configuration_impl>());
if (configuration_path.length()) {
configuration_->set_configuration_path(configuration_path);
}
configuration_->load(name_);
#endif // VSOMEIP_ENABLE_MULTIPLE_ROUTING_MANAGERS
}
//这里是初始化安全模式,根据配置信息文件中的定义来取值
// Set security mode
auto its_security = security::get();
if (its_security->is_enabled()) {
if (its_security->is_audit()) {
security_mode_ = security_mode_e::SM_AUDIT;
} else {
security_mode_ = security_mode_e::SM_ON;
}
} else {
security_mode_ = security_mode_e::SM_OFF;
}
...
...
...
std::shared_ptr<configuration> its_configuration = get_configuration();
if (its_configuration) {
VSOMEIP_INFO << "Initializing vsomeip application \"" << name_ << "\".";
client_ = its_configuration->get_id(name_);
// Max dispatchers is the configured maximum number of dispatchers and
// the main dispatcher
max_dispatchers_ = its_configuration->get_max_dispatchers(name_) + 1;
max_dispatch_time_ = its_configuration->get_max_dispatch_time(name_);
#ifdef VSOMEIP_HAS_SESSION_HANDLING_CONFIG
has_session_handling_ = its_configuration->has_session_handling(name_);
if (!has_session_handling_)
VSOMEIP_INFO << "application: " << name_
<< " has session handling switched off!";
#endif // VSOMEIP_HAS_SESSION_HANDLING_CONFIG
//初始化路由模块
//首先从当前应用程序的配置信息中获取路由管理的名称
std::string its_routing_host = its_configuration->get_routing_host();
if (its_routing_host != "") {
//如果配置的路由管理名称与应用程序名称一致,则表示当前的应用程序也是路由管理者
//(host模式,反之为proxy模式)
is_routing_manager_host_ = (its_routing_host == name_);
if (is_routing_manager_host_ &&
!utility::is_routing_manager(configuration_)) {
#ifndef VSOMEIP_ENABLE_MULTIPLE_ROUTING_MANAGERS
VSOMEIP_ERROR << "application: " << name_ << " configured as "
"routing but other routing manager present. Won't "
"instantiate routing";
is_routing_manager_host_ = false;
return false;
#else
is_routing_manager_host_ = true;
#endif // VSOMEIP_ENABLE_MULTIPLE_ROUTING_MANAGERS
}
} else {
//如果配置信息中没有找到相关路由管理名称,则根据环境变量中定义的基础路径中的文件锁去
//判断当前应用程序是否为路由,能操作文件锁,则为路由。
is_routing_manager_host_ = utility::is_routing_manager(configuration_);
}
if (is_routing_manager_host_) {
//创建host类型的路由
VSOMEIP_INFO << "Instantiating routing manager [Host].";
if (client_ == VSOMEIP_CLIENT_UNSET) {
client_ = static_cast<client_t>(
(configuration_->get_diagnosis_address() << 8)
& configuration_->get_diagnosis_mask());
utility::request_client_id(configuration_, name_, client_);
}
routing_ = std::make_shared<routing_manager_impl>(this);
} else {
//创建proxy类型的路由代理
VSOMEIP_INFO << "Instantiating routing manager [Proxy].";
routing_ = std::make_shared<routing_manager_proxy>(this, client_side_logging_, client_side_logging_filter_);
}
//设置当前的client id
routing_->set_client(client_);
//路由模块初始化
routing_->init();
#ifdef USE_DLT
// Tracing
std::shared_ptr<trace::connector_impl> its_connector
= trace::connector_impl::get();
std::shared_ptr<cfg::trace> its_trace_configuration
= its_configuration->get_trace();
its_connector->configure(its_trace_configuration);
#endif
VSOMEIP_INFO << "Application(" << (name_ != "" ? name_ : "unnamed")
<< ", " << std::hex << std::setw(4) << std::setfill('0') << client_
<< ") is initialized ("
<< std::dec << max_dispatchers_ << ", "
<< std::dec << max_dispatch_time_ << ").";
is_initialized_ = true;
}
#ifdef VSOMEIP_ENABLE_SIGNAL_HANDLING
//这里如果协议栈编译支持系统信号处理,则添加信号监听器,用于应用停止。
if (is_initialized_) {
signals_.add(SIGINT);
signals_.add(SIGTERM);
// Register signal handler
auto its_signal_handler =
[this] (boost::system::error_code const &_error, int _signal) {
if (!_error) {
switch (_signal) {
case SIGTERM:
case SIGINT:
catched_signal_ = true;
stop();
break;
default:
break;
}
}
};
signals_.async_wait(its_signal_handler);
}
#endif
//通知当前应用状态变更事件
if (configuration_) {
auto its_plugins = configuration_->get_plugins(name_);
auto its_app_plugin_info = its_plugins.find(plugin_type_e::APPLICATION_PLUGIN);
if (its_app_plugin_info != its_plugins.end()) {
for (auto its_library : its_app_plugin_info->second) {
auto its_application_plugin = plugin_manager::get()->get_plugin(
plugin_type_e::APPLICATION_PLUGIN, its_library);
if (its_application_plugin) {
VSOMEIP_INFO << "Client 0x" << std::hex << get_client()
<< " Loading plug-in library: " << its_library << " succeeded!";
std::dynamic_pointer_cast<application_plugin>(its_application_plugin)->
on_application_state_change(name_, application_plugin_state_e::STATE_INITIALIZED);
}
}
}
} else {
std::cerr << "Configuration module could not be loaded!" << std::endl;
std::exit(EXIT_FAILURE);
}
//返回初始化结果
return is_initialized_;
}
上述对application的init函数中的功能做了一个整体流程梳理,流程中还包含了两个比较大的模块的初始化,这里我们来接着跟。
配置信息模块初始化
首先是配置信息模块的初始化流程,在之前的demo中我们有设置VSOMEIP_CONFIGURATION这个环境变量来指定xxx.json这个文件的路径,这个文件就是协议栈运行所配置的一些信息,在协议栈的初始化过程中,会解析这个json文件,然后将其中的配置字存放在内存中供其他模块使用查询。那么现在我们就来看看这个配置信息模块的一个初始化流程吧。
配置信息加载的流程实现,在vsomeip/implementation/configuration/src/configuration_impl.cpp这个文件中实现,configuration_impl这个类的实例由插件管理器通过dlopen加载libvsomeip3-cfg.so库来完成,插件管理器的实现这里先不分析,我们只需知道通过配置插件模块中的get_configuration函数得到了一个类型为configuration_impl的共享指针即可。
//传入的参数就是application的名称
std::shared_ptr<configuration>
configuration_plugin_impl::get_configuration(const std::string &_name) {
std::lock_guard<std::mutex> its_lock(mutex_);
if (!default_) {
default_ = std::make_shared<cfg::configuration_impl>();
default_->load(_name);
}
接下来就到了configuration_impl中的load方法中,这个函数里面做了加载解析的主要逻辑,主要工作如下:
- 定位配置文件或者文件夹的路径
代码逻辑如下:
// Predefine file / folder
/*1. 首先初始化文件或者文件夹的路径为宏定义中的默认路径,VSOMEIP_DEFAULT_CONFIGURATION_FILE宏定义了
默认配置文件的路径,VSOMEIP_DEFAULT_CONFIGURATION_FOLDER定义了默认配置文件夹的路径(这个默认的
是最低优先级,如果后续两个配置存在,则默认的不起作用)*/
std::string its_file(VSOMEIP_DEFAULT_CONFIGURATION_FILE); // configuration file
std::string its_folder(VSOMEIP_DEFAULT_CONFIGURATION_FOLDER); // configuration folder
//再去获取可执行程序所在路径中是否存在配置文件与文件夹,如果存在,则覆盖默认的配置
// Override with local file / folder (if existing)
std::string its_local_file(VSOMEIP_LOCAL_CONFIGURATION_FILE);
if (utility::is_file(its_local_file)) {
its_file = its_local_file;
}
std::string its_local_folder(VSOMEIP_LOCAL_CONFIGURATION_FOLDER);
if (utility::is_folder(its_local_folder)) {
its_folder = its_local_folder;
}
//再从进程的环境变量中查找名为VSOMEIP_CONFIGURATION的环境变量,如果环境变量中保存的值是一个文件
// 路径,则使用该配置文件,如果是文件夹,则从该文件夹中查找配置
// Override with path from environment (if existing)
const char *its_env = getenv(VSOMEIP_ENV_CONFIGURATION);
if (nullptr != its_env) {
if (utility::is_file(its_env)) {
its_file = its_env;
its_folder = "";
} else if (utility::is_folder(its_env)) {
its_folder = its_env;
its_file = "";
}
}
拿到路径后,基本上就是获取json文件,解析json文件,逻辑依次如下:
load_logging(e, its_warnings)
load_unicast_address(e);
load_netmask(e);
load_device(e);
load_service_discovery(e);
load_npdu_default_timings(e);
load_services(e);
load_internal_services(e);
load_clients(e);
load_watchdog(e);
load_selective_broadcasts_support(e);
load_e2e(e);
load_debounce(e);
load_acceptances(e);
load_secure_services(e);
解析完成后,所有的值都会被保存在内存中,后续供给其他模块使用。
路由模块初始化
配置信息解析完成后,在主流程的代码里面看到有对route_manager接口的初始化,路由模块的实现在源码的vsomeip/implementation/routing中,路由模块会根据角色不同分为两种,分别是host与proxy, 他们其中的关系如下图:
上面简单的画了一个图梳理了一下路由中各个接口的对应关系,这个在后面梳理消息传递的逻辑中对照着看会比较轻松。这里没有画类图,接口中的方法实在是太多了,大家将就着看。
Host端路由模块初始化
Host模式的路由管理器实现在routing_manager_impl.cpp中, 构造函数如下:
routing_manager_impl::routing_manager_impl(routing_manager_host *_host) :
routing_manager_base(_host),
version_log_timer_(_host->get_io()),
if_state_running_(false),
sd_route_set_(false),
routing_running_(false),
status_log_timer_(_host->get_io()),
memory_log_timer_(_host->get_io()),
//创建了一个通信端点管理器
ep_mgr_impl_(std::make_shared<endpoint_manager_impl>(this, io_, configuration_)),
pending_remote_offer_id_(0),
last_resume_(std::chrono::steady_clock::now().min()),
statistics_log_timer_(_host->get_io()),
ignored_statistics_counter_(0)
{
}
routing_manager_base::routing_manager_base(routing_manager_host *_host) :
//这个host_的指针指向application_impl
host_(_host),
io_(host_->get_io()),
client_(host_->get_client()),
configuration_(host_->get_configuration())
#ifdef USE_DLT
, tc_(trace::connector_impl::get())
#endif
{
...
...
}
然后再看init方法
void routing_manager_impl::init() {
//将通信端点管理器赋给父类中的ep_mgr_指针
routing_manager_base::init(ep_mgr_impl_);
//创建stub端
// TODO: Only instantiate the stub if needed
stub_ = std::make_shared<routing_manager_stub>(this, configuration_);
//stub端执行init
stub_->init();
/*
stub的init代码就是如下一行,创建一个用于本地域通讯的服务端
endpoint_ = host_->get_endpoint_manager()->create_local_server(
&is_socket_activated_, shared_from_this());
*/
//加载SD模块,SD模块的init流程又是一大块,放在文后分析
if (configuration_->is_sd_enabled()) {
VSOMEIP_INFO<< "Service Discovery enabled. Trying to load module.";
auto its_plugin = plugin_manager::get()->get_plugin(
plugin_type_e::SD_RUNTIME_PLUGIN, VSOMEIP_SD_LIBRARY);
if (its_plugin) {
VSOMEIP_INFO << "Service Discovery module loaded.";
discovery_ = std::dynamic_pointer_cast<sd::runtime>(its_plugin)->create_service_discovery(this, configuration_);
discovery_->init();
} else {
VSOMEIP_ERROR << "Service Discovery module could not be loaded!";
std::exit(EXIT_FAILURE);
}
}
好了,到这里总结一下,host的路由管理在初始化的时候,application_impl会创建routing_manager_impl类,并将自身的指针传给路由实现类,路由实现类创建的时候,会创建通信端点管理类,路由stub类,然后通过Stub类的init创建了一个用于本机内部通讯的Server端网络端点,之后再启动SD模块。
相关时序图如下:
Proxy端路由模块初始化
Proxy模式的路由管理器实现在routing_manager_proxy.cpp中, 相对于host端,proxy的初始化要简单的多,其构造函数如下:
routing_manager_proxy::routing_manager_proxy(routing_manager_host *_host,
bool _client_side_logging,
const std::set<std::tuple<service_t, instance_t> > & _client_side_logging_filter) :
routing_manager_base(_host),
is_connected_(false),
is_started_(false),
state_(inner_state_type_e::ST_DEREGISTERED),
sender_(nullptr),
receiver_(nullptr),
register_application_timer_(io_),
request_debounce_timer_ (io_),
request_debounce_timer_running_(false),
client_side_logging_(_client_side_logging),
client_side_logging_filter_(_client_side_logging_filter)
{
}
构造函数初始化了一些状态与属性
void routing_manager_proxy::init() {
routing_manager_base::init(std::make_shared<endpoint_manager_base>(this, io_, configuration_));
{
std::lock_guard<std::mutex> its_lock(sender_mutex_);
sender_ = ep_mgr_->create_local(VSOMEIP_ROUTING_CLIENT);
}
}
init函数就调用了网络通信端点管理器则创建了一个local_client_endpoint赋值给sender_, 用来与host端通信。
SD模块初始化
SD模块的初始化也比较简单,就是对SD协议中规定的各类参数进行赋值
service_discovery_impl::service_discovery_impl(
service_discovery_host *_host,
const std::shared_ptr<configuration>& _configuration)
: io_(_host->get_io()),
host_(_host),
configuration_(_configuration),
port_(VSOMEIP_SD_DEFAULT_PORT),
reliable_(false),
serializer_(std::make_shared<serializer>(
configuration_->get_buffer_shrink_threshold())),
deserializer_(std::make_shared<deserializer>(
configuration_->get_buffer_shrink_threshold())),
ttl_timer_(_host->get_io()),
ttl_timer_runtime_(VSOMEIP_SD_DEFAULT_CYCLIC_OFFER_DELAY / 2),
ttl_(VSOMEIP_SD_DEFAULT_TTL),
subscription_expiration_timer_(_host->get_io()),
max_message_size_(VSOMEIP_MAX_UDP_SD_PAYLOAD),
initial_delay_(0),
offer_debounce_time_(VSOMEIP_SD_DEFAULT_OFFER_DEBOUNCE_TIME),
repetitions_base_delay_(VSOMEIP_SD_DEFAULT_REPETITIONS_BASE_DELAY),
repetitions_max_(VSOMEIP_SD_DEFAULT_REPETITIONS_MAX),
cyclic_offer_delay_(VSOMEIP_SD_DEFAULT_CYCLIC_OFFER_DELAY),
offer_debounce_timer_(_host->get_io()),
find_debounce_time_(VSOMEIP_SD_DEFAULT_FIND_DEBOUNCE_TIME),
find_debounce_timer_(_host->get_io()),
main_phase_timer_(_host->get_io()),
is_suspended_(false),
is_diagnosis_(false),
last_msg_received_timer_(_host->get_io()),
last_msg_received_timer_timeout_(VSOMEIP_SD_DEFAULT_CYCLIC_OFFER_DELAY +
(VSOMEIP_SD_DEFAULT_CYCLIC_OFFER_DELAY / 10)) {
next_subscription_expiration_ = std::chrono::steady_clock::now() + std::chrono::hours(24);
}
void
service_discovery_impl::init() {
runtime_ = std::dynamic_pointer_cast<sd::runtime>(
plugin_manager::get()->get_plugin(
plugin_type_e::SD_RUNTIME_PLUGIN, VSOMEIP_SD_LIBRARY));
unicast_ = configuration_->get_unicast_address();
sd_multicast_ = configuration_->get_sd_multicast();
boost::system::error_code ec;
sd_multicast_address_ = boost::asio::ip::address::from_string(sd_multicast_, ec);
port_ = configuration_->get_sd_port();
reliable_ = (configuration_->get_sd_protocol() == "tcp");
max_message_size_ = (reliable_ ? VSOMEIP_MAX_TCP_SD_PAYLOAD :
VSOMEIP_MAX_UDP_SD_PAYLOAD);
ttl_ = configuration_->get_sd_ttl();
// generate random initial delay based on initial delay min and max
std::uint32_t initial_delay_min =
configuration_->get_sd_initial_delay_min();
std::uint32_t initial_delay_max =
configuration_->get_sd_initial_delay_max();
if (initial_delay_min > initial_delay_max) {
const std::uint32_t tmp(initial_delay_min);
initial_delay_min = initial_delay_max;
initial_delay_max = tmp;
}
std::random_device r;
std::mt19937 e(r());
std::uniform_int_distribution<std::uint32_t> distribution(
initial_delay_min, initial_delay_max);
initial_delay_ = std::chrono::milliseconds(distribution(e));
repetitions_base_delay_ = std::chrono::milliseconds(
configuration_->get_sd_repetitions_base_delay());
repetitions_max_ = configuration_->get_sd_repetitions_max();
cyclic_offer_delay_ = std::chrono::milliseconds(
configuration_->get_sd_cyclic_offer_delay());
offer_debounce_time_ = std::chrono::milliseconds(
configuration_->get_sd_offer_debounce_time());
ttl_timer_runtime_ = cyclic_offer_delay_ / 2;
ttl_factor_offers_ = configuration_->get_ttl_factor_offers();
ttl_factor_subscriptions_ = configuration_->get_ttl_factor_subscribes();
last_msg_received_timer_timeout_ = cyclic_offer_delay_
+ (cyclic_offer_delay_ / 10);
}
基本的初始化分析完了,下一篇分析application的启动流程