Linux 下 so 共享库的 dlclose 卸载问题

Saleayas 2016-04-23 11:04:38
在 Linux 下创建一个共享库,在该库中使用
void Init(void) __attribute__((constructor));
void Final(void) __attribute__((destructor));
标识一对函数,来显示模块的加载和卸载。
同时创建一个测试的 Console 程序,测试加载和卸载。
简单的情况下,当dlopen 的时候,Init 函数被调用, dlclose 的时候,Final 函数被调用。

但是,当我在共享库中添加一个类。并且这个类中添加一个成员函数(不管是否是静态的)。
如果这个函数使用了一个静态变量(不是类的静态变量,是函数的静态变量)。
同时导出函数的呼叫路径上使用了这个函数。
那么此时 当 dlclose 的时候, Final 函数没有被调用。而是等到整个 Console 程序退出时, Final 函数才被调用。

共享库的 main.cpp 代码
class CMath
{
public:
void A()
{
int const es[] = { 1, 2 };
int index = 0;
printf("%d", es[index]);
}

static void B()
{
static int const es[] = { 1, 2 };
int index = 0;
printf("%d", es[index]);
}

// static void C()
// {
// int const index = 0;
// printf("%d", _es[index]);
// }
//
// static int const _es[2];
};

//int const CMath::_es[2] = {1, 2};

void Export()
{
//CMath _;
//_.A();
CMath::B();
//CMath::C();
}
void Export() __attribute__((visibility("default")));

static void _Init(void)
{
printf("module loaded.\n");
}
static void _Final(void)
{
printf("module unloaded.\n");
}

void _Init(void) __attribute__((constructor));
void _Final(void) __attribute__((destructor));


// 测试代码 main.c 代码。
int main()
{
void *handle;
printf("dlopen ...\n");
handle = dlopen("libShare.so", RTLD_LAZY);
printf("dlopened.\n");
assert(handle);

printf("dlclose ...\n");
dlclose(handle);
printf("dlclosed.\n");

return getchar();
}


请教一下,这是什么问题。
还有就是怎样解决这个问题。
我需要在成员函数中定义静态变量。
...全文
1256 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
aSpecialForce 2019-07-26
  • 打赏
  • 举报
回复
为什么我按照你的思路,在动态库中定义的类的函数里加静态变量,但是dlclose的时候还是调用了Final 函数?c++代码,g++编译
avoid1114 2018-06-13
  • 打赏
  • 举报
回复
有个C#调用C++ dll的问题想请教你,不知道能否加我一下qq 41922661
Saleayas 2016-04-27
  • 打赏
  • 举报
回复
我测试了一下, 使用 inline 的时候,g++ 编译会使用 weak 属性,同时放到特殊的 section 中。 估计这样会导致链接的时候有特殊的要求。导致 dlclose 不能正常卸载。 如果使用 inline static 修饰,那么和没有 inline 修饰是一致的。 可惜,类成员函数内联定义的话,不可以使用 static 修饰,类的 static 修饰有自己的定义。 我尝试把类的成员函数都不定义成内联的。似乎解决了问题。 可惜,模板类的成员函数,就算不定义成内联的,也依然解决不了。 // 放弃了。 就算,我不使用上面的特殊方式,但是我的代码中可能会引用其他的静态库。 而这些静态库可能会有这些特殊的定义,毕竟是第三方静态库。 // 谢谢 chehw_1 的指点。
苦逼码农 2016-04-26
  • 打赏
  • 举报
回复
引用 8 楼 Saleayas 的回复:
[quote=引用 7 楼 chehw_1 的回复:] 把成员函数定义在类内部时,这个函数会被视为inline的模式。编译器编译时应该与在类外定义的方式会有一些小区别。 调用dllclose时,编译器只有在判断没有side-effect时,才确定会调用.dtor的代码。如果可能有side-effect,虽然你在dllopen时用的是RTLD_LAZY模式,但dllclose时未必会调用.dtor的代码(没有规定说在这种情况下也一定要调用,实证的结果是确实没在dllclose时调用)。 C++的语法过于复杂,编译器处理起来应该有很大难度,在这种极端的特例情况下,不确定所有编译器会编译出相同的结果,因此最好避免这样用(dll的代码中,在类内定义成员函数)。 如果想确保程序完全按你的意图实现,可以考虑用C来写这个dll,这样就不会产生歧义。
谢谢您的指点,我进一步测试了一下。 同样的代码,如果一个 函数内部有静态变量,而这个函数被定义为 inline 的。 当使用 gcc 编译的时候,也就是 C 语言模式,就是正确的。 同样的文件,同样以 .c 结尾,如果强制使用 g++ 编译,那么结果就是不正确的。 这样就和类成员函数没有关系了,和 inline 有关系。 我是否可以认为这是 g++ 的 BUG?? [/quote] 可以去mail list去问问, http://bbs.csdn.net/topics/391941907 顺便帮忙看看这个
Saleayas 2016-04-26
  • 打赏
  • 举报
回复
引用 7 楼 chehw_1 的回复:
把成员函数定义在类内部时,这个函数会被视为inline的模式。编译器编译时应该与在类外定义的方式会有一些小区别。 调用dllclose时,编译器只有在判断没有side-effect时,才确定会调用.dtor的代码。如果可能有side-effect,虽然你在dllopen时用的是RTLD_LAZY模式,但dllclose时未必会调用.dtor的代码(没有规定说在这种情况下也一定要调用,实证的结果是确实没在dllclose时调用)。 C++的语法过于复杂,编译器处理起来应该有很大难度,在这种极端的特例情况下,不确定所有编译器会编译出相同的结果,因此最好避免这样用(dll的代码中,在类内定义成员函数)。 如果想确保程序完全按你的意图实现,可以考虑用C来写这个dll,这样就不会产生歧义。
谢谢您的指点,我进一步测试了一下。 同样的代码,如果一个 函数内部有静态变量,而这个函数被定义为 inline 的。 当使用 gcc 编译的时候,也就是 C 语言模式,就是正确的。 同样的文件,同样以 .c 结尾,如果强制使用 g++ 编译,那么结果就是不正确的。 这样就和类成员函数没有关系了,和 inline 有关系。 我是否可以认为这是 g++ 的 BUG??
chehw_1 2016-04-25
  • 打赏
  • 举报
回复
把成员函数定义在类内部时,这个函数会被视为inline的模式。编译器编译时应该与在类外定义的方式会有一些小区别。 调用dllclose时,编译器只有在判断没有side-effect时,才确定会调用.dtor的代码。如果可能有side-effect,虽然你在dllopen时用的是RTLD_LAZY模式,但dllclose时未必会调用.dtor的代码(没有规定说在这种情况下也一定要调用,实证的结果是确实没在dllclose时调用)。 C++的语法过于复杂,编译器处理起来应该有很大难度,在这种极端的特例情况下,不确定所有编译器会编译出相同的结果,因此最好避免这样用(dll的代码中,在类内定义成员函数)。 如果想确保程序完全按你的意图实现,可以考虑用C来写这个dll,这样就不会产生歧义。
Saleayas 2016-04-25
  • 打赏
  • 举报
回复
我的共享库就只有一个 main.cpp 文件。 没有任何多余的东西。更加没有头文件之说。 唯一的区别就是 使用了静态变量的成员函数是否是定义在类内部。 我认为这是 GNU C++ 的Bug,不管怎样定义就不应该引起逻辑错误。
lm_whales 2016-04-24
  • 打赏
  • 举报
回复
放在头文件中,类的成员函数就会出现在 执行文件中,而不仅仅是共享库中 这可能会造成 静态变量不止一份,活着共享库中不存在静态变量 调用 共享库的程序中存在静态变量 凡涉及 静态变量的 不论是win 动态库dll,还是linux 共享库so 都要加倍小心
Saleayas 2016-04-23
  • 打赏
  • 举报
回复
编译和测试环境。 Hyper-V 虚拟机, Ubuntu 操作系统。 GNU GCC Compiler 。
Saleayas 2016-04-23
  • 打赏
  • 举报
回复
进一步测试发现一个很奇怪的问题。 如果我把函数定义在类定义之中。 那么这个共享库就不能在 dlclose 的时候卸载。 如果我把这个函数定义在类之外,那么就正常了。 区别代码:
//#define INCLASS

class CMath
{
public:
	static void B()
#ifdef INCLASS
	{
		static int es[] = { 1, 2 };
		int v = es[0];
		printf("%d", v);
	}
#else
	;
#endif
};

#ifdef INCLASS
#else
void CMath::B()
{
	static int es[] = { 1, 2 };
	int v = es[0];
	printf("%d", v);
}
#endif
宏定义是否存在,将影响共享库在 dlclose 的时候是否可以正常卸载。 请高手指点一二,不甚感激。
Saleayas 2016-04-23
  • 打赏
  • 举报
回复
可是类静态变量,或者是普通函数的内部定义的静态变量,难道就没有生命周期了吗? 我测试过普通的函数,和类的静态变量,都没有这个问题。 我在测试一下,引用静态类如何。 想知道,如何解决这个问题。 我对于 Linux 下的 共享库了解得不深。
ztenv 版主 2016-04-23
  • 打赏
  • 举报
回复
静态变量的生命周期

64,639

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧