写动态库如何释放从内存中加载动态库

Linux 动态库如何节约内存? - 知乎15被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答0添加评论分享收藏感谢收起0添加评论分享收藏感谢收起写回答当前位置: &
c#动态调用delphi
dll,如何释放资源
麻烦先看一下代码:classdld{///&summary&///参数传递方式枚举,ByValue表示值传递,ByRef表示址传递///&/summary&publicenumModePass{ByValue=0x0001,ByRef=0x0002}///&summary&///原型是:HMODULELoadLibrary(LPCTSTRlpFileName);///&/summary&///&paramname="lpFileName"&DLL文件名&/param&///&returns&函数库模块的句柄&/returns&[DllImport("kernel32.dll")]staticexternIntPtrLoadLibrary(stringlpFileName);///&summary&///原型是:FARPROCGetProcAddress(HMODULEhModule,LPCWSTRlpProcName);///&/summary&///&paramname="hModule"&包含需调用函数的函数库模块的句柄&/param&///&paramname="lpProcName"&调用函数的名称&/param&///&returns&函数指针&/returns&[DllImport("kernel32.dll")]staticexternIntPtrGetProcAddress(IntPtrhModule,stringlpProcName);///&summary&///原型是:BOOLFreeLibrary(HMODULEhModule);///&/summary&///&paramname="hModule"&需释放的函数库模块的句柄&/param&///&returns&是否已释放指定的Dll&/returns&[DllImport("kernel32",EntryPoint="FreeLibrary",SetLastError=true)]staticexternboolFreeLibrary(IntPtrhModule);///&summary&///Loadlibrary返回的函数库模块的句柄///&/summary&privateIntPtrhModule=IntPtr.Z///&summary&///GetProcAddress返回的函数指针///&/summary&privateIntPtrfarProc=IntPtr.Z///&summary&///装载Dll///&/summary&///&paramname="lpFileName"&DLL文件名&/param&publicvoidLoadDll(stringlpFileName){hModule=LoadLibrary(lpFileName);//if(hModule==IntPtr.Zero)//throw(newException("没有找到:"+lpFileName+"."));}//publicvoidLoadDll(IntPtrHMODULE)//{//if(HMODULE==IntPtr.Zero)//throw(newException("所传入的函数库模块的句柄HMODULE为空."));//hModule=HMODULE;//}///&summary&///获得函数指针///&/summary&///&paramname="lpProcName"&调用函数的名称&/param&publicvoidLoadFun(stringlpProcName){//若函数库模块的句柄为空,则抛出异常if(hModule==IntPtr.Zero)throw(newException("函数库模块的句柄为空,请确保已进行LoadDll操作!"));//取得函数指针farProc=GetProcAddress(hModule,lpProcName);//若函数指针,则抛出异常if(farProc==IntPtr.Zero)throw(newException("没有找到:"+lpProcName+"这个函数的入口点"));}///&summary&///获得函数指针///&/summary&///&paramname="lpFileName"&包含需调用函数的DLL文件名&/param&///&paramname="lpProcName"&调用函数的名称&/param&//publicvoidLoadFun(stringlpFileName,stringlpProcName)//{//取得函数库模块的句柄//hModule=LoadLibrary(lpFileName);////若函数库模块的句柄为空,则抛出异常//if(hModule==IntPtr.Zero)//throw(newException("没有找到:"+lpFileName+"."));////取得函数指针//farProc=GetProcAddress(hModule,lpProcName);////若函数指针,则抛出异常//if(farProc==IntPtr.Zero)//throw(newException("没有找到:"+lpProcName+"这个函数的入口点"));//}///&summary&///卸载Dll///&/summary&publicvoidUnLoadDll(){FreeLibrary(hModule);hModule=IntPtr.ZfarProc=IntPtr.Z}~dld(){if(hModule!=IntPtr.Zero)FreeLibrary(hModule);//释放资源}///&summary&///调用所设定的函数///&/summary&///&paramname="ObjArray_Parameter"&实参&/param&///&paramname="TypeArray_ParameterType"&实参类型&/param&///&paramname="ModePassArray_Parameter"&实参传送方式&/param&///&paramname="Type_Return"&返回类型&/param&///&returns&返回所调用函数的object&/returns&publicobjectInvoke(object[]ObjArray_Parameter,Type[]TypeArray_ParameterType,ModePass[]ModePassArray_Parameter,TypeType_Return){//下面3个if是进行安全检查,若不能通过,则抛出异常if(hModule==IntPtr.Zero)throw(newException("函数库模块的句柄为空,请确保已进行LoadDll操作!"));if(farProc==IntPtr.Zero)throw(newException("函数指针为空,请确保已进行LoadFun操作!"));if(ObjArray_Parameter.Length!=ModePassArray_Parameter.Length)throw(newException("参数个数及其传递方式的个数不匹配."));//下面是创建MyAss
你写在析构函数里面没用的,应该写一个dispose方法,然后用using或者用完后显式调用。貌似没用啊?&private&bool&m_&&&&&&&&~dld()&&&&&&&&{&&&&&&&&&&&&Dispose(false);&&&&&&&&}&&&&&&&&public&void&Dispose()&&&&&&&&{&&&&&&&&&&&&Dispose(true);&&&&&&&&&&&&&GC.SuppressFinalize(this);&&&&&&&&}&&&&&&&&protected&virtual&void&Dispose(bool&isDisposing)&&&&&&&&&{&&&&&&&&&&&&&if&(!m_disposed)&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&if&(isDisposing)&&&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&FreeLibrary(hModule);&//释放资源&&&&&&&&&&&&&&&&&&&&&FreeLibrary(farProc);&//释放资源&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&//&清理非托管资源&&&&&&&&&&&&&&&&if&(hModule&!=&IntPtr.Zero)&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&hModule&=&IntPtr.Z&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&if&(farProc&!=&IntPtr.Zero)&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&farProc&=&IntPtr.Z&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&m_disposed&=&&&&&&&&&&&&&&}&&&&&&&&&}&&&&&&&&&&&&&&&&public&void&Close()&&&&&&&&&{&&&&&&&&&&&&&((IDisposable)this).Dispose();&&&&&&&&}调用:myfun.Close();myfun.Dispose();内存还是增加。哪里不对吗?套在using里面吧.会自动处理了.这样么?也没用啊?&&using&(dld&myfun&=&new&dld())&&&&&&&&&&&&{&&&&&&&&&&&&&&&&myfun.LoadDll(dllfilename);&//&加载&"XXX.dll"&&&&&&&&&&&&&&&&&&myfun.LoadFun("sethbmd");&//&调入函数&sethbmd,&"sethbmd"&是它的入口,可通过&Depends&查看&..........................................&&&&&&&&&&&&&&&&if&((bool)myfun.Invoke(Parameters,&ParameterTypes,&themode,&Type_Return)&==&true)&&&&&&&&&&&&&&&&{..........................................&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&}肿么办?捉急了。这样调用才能更改dll文件名称。还有其他能按需更改dll文件名的方法吗?懒得看这种代码,估计根本与什么“释放资源”无关。既然是你的delphi类库,那么释放资源当然应该是它的事情。你根据什么说.net可能知道如何给它释放啊?你可以找找看,或者你也可以想象一下,在什么地方你可能看到说.net懂得自动释放你的什么delphi、c、c++程序中的占用的什么资源的?不可能的事情。别说.net不行,就算你些c++程序调用另外一个不知名的人给你的c++编译的dll也是一样,你也是不知道如何释放那个c++程序重的什么资源内存的。凡是不带自动垃圾收集机制的程序,或者虽然有但是只是开源社区提供的垃圾开源代码而不是原生系统垃圾收集机制,都是这同一类东西。轻易不要调用这类东西。前辈说的极是,我估计就是这样的代码&不能释放内存。但是用.net的[DllImport("project2.dll",&CallingConvention&=&CallingConvention.StdCall)]&&&&&&&&public&static&extern&bool&sethbmd(IntPtr&f,&string&ip,&int&dk,&int&jh,&byte[]&kh,&byte[]&xm,&byte[]&bz,&int&cs);//下发黑白名单去跑的话,内存一直没增加,估计内存得到了释放。调用的dll方法也成功了。但是这样的话&我没法动态改变调用的dll文件,dll文件名只能是常量。而在文章:http://www.codeproject.com/Articles/734492/Reflection-Emit-based-native-library-call中,内存也一样的无法得到释放。虽然他也自定义了dispose方法。但是不管用的。前辈说的极是,我估计就是这样的代码&不能释放内存。但是用.net的[DllImport("project2.dll",&CallingConvention&=&CallingConvention.StdCall)]&&&&&&&&public&static&extern&bool&sethbmd(IntPtr&f,&string&ip,&int&dk,&int&jh,&byte[]&kh,&byte[]&xm,&byte[]&bz,&int&cs);//下发黑白名单去跑的话,内存一直没增加,估计内存得到了释放。调用的dll方法也成功了。但是这样的话&我没法动态改变调用的dll文件,dll文件名只能是常量。而在文章:http://www.codeproject.com/Articles/734492/Reflection-Emit-based-native-library-call中,内存也一样的无法得到释放。虽然他也自定义了dispose方法。但是不管用的。[DllImport("project2.dll",&CallingConvention&=&CallingConvention.StdCall)]或者说只加载过一次,而后每次都调用的同一个dll。所以内存没有增加。请问&最后是怎么解决的??
本站所有文章全部来源于互联网,版权归属于原作者。本站所有转载文章言论不代表本站观点,如是侵犯了原作者的权利请发邮件联系站长(),我们收到后立即调整或删除。
varisOk=假设这个变量一直在变化,如何定义一个事件监听这个值的变化呢?当它是true时就执行代码1,是false时就执行代码2。这里不用setInterval,有大神知道么
sql得到的结果集的列是不固定性的,也就是说第一次查询的的结果集的列可能是4列,第二次查询的时候可能是5列。那么如何去映射这种结果集呢?
各位大侠,我自己写了一个winform程序,并进行发布了,再自己安装的时候却没有像一般软件安装一样弹出选择安装目录对话框,请问应该怎么设置。不需要默认的,一定要用户自己选择安装目录。
html中引入jquery时,需要引入jquery库文件与jquery代码文件。jquery库文件是一个js文件,可以通过&script&&/script&引入,而jquery代码能否不写在js文件中,而直接在html编写?
view中这样写的&headid="Head1"runat="server"&&metaname="viewport"content="width=device-width"/&&scriptsrc="../../jqueryeasyui/jquery-1.8.0.min.js"&&/script&&scripttype="text/javas...
我可以贷款多少?
您的房子原来是按揭的么?因该怎么加名字啊?
首付可以这样组合吗?
想做一个数据库的条件查询,如下图:通过对某一项打勾,从而获得该项的名称。自定义了一个函数获取唯一值的函数获得该field下的所有值,恰巧要用到checkedboxlist中被选中项的名称。可是不知道怎么获取其名称,希望大家给看看。下面是获取唯一值得函数,也请大家批评指正。publicstaticstringQueryFieldValue(StringaccessPath,stringtableNa...
比如说我在C#中调用的是一个可以实现复制文件的exe,如果这个exe发现目的路径或者源路径不存在,这时会有错误出现,我该如何在c#中获知呢,困扰好几天了,求大神帮个忙。参考:首先,声明一点,凡是使用malloc之类命令动态申请的内存,必须进行释放操作,否则就会发生内存泄漏问题。
DLL中申请的内存释放,如果没有做过,很可能会认为是直接在调用程序中释放就可以了,其实不然,如果真那么做了,会发现,程序总是不明不白的当掉。那么,如何释放DLL中申请的动态内存呢?
首先,说明一点,对于DLL,采用了一种保护机制,那就是,DLL中的任何对象都是对外隐藏的,虽然我们通过传址调用的方式,在调用过DLL之后,可以访问DLL中动态申请的内存空间,但是,我们的调用程序并不能释放这个空间,原因就是因为它是对外隐藏的,由于这个原因,我们也只是获得了访问这类空间的权利,如果真的要释放的话,还得要被调用的DLL本向来完成,所以,我们在编写DLL的时候,如果发生了动态内存申请,并且,由于这些动态申请的内存会被调用的父进程程序访问,DLL结束的时候,并不能立即释放这些动态内存,那么,这时就需要我们同时编写一个专门用来释放这些动态内存的函数,和DLL编译到一起,然后,在父进程程序使用完这些动态内存空间后,再以传址的方式,调用这个释放函数,这样就可以将这些DLL中申请的动态内存空间释放掉了。
&&&&&&& #include &stdio.h&
&&&&&&& #include &string.h&
&&&&&&& #include &malloc.h&
&&&&&&&_declspec(dllexport)& void &testFunc(char **tmp)
&&&&&&&&&&&&&&&&&&& *tmp = (char *)malloc(sizeof(char)*100);
&&&&&&&&&&&&&&&&&&& memset(*tmp,'\0',100);
&&&&&&&&&&&&&&&&&&&& strcpy(*tmp,"susan");
&&&&&&&& }
&&&&&&&& _declspec(dllexport)& freeTestFuncMemory(char **tmp)
&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&& if(NULL != *tmp)
&&&&&&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& free(*tmp);
&&&&&&&&&&&&&&&&&&&& }
&&&&&&&&& }
&&&&&&&&& 上述两个DLL要编译到一个LIB库里。
&&&&&&&& int main(int argc,char **args)
&&&&&&&& {
&&&&&&&&&&&&&&&&&&& char *employeeName = NULL;
&&&&&&&&&&&&&&&&&&&&testFunc(&employeeName);
&&&&&&&&&&&&&&&&&&&&printf("%s\n",employeeName );&&&&&& /*&这里会输出 susan&*/
&&&&&&&&&&&&&&&&&&& freeTestFuncMemory((&employeeName);
&&&&&&&&&&&&&&&&&&&&printf("%s\n",employeeName );&&&&&&& /*&这里会输出乱码 */
实际应用的时候,一定要注意,调用完分配内存的函数后,最好马上用三方变量将动态内存的值接出去,然后马上调用释放函数,否则如果在释放前又调用了其它DLL,那么再释放的时候就会出问题。我这次应用比较急,就没有再细查解决方法,哪位朋友发现方法了,麻烦可以给小弟留个言,谢谢。
这个例子不是很完整,不过原理是没有问题的,参照的话,请自行补充。
---------------------------------------------------------------------------------------------------------------------------------------
关于动态链接库中动态申请的内存区域如何传递到用户空间:刚开始尝试用指针,比如在dll中申请了一个char*的一串内存区域。
于是就有了下面的几个疑惑:
1. 比如在一个dll中的初始化函数中申请了一块char*的内存,之后函数返回,那么这块用malloc申请的内存是谁在维护?操作系统接管之后会不会被乱用掉???因为后续还会针对这块内存进行处理,不希望操作系统对这块内存进行额外的操作!
2. 刚开始使用同样的char*变量从dll的导出函数中将这块内存的地址传出,但是好像不对,于是考虑到了以前使用函数进行参数传递的时候,要想传递比如10个int型的数据,那么传递int*的变量就可以了。传递的必须是地址,所以,对于char*的一篇内存,我要传递过去的话,实际上是应该使用char**,就是指针的指针,而不能使用char*。如果使用char*进行传递的话,比如在User定义了一个char* pData变量,该变量作为参数送入了DLL的导出函数中,然后该函数中对char* pData进行了赋值,赋值为DLL中动态申请的char*的首地址,那么实际上char*
pData的值并没有发生改变。对这个变量pData赋值,是没有用的,你得是对这个变量的存储地址进行赋值才有效,所以,必须对这个pData的存储地址进行赋值。还是有点乱,重新捋一下:对于传递好几个int型的变量的时候,传递的是int型变量的地址,通过操作它们的内存地址以达到传递int型变量的目的。操作地址赋值,就是往存储这个变量的地址上写入要传递的变量,才能达到真的传递参数的目的;那么说到这个char*的内存区域pMem,pMem实际上就等于这块内存空间的起始地址,那么我希望传递这个地址,是不是得通过(char*)*ppMem的地址上的内容才能传递呢?即*ppMem
= pMem即可,这个时候使用*ppMem就可以访问到这片内存区域了,具体的就是(*ppMem)[0,1,2....]。
目前对于第一个疑惑,还是不太清楚,DLL中malloc的内存,在函数返回后,操作系统会对这片内存如何处理?会不会有别的东西乱入这篇内存区域,导致我后续使用这篇内存区域的时候出现难以察觉的问题。
阅读(...) 评论()技巧:多共享动态库中同名对象重复析构问题的解决方法
Linux 支持的共享程序库(lib*.so)技术不仅能够有效利用系统资源,而且还对程序设计带来了很大的便利性、通用性等,因此被各种级别的应用系统广泛采用。
动态链接的共享库是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的:通过动态链接器,将动态共享库映射进应用程序的可执行内存中(动态链接);在启动应用程序时,动态装载器将所需的共享目标库映射到应用程序的内存(动态装载)。
在通常情况下,共享库都是通过使用附加选项 -fpic 或 -fPIC 进行编译,从目标代码产生位置无关的代码(Position Independent Code,PIC),使用 -shared选项将目标代码放进共享目标库中。位置无关代码需要能够被加载到不同进程的不同地址,并且能得以正确的执行,故其代码要经过特别的编译处理:位置无关代码(PIC)对常量和函数入口地址的操作都是采用基于基寄存器(base register)BASE+ 偏移量的相对地址的寻址方式。即使程序被装载到内存中的不同地址,即 BASE 值不同,而偏移量是不变的,所以程序仍然可以找到正确的入口地址或者常量。
然而,当应用程序链接了多个共享库,如果在这些共享库中,存在相同作用域范围的同名静态成员变量或者同名 ( 非静态 ) 全局变量,那么当程序访问完静态成员变量或全局变量结束析构时,由于某内存块的 double free 会导致 core dump,这是由于 Linux 编译器的缺陷造成的。应用场景原型该问题源于笔者所从事的开发项目:IBM Tivoli Workload Scheduler (TWS) LoadLeveler。LoadLeveler是 IBM在高性能计算(High Performance Computing,HPC)领域的一款作业调度软件。它主要分为两个大的模块,分别是调度模块(scheduler)和资源管理模块(resource manger)。
两个模块中分别含有关于配置管理功能的共享库,由于某些配置管理选项为两模块所共同采用,所以两模块之间共享了部分源文件代码,其中包含有同名的类静态成员。可以通过以下简单的模型进行描述:图 1. 应用场景对应的各模块代码片段如下图所示:图 2. 应用场景模拟代码其中,test.c 是主程序,包含有两个头文件:api1.h 与 api2.h;头文件 api1.h 包含头文件 lib1/lib.h 和一功能函数 func_api1(),api2.h 包含头文件 lib2/lib.h 和一功能函数 func_api2();目录 lib1 和 lib2 下的源文件分别编译生成共享库 lib1.so 和 lib2.so。同时,头文件 lib1/lib.h 与 lib2/lib.h 链接到同一共享文件 lib.h。在文件 lib.h 中定义有一静态成员变量“static std::vector&int& vec_int”。功能函数与各静态成员函数代码清单功能函数 func_api1() 与 func_api2() 的实现类似,通过调用静态成员函数达到访问静态成员变量 vec_int的目的:清单 1. 功能函数 func_api1(int) void func_api1(int i) {
printf("%s.\n", __FILE__);
A::set(i);
A::print();
}静态成员函数 A::set() 与 A::print() 的实现如下:清单 2. 静态成员函数 A::set(int) void A::set(int num) {
vec_int.clear();
for (int i = 0; i & i++) {
vec_int.push_back(i);
}清单 3. 静态成员函数 A::print() void A::print() {
for (int i = 0; i & vec_int.size(); i++) {
printf("vec_int[%d] = %d, addr: %p.\n", i, vec_int[i], &vec_int[i]);
printf("vec_int addr: %p.\n", &vec_int);
}A::set() 对静态成员 vec_int进行赋值操作,而 A::print() 则打印其中的值与当前项的内存地址。运行结果如果两个共享库是通过选项 -fpic或 -fPIC编译的话,运行程序 test,输出如下:清单 4. 选项 -fPIC 的测试结果 $ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
$ g++ -g -o lib1.so -fPIC-rdynamic -shared lib1/lib.c
$ g++ -g -o lib2.so -fPIC-rdynamic -shared lib2/lib.c
$ g++ -g -o test -L./ -l1 -l2 test.c
vec_int[0] = 0, addr: 0x9cbf028.
vec_int[1] = 1, addr: 0x9cbf02c.
vec_int[2] = 2, addr: 0x9cbf030.
vec_int[3] = 3, addr: 0x9cbf034.
vec_int addr: 0xe89228.
*** glibc detected *** ./test: double free or corruption (fasttop): 0x09cbf028***
======= Backtrace:=========
/lib/libc.so.6[0x2b2b16]
/lib/libc.so.6(cfree+0x90)[0x2b6030]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x5d1731]
./lib1.so(_ZN9__gnu_cxx13new_allocatorIiE10deallocateEPij+0x1d)[0xe88417]./lib1.so(_ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPij+0x33)[0xe88451]./lib1.so(_ZNSt12_Vector_baseIiSaIiEED2Ev+0x42)[0xe8849a]./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c]
./lib2.so[0x961d6c]
/lib/libc.so.6(__cxa_finalize+0xa9)[0x275c79]
./lib2.so[0x961c34]
./lib2.so[0x962d3c]
/lib/ld-linux.so.2[0x23a7de]
/lib/libc.so.6(exit+0xe9)[0x2759c9]
/lib/libc.so.6(__libc_start_main+0xe4)[0x25fdf4]
./test(__gxx_personality_v0+0x45)[0x80484c1]
======= Memory map:========
63000 r-xp :1b 7668734
64000 rwxp :1b 7668734
71000 r-xp :00 0
00e00 r-xp :1b 7668022
00ea000 rwxp :1b 7668022
49000 r-xp :1b 7668748
4a000 rw-p :1b 7668748
09cbf000-09ce0000 rw-p 09cbf000 00:00 0
Abort(coredump)
$从程序的输出直观的看到,core 产生是由于堆内存区域(09cbf000-09ce0000)中起始地址为 0x09cbf028的内存区被释放了两次导致的,该地址正式静态成员变量 vec_int的第一个元素的地址。为什么会出现同一块内存区,被释放两次的情形呢?原因分析我们知道,静态成员变量与全局变量类似,都采用了静态存储方式。对于加了选项 -fpic或 -fPIC的共享库,这些变量的地址都存放在该共享库的全局偏移表(Global Offset Table,GOT)中。通过 objdump或者 readelf命令分析共享库 lib1.so,结果如下:清单 5. objdump 分析共享库 lib1.so 的输出 $ objdump -x -R lib1.so
file format elf32-i386
0 .gnu.hash
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .dynamic
CONTENTS, ALLOC, LOAD, DATA
CONTENTS, ALLOC, LOAD, DATA
20 .got.plt
CONTENTS, ALLOC, LOAD, DATA
DYNAMIC RELOCATION RECORDS
R_386_GLOB_DAT
__gmon_start__
R_386_GLOB_DAT
_Jv_RegisterClasses
000030fc R_386_GLOB_DAT
_ZN1A7vec_intE
R_386_GLOB_DAT
__cxa_finalize
......清单 6. readelf 分析共享库 lib1.so 的输出 $ objdump -x -R lib1.so
file format elf32-i386
0 .gnu.hash
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .dynamic
CONTENTS, ALLOC, LOAD, DATA
CONTENTS, ALLOC, LOAD, DATA
20 .got.plt
CONTENTS, ALLOC, LOAD, DATA
DYNAMIC RELOCATION RECORDS
R_386_GLOB_DAT
__gmon_start__
R_386_GLOB_DAT
_Jv_RegisterClasses
000030fc R_386_GLOB_DAT
_ZN1A7vec_intE
R_386_GLOB_DAT
__cxa_finalize
......从上面两个命令的输出结果中可以看出,共享库 lib1.so中 GOT段的起始内存地址为 ,大小为 20 字节 (0x14);静态成员变量 vec_int在共享库 lib1.so中的起始偏移地址为 000030fc。显然,vec_int位于该共享库的 GOT段内。当应用程序同时链接 lib1.so和 lib2.so时,同名静态成员变量 vec_int分别位于其共享库的 GOT区。当程序运行时,系统从符号表中查找并装载构造一份 vec_int数据,这点从程序运行的输出结果(清单 4)的“Backtrace”部分可以看到:只有 lib1.so中的静态成员变量被装载构造;同时,通过内存映射(Memory map)部分(清单 4),可以观察到 vec_int对象的地址 0xe89228正好处在为共享库 lib1.so分配的可读内存区 00ea000中:00ea000 rwxp :1b 7668022
./lib1.so然后,当程序结束时,却对该变量进行了两次析构操作,通过 gdb分析 core 文件:清单 7. core 文件分析结果 $ gdb ./test core.28440
Core was generated by `./test'.
Program terminated with signal 6, Aborted.
0x in __kernel_vsyscall ()
(gdb) where
0x in __kernel_vsyscall ()
0x00272d10 in raise () from /lib/libc.so.6
0x in abort () from /lib/libc.so.6
0x002aae5b in __libc_message () from /lib/libc.so.6
0x002b2b16 in _int_free () from /lib/libc.so.6
0x002b6030 in free () from /lib/libc.so.6
0x005d1731 in operator delete () from /usr/lib/libstdc++.so.6
0x00e88417 in __gnu_cxx::new_allocator&int&::deallocate
(this=0xe89228, __p=0x9cbf028)
at /usr/lib/gcc/i386-redhat-linux/.../ext/new_allocator.h:94
0x00e88451 in std::_Vector_base&int, ... (this=0xe89228, __p=0x9cbf028, __n=4)
at /usr/lib/gcc/.../include/c++/4.1.2/bits/stl_vector.h:133
0x00e8849a in ~_Vector_base (this=0xe89228)
at /usr/lib/gcc/.../include/c++/4.1.2/bits/stl_vector.h:119
#10 0x00e8850cin ~vector (this=0xe89228) at /usr/lib/gcc/.../stl_vector.h:272
#11 0x00961d6c in __tcf_0 () at lib2/lib.c:3
#12 0x00275c79 in __cxa_finalize () from /lib/libc.so.6
#13 0x00961c34 in __do_global_dtors_aux () from ./lib2.so
#14 0x00962d3c in _fini () from ./lib2.so
#15 0x0023a7de in _dl_fini () from /lib/ld-linux.so.2
#16 0x in exit () from /lib/libc.so.6
#17 0x0025fdf4 in __libc_start_main () from /lib/libc.so.6
#18 0x in _start ()
(gdb)从清单 7 中可以看出,从帧 #14 开始,程序进行 lib2.so中的析构操作,直到 #11,都运行在 lib2.so中,当进入帧 #10 时,进行变量析构时,其地址为 0x00e8850c,该地址中的对象是程序启动时由共享库 lib1.so装载构造出来的(清单 1):./lib1.so(_ZNSt6vectorIiSaIiEED1Ev+0x60)[0xe8850c]当程序结束时,运行库 glibc检测到共享库 lib2.so析构了并非由其构造的对象,导致了 core dump。这种情况下,如果替换使用选项 -fpie或 -fPIE,操作步骤与运行结果如下所示:清单 8. 选项 -fPIE 的测试结果 $ export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH
$ g++ -g -o lib1.so -fPIE-rdynamic -shared lib1/lib.c
$ g++ -g -o lib2.so -fPIE-rdynamic -shared lib2/lib.c
$ g++ -g -pie -o test -L./ -l1 -l2 test.c
vec_int[0] = 0, addr: 0x80e3028.
vec_int[1] = 1, addr: 0x80e302c.
vec_int[2] = 2, addr: 0x80e3030.
vec_int[3] = 3, addr: 0x80e3034.
vec_int addr: 0x75e224.
$程序运行结果符合期望并正常结束。这是因为,当使用选项 -fpie或 -fPIE时,生成的共享库不会为静态成员变量或全局变量在 GOT中创建对应的条目(通过 objdump或 readelf命令可以查看,此处不再赘述),从而避免了由于静态对象“构造一次,析构两次”而对同一内存区域释放两次引起的程序 core dump。选项 -fpie和 -fPIE与 -fpic及 -fPIC的用法很相似,区别在于前者总是将生成的位置无关代码看作是属于程序本身,并直接链接进该可执行程序,而非存入全局偏移表 GOT中;这样,对于同名的静态或全局对象的访问,其构造与析构操作将保持一一对应。结束语通过使用选项 -fpie或 -fPIE代替 -fpic或者 -fPIC,使得生成的共享库不会为静态成员变量或全局变量在 GOT中创建对应的条目,同时也就避免了针对同名静态对象“构造一次,析构两次”的不当操作。
相关主题(developerWorks 中国,2008 年 9 月)介绍 Linux 动态链接共享库的工作流程。(developerWorks 中国,2003 年 8 月)概要介绍 Linux 静态和动态链接技术的发展,原理与实现。 在
寻找为 Linux 开发人员(包括 )准备的更多参考资料,查阅我们 。 在 developerWorks 上查阅所有
和 。 随时关注 developerWorks 和。
添加或订阅评论,请先或。
有新评论时提醒我
static.content.url=http://www.ibm.com/developerworks/js/artrating/SITE_ID=10Zone=LinuxArticleID=555427ArticleTitle=技巧:多共享动态库中同名对象重复析构问题的解决方法publish-date=}

我要回帖

更多关于 c语言动态内存释放 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信