alibaba00  

linux 下 a.so 和b.so都分别静态加载了grpc的静态库。 当一个进程内同时加载a.so和b.so,会导致进程崩溃。

Thread 1 (Thread 0x7f20d2cc8700 (LWP 102251)): #0 0x00007f20b21fd4fe in grpc_core::Channel::CreateWithBuilder(grpc_core::ChannelStackBuilder) () from /root/Release/plugin//MiddleGrpcPlugin/libMiddleGrpcPlugin.so #1 0x00007f20b21fe164 in grpc_core::Channel::Create(...) #2 0x00007f20b2123c8d in ?? () #3 0x00007f20b2123f4b in grpc_channel_create () #4 0x00007f20b2098966 in ?? () #5 0x00007f20b20988a4 in ?? () #6 0x00007f20b2097d5d in grpc::CreateCustomChannel(...) () #7 0x00007f20b1df385f in MiddleGrpcPlugin::ConnectToGrpc() () #8 0x00007f20b1dfed9f in MiddleGrpcPlugin::InitObj() () #9 0x00000000006e25f7 in DAQPRunManager::InitAndRunPluginObj_run() () #10 0x00000000006e30ce in DAQPRunManager::InitAndRunPluginObj(void) ()
这种情况在windows下是没有的。只有linux下存在。


采用GCC的链接编译选项的方式,解决问题。-Wl,--exclude-libs=ALL

✔ 问题本质:

Linux 下两个 .so 各自静态链接了 gRPC 静态库(libgrpc.a、libgrpc++.a)→ 导致 gRPC 在进程内被加载两套 → 全局单例重复初始化 → 崩溃。

为什么 Windows 下没问题?

因为 Windows 的默认链接行为是:

  • 静态库中符号是 本地(private) 的,不会发生全局冲突。

Linux 的静态库 + shared library 组合会将符号提升为默认全局可见,尤其是 when building shared object with static libs → 符号被暴露给 ELF 动态符号表(.dynsym)

因此:

a.so   含有一份 grpc 全局状态
b.so   又含有同样一份 grpc 全局状态

最终:

  • gRPC 中大量 全局单例(Core config、ExecCtx、TimerManager、Fork support) 会被重复构造
  • grpc_init() / grpc_shutdown() 被多次调用
  • TLS、Pollset、Epoll、iomgr 内部状态冲突
  • 最终 Segmentation Fault / Abort

你遇到的栈回溯:

grpc_core::Channel::CreateWithBuilder(grpc_core::ChannelStackBuilder*)

正是 ChannelStack 的全局 registry 冲突 导致崩溃。


✔ 解决根本方案(任选其一,但必须遵循)


方案 1(强烈推荐)

把 gRPC 改成动态库(libgrpc.so / libgrpc++.so)并让 a.so 与 b.so 共享同一份 gRPC

这是所有官方推荐的做法,100% 解决。

✔ 修改 CMake 示例

你的 a.so / b.so 不要再静态链接 gRPC

target_link_libraries(a PRIVATE grpc grpc++)
target_link_libraries(b PRIVATE grpc grpc++)

使用动态库后,进程内只有一份 gRPC 全局状态,不崩溃。


方案 2(适用于你不能用动态库的场景)

强制隐藏符号(-fvisibility=hidden)禁止 gRPC 的符号泄露到 ELF 动态表

否则 a.so 和 b.so 会互相看到彼此的 gRPC 符号 → 导致冲突。

✔ 在 a.so 和 b.so 的 CMake 中加入:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")

并在 target 上指定隐藏符号:

target_compile_options(a PRIVATE -fvisibility=hidden)
target_compile_options(b PRIVATE -fvisibility=hidden)

➤ 效果

让每个 .so 内的 gRPC 符号对外不可见,避免符号冲突。

但 gRPC 内部仍是两套副本,不一定 100% 避免崩溃。
如果 gRPC 版本较老,这种方案可能依然死。


方案 3(次优)

➤ 在链接阶段使用 -Wl,--exclude-libs=ALL

可以让静态库中的符号不外露到 .so

target_link_libraries(a PRIVATE -Wl,--exclude-libs,ALL libgrpc.a libgrpc++.a)
target_link_libraries(b PRIVATE -Wl,--exclude-libs,ALL libgrpc.a libgrpc++.a)

这个方法本质上和方案 2 类似,但通常效果更好。


方案 4(被动解决)

➤ 把 a.so 与 b.so 合并成一个 so

让它们共享同一份 gRPC 静态库。

但这通常不符合工程需求,所以不建议。


✔ 最推荐方案总结

方案 稳定性 推荐度 说明
1. 使用动态库(最推荐) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ gRPC 设计就是要共享全局状态
2. -fvisibility=hidden ⭐⭐⭐⭐ ⭐⭐⭐ 能解决多数冲突,但不完全稳
3. --exclude-libs=ALL ⭐⭐⭐ ⭐⭐⭐ 隐藏符号,但不保证兼容性
4. 合并 so ⭐⭐⭐ 通常不能用

下面给你 最直接可复制的 gcc 编译/链接命令写法,对应前面提到的几种解决方案。
无论你是否用 Makefile,都可以直接套用。


目标:a.so / b.so 静态链接 gRPC 但不互相冲突

假设你现在的命令类似这样:

gcc -shared -o a.so a.o libgrpc.a libgrpc++.a ...
gcc -shared -o b.so b.o libgrpc.a libgrpc++.a ...

下面根据你选用的方案替换。


方案 1(最推荐)使用动态库(无冲突)

如果你可以把 gRPC 改成动态库:

g++ -shared -o a.so a.o -lgrpc++ -lgrpc ...
g++ -shared -o b.so b.o -lgrpc++ -lgrpc ...

这会 100% 解决崩溃问题



方案 2(次推荐)隐藏符号:-fvisibility=hidden

这是 Linux 下静态链接多份 gRPC 最常用的解决方式:

编译阶段加:

g++ -fvisibility=hidden -c a.cpp -o a.o
g++ -fvisibility=hidden -c b.cpp -o b.o

链接阶段照常:

g++ -shared -o a.so a.o libgrpc++.a libgrpc.a ...
g++ -shared -o b.so b.o libgrpc++.a libgrpc.a ...

这样 a.so、b.so 内部的 gRPC 符号不会互相看到,大多数 gRPC 版本都不会再崩溃。



方案 3(更稳定)隐藏静态库符号:-Wl,--exclude-libs=ALL

推荐程度高于方案 2,因为它直接告诉链接器不要把静态库里的符号暴露到 ELF 动态符号表

➤ 你的编译命令写成:

g++ -fPIC -c a.cpp -o a.o
g++ -shared -Wl,--exclude-libs,ALL -o a.so a.o libgrpc++.a libgrpc.a ...

g++ -fPIC -c b.cpp -o b.o
g++ -shared -Wl,--exclude-libs,ALL -o b.so b.o libgrpc++.a libgrpc.a ...

这是 Linux 官方推荐避免静态库符号泄漏的方式



方案 4(如果你不能修改任何编译器参数)

唯一方式是:
把 a.so 和 b.so 合并成一个 so
使其只包含一次 gRPC。
但通常不推荐。


🎯 你应该用哪个?

方案 稳定性 推荐度 一句话说明
动态库 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ 最正确的方式,不崩溃
--exclude-libs=ALL ⭐⭐⭐⭐ ⭐⭐⭐⭐ 最稳的静态方案
-fvisibility=hidden ⭐⭐⭐ ⭐⭐⭐ 较稳,但不如 exclude-libs
合并 so ⭐⭐⭐ 无意义一般不用

📌 最终,你可以直接用这一套命令(最推荐静态方案):

g++ -fPIC -fvisibility=hidden -c a.cpp -o a.o
g++ -shared -Wl,--exclude-libs,ALL -o a.so a.o libgrpc++.a libgrpc.a ...

g++ -fPIC -fvisibility=hidden -c b.cpp -o b.o
g++ -shared -Wl,--exclude-libs,ALL -o b.so b.o libgrpc++.a libgrpc.a ...
posted on 2025-12-05 16:44  不老天神  阅读(0)  评论(0)    收藏  举报