拆分SO中遇到的问题:
1、APP_STL := c++_static,得改为 c++_shared
每个 so 都独立包含一份 C++ 运行时:
c++_static 会将 LLVM libc++ 的静态库(libc++_static.a)完整地链接到每一个生成的 .so 文件中。假设你有 libA.so 和 libB.so,它们各自都包含了一份相同的 C++ 运行时代码。
重复的全局状态:
C++ 运行时中有一些关键全局状态,例如:
异常处理表(unwinding tables)
堆内存分配器(new/delete)
类型信息(RTTI)
具体崩溃场景:
1.跨 so 抛异常:在 libA.so 中抛出一个异常,希望在 libB.so 中捕获。由于两个 so 使用不同的异常处理表,捕获会失败,通常导致 std::terminate 或直接崩溃。
2.跨 so 分配/释放内存:在 libA.so 中用 new 分配内存,在 libB.so 中用 delete 释放。因为每个 so 有自己的堆管理,可能导致堆损坏或内存泄漏。
比如说在常用特殊功能中,我在子so中new分配内容,把它给特殊功能的so用智能指针保管
子 SO 中的 new 调用的是子 SO 自己那份 operator new,它从子 SO 的堆(可能是通过 malloc 封装的独立)分配内存。
主 SO 中的 std::shared_ptr 析构时调用 delete,调用的是主 SO 自己那份 operator delete,它会去主 SO 的堆释放内存。
这两个堆是不同的,甚至可能使用了不同的内存分配器实现。在一个堆分配、另一个堆释放是绝对禁止的,后果包括:堆元数据损坏
双重释放或释放未分配地址
立即崩溃(如 SIGSEGV 或 malloc: *** error for object ...)
3.动态类型检查失败:dynamic_cast 或 typeid 跨 so 使用时,RTTI 比较会失败(即使同一个类在不同 so 中被视为不同类型)。
4.增大包体积:每个 so 都多了一份运行时库代码,总大小显著增加。
2、代码拆分,之前所有的代码都在一起虽然分了不同的cpp文件,但是都是同一个类,同一个头文件;
拆分代码按照类别 定义不同的类,都继承与CSpBase类(包含了 公用方法等);
若是不拆分的做法
switch (HotSpType)
{
case enum_OilReset:
m_pHotSpFunction = make_shared<CNissanHotSp_01_OilReset>();
break;
case enum_BMS:
m_pHotSpFunction = make_shared<CNissanHotSp_02_BMS>();
break;
} 随后初始化指针,随后若是 点击功能后再根据功能ID划分,子类重写
这样的话就每个子类都要重写函数,直接定义在基类,子类在构造函数中调用 RegisterFunction 来注册自己;
template<typename ClassType>
void RegisterFunction(const string& funcName,
const ErrorCode_t(ClassType::* memFunc)(string&) const,
ClassType* pThis) {
m_mapFuncAddr[funcName] = [pThis, memFunc](string& arg) -> ErrorCode_t {
return (pThis->*memFunc)(arg);
};
}
还可以更进一步
class Base {
protected:
map<string, function<ErrorCode_t(string&)>> m_mapFuncAddr;
public:
void RegisterFunction(const string& name, function<ErrorCode_t(string&)> func) {
m_mapFuncAddr[name] = move(func);
}
};
class Derived : public Base {
public:
Derived() {
RegisterFunction("process", [this](string& arg) {
return this->Process(arg); // Process 可以是任意签名,只要返回值能转为 ErrorCode_t
});
}
ErrorCode_t Process(string& arg) const { /*...*/ }
};
使用 function 去除模板化
3、按照功能来导出so、各so都导出接口
EXPORT_SYMBOL void Lib_ReadVersion(void* pSysEnter, const char* strEcuName, const char** ppVecID, int nIDCount);
extern "C" {
void Lib_ReadVersion(void* pSysEnter, const char* strEcuName, const char** ppVecID, int nIDCount) {
// 转换参数
string ecuName = strEcuName ? strEcuName : "";
vector<string> vecIDs;
for (int i = 0; i < nIDCount; ++i) {
vecIDs.push_back(ppVecID[i]);
}
TopDon_AD900_Nissan::CInformation information;
information.SetSysEnterPointer((TopDon_AD900_Nissan::CEnterSys*)pSysEnter);
information.ReadVersion(ecuName, vecIDs);
}
}
给外边So调用;
4、通用的接口、单列类、以及so资源管理类、如何跨so调用问题;
因为默认的情况下只会将本地的主so拷贝的工作目录。所以主so就不能链接其他的子so;但是不能每个so都包含通用接口的文件、资源管理文件、
更何况它还是单列的,全局唯一;
想法一、将这些公用的都变成一个so 名为 Comm.so,其他的子so都链接它,在主程序运行的时候将单列类的地址传递给子so,以及资源管理对象的地址也传递过去。
想法二、主so只包含mian函数,再重新定义一个主流程so,比如Nissan.so并暴露出主流程接口,mian函数当中将Comm.so拷贝到工作目录并打开,
这样所有的so都可以链接Comm.so,及所有内容都唯一了。