OCCI调用createConnection无异常直接崩溃问题排查咨询
- 业务逻辑流程:
- 类构造函数中初始化OCCI Environment对象
- 连接创建函数入口先校验Environment指针非空,确认可正常调用接口获取客户端版本
- 调用
createConnection()方法时程序直接退出,无法进入catch异常捕获块
- 已知验证信息:
- 数据库用户名、密码、连接串均有效,在独立简易demo中可正常连通
- 编译链接参数:
-lclntsh -locci_gcc53 -lclntshcore -lnnz21 - 编译器版本:
gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5) - C11、C17编译标准下均复现问题
- 使用Oracle Instant Client 21.5版本
LD_LIBRARY_PATH配置为/home/OCCI/instantclient_21_5:/opt/product/oracle/19.0.0/client_1/lib:.:/usr/lib64:/usr/local/lib64- 同环境下独立简易demo运行正常
- 问题代码:
OCCIDbs::OCCIDbs(){ m_pOCCIenv = oracle::occi::Environment::createEnvironment(oracle::occi::Environment::DEFAULT); } SqlDbConnection* OCCIDbs::CreateConnectedConnection() { if (!m_pOCCIenv) { LOGD("OCCIDbs::CreateConnectedConnection failed m_pOCCIenv is null"); return nullptr; } LOGD("OCCIDbs::CreateConnectedConnection m_sUsername=%s, m_sPassword=%s, m_sDbname=%s", m_sUsername.c_str(), m_sPassword.c_str(), m_sDbname.c_str()); oracle::occi::Connection* p = nullptr; try { int majorVersion; int minorVersion; int updateNum; int patchNumber; int portUpdateNum; m_pOCCIenv->getClientVersion(majorVersion, minorVersion, updateNum, patchNumber, portUpdateNum); LOGD("OCCIDbs::CreateConnectedConnection() minorVersion =%d", minorVersion); LOGD("OCCIDbs::CreateConnectedConnection() m_sUsername=%s m_sPassword=%s m_sDbname=%s", m_sUsername.c_str(), m_sPassword.c_str(), m_sDbname.c_str()); p = m_pOCCIenv->createConnection(m_sUsername, m_sPassword, m_sDbname); LOGD("OCCIDbs::CreateConnectedConnection() after createConnection()"); } catch (oracle::occi::SQLException& sqlExcp) { LOGD("OCCIDbs::CreateConnectedConnection Error=%d : %s", sqlExcp.getErrorCode(), sqlExcp.getMessage()); return nullptr; } catch (std::exception& e) { LOGD("OCCIDbs::CreateConnectedConnection exception: %s", e.what()); return nullptr; } LOGD("OCCIDbs::CreateConnectedConnection after createConnection()"); }
1. GCC C++ ABI不兼容
你链接的libocci_gcc53是Oracle官方使用GCC5.3编译、采用旧版C++ ABI(编译时定义_GLIBCXX_USE_CXX11_ABI=0)的库。GCC5.1之后版本默认开启新版C11 ABI(_GLIBCXX_USE_CXX11_ABI=1),如果你编译大型程序时没有手动指定ABI宏,跨动态库传递std::string等对象时会因为内存布局不一致触发段错误。这类错误是操作系统发送SIGSEGV信号直接终止进程,不会被C异常捕获块接住。
简易demo代码结构简单,没有触发跨so的对象内存布局错位问题,所以可以正常运行。
排查方法:
- 编译大型程序时添加编译选项
-D_GLIBCXX_USE_CXX11_ABI=0,全量重编所有依赖后测试。 - 替换为匹配当前GCC版本、使用新ABI编译的OCCI库,移除链接参数里的
_gcc53后缀版本。
2. 运行时Oracle库版本冲突
你的LD_LIBRARY_PATH同时配置了21.5版本Instant Client和19.0.0版本Oracle Client路径,大型程序依赖关系复杂,动态加载时可能混合加载两个版本的库:比如加载21版本的libocci,同时加载19版本的libclntsh,跨版本的内部接口不兼容会直接触发崩溃。简易demo依赖少,运行时优先命中了同版本的全量库所以正常。
排查方法:
- 执行
ldd 你的程序可执行文件 | grep -E 'libclntsh|libocci|libnnz',检查输出的库路径,确认所有Oracle相关库都来自同一个版本目录,禁止混载19和21版本的库。 - 临时移除
LD_LIBRARY_PATH中19.0.0版本的Client路径,只保留21.5版本Instant Client路径后运行测试。
3. 信号处理逻辑拦截
大型程序如果自定义注册了SIGSEGV、SIGABRT等致命信号的处理函数,或者链接了覆盖默认信号处理的模块(比如内存检测库、安全加固组件),OCCI内部触发的致命错误会被直接拦截退出,不会走到C++异常抛出流程。
排查方法:
- 执行
ulimit -c unlimited开启coredump生成,程序崩溃后用gdb加载coredump文件查看崩溃栈,直接定位崩溃点在OCCI库内部还是其他模块。 - 临时屏蔽程序中所有自定义信号注册逻辑,再运行测试看是否能正常捕获异常。
4. 全局/静态对象初始化顺序问题
如果OCCIDbs实例是全局对象或静态对象,构造函数执行时OCCI依赖的其他全局资源可能还未完成初始化,此时createEnvironment虽然返回非空指针,但内部状态不完整,等调用createConnection时才触发未定义行为。简易demo中OCCI相关对象都在main函数执行后初始化,不存在顺序问题。
排查方法:
- 不要在全局/静态对象的构造函数中初始化OCCI Environment,把初始化逻辑挪到main函数执行后、第一次创建数据库连接前手动调用,测试是否恢复正常。
内容的提问来源于stack exchange,提问作者Arnon

