因此,这是一个悬而未决的问题。但是,可以说我有一个大型应用程序,该应用程序全局覆盖了各种new
和delete
运算符,以便它们使用自制的jemalloc风格的舞台和自定义对齐方式。
一切都很好,但是我一直在遇到段错误问题,因为其他基于C++的DLL及其依赖项在不应该使用的重载分配器(即LLVM)下也使用了它们,这使小的自定义分配器陷入了困境(内存不足和内存不足)。更多压力)。
测试变通办法,我将这些全局运算符包装(并移动到了一个类中),并使所有基类都继承自该类。很好,它适用于类,但不适用于基本类型。那就是问题所在。
鉴于C++不允许有用的事情,例如每个namespace
具有单独的分配器,或限制每个可执行模块的new
运算符,因此在无法直接子类化int
的基本数据类型中,最好的模拟方法是什么?
最明显的方法是将它们包装在自定义模板中,但问题是性能。我是否必须只模拟第二层下的所有数组和索引操作,以便我可以从其他位置进行malloc
而不必更改其余功能代码?有更好的办法吗?
附注:我也一直在考虑使用带有额外参数的特殊全局new
/ delete
运算符,同时不使用标准参数。因此,确保我是唯一的调用那些全局函数的人(我的可执行模块是)。它应该是一个简单的搜索和替换。
最佳答案
好吧,快速更新。最后,我要解决此难题的方法是手动检测调用了覆盖的全局分配器的代码是否来自主要的可执行模块,并在仍在使用的同时有条件地将所有外部new
/ delete
调用重定向到其相应的malloc
/ free
我们自己内部代码的自定义竞技场分配器。
怎么样?经过一些研发,我发现可以通过使用MSVC内置的_ReturnAddress()
和GCC / Clang上的__builtin_extract_return_addr(__builtin_return_address(0))
来完成;我可以说,到目前为止,它在生产软件中似乎运行良好。
现在,当我们地址空间中的某些C++代码需要一些内存时,我们可以看到它的来源。
但是,如何确定该地址是我们处理空间中其他模块的一部分还是我们自己的模块中的一部分?我们可能需要找出主程序的基地址和结束地址,在启动时将它们作为全局变量进行缓存,并检查返回地址是否在范围之内。
所有这些只需极少的开销。但是,我们的第二个问题是,在每个平台中,检索基址都是不同的。经过一些研究,我发现事情比预期的要简单:
#include <windows.h>
#include <psapi.h>
inline void __initialize_base_address()
{
MODULEINFO minfo;
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &minfo, sizeof(minfo));
base_addr = (uintptr_t) minfo.lpBaseOfDll;
base_end = (uintptr_t) minfo.lpBaseOfDll + minfo.SizeOfImage;
}
_init
和_fini
函数似乎总是包装了其余.text
节符号。有时很难找到适用于所有地方的最简单的解决方案:#include <link.h>
inline void __initialize_base_address()
{
void *handle = dlopen(0, RTLD_NOW);
base_addr = (uintptr_t) dlsym(handle, "_init");
base_end = (uintptr_t) dlsym(handle, "_fini");
dlclose(handle);
}
_NSGetMachExecuteHeader()
只是内部_mh_execute_header
链接器全局程序的包装。如果您需要做任何有关解析Mach-O格式及其结构的事情,那么getsect.h
是可行的方法:#include <mach-o/getsect.h>
#include <mach-o/ldsyms.h>
#include <crt_externs.h>
inline void __initialize_base_address()
{
size_t size;
void *ptr = getsectiondata(&_mh_execute_header, SEG_TEXT, SECT_TEXT, &size);
base_addr = (uintptr_t) _NSGetMachExecuteHeader();
base_end = (uintptr_t) ptr + size;
}
要记住的另一件事是,这个cpp模块正在使用我们的内部分配器,该问题在全局范围内覆盖了导致新的怪异bug的问题,这在Linux中似乎是一个问题,也许macOS,我在Windows中没有这个问题,可能是因为在此过程中没有加载冲突的DLL,这些DLL主要是基于C API的。我认为,或者平台为每个模块使用不同的C++运行时。
我遇到的主要问题是由Mesa3D引起的,Mesa3D对许多GLSL着色器编译器使用LLVM(纯C++输入和输出),并且喜欢吞噬我的定制化内存 Realm 中的大块。
由于其庞大的规模和复杂性,重写结构上依赖于这些分配器的遗留程序是不可能的,因此事实证明这是使事情按预期工作的最佳方法。
仅有几行可选的,偷偷摸摸的,额外的每平台代码。
关于c++ - 用自定义分配器及其替代方法重载基本类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44510812/