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 ...

浙公网安备 33010602011771号