SOME/IP开源库Vsomeip分析5 - 策略加载

前言

非常抱歉,因为个人原因,之前说到的策略解析部分的分析拖到现在。

之前我们主要针对服务的启动和使用来进行分析。但是在整个使用过程中,也不可避免的需要进行相关的配置。

而目前vsomeip提供的配置是比较丰富的,但是官方文档介绍的比较少,以及对应策略插件的相关信息也没有介绍。

本文主要从策略加载,使用配置等等方面进行介绍。

init

之前的文章中,我们知道,整个application在init的时候会去初始化configuration相关的逻辑。

configuration是以插件的形式加载的,在编译完成之后,我们也可以看到对应的libvsomeipv3-cfg.so

插件的好处就是,在后续,如果我们觉得vsomeip本身的配置逻辑不好,也可以自己实现相关的接口进行替换。

这里init过程中主要做了如下的几件事:

  1. 加载我们在启动时配置的环境变量,如果没有指定的话,就去取默认的/etc/vsomeip路径下查找。
  2. 加载对应的so
  3. 加载自身配置
bool application_impl::init() {
  
  ...
  
  std::string configuration_path;

    // load configuration from module
    std::string config_module = "";
    //获取启动时设置的configuration文件的位置 环境变量
    const char *its_config_module = getenv(VSOMEIP_ENV_CONFIGURATION_MODULE);
    if (nullptr != its_config_module) {
        std::cerr << "test its_config_module not nullptr" << std::endl;
        // TODO: Add loading of custom configuration module
    } else { // load default module
#ifndef VSOMEIP_ENABLE_MULTIPLE_ROUTING_MANAGERS
    std::cerr << "test its_config_module is nullptr" << std::endl;
        //加载libvsomeipv3-cfg.so,并将对应的函数指针保存
        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) {
                //通过application name加载对应的configuration文件
                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
    }
  ...
}

加载so

其实加载so过程主要做的就是吧对应的函数保存到plugin manager里面,方便后续调用方便。

首先时判断是否已经加载过了,如果没有再去加载。

std::shared_ptr<plugin> plugin_manager_impl::get_plugin(plugin_type_e _type, std::string _name) {
    std::lock_guard<std::recursive_mutex> its_lock_start_stop(plugins_mutex_);
    auto its_type = plugins_.find(_type);
    if (its_type != plugins_.end()) {
        auto its_name = its_type->second.find(_name);
        if (its_name != its_type->second.end()) {
            return its_name->second;
        }
    }
    return load_plugin(_name, _type, 1);
}

这里先去load_symbol,内部实际上就是dlsym操作

std::shared_ptr<plugin> plugin_manager_impl::load_plugin(const std::string& _library,
        plugin_type_e _type, uint32_t _version) {
    void* handle = load_library(_library);
    plugin_init_func its_init_func = reinterpret_cast<plugin_init_func>(
            load_symbol(handle, VSOMEIP_PLUGIN_INIT_SYMBOL));
    if (its_init_func) {
        create_plugin_func its_create_func = (*its_init_func)();
        if (its_create_func) {
            handles_[_type][_library] = handle;
            auto its_plugin = (*its_create_func)();
            if (its_plugin) {
                if (its_plugin->get_plugin_type() == _type
                        && its_plugin->get_plugin_version() == _version) {
                    add_plugin(its_plugin, _library);
                    return its_plugin;
                } else {
                    VSOMEIP_ERROR << "Plugin version mismatch. Ignoring plugin "
                            << its_plugin->get_plugin_name();
                }
            }
        }
    }
    return nullptr;
}

之后就是add_plugin,主要就是吧加载的插件加入到plugin_manager管理的map里面。

void plugin_manager_impl::add_plugin(const std::shared_ptr<plugin> &_plugin, const std::string& _name) {
    plugins_[_plugin->get_plugin_type()][_name] = _plugin;
}

加载策略

加载完plugin之后,就是加载 策略了。

这里主要时去加载默认的策略。

//cfg插件通过application name加载对应的配置信息
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);
    }

#ifdef VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS
    auto its_configuration(default_);
    if (its_configuration->has_overlay(_name)) {
        VSOMEIP_INFO << "Loading configuration overlay for \"" << _name << "\"";
        auto its_iterator = configurations_.find(_name);
        if (its_iterator != configurations_.end()) {
            its_configuration = its_iterator->second;
        } else {
            its_configuration = std::make_shared<cfg::configuration_impl>(
                    *(its_configuration.get()));
            its_configuration->load_overlay(_name);
            configurations_[_name] = its_configuration;
        }
    }
    return its_configuration;
#else
    return default_;
#endif // VSOMEIP_ENABLE_CONFIGURATION_OVERLAYS
}

初始化configuration_impl,主要指定了一些默认的参数。

configuration_impl::configuration_impl()
    : default_unicast_("local"),
      is_loaded_(false),
      is_logging_loaded_(false),
      is_overlay_(false),
      diagnosis_(VSOMEIP_DIAGNOSIS_ADDRESS),
      diagnosis_mask_(0xFF00),
      has_console_log_(true),
      has_file_log_(false),
      has_dlt_log_(false),
      logfile_("/tmp/vsomeip.log"),
      loglevel_(vsomeip_v3::logger::level_e::LL_INFO),
      is_sd_enabled_(VSOMEIP_SD_DEFAULT_ENABLED),
      sd_protocol_(VSOMEIP_SD_DEFAULT_PROTOCOL),
      sd_multicast_(VSOMEIP_SD_DEFAULT_MULTICAST),
      sd_port_(VSOMEIP_SD_DEFAULT_PORT),
      sd_initial_delay_min_(VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MIN),
      sd_initial_delay_max_(VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MAX),
      sd_repetitions_base_delay_(VSOMEIP_SD_DEFAULT_REPETITIONS_BASE_DELAY),
      sd_repetitions_max_(VSOMEIP_SD_DEFAULT_REPETITIONS_MAX),
      sd_ttl_(VSOMEIP_SD_DEFAULT_TTL),
      sd_cyclic_offer_delay_(VSOMEIP_SD_DEFAULT_CYCLIC_OFFER_DELAY),
      sd_request_response_delay_(VSOMEIP_SD_DEFAULT_REQUEST_RESPONSE_DELAY),
      sd_offer_debounce_time_(VSOMEIP_SD_DEFAULT_OFFER_DEBOUNCE_TIME),
      max_configured_message_size_(0),
      max_local_message_size_(0),
      max_reliable_message_size_(0),
      max_unreliable_message_size_(0),
      buffer_shrink_threshold_(VSOMEIP_DEFAULT_BUFFER_SHRINK_THRESHOLD),
      trace_(std::make_shared<trace>()),
      watchdog_(std::make_shared<watchdog>()),
      log_version_(true),
      log_version_interval_(10),
      permissions_shm_(VSOMEIP_DEFAULT_SHM_PERMISSION),
      permissions_uds_(VSOMEIP_DEFAULT_UDS_PERMISSIONS),
      network_("vsomeip"),
      e2e_enabled_(false),
      log_memory_(false),
      log_memory_interval_(0),
      log_status_(false),
      log_status_interval_(0),
      endpoint_queue_limit_external_(QUEUE_SIZE_UNLIMITED),
      endpoint_queue_limit_local_(QUEUE_SIZE_UNLIMITED),
      tcp_restart_aborts_max_(VSOMEIP_MAX_TCP_RESTART_ABORTS),
      tcp_connect_time_max_(VSOMEIP_MAX_TCP_CONNECT_TIME),
      has_issued_methods_warning_(false),
      has_issued_clients_warning_(false),
      udp_receive_buffer_size_(VSOMEIP_DEFAULT_UDP_RCV_BUFFER_SIZE),
      npdu_default_debounce_requ_(VSOMEIP_DEFAULT_NPDU_DEBOUNCING_NANO),
      npdu_default_debounce_resp_(VSOMEIP_DEFAULT_NPDU_DEBOUNCING_NANO),
      npdu_default_max_retention_requ_(VSOMEIP_DEFAULT_NPDU_MAXIMUM_RETENTION_NANO),
      npdu_default_max_retention_resp_(VSOMEIP_DEFAULT_NPDU_MAXIMUM_RETENTION_NANO),
      shutdown_timeout_(VSOMEIP_DEFAULT_SHUTDOWN_TIMEOUT),
      log_statistics_(true),
      statistics_interval_(VSOMEIP_DEFAULT_STATISTICS_INTERVAL),
      statistics_min_freq_(VSOMEIP_DEFAULT_STATISTICS_MIN_FREQ),
      statistics_max_messages_(VSOMEIP_DEFAULT_STATISTICS_MAX_MSG) {
    unicast_ = unicast_.from_string(VSOMEIP_UNICAST_ADDRESS);
    netmask_ = netmask_.from_string(VSOMEIP_NETMASK);
    for (auto i = 0; i < ET_MAX; i++)
        is_configured_[i] = false;
}

接下来就是加载对应配置的逻辑了,这里 load 函数主要做了如下的事情 :

  1. 加载默认的配置文件,这部分主要时系统/etc/下面的内容
  2. 设置默认的本地配置文件内,./vsomeip.json
  3. 如果本地存在配置目录,替换掉之前默认的目录
  4. 如果在启动的时候,有主动设置过对应的配置信息,就加载设置的参数。
  5. 加载一些系统强制的相关策略
  6. 解析对应的 策略json文件
  7. 如果时routing进程 的话,加载全部的配置信息
bool configuration_impl::load(const std::string &_name) {
    std::lock_guard<std::mutex> its_lock(mutex_);
    if (is_loaded_)
        return true;

    //对应的配置文件先设置默认值
    // /etc/vsomeip.json ; /etc/vsomeip
    // Predefine file / 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)
    //设置默认的本地配置文件./vsomeip.json
    //如果存在,覆盖之前的默认文件
    std::string its_local_file(VSOMEIP_LOCAL_CONFIGURATION_FILE);
    if (utility::is_file(its_local_file)) {
        its_file = its_local_file;
    }

    //设置本地默认配置文件目录./vsomip
    //如果存在,覆盖之前的默认文件
    std::string its_local_folder(VSOMEIP_LOCAL_CONFIGURATION_FOLDER);
    if (utility::is_folder(its_local_folder)) {
        its_folder = its_local_folder;
    }

    // Override with path from environment (if existing)
    //这里读取对应的env信息(启动服务时指定的),如果存在对应的文件或者目录,替换之前的文件或者目录。
    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 = "";
        }
    }

    //创建一个set,用来存储需要加载配置文件的目录或者文件
    std::set<std::string> its_input;
    if (its_file != "") {
        its_input.insert(its_file);
    }
    if (its_folder != "") {
        its_input.insert(its_folder);
#ifndef _WIN32
        // load security configuration files from UID_GID sub folder if existing
        // 如果是非win系统,那么从对应的目录中查找以UID_GID为名字的子目录
        //例如:its_folder = /etc/vsomeip
        //当前进程的uid=1000,gid=1000,对应的安全配置目录应该是 /etc/vsomeip/1000_1000
        std::stringstream its_security_config_folder;
        its_security_config_folder << its_folder << "/" << getuid() << "_" << getgid();
        if (utility::is_folder(its_security_config_folder.str())) {
            its_input.insert(its_security_config_folder.str());
        }
#endif
    }

    // Determine standard configuration file
    its_env = getenv(VSOMEIP_ENV_MANDATORY_CONFIGURATION_FILES);
    if (nullptr != its_env) {
        std::string its_temp(its_env);
        set_mandatory(its_temp);
    } else {
        //设置相关强制性信息文件
        //vsomeip这里应该定义了很多规范化的文件名,来处理不同的配置文件,这些文件的含义目前还不清楚,后续需要继续分析
        //vsomeip_std.json,vsomeip_app.json,vsomeip_plc.json,vsomeip_log.json,vsomeip_security.json,vsomeip_whitelist.json
        set_mandatory(VSOMEIP_MANDATORY_CONFIGURATION_FILES);
    }

    // Start reading
    std::set<std::string> its_failed;

    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
    std::vector<configuration_element> its_mandatory_elements;
    std::vector<configuration_element> its_optional_elements;

    // Dummy initialization; maybe we'll find no logging configuration
    logger::logger_impl::init(shared_from_this());

    // Look for the standard configuration file
    /** void configuration_impl::read_data(const std::set<std::string> &_input,
      * std::vector<configuration_element> &_elements, std::set<std::string> &_failed,
      * bool _mandatory_only)
     **/
    //将对应的文件读出对应json的对象,然后存储再its_mandatory_elements中。这里先去读对应的规范化的文件
    read_data(its_input, its_mandatory_elements, its_failed, true);
    load_data(its_mandatory_elements, true, false);

    // If the configuration is incomplete, this is the routing manager configuration or
    // the routing is yet unknown, read the full set of configuration files
    //如果配置是不完全的,routing信息没有配置的话,那么继续加载全部的配置到routing
    if (its_mandatory_elements.empty() ||
            _name == get_routing_host() ||
            "" == get_routing_host()) {
        read_data(its_input, its_optional_elements, its_failed, false);
        load_data(its_mandatory_elements, false, true);
        load_data(its_optional_elements, true, true);
    }

    // Tell, if reading of configuration file(s) failed.
    // (This may file if the logger configuration is incomplete/missing).
    for (auto f : its_failed)
        VSOMEIP_WARNING << "Reading of configuration file \""
            << f << "\" failed. Configuration may be incomplete.";

    // set global unicast address for all services with magic cookies enabled
    set_magic_cookies_unicast_address();

    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    VSOMEIP_INFO << "Parsed vsomeip configuration in "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count()
            << "ms";

    for (auto i : its_input) {
        if (utility::is_file(i))
            VSOMEIP_INFO << "Using configuration file: \"" << i << "\".";

        if (utility::is_folder(i))
            VSOMEIP_INFO << "Using configuration folder: \"" << i << "\".";
    }

    is_loaded_ = true;

    return is_loaded_;
}

这里readdata主要就是调用json解析库去解析json信息。

void configuration_impl::read_data(const std::set<std::string> &_input,
        std::vector<configuration_element> &_elements, std::set<std::string> &_failed,
        bool _mandatory_only) {
    for (auto i : _input) {
        //如果是文件的话,直接解析json
        if (utility::is_file(i)) {
            //判断当前文件是否是mandatory文件,并和传入的参数比较
            if (is_mandatory(i) == _mandatory_only) {
                boost::property_tree::ptree its_tree;
                try {
                    boost::property_tree::json_parser::read_json(i, its_tree);
                    _elements.push_back({ i, its_tree });
                }
                catch (boost::property_tree::json_parser_error &e) {
    #ifdef _WIN32
                    e; // silence MSVC warning C4101
    #endif
                    _failed.insert(i);
                }
            }
        } else if (utility::is_folder(i)) {
            //如果是目录,那么遍历目录内的文件。
            boost::filesystem::path its_path(i);
            for (auto j = boost::filesystem::directory_iterator(its_path);
                    j != boost::filesystem::directory_iterator();
                    j++) {
                auto its_file_path = j->path();
                if (!boost::filesystem::is_directory(its_file_path)) {
                    const std::string& its_name = its_file_path.string();
                    if (is_mandatory(its_name) == _mandatory_only) {
                        boost::property_tree::ptree its_tree;
                        try {
                            boost::property_tree::json_parser::read_json(its_name, its_tree);
                            _elements.push_back({its_name, its_tree});
                        }
                        catch (...) {
                            _failed.insert(its_name);
                        }
                    }
                }
            }
        }
    }
}

之后是load_data操作主要就是加载对应的具体配置信息到内存对应的结构上了。

bool configuration_impl::load_data(const std::vector<configuration_element> &_elements,
        bool _load_mandatory, bool _load_optional) {
    // Load logging configuration data
    std::set<std::string> its_warnings;

    //日志相关信息
    if (!is_logging_loaded_) {
        for (const auto& e : _elements)
            is_logging_loaded_
                = load_logging(e, its_warnings) || is_logging_loaded_;

        if (is_logging_loaded_) {
            logger::logger_impl::init(shared_from_this());
            for (auto w : its_warnings)
                VSOMEIP_WARNING << w;
        }
    }

    bool has_routing(false);
    bool has_applications(false);
    if (_load_mandatory) {
        // Load mandatory configuration data
        for (const auto& e : _elements) {
            //加载routing信息
            has_routing = load_routing(e) || has_routing;
            //加载applications信息
            has_applications = load_applications(e) || has_applications;
            //加载网络相关信息
            load_network(e);
            //诊断地址?
            load_diagnosis_address(e);
            //关机超时时间?
            load_shutdown_timeout(e);
            //payload大小
            load_payload_sizes(e);
            //todo
            load_endpoint_queue_sizes(e);
            // todo
            load_tcp_restart_settings(e);
            // 加载files-permissions信息
            load_permissions(e);
            //加载安全插件信息
            load_security(e);
            load_tracing(e);
            load_udp_receive_buffer_size(e);
        }
    }

    if (_load_optional) {
        for (const auto& e : _elements) {
            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);
        }
    }

    return is_logging_loaded_ && has_routing && has_applications;
}
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值