DLL内存管理
我对 Windows 如何管理 .dll 的内存几乎没有疑问。
当 .dll 被加载到主机中时 进程中,内存是如何管理的?
.dll 是否可以访问整个 主机进程可用的内存 或者只是其中的一部分?即是 当内存有限制时 由内部函数分配 .dll?
STL 类如字符串、向量(动态地 增加存储)等使用 dll,在这里工作没有问题吗?
I have few doubts regarding how windows manages a .dll's memory.
when .dll's are loaded into the host
process, how is the memory managed?Does .dll get access to the entire
memory available to the host process
or just a portion of it? i.e is
there a limitation when memory is
allocated by a function inside the
.dll?Will STL classes like string, vector (dynamically
increasing storage) etc used by the
dll, work without issue here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
通常,“内存管理”是责任分散的。操作系统将大块的地址空间交给运行时,然后运行时将其分成较小的位交给程序。该地址空间可能分配也可能没有分配 RAM。 (如果没有,将会有交换空间来支持它)
基本上,当加载 DLL 时,Windows 会为代码和数据段分配地址空间,并调用
DllMain()
。 C++ 编译器将安排从DllMain()
调用全局构造函数。如果它是用 C++ 编写的 DLL,则它可能依赖于 C++ 运行时 DLL,而后者又依赖于 Kernel32.DLL 和 User32.DLL。 Windows 理解此类依赖关系,并将安排它们以正确的顺序加载。一个证明只有一个地址空间,因此 DLL 将可以访问该进程的所有内存。如果一个 DLL 在两个进程中加载,则会有代码和数据的两个逻辑副本。 (不过,代码和只读数据的副本可能共享相同的物理 RAM)。
如果 DLL 使用操作系统函数分配内存,Windows 会将内存分配给 DLL 进行分配的进程。进程必须返回内存,但进程中的任何代码都可以这样做。如果您的 DLL 使用 C++ 函数分配内存,它将通过在 C++ 运行时 DLL 中调用
operator new
来实现。该内存必须通过在(同一)C++ 运行时 DLL 中调用operator delete
来返回。再说一次,谁做的并不重要。像
vector<>
这样的 STL 类可以多次实例化,但只要您使用相同的编译器就没有关系。所有实例化基本上都是相等的,并且所有实例化都将向量的内存返回到相同的释放函数。此解释中有 2 个主要假设:
如果需要,则针对 C++ 运行时的静态链接很有用发布一个独立的 EXE。但是,如果您已经发布了 DLL,则也应该将 C++ 运行时保留在其自己的 DLL 中。
"Memory management" is a split responsibility, typically. The OS hands address space in big chunks to the runtime, which then hands it out in smaller bits to the program. This address space may or may not have RAM allocated. (If not, there will be swap space to back it)
Basically, when a DLL is loaded, Windows allocates address space for the code and data segements, and calls
DllMain()
. The C++ compiler will have arranged to call global ctors fromDllMain()
. If it's DLL written in C++, it will likely depend on a C++ runtime DLL, which in turn will depend on Kernel32.DLL and User32.DLL. Windows understands such dependencies and will arrange for them to be loaded in the correct order.There is only one address space for a provess, so a DLL will get access to all memory of the process. If a DLL is loaded in two processes, there will be two logical copies of the code and the data. (copies of the code and read-only data might share the same physical RAM though).
If the DLL allocates memory using OS functions, Windows will allocate the memory to the process from which the DLL made that allocation. The process must return the memory, but any code in the process may do so. If your DLL allocates memory using C++ functions, it will do so by calling
operator new
in the C++ runtime DLL. That memory must be returned by callingoperator delete
in the (same) C++ runtime DLL. Again, it doesn't matter who does that.STL classes like
vector<>
can be multiply instantiated, but it doesn't matter as long as you're using the same compiler. All instantiations will be substantially equal, and all will return the vector's memory to the same deallocation function.There are 2 main assumptions in this explanation:
Static linking against the C++ runtime is useful if you want to ship an single, self-contained EXE. But if you're already shipping DLLs, you should keep the C++ runtime in its own DLL too.
将 DLL 加载到主机进程后,DLL 中“生存”的代码与原始可执行模块中“生存”的代码没有任何区别。对于正在执行的进程,所有内存范围都是相同的,无论它们来自 DLL 还是来自原始可执行文件。
DLL 中的代码的功能与原始 exec 模块中编译的代码的功能没有区别。
也就是说,使用堆时存在差异 - 这些问题在 Space_C0wb0y 提供的问题中进行了解释 评论
如果您在 DLL 的接口中使用它们,它们将会产生问题(但仍然是可以解决的问题)。如果不在 DLL 接口级别使用它们,不会(或者仅在极少数情况下)会产生问题。我确信对此还有一些更具体的问题+答案。
基本上,如果您在接口级别使用它们,DLL 和 EXE 必须使用“完全相同”的相同标志进行编译,即类型需要二进制兼容。即,如果 DLL 中的编译器标志(优化等)与 EXE 中的编译器标志不同,使得
std::string
在 EXE 与 DLL 中的内存中布局不同,那么在两者之间传递字符串对象将导致崩溃或无声错误(或者恶魔从你的鼻子里飞出来)。如果您仅在函数内部或 DLL 内部函数之间使用 STL 类型,那么它们与 EXE 的兼容性并不重要。
After a DLL has been loaded into the host process, there is no distinction whatsoever for code "living" in the DLL vs. code "living" in the original executable module. For the process being executed all memory ranges are the same, whether they come from a DLL or from the original executable.
There are no differences as to what the code from the DLL can do vs. what the code compiled in the original exec module can do.
That said, there are differences when using the heap - these are explained in the questions Space_C0wb0y provided the links for in the comments
They will create issues (solvable ones, but still) if you use them in the interface of your DLL. The will not (or should only under very rare circumstances) create issues if you do not use them on the DLL interface level. I am sure there are a few more specific questions+answers around for this.
Basically, if you use them at the interface level, the DLL and the EXE have to be compiled with "exactly" the same flags, i.e. the types need to be binary compatible. I.e. if the comiler flags (optimization, etc.) in your DLL differ from the ones in the EXE such that a
std::string
is layed out differently in memory in the EXE vs. the DLL, then passing a string object between the two will result in a crash or silent errors (or demons flying out of your nose).If you only use the STL types inside of functions or between functions internal to your DLL, then their compatibility with the EXE doesn't matter.