vsomeip commonapi 官方示例01
1 前言
vsomeip是SOME/IP规范的开源实现,作为COVESA项目的一部分设计。同时COVESA开发了CommonAPI C++的一套库和工具,方便开发者快速定义和实现IPC/RPC通信接口并基于vsomeip进行符合SOME/IP规范的通信.
本文主要会结合COVESA官方的capicxx-core-tools仓库中的示例01进行分析,介绍接口定义和接口代码实现逻辑
2 准备
参考前篇文章”基于vsomeip commonapi的demo” 在本地准备好代码,并至少完成到./gen.sh这一步
3 分析介绍
3.1 代码结构
笔者的代码也包括各个依赖例如capicxx-core-tools capicxx-someip-runtime, 呈现为如下结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
├── build
├── capicxx-core-runtime
├── capicxx-core-tools
├── capicxx-someip-runtime
├── CMakeLists.txt -> vsomeip-demo/CMakeLists.txt
├── config
├── envsetup.sh -> vsomeip-demo/scripts/envsetup.sh
├── gen.sh -> vsomeip-demo/scripts/gen.sh
├── googletest
├── prebuilts
│ ├── commonapi_core_generator
│ └── commonapi_someip_generator
├── run.sh -> vsomeip-demo/scripts/run.sh
├── vsomeip
└── vsomeip-demo
官方的示例01在 capicxx-core-tools/CommonAPI-Examples/E01HelloWorld 路径下,关键目录结构参考
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
capicxx-core-tools/CommonAPI-Examples/E01HelloWorld
├── ...
├── commonapi4someip.ini
├── fidl
│ ├── E01HelloWorld.fidl
│ └── E01HelloWorld-SomeIP.fdepl
├── src
│ ├── E01HelloWorldClient.cpp
│ ├── E01HelloWorldService.cpp
│ ├── E01HelloWorldStubImpl.cpp
│ └── E01HelloWorldStubImpl.hpp
├── src-gen
│ ├── core
│ │ └── v0
│ │ └── commonapi
│ │ └── examples
│ │ ├── E01HelloWorld.hpp
│ │ ├── E01HelloWorldProxyBase.hpp
│ │ ├── E01HelloWorldProxy.hpp
│ │ ├── E01HelloWorldStubDefault.hpp
│ │ └── E01HelloWorldStub.hpp
│ └── someip
│ └── v0
│ └── commonapi
│ └── examples
│ ├── E01HelloWorldSomeIPCatalog.json
│ ├── E01HelloWorldSomeIPDeployment.cpp
│ ├── E01HelloWorldSomeIPDeployment.hpp
│ ├── E01HelloWorldSomeIPProxy.cpp
│ ├── E01HelloWorldSomeIPProxy.hpp
│ ├── E01HelloWorldSomeIPStubAdapter.cpp
│ └── E01HelloWorldSomeIPStubAdapter.hpp
├── vsomeip-client.json
├── vsomeip-local.json
└── vsomeip-service.json
其中大部分文件都是仓库的示例中原始带的,除了两处生成代码目录,通过./gen.sh调用commonapi_core_generator和commonapi_someip_generator生成
E01HelloWorld/src-gen/core中的代码是E01HelloWorld.fidl对应生成E01HelloWorld/src-gen/someip中的代码是由E01HelloWorld-SomeIP.fdepl对应生成
主要实现的业务逻辑则在E01HelloWorld/src/目录下
3.2 E01HelloWorld Service代码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// E01HelloWorldService.cpp
CommonAPI::Runtime::setProperty("LogContext", "E01S"); // for android log or dlt log
CommonAPI::Runtime::setProperty("LogApplication", "E01S"); // for android log or dlt log
CommonAPI::Runtime::setProperty("LibraryBase", "E01HelloWorld");
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get(); //
std::string domain = "local"; // default value: "local"
std::string instance = "commonapi.examples.HelloWorld"; // InstanceId in E01HelloWorld-SomeIP.fdepl
std::string connection = "service-sample";
std::shared_ptr<E01HelloWorldStubImpl> myService = std::make_shared<E01HelloWorldStubImpl>();
bool successfullyRegistered = runtime->registerService(domain, instance, myService, connection); //
while (!successfullyRegistered) {
std::cout << "Register Service failed, trying again in 100 milliseconds..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
successfullyRegistered = runtime->registerService(domain, instance, myService, connection);
}
std::cout << "Successfully Registered Service!" << std::endl;
3.2.1 instance 字段 & fidl/fdepl配置
其中关键的字段instance需要和E01HelloWorld-SomeIP.fdepl中的InstanceId一致
1
2
3
define org.genivi.commonapi.someip.deployment for provider as Service {
instance commonapi.examples.E01HelloWorld {
InstanceId = "commonapi.examples.HelloWorld"
其实本质上是对应到生成代码中的匹配字符串
1
2
3
4
5
6
7
8
9
// E01HelloWorld/src-gen/someip/v0/commonapi/examples/E01HelloWorldSomeIPStubAdapter.cpp
void initializeE01HelloWorldSomeIPStubAdapter() {
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld", // 由fdepl文件定义生成的字段
0x1234, 0x5678, 0, 1);
CommonAPI::SomeIP::Factory::get()->registerStubAdapterCreateMethod(
"commonapi.examples.E01HelloWorld:v0_1",
&createE01HelloWorldSomeIPStubAdapter);
}
E01HelloWorldSomeIPStubAdapter.cpp中使用的地址字符串和fidlfdepl中定义的完整关系可以参考如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld",
// ──┬── ───────┬──────── ──────┬────────── ──┬─ ─────────────┬────────────
// │ │ │ │ │
// │ │ │ │ └─ fdepl InstanceId
// │ │ │ └─ fidl version (0.1)
// │ │ └─ fidl interface name
// │ └─ fidl package
// └─ 默认或代码指定
0x1234, 0x5678, 0, 1);
// ────┬─ ────┬─ ─┬ ─┬
// │ │ │ └─ minor version (1)
// │ │ └─ major version (0)
// │ └─ fdepl SomeIpInstanceID (22136)
// └─ fdepl SomeIpServiceID (4660)
3.2.2 connection 字段 & vsomeip.json配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
C++ 用户代码 (E01HelloWorldService.cpp:26)
--------
runtime->registerService(domain, instance, myService, "service-sample")
↓
CommonAPI::Runtime (Runtime.hpp:116 - template方法)
--------
registerService()
→ 调用 registerStub(_domain, Stub_::StubInterface::getInterface(), _instance, _service, _connectionId)
↓
CommonAPI::Runtime (Runtime.cpp)
--------
registerStub(_domain, _interface, _instance, _stub, "service-sample")
→ 调用 registerStubHelper(_domain, _interface, _instance, _stub, "service-sample", false)
↓
CommonAPI::Runtime (Runtime.cpp)
--------
registerStubHelper(..., "service-sample", _useDefault)
→ 遍历 factories_ map,找到 SomeIP Factory
→ 调用 factory.second->registerStub(_domain, _interface, _instance, _stub, "service-sample")
↓
CommonAPI::SomeIP::Factory (Factory.cpp)
--------
Factory::registerStub(_domain, _interface, _instance, _stub, "service-sample")
→ 调用 getConnection("service-sample")
↓
CommonAPI::SomeIP::Factory (Factory.cpp)
--------
Factory::getConnection("service-sample")
→ 查找 connections_ map: connections_.find("service-sample")
→ 如果找到:返回已有的 Connection 对象(复用)
→ 如果未找到:创建新的
↓
CommonAPI::SomeIP::Factory (Factory.cpp)
--------
new Connection("service-sample")
→ std::make_shared<Connection>("service-sample")
↓
CommonAPI::SomeIP::Connection (Connection.cpp)
--------
Connection::Connection("service-sample")
→ 初始化成员变量
→ application_ = vsomeip::runtime::get()->create_application("service-sample")
↓
vsomeip 库 (vsomeip内部实现)
--------
vsomeip::runtime::create_application("service-sample")
→ 读取环境变量 VSOMEIP_CONFIGURATION 指定的配置文件
→ 解析 JSON 配置文件
→ 在 applications[] 数组中查找 name == "service-sample"
↓
vsomeip-service.json
--------------------
{
"applications": [
{
"name": "service-sample", ← 必须匹配!
"id": "0x1277" ← 使用此 application ID
}
],
"routing": "service-sample" ← 此应用作为 routing manager
}
↓
返回路径
--------
vsomeip::application 对象
→ 返回给 Connection::application_
→ Connection 对象创建完成
→ 存入 Factory::connections_["service-sample"]
→ 用于创建 StubAdapter
→ 注册服务成功
对于E01HelloWorldService这个vsomeip-commonapi程序来说有两种方式来设定vsomeip application name
- 使用类似”基于vsomeip commonapi的demo”文章示例代码中的
VSOMEIP_APPLICATION_NAME=service-sample ./build/bin/E01HelloWorldService来指定VSOMEIP_APPLICATION_NAME参数 - 另一种就是通过本小节中
runtime->registerService里指定connection参数,最终通过各层函数调用变成vsomeip application name
这个name最终会和vsomeip.json的对应application通过名字来匹配,获取该配置下的例如id等信息. 之前的文章”vsomeip clientid分配逻辑”中使用的就是这个id
3.2.3 init func before registerService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// E01HelloWorldSomeIPStubAdapter.cpp
std::shared_ptr<CommonAPI::SomeIP::StubAdapter> createE01HelloWorldSomeIPStubAdapter(
const CommonAPI::SomeIP::Address &_address,
const std::shared_ptr<CommonAPI::SomeIP::ProxyConnection> &_connection,
const std::shared_ptr<CommonAPI::StubBase> &_stub) {
return std::make_shared< E01HelloWorldSomeIPStubAdapter<::v0::commonapi::examples::E01HelloWorldStub>>(_address, _connection, _stub);
// return E01HelloWorldSomeIPStubAdapter
}
void initializeE01HelloWorldSomeIPStubAdapter() {
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld",
0x1234, 0x5678, 0, 1);
// 记录k/v到Factory.stubAdapterCreateFunctions_
CommonAPI::SomeIP::Factory::get()->registerStubAdapterCreateMethod(
"commonapi.examples.E01HelloWorld:v0_1",
&createE01HelloWorldSomeIPStubAdapter);
INITIALIZER(registerE01HelloWorldSomeIPStubAdapter) {
// 记录到Factory.initializers_
CommonAPI::SomeIP::Factory::get()->registerInterface(initializeE01HelloWorldSomeIPStubAdapter);
}
void
Factory::registerStubAdapterCreateMethod(
const std::string &_interface,
StubAdapterCreateFunction _function) {
COMMONAPI_INFO("Registering function for creating \"", _interface,
"\" stub adapter.");
stubAdapterCreateFunctions_[_interface] = _function;
}
3.2.4 INITIALIZER宏和库加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
INITIALIZER(registerE01HelloWorldSomeIPStubAdapter) {
CommonAPI::SomeIP::Factory::get()->registerInterface(initializeE01HelloWorldSomeIPStubAdapter);
}
// 宏可以展开为
// 第一行:函数前向声明 + constructor 属性
static void registerE01HelloWorldSomeIPStubAdapter(void) __attribute__((constructor));
// 第二行:函数实现
static void registerE01HelloWorldSomeIPStubAdapter(void) {
CommonAPI::SomeIP::Factory::get()->registerInterface(initializeE01HelloWorldSomeIPStubAdapter);
}
__attribute__((constructor))
编译时:编译器将该函数指针记录在 ELF 文件的特殊段(.init_array 或 .ctors)中
- 加载时:动态链接器(ld-linux.so)在加载共享库时会执行:
1 2 3 4 5
加载 libE01HelloWorld-someip.so ↓ 执行 .init_array 段中的所有函数指针 ↓ 自动调用 registerE01HelloWorldSomeIPStubAdapter()
- 执行顺序: 在 main() 函数执行之前(对于可执行文件) 在 dlopen() 返回之前(对于动态加载的共享库)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Runtime.cpp
Runtime::registerStub()
{
initFactories();
library = getLibrary(_domain, _interface, _instance, false);
bool libraryLoaded = loadLibrary(library);
}
// 查找需要的库
std::string Runtime::getLibrary(
const std::string &_domain,
const std::string &_interface,
const std::string &_instance,
bool _isProxy) {
std::string address = _domain + ":" + _interface + ":" + _instance;
// 检查属性 "LibraryBase"
library = getProperty("LibraryBase");
if (library != "") {
library = "lib" + library + "-" + defaultBinding_;
// 结果: "libE01HelloWorld-someip"
}
return library;
}
bool Runtime::loadLibrary(const std::string &_library) {
// Linux: 使用 dlopen 加载共享库
if (dlopen(itsLibrary.c_str(), RTLD_LAZY | RTLD_GLOBAL) != NULL) {
loadedLibraries_.insert(itsLibrary);
return true;
}
}
当 libE01HelloWorld-someip.so 被 dlopen 加载时,INITIALIZER 宏自动执行初始化函数
3.2.5 registerService
涉及的关键流程代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// E01HelloWorldService.cpp
std::shared_ptr<E01HelloWorldStubImpl> myService = std::make_shared<E01HelloWorldStubImpl>();
bool successfullyRegistered = runtime->registerService(domain, instance, myService, connection);
// myService --->E01HelloWorldStubImpl
// runtime.hpp
template<typename Stub_>
COMMONAPI_METHOD_EXPORT bool registerService(const std::string &_domain,
const std::string &_instance,
std::shared_ptr<Stub_> _service,
const ConnectionId_t &_connectionId = DEFAULT_CONNECTION_ID) {
return registerStub(_domain, Stub_::StubInterface::getInterface(), _instance, _service, _connectionId);
// _service
}
↓
Runtime::registerStub(const std::string &_domain, const std::string &_interface, const std::string &_instance, std::shared_ptr<StubBase> _stub, std::shared_ptr<MainLoopContext> _context) {// std::shared_ptr<StubBase> _stub = _service; (隐式转换 E01HelloWorldStubImpl --> StubBase)
↓
Runtime::registerStubHelper(...)
↓
// Factory.cpp
bool Factory::registerStub(
const std::string &_domain,
const std::string &_interface, // "commonapi.examples.E01HelloWorld:v0_1"
const std::string &_instance, // "commonapi.examples.HelloWorld"
std::shared_ptr<StubBase> _stub, // template<typename Stub_> std::shared_ptr<Stub_> _service
const ConnectionId_t &_connection) {
COMMONAPI_INFO("Registering stub for \"", _domain, ":", _interface, ":", _instance, "\"");
// 【关键】查找在阶段 4 中注册的创建函数
auto stubAdapterCreateFunctionsIterator = stubAdapterCreateFunctions_.lower_bound(_interface);
if (stubAdapterCreateFunctionsIterator != stubAdapterCreateFunctions_.end()) {
std::string itsInterface(_interface);
if (stubAdapterCreateFunctionsIterator->first != _interface) {
// 版本匹配逻辑...
std::string itsInterfaceMajor(_interface.substr(0, _interface.find('_')));
if (stubAdapterCreateFunctionsIterator->first.find(itsInterfaceMajor) != 0)
return false;
itsInterface = stubAdapterCreateFunctionsIterator->first;
}
// 构造 CommonAPI 地址
CommonAPI::Address address(_domain, itsInterface, _instance);
// "local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld"
// 地址转换:CommonAPI 地址 → SOME/IP 物理地址
Address someipAddress;
AddressTranslator::get()->translate(address, someipAddress);
// someipAddress = {0x1234, 0x5678, 0, 1}
// 获取连接
std::shared_ptr<Connection> itsConnection = getConnection(_connection);
if (itsConnection) {
// 【关键】调用在阶段 4 中注册的创建函数
std::shared_ptr<StubAdapter> adapter = stubAdapterCreateFunctionsIterator->second(
// 【关键】return E01HelloWorldSomeIPStubAdapter --- CommonAPI someip class
someipAddress, // SOME/IP 地址
itsConnection, // vsomeip 连接
_stub // 用户的 StubImpl // E01HelloWorldStubImpl --- CommonAPI core class
);
// 等价于:createE01HelloWorldSomeIPStubAdapter(someipAddress, itsConnection, _stub)
if (adapter) {
adapter->registerSelectiveEventHandlers();
adapter->init(adapter);
if (registerStubAdapter(adapter))
return true;
}
}
}
COMMONAPI_ERROR("Registering stub for \"", _domain, ":", _interface, ":", _instance, "\" failed!");
return false;
}
bool
Factory::registerStubAdapter(std::shared_ptr<StubAdapter> _adapter) {
const std::shared_ptr<ProxyConnection> connection
= _adapter->getConnection();
CommonAPI::Address address;
Address someipAddress = _adapter->getSomeIpAddress();
if (AddressTranslator::get()->translate(someipAddress, address)) {
std::lock_guard<std::mutex> itsLock(servicesMutex_);
const auto &insertResult
= services_.insert( { address.getAddress(), _adapter } );// 记录k/v(commonapi address/_adapter)到Factory.services_
const bool &isInsertSuccessful = insertResult.second;
if (isInsertSuccessful) {
std::shared_ptr<StubManager> manager
= connection->getStubManager();
manager->registerStubAdapter(_adapter); // 记录k/v(service,instance/_adapter)到StubManager.registeredStubAdapters_
return true;
}
}
return false;
}
3.2.6 实现类的逻辑
实现类的继承关系
1
2
3
4
5
6
7
8
9
E01HelloWorldStubImpl
↓ 继承
E01HelloWorldStubDefault
↓ virtual 继承
E01HelloWorldStub
↓ virtual 继承
CommonAPI::Stub<...>
↓ virtual 继承
CommonAPI::StubBase // 【最顶层基类】
实现类的传递逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1. 用户代码
std::shared_ptr<E01HelloWorldStubImpl> myService
↓ 传递给 registerService
2. Runtime::registerService (模板)
template<typename Stub_>
std::shared_ptr<Stub_> _service // Stub_ = E01HelloWorldStubImpl
↓ 传递给 registerStub
3. Runtime::registerStub (非模板)
std::shared_ptr<StubBase> _stub
【隐式向上转型:E01HelloWorldStubImpl* → StubBase*】
↓ 传递给 Factory::registerStub
4. Factory::registerStub
std::shared_ptr<StubBase> _stub
↓ 传递给 createE01HelloWorldSomeIPStubAdapter
5. createE01HelloWorldSomeIPStubAdapter
std::shared_ptr<StubBase> _stub
↓ 传递给 StubAdapter 构造函数
6. E01HelloWorldSomeIPStubAdapterInternal 构造函数
6.1 【父类】调用父类StubAdapterHelper 构造函数
std::shared_ptr<StubBase> _stub 【保存impl对象指针】
stub_ = std::dynamic_pointer_cast<StubClass_>(_stub);
↓ dynamic_pointer_cast
6.1 存储在 stub_ 成员
std::shared_ptr<E01HelloWorldStub> stub_
【显式向下转型:StubBase* → E01HelloWorldStub*】
✓ 指向同一个 E01HelloWorldStubImpl 对象
6.2 【初始化列表】构造 sayHelloStubDispatcher
sayHelloStubDispatcher(&E01HelloWorldStub::sayHello, )
调用 其基础模板类MethodWithReplyStubDispatcher构造
stubFunctor_(_stubFunctor)
即stubFunctor_ = &E01HelloWorldStub::sayHello,
这里的E01HelloWorldStub对象地址应该已经是6.1中的E01HelloWorldStubImpl对象了
6.3 【构造函数体】
addStubDispatcher(0x7530, &sayHelloStubDispatcher)
└─→ stubDispatcherTable_[0x7530] = &sayHelloStubDispatcher
初始化构造完成后的内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
════════════════════════════════════════════════════════════════════════════
E01HelloWorldSomeIPStubAdapterInternal 对象内存视图
════════════════════════════════════════════════════════════════════════════
【对象1: E01HelloWorldSomeIPStubAdapterInternal】
[继承部分1] CommonAPI::SomeIP::StubAdapter
• address_ : "local:commonapi.examples.HelloWorld:v0_1"
• connection_ : shared_ptr<ProxyConnection>
[继承部分2] StubAdapterHelper<E01HelloWorldStub>
• stub_ : shared_ptr<E01HelloWorldStub> <-- 6.1
└─> 指向 0x1234 (E01HelloWorldStubImpl 对象)
• stubDispatcherTable_ : map<method_id, Dispatcher*>
[0x7530] --> &sayHelloStubDispatcher <-- 6.3
[继承部分3] enable_shared_from_this
• weak_this_ : weak_ptr to self
[成员变量1] getE01HelloWorldInterfaceVersionStubDispatcher
• 用于处理 getInterfaceVersion 属性访问
[成员变量2] sayHelloStubDispatcher : MethodWithReplyStubDispatcher
• stubFunctor_ : &E01HelloWorldStub::sayHello <-- 6.2
• stub_ : shared_ptr<E01HelloWorldStub> <-- 6.1
└─> 指向 0x1234 (E01HelloWorldStubImpl 对象)
════════════════════════════════════════════════════════════════════════════
【两个 stub_ 都指向下面这个对象】
↓
【对象2: E01HelloWorldStubImpl】 (堆上分配)
[虚函数表指针]
• vptr --> E01HelloWorldStubImpl 的 vtable
vtable[sayHello] --> 用户实现代码地址
[继承层次的成员变量]
• E01HelloWorldStubImpl 的成员 (无额外成员)
• E01HelloWorldStubDefault 的成员
• E01HelloWorldStub 的成员
• CommonAPI::Stub<...> 的成员
• StubBase 的成员
════════════════════════════════════════════════════════════════════════════
3.2.7 总体流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 阶段 1: 库加载时(dlopen libE01HelloWorld-someip.so) │
└─────────────────────────────────────────────────────────────────────────────────┘
Runtime::registerStub -> loadLibrary
1. 动态链接器加载 libE01HelloWorld-someip.so
↓
2. 执行 .init_array 段中的 constructor 函数
↓
3. INITIALIZER(registerE01HelloWorldSomeIPStubAdapter) 自动执行
↓
4. Factory::get()->registerInterface(initializeE01HelloWorldSomeIPStubAdapter)
↓
5. isInitialized_ == false → initializers_.push_back(initializeE01HelloWorldSomeIPStubAdapter)
// 【记录】initializeE01HelloWorldSomeIPStubAdapter到Factory.initializers_
✓ 函数指针已保存,等待调用
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 阶段 2: 用户代码调用 registerService │
└─────────────────────────────────────────────────────────────────────────────────┘
6. runtime->registerService(domain, instance, myService, connection)
↓
7. Runtime::registerStub(_domain, "commonapi.examples.E01HelloWorld:v0_1", _instance, ...) // Runtime::registerStub
↓
8. if (!isInitialized_) initFactories() ← 触发初始化
↓
9. defaultFactory_->init() // SOME/IP Factory
↓
10. for (auto i : initializers_) i() // 遍历执行保存的函数
↓
11. 【执行】initializeE01HelloWorldSomeIPStubAdapter()
// 【执行】initializeE01HelloWorldSomeIPStubAdapter
├─→ AddressTranslator::insert("local:...HelloWorld", 0x1234, 0x5678, 0, 1)
│ ✓ 地址映射已注册
└─→ registerStubAdapterCreateMethod("commonapi.examples.E01HelloWorld:v0_1",
// 【记录】createE01HelloWorldSomeIPStubAdapter k/v到Factory.stubAdapterCreateFunctions_
&createE01HelloWorldSomeIPStubAdapter)
✓ 创建函数已注册
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 阶段 3: 创建 StubAdapter 实例 │
└─────────────────────────────────────────────────────────────────────────────────┘
12. Runtime::registerStubHelper(...) // Runtime::registerStubHelper
↓
13. Factory::registerStub(domain, interface, instance, stub, connection) // Factory::registerStubHelper
↓
14. auto it = stubAdapterCreateFunctions_.lower_bound("commonapi.examples.E01HelloWorld:v0_1")
↓
15. 找到在步骤 11 中注册的创建函数
↓
16. AddressTranslator::translate(CommonAPI::Address → SOME/IP Address)
使用步骤 11 中注册的映射
↓
17. adapter = it->second(someipAddress, connection, stub) // 【执行】stubAdapterCreateFunctions_中存的 createE01HelloWorldSomeIPStubAdapter
等价于:createE01HelloWorldSomeIPStubAdapter(...)
↓
18. 创建 E01HelloWorldSomeIPStubAdapter<E01HelloWorldStub>实例 // 【创建】E01HelloWorldSomeIPStubAdapter (someipAddress, connection, E01HelloWorldStubImpl)
↓
19. adapter->registerSelectiveEventHandlers()
↓
20. adapter->init(adapter)
↓
21. registerStubAdapter(adapter) // 注册到 services_ 映射表
✓ 服务注册完成
整个流程体现的特点
- 编译时:编译成独立的共享库 libE01HelloWorld-someip.so
- 运行时:通过 dlopen 动态加载
- 自动注册:INITIALIZER 宏在加载时自动执行注册
- 透明使用:用户代码无需显式 include,通过 Factory 模式自动创建
3.2.8 完整的时序
包括库加载,registerService,实际的业务过程的时序可以参考如下的plantuml
3.3 E01HelloWorld Client代码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// E01HelloWorldClient.cpp
CommonAPI::Runtime::setProperty("LogContext", "E01C"); // for android log or dlt log
CommonAPI::Runtime::setProperty("LogApplication", "E01C"); // for android log or dlt log
CommonAPI::Runtime::setProperty("LibraryBase", "E01HelloWorld");
std::shared_ptr<CommonAPI::Runtime> runtime = CommonAPI::Runtime::get();
std::string domain = "local"; // default value: "local"
std::string instance = "commonapi.examples.HelloWorld"; // InstanceId in E01HelloWorld-SomeIP.fdepl
std::string connection = "client-sample";
std::shared_ptr<E01HelloWorldProxy<>> myProxy = runtime->buildProxy<E01HelloWorldProxy>(domain,
instance, connection);
std::cout << "Checking availability!" << std::endl;
while (!myProxy->isAvailable())
std::this_thread::sleep_for(std::chrono::microseconds(10));
std::cout << "Available..." << std::endl;
const std::string name = "World";
CommonAPI::CallStatus callStatus;
std::string returnMessage;
myProxy->sayHello(name, callStatus, returnMessage, &info);
3.3.1 instance 字段 & fidl/fdepl配置
Client 端的 instance 字段同样需要和 E01HelloWorld-SomeIP.fdepl 中的 InstanceId 一致,因为 Client 和 Service 要连接到同一个服务实例。
1
2
3
define org.genivi.commonapi.someip.deployment for provider as Service {
instance commonapi.examples.E01HelloWorld {
InstanceId = "commonapi.examples.HelloWorld"
Client 端生成代码中的地址映射和 Service 端完全相同:
1
2
3
4
5
6
7
8
9
// E01HelloWorld/src-gen/someip/v0/commonapi/examples/E01HelloWorldSomeIPProxy.cpp
void initializeE01HelloWorldSomeIPProxy() {
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld", // 由fdepl文件定义生成的字段
0x1234, 0x5678, 0, 1);
CommonAPI::SomeIP::Factory::get()->registerProxyCreateMethod(
"commonapi.examples.E01HelloWorld:v0_1",
&createE01HelloWorldSomeIPProxy);
}
Client 和 Service 使用相同的地址映射关系确保它们能够正确通信:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld",
// ──┬── ───────┬──────── ──────┬────────── ──┬─ ─────────────┬────────────
// │ │ │ │ │
// │ │ │ │ └─ fdepl InstanceId (Client 和 Service 必须匹配)
// │ │ │ └─ fidl version (0.1)
// │ │ └─ fidl interface name
// │ └─ fidl package
// └─ 默认或代码指定
0x1234, 0x5678, 0, 1);
// ────┬─ ────┬─ ─┬ ─┬
// │ │ │ └─ minor version (1)
// │ │ └─ major version (0)
// │ └─ fdepl SomeIpInstanceID (22136) - 必须和 Service 端相同
// └─ fdepl SomeIpServiceID (4660) - 必须和 Service 端相同
关键点:
- Client 和 Service 的
instance字段必须完全一致 - 生成代码中的 Service ID (0x1234) 和 Instance ID (0x5678) 必须相同
- 这样 Client 才能找到并连接到正确的 Service 实例
3.3.2 connection 字段 & vsomeip.json配置
Client 端的 connection 参数和 Service 端类似,最终会作为 vsomeip application name 使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
C++ 用户代码 (E01HelloWorldClient.cpp)
--------
runtime->buildProxy<E01HelloWorldProxy>(domain, instance, "client-sample")
↓
CommonAPI::Runtime (Runtime.hpp - template方法)
--------
buildProxy()
→ 调用 createProxy(_domain, _instance, _connectionId)
↓
CommonAPI::Runtime (Runtime.cpp)
--------
createProxy(_domain, _instance, "client-sample")
→ 调用 createProxyHelper(_domain, _interface, _instance, "client-sample", false)
↓
CommonAPI::Runtime (Runtime.cpp)
--------
createProxyHelper(..., "client-sample", _useDefault)
→ 遍历 factories_ map,找到 SomeIP Factory
→ 调用 factory.second->createProxy(_domain, _interface, _instance, "client-sample")
↓
CommonAPI::SomeIP::Factory (Factory.cpp)
--------
Factory::createProxy(_domain, _interface, _instance, "client-sample")
→ 调用 getConnection("client-sample")
↓
CommonAPI::SomeIP::Factory (Factory.cpp)
--------
Factory::getConnection("client-sample")
→ 查找 connections_ map: connections_.find("client-sample")
→ 如果找到:返回已有的 Connection 对象(复用)
→ 如果未找到:创建新的
↓
CommonAPI::SomeIP::Factory (Factory.cpp)
--------
new Connection("client-sample")
→ std::make_shared<Connection>("client-sample")
↓
CommonAPI::SomeIP::Connection (Connection.cpp)
--------
Connection::Connection("client-sample")
→ 初始化成员变量
→ application_ = vsomeip::runtime::get()->create_application("client-sample")
↓
vsomeip 库 (vsomeip内部实现)
--------
vsomeip::runtime::create_application("client-sample")
→ 读取环境变量 VSOMEIP_CONFIGURATION 指定的配置文件
→ 解析 JSON 配置文件
→ 在 applications[] 数组中查找 name == "client-sample"
↓
vsomeip-client.json
--------------------
{
"applications": [
{
"name": "client-sample", ← 必须匹配!
"id": "0x1343" ← 使用此 application ID
}
]
}
↓
返回路径
--------
vsomeip::application 对象
→ 返回给 Connection::application_
→ Connection 对象创建完成
→ 存入 Factory::connections_["client-sample"]
→ 用于创建 Proxy
→ 代理创建成功
对于 E01HelloWorldClient 这个 vsomeip-commonapi 程序来说,同样有两种方式来设定 vsomeip application name:
- 使用环境变量:
VSOMEIP_APPLICATION_NAME=client-sample ./build/bin/E01HelloWorldClient - 通过
runtime->buildProxy里指定connection参数
这个 name 最终会和 vsomeip-client.json 的对应 application 通过名字来匹配,获取该配置下的 id 等信息。
重要差异:
- Service 端使用
registerService()+connection参数 - Client 端使用
buildProxy()+connection参数 - 两者都最终调用
Factory::getConnection()创建或复用 Connection 对象 - Service 和 Client 的 connection name 可以不同(通常也应该不同)
3.3.3 init func before buildProxy
Client 端的初始化函数结构与 Service 端类似,但注册的是 Proxy 创建函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// E01HelloWorldSomeIPProxy.cpp
std::shared_ptr<CommonAPI::SomeIP::Proxy> createE01HelloWorldSomeIPProxy(
const CommonAPI::SomeIP::Address &_address,
const std::shared_ptr<CommonAPI::SomeIP::ProxyConnection> &_connection) {
return std::make_shared<E01HelloWorldSomeIPProxy>(_address, _connection);
// return E01HelloWorldSomeIPProxy
}
void initializeE01HelloWorldSomeIPProxy() {
CommonAPI::SomeIP::AddressTranslator::get()->insert(
"local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld",
0x1234, 0x5678, 0, 1);
// 记录 k/v 到 Factory.proxyCreateFunctions_
CommonAPI::SomeIP::Factory::get()->registerProxyCreateMethod(
"commonapi.examples.E01HelloWorld:v0_1",
&createE01HelloWorldSomeIPProxy);
}
INITIALIZER(registerE01HelloWorldSomeIPProxy) {
// 记录到 Factory.initializers_
CommonAPI::SomeIP::Factory::get()->registerInterface(initializeE01HelloWorldSomeIPProxy);
}
对应的 Factory 注册方法:
1
2
3
4
5
6
7
void Factory::registerProxyCreateMethod(
const std::string &_interface,
ProxyCreateFunction _function) {
COMMONAPI_INFO("Registering function for creating \"", _interface,
"\" proxy.");
proxyCreateFunctions_[_interface] = _function;
}
Client 端与 Service 端的差异:
| 项目 | Service 端 | Client 端 |
|---|---|---|
| 创建函数名 | createE01HelloWorldSomeIPStubAdapter | createE01HelloWorldSomeIPProxy |
| 返回类型 | shared_ptr<StubAdapter> | shared_ptr<Proxy> |
| 注册方法 | registerStubAdapterCreateMethod() | registerProxyCreateMethod() |
| 存储位置 | Factory.stubAdapterCreateFunctions_ | Factory.proxyCreateFunctions_ |
| INITIALIZER 名称 | registerE01HelloWorldSomeIPStubAdapter | registerE01HelloWorldSomeIPProxy |
两者的地址映射完全相同(0x1234, 0x5678),确保 Client 和 Service 能够正确匹配。
3.3.4 INITIALIZER宏和库加载
Client 端的 INITIALIZER 宏机制与 Service 端完全相同:
1
2
3
4
5
6
7
8
9
10
11
12
13
INITIALIZER(registerE01HelloWorldSomeIPProxy) {
CommonAPI::SomeIP::Factory::get()->registerInterface(initializeE01HelloWorldSomeIPProxy);
}
// 宏可以展开为
// 第一行:函数前向声明 + constructor 属性
static void registerE01HelloWorldSomeIPProxy(void) __attribute__((constructor));
// 第二行:函数实现
static void registerE01HelloWorldSomeIPProxy(void) {
CommonAPI::SomeIP::Factory::get()->registerInterface(initializeE01HelloWorldSomeIPProxy);
}
__attribute__((constructor)) 的执行时机:
编译时:编译器将该函数指针记录在 ELF 文件的特殊段(.init_array 或 .ctors)中
- 加载时:动态链接器(ld-linux.so)在加载共享库时会执行:
1 2 3 4 5
加载 libE01HelloWorld-someip.so ↓ 执行 .init_array 段中的所有函数指针 ↓ 自动调用 registerE01HelloWorldSomeIPProxy()
- 执行顺序:
- 在 main() 函数执行之前(对于可执行文件)
- 在 dlopen() 返回之前(对于动态加载的共享库)
库加载流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Runtime.cpp
Runtime::createProxy()
{
initFactories();
library = getLibrary(_domain, _interface, _instance, true); // true 表示是 Proxy
bool libraryLoaded = loadLibrary(library);
}
// 查找需要的库
std::string Runtime::getLibrary(
const std::string &_domain,
const std::string &_interface,
const std::string &_instance,
bool _isProxy) { // Client 端传入 true
std::string address = _domain + ":" + _interface + ":" + _instance;
// 检查属性 "LibraryBase"
library = getProperty("LibraryBase");
if (library != "") {
library = "lib" + library + "-" + defaultBinding_;
// 结果: "libE01HelloWorld-someip"
}
return library;
}
bool Runtime::loadLibrary(const std::string &_library) {
// Linux: 使用 dlopen 加载共享库
if (dlopen(itsLibrary.c_str(), RTLD_LAZY | RTLD_GLOBAL) != NULL) {
loadedLibraries_.insert(itsLibrary);
return true;
}
}
当 libE01HelloWorld-someip.so 被 dlopen 加载时,INITIALIZER 宏自动执行初始化函数。
关键点:
- Client 和 Service 端共享同一个
libE01HelloWorld-someip.so库 - 该库包含了 Proxy 和 StubAdapter 的注册代码
- INITIALIZER 宏确保两者的创建函数都被自动注册
- 无论是 Client 还是 Service 端调用,库只会加载一次
3.3.5 buildProxy
涉及的关键流程代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// E01HelloWorldClient.cpp
std::shared_ptr<E01HelloWorldProxy<>> myProxy = runtime->buildProxy<E01HelloWorldProxy>(domain,
instance, connection);
// E01HelloWorldProxy 是模板参数
// Runtime.hpp
template<template<typename ...> class Proxy_, typename ... AttributeExtensions_>
COMMONAPI_METHOD_EXPORT std::shared_ptr<Proxy_<AttributeExtensions_...>>
buildProxy(const std::string &_domain,
const std::string &_instance,
const ConnectionId_t &_connectionId = DEFAULT_CONNECTION_ID) {
return std::dynamic_pointer_cast<Proxy_<AttributeExtensions_...>>(
createProxy(_domain, Proxy_<AttributeExtensions_...>::getInterface(), _instance, _connectionId));
// 调用 createProxy
}
↓
Runtime::createProxy(const std::string &_domain, const std::string &_interface, const std::string &_instance, const ConnectionId_t &_connectionId) {
↓
Runtime::createProxyHelper(...)
↓
// Factory.cpp
std::shared_ptr<CommonAPI::Proxy>
Factory::createProxy(
const std::string &_domain,
const std::string &_interface, // "commonapi.examples.E01HelloWorld:v0_1"
const std::string &_instance, // "commonapi.examples.HelloWorld"
const ConnectionId_t &_connectionId) {
COMMONAPI_VERBOSE("Creating proxy for \"", _domain, ":", _interface, ":", _instance, "\"");
// 【关键】查找在阶段 4 中注册的创建函数
auto proxyCreateFunctionsIterator = proxyCreateFunctions_.lower_bound(_interface);
if (proxyCreateFunctionsIterator != proxyCreateFunctions_.end()) {
std::string itsInterface(_interface);
if (proxyCreateFunctionsIterator->first != _interface) {
// 版本匹配逻辑...
std::string itsInterfaceMajor(_interface.substr(0, _interface.find('_')));
if (proxyCreateFunctionsIterator->first.find(itsInterfaceMajor) != 0)
return nullptr;
itsInterface = proxyCreateFunctionsIterator->first;
}
// 构造 CommonAPI 地址
CommonAPI::Address address(_domain, itsInterface, _instance);
// "local:commonapi.examples.E01HelloWorld:v0_1:commonapi.examples.HelloWorld"
// 地址转换:CommonAPI 地址 → SOME/IP 物理地址
Address someipAddress;
AddressTranslator::get()->translate(address, someipAddress);
// someipAddress = {0x1234, 0x5678, 0, 1}
// 获取连接
std::shared_ptr<Connection> itsConnection = getConnection(_connectionId);
if (itsConnection) {
// 【关键】调用在阶段 4 中注册的创建函数
std::shared_ptr<Proxy> proxy = proxyCreateFunctionsIterator->second(
// 【关键】return E01HelloWorldSomeIPProxy
someipAddress, // SOME/IP 地址
itsConnection // vsomeip 连接
);
// 等价于:createE01HelloWorldSomeIPProxy(someipAddress, itsConnection)
if (proxy && proxy->init())
return proxy;
}
}
COMMONAPI_ERROR("Creating proxy for \"", _domain, ":", _interface, ":", _instance, "\" failed!");
return nullptr;
}
与 Service 端 registerService 的对比:
| 步骤 | Service 端 (registerService) | Client 端 (buildProxy) |
|---|---|---|
| 1. 查找创建函数 | stubAdapterCreateFunctions_.lower_bound() | proxyCreateFunctions_.lower_bound() |
| 2. 地址转换 | CommonAPI 地址 → SOME/IP 地址 | CommonAPI 地址 → SOME/IP 地址 |
| 3. 获取连接 | getConnection(_connection) | getConnection(_connectionId) |
| 4. 调用创建函数 | createE01HelloWorldSomeIPStubAdapter() | createE01HelloWorldSomeIPProxy() |
| 5. 返回对象 | 存为内部成员<str,shared_ptr<StubAdapter>> Factory::services_ | shared_ptr<Proxy> |
| 6. 初始化 | adapter->init() | proxy->init() |
| 7. 注册 | registerStubAdapter(adapter) 注册到 services_ | 不需要额外注册,直接返回 |
关键差异:
- Service 端需要调用
registerStubAdapter()将 adapter 注册到Factory.services_和StubManager.registeredStubAdapters_ - Client 端创建完 Proxy 后直接返回给用户使用,不需要注册到全局管理器
- Service 端需要向 vsomeip 注册消息处理器和 offer_service
- Client 端只需要 Proxy 对象来发送请求
3.3.6 Proxy类的逻辑
Proxy 类的继承关系:
1
2
3
4
5
6
7
8
9
10
11
E01HelloWorldProxy<> (用户使用的模板类)
↓ virtual 继承
E01HelloWorldProxyBase (生成的抽象基类)
↓ virtual 继承
CommonAPI::Proxy // 【CommonAPI Core 基类】
↑
│ 实际实现类
↓
E01HelloWorldSomeIPProxy (SOME/IP 具体实现)
↓ virtual 继承
CommonAPI::SomeIP::Proxy // 【CommonAPI SOME/IP 基类】
Proxy 类的创建和传递逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
1. 用户代码
std::shared_ptr<E01HelloWorldProxy<>> myProxy
↓ buildProxy 调用
2. Runtime::buildProxy (模板)
template<template<typename ...> class Proxy_, typename ... AttributeExtensions_>
std::shared_ptr<Proxy_<AttributeExtensions_...>>
// Proxy_ = E01HelloWorldProxy
↓ 调用 createProxy
3. Runtime::createProxy (非模板)
std::shared_ptr<CommonAPI::Proxy>
【向上转型:具体 Proxy → CommonAPI::Proxy】
↓ 传递给 Factory::createProxy
4. Factory::createProxy
std::shared_ptr<CommonAPI::Proxy>
// 参考3.3.5 函数分析, proxyCreateFunctionsIterator->second(someipAddress, itsConnection)
// 相当于
↓ 调用 createE01HelloWorldSomeIPProxy
5. createE01HelloWorldSomeIPProxy
return std::make_shared<E01HelloWorldSomeIPProxy>(_address, _connection);
【创建 SOME/IP 实现类】
回到buildProxy函数这里返回,并构造E01HelloWorldProxy对象myProxy
std::shared_ptr<E01HelloWorldProxy<>> myProxy = runtime->buildProxy<E01HelloWorldProxy>
↓ 返回
6. E01HelloWorldProxy 构造函数
E01HelloWorldProxy(std::shared_ptr<CommonAPI::Proxy> delegate):
delegate_(std::dynamic_pointer_cast<E01HelloWorldProxyBase>(delegate)) {
}
【关键】通过 delegate 模式,E01HelloWorldProxy 持有 E01HelloWorldSomeIPProxy
Proxy 对象的委托关系:
1
2
3
4
5
6
7
8
9
【用户持有的对象】
std::shared_ptr<E01HelloWorldProxy<>>
│
│ 成员变量 delegate_
↓
【实际实现对象】
std::shared_ptr<E01HelloWorldSomeIPProxy>
↓ 继承自
CommonAPI::SomeIP::Proxy
方法调用流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 用户调用
myProxy->sayHello(name, callStatus, returnMessage, &info);
↓ E01HelloWorldProxy::sayHello (inline 模板方法)
// E01HelloWorldProxy.hpp:118
delegate_->sayHello(_name, _internalCallStatus, _message, _info);
↓ 调用实际实现
// E01HelloWorldSomeIPProxy::sayHello
void E01HelloWorldSomeIPProxy::sayHello(...) {
// 序列化参数
CommonAPI::Deployable<std::string, ...> deploy_name(_name, ...);
// 调用 ProxyHelper 发送 SOME/IP 消息
CommonAPI::SomeIP::ProxyHelper<...>::callMethodWithReply(
*this,
CommonAPI::SomeIP::method_id_t(0x7530), // Method ID
true, // reliable
false, // not selective
_info,
deploy_name,
_internalCallStatus,
deploy_message);
_message = deploy_message.getValue();
}
Proxy 设计模式分析:
- 委托模式 (Delegate Pattern):
E01HelloWorldProxy不直接实现功能- 通过
delegate_成员委托给E01HelloWorldSomeIPProxy - 实现了接口与实现的分离
- 模板与类型擦除:
E01HelloWorldProxy<>是模板类,支持属性扩展createProxy返回shared_ptr<CommonAPI::Proxy>(类型擦除)buildProxy通过dynamic_pointer_cast恢复具体类型
- 虚继承 (Virtual Inheritance):
- 避免菱形继承问题
E01HelloWorldProxy和E01HelloWorldSomeIPProxy都继承自CommonAPI::Proxy- 确保只有一个
CommonAPI::Proxy基类实例
与 Service 端 Stub 的对比:
| 项目 | Service 端 (Stub) | Client 端 (Proxy) |
|---|---|---|
| 用户类 | E01HelloWorldStubImpl | E01HelloWorldProxy<> |
| 生成的基类 | E01HelloWorldStubDefault | E01HelloWorldProxyBase |
| SOME/IP 实现 | E01HelloWorldSomeIPStubAdapter | E01HelloWorldSomeIPProxy |
| 关系 | 继承关系(Impl 继承 Default) | 委托关系(Proxy 持有 SomeIPProxy) |
| 用户代码 | 重写虚函数实现业务逻辑 | 调用方法发送请求 |
| 方向 | 接收消息,调用用户实现 | 发送消息到服务端 |
3.3.7 总体流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 阶段 1: 库加载时(dlopen libE01HelloWorld-someip.so) │
└─────────────────────────────────────────────────────────────────────────────────┘
Runtime::createProxy -> loadLibrary
1. 动态链接器加载 libE01HelloWorld-someip.so
↓
2. 执行 .init_array 段中的 constructor 函数
↓
3. INITIALIZER(registerE01HelloWorldSomeIPProxy) 自动执行
↓
4. Factory::get()->registerInterface(initializeE01HelloWorldSomeIPProxy)
↓
5. isInitialized_ == false → initializers_.push_back(initializeE01HelloWorldSomeIPProxy)
// 【记录】initializeE01HelloWorldSomeIPProxy到Factory.initializers_
✓ 函数指针已保存,等待调用
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 阶段 2: 用户代码调用 buildProxy │
└─────────────────────────────────────────────────────────────────────────────────┘
6. runtime->buildProxy<E01HelloWorldProxy>(domain, instance, connection)
↓
7. Runtime::createProxy(_domain, "commonapi.examples.E01HelloWorld:v0_1", _instance, ...) // Runtime::createProxy
↓
8. if (!isInitialized_) initFactories() ← 触发初始化
↓
9. defaultFactory_->init() // SOME/IP Factory
↓
10. for (auto i : initializers_) i() // 遍历执行保存的函数
↓
11. 【执行】initializeE01HelloWorldSomeIPProxy()
// 【执行】initializeE01HelloWorldSomeIPProxy
├─→ AddressTranslator::insert("local:...HelloWorld", 0x1234, 0x5678, 0, 1)
│ ✓ 地址映射已注册
└─→ registerProxyCreateMethod("commonapi.examples.E01HelloWorld:v0_1",
// 【记录】createE01HelloWorldSomeIPProxy k/v到Factory.proxyCreateFunctions_
&createE01HelloWorldSomeIPProxy)
✓ 创建函数已注册
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 阶段 3: 创建 Proxy 实例 │
└─────────────────────────────────────────────────────────────────────────────────┘
12. Runtime::createProxyHelper(...) // Runtime::createProxyHelper
↓
13. Factory::createProxy(domain, interface, instance, connection) // Factory::createProxy
↓
14. auto it = proxyCreateFunctions_.lower_bound("commonapi.examples.E01HelloWorld:v0_1")
↓
15. 找到在步骤 11 中注册的创建函数
↓
16. AddressTranslator::translate(CommonAPI::Address → SOME/IP Address)
使用步骤 11 中注册的映射
↓
17. proxy = it->second(someipAddress, connection) // 【执行】proxyCreateFunctions_中存的 createE01HelloWorldSomeIPProxy
等价于:createE01HelloWorldSomeIPProxy(...)
↓
18. 创建 E01HelloWorldSomeIPProxy 实例 // 【创建】E01HelloWorldSomeIPProxy (someipAddress, connection)
↓
19. proxy->init()
↓
20. return proxy (返回 shared_ptr<CommonAPI::SomeIP::Proxy>)
↓
21. std::make_shared<E01HelloWorldProxy<>>(proxy) // 【创建】E01HelloWorldProxy 包装对象
↓
22. E01HelloWorldProxy 构造函数初始化 delegate_ = E01HelloWorldSomeIPProxy
✓ Proxy 创建完成
↓
23. return myProxy (返回给用户代码)
整个流程体现的特点:
- 编译时:Client 和 Service 共享同一个共享库 libE01HelloWorld-someip.so
- 运行时:通过 dlopen 动态加载
- 自动注册:INITIALIZER 宏在加载时自动执行注册(Proxy 和 StubAdapter 都注册)
- 透明使用:用户代码无需显式 include,通过 Factory 模式自动创建
- 委托模式:E01HelloWorldProxy 通过 delegate_ 委托给 E01HelloWorldSomeIPProxy 实现
与 Service 端流程的对比:
| 步骤 | Service 端 | Client 端 |
|---|---|---|
| 库加载 | INITIALIZER(registerE01HelloWorldSomeIPStubAdapter) | INITIALIZER(registerE01HelloWorldSomeIPProxy) |
| 用户调用 | runtime->registerService() | runtime->buildProxy() |
| 初始化函数 | initializeE01HelloWorldSomeIPStubAdapter() | initializeE01HelloWorldSomeIPProxy() |
| 注册方法 | registerStubAdapterCreateMethod() | registerProxyCreateMethod() |
| 存储位置 | Factory.stubAdapterCreateFunctions_ | Factory.proxyCreateFunctions_ |
| 创建函数 | createE01HelloWorldSomeIPStubAdapter() | createE01HelloWorldSomeIPProxy() |
| 创建对象 | E01HelloWorldSomeIPStubAdapter | E01HelloWorldSomeIPProxy |
| 额外包装 | 无(用户直接继承 StubDefault) | E01HelloWorldProxy(委托模式) |
| 注册到管理器 | registerStubAdapter() 注册到 services_ | 不需要(直接返回给用户) |
| 后续操作 | offer_service() 对外提供服务 | 无需 offer(作为客户端) |
关键差异说明:
- 共享库加载:
- Client 和 Service 都加载
libE01HelloWorld-someip.so - 该库包含 Proxy 和 StubAdapter 的注册代码
- INITIALIZER 宏确保两者都被自动注册
- Client 和 Service 都加载
- 创建流程:
- Service 端:
registerService→ 创建 StubAdapter → 注册到全局管理器 → offer_service - Client 端:
buildProxy→ 创建 SomeIPProxy → 包装到 Proxy → 直接返回
- Service 端:
- 对象管理:
- Service 端:StubAdapter 被注册到
Factory.services_和StubManager.registeredStubAdapters_ - Client 端:Proxy 对象直接由用户持有,无需全局注册
- Service 端:StubAdapter 被注册到
- 通信角色:
- Service 端:注册消息处理器,等待接收请求
- Client 端:持有 Proxy 对象,主动发送请求
3.3.8 完整的时序
包括库加载,buildProxy,实际的业务过程的时序可以参考如下的plantuml