工作拆分so总结

拆分SO中遇到的问题:

1、APP_STL := c++_static,得改为 c++_shared

每个 so 都独立包含一份 C++ 运行时:

c++_static 会将 LLVM libc++ 的静态库(libc++_static.a)完整地链接到每一个生成的 .so 文件中。假设你有 libA.solibB.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_mapFuncAddrfuncName = 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_mapFuncAddrname = 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(ppVecIDi);

}

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,及所有内容都唯一了。

相关推荐
吃好睡好便好1 天前
提取矩阵某一行或某一列元素
开发语言·人工智能·线性代数·算法·matlab·矩阵
better_liang1 天前
每日Java面试场景题知识点之-消息队列MQ核心场景与实战
java·面试·kafka·消息队列·rabbitmq·rocketmq·mq
小江的记录本1 天前
【JVM虚拟机】垃圾回收GC:四种引用类型:强引用、软引用、弱引用、虚引用(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
小马爱打代码1 天前
Spring源码 第四篇:Spring 5 源码深度拆解:AOP 全流程核心原理
java·后端·spring
better_liang1 天前
每日Java面试场景题知识点之-SpringBoot启动流程
java·面试·springboot·源码解析·启动流程
RyFit1 天前
Java + AI 实战:Spring AI 从入门到企业级落地
java·人工智能·spring
云泽8081 天前
笔试算法 -位运算篇(二):从唯一字符到消失数字
c++·算法·位运算
ʚ希希ɞ ྀ1 天前
不同路径|| -- dp
算法
ZhengEnCi1 天前
01-如何监听接口调用情况?
java·spring boot·后端
IT 行者1 天前
SimHash 与 MinHash:相似性计算的双子星算法
算法·hash·比对