windows的malloc到virtualAlloc调用
? 先介绍下基本知识
Win32 内存结构
首先要搞清楚【虚拟内存、交换文件、页面文件、RAM、物理存储、虚拟地址空间】这几个概念!
虚拟内存是操作系统的内存管理方式,对上层应用屏蔽了页面文件与RAM的区别与调度方式。先看看操作系统,再来说吧!
Windows操作系统中的内存结构
? 在没有充分的理解函数集合是如何工作的以及它们各自是如何影响操作系统的情况下,在你的应用程序中确定用于管理内存的函数或函数集合是很困难的。下面重点介绍虚拟内存(Virtual Memory)以及如何使用它们,它们的是如何与操作系统交互的。
?
? 仔细看上图,CRT只提供了操Heap Memory API上的内存操纵方式(malloc/free、new/delete等)。
? Win32提供下列3中操纵内存的机制:
? 虚拟内存(Virtual Memory API,VirtualAlloc、VirtualFree等),适合于管理大的对象数据或结构
? 内存映射文件(Memory Mapped File API,CreateFileMapping、MapViewOfFile等),最适合于管理大的数据流(通常来自文件)和在多个进程间共享数据
? 堆(Heap Memory API,HeapCreate、HeapAlloc等;Local、Global Memory API在Windows NT上已经没有了,不分局部堆与全局堆,只有一种类型的堆),最适合于管理大量的小对象
虚拟内存提供操作功能
- 保留(reserve)、提交(commit)和释放(free)虚拟内存
- 修改虚拟内存页(Page)的保护属性
- 虚拟内存页加锁(Lock)
- 查询应用程序的虚拟内存
注意:虚拟内存指的是操作系统对上层应用提供的内存抽象,是内存管理的一种方式,与Windows系统属性中虚拟内存不是一回事。Windows系统属性中的虚拟内存是指分页文件。
在地址空间中保留区域(申请虚拟内存,不被其他申请操作给占用)
LPVOID
VirtualAlloc(
_In_opt_ LPVOID lpAddress, // 按64-KB向上取整
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
)
虚拟内存(VirtualAlloc),堆(HeapAlloc/malloc/new)和Memory Mapped File
虚地址空间
? 在Win32中,每个进程的虚地址空间是4GB。32位指针的值能从0x00000000到0xFFFFFFFF。这使得指针能有4,294,967,296种值,这覆盖了进程的4GB空间。
32位CPU,32位操作系统,32位应用程序的定义是什么?
? 所有的Win32进程都有自己的私人地址空间。当进程种的线程在运行时,线程只能访问属于本进程的内存。对运行的线程来说,属于其他所有进程的内存是隐藏的和不可访问的(如果使用系统提供的api,还是可以对其他进程的内存进行读写的)。
? 进程拥有的4GB空间是虚地址空间,而不是物理内存。该地址空间只不过是一段内存地址。物理内存需要被分配或映射到地址空间的分区。如何将物理内存映射到虚地址空间的分区?
Windows NT如何划分进程的地址空间
?
地址空间中的区域
? 当进程被创建并分配了地址空间后,大部分可用的地址空间是空闲的(或未分配的)
? Win32调用VirtualAlloc函数来分配区域,分配一块区域的行为被称为保留(reserving)
? 页是系统用来管理内存的单位,Win32实现使用4KB的页大小
? 地址空间的分配以页为单位,例如:请求分配5KB大小的地址空间,系统保留的区域为8KB
? Win32调用VirtualFree释放(release)区域
?
在区域内提交物理存储
? 要使用虚地址空间中的一块保留区域,就必须分配物理存储,然后将其映射到保留区域,这一过程叫做提交物理存储。物理存储总是以页为单位提交的。要把物理存储提交给保留区域,还要调用VirtualAlloc函数。
? 通过调用VirtualAlloc来向地址空间提交物理存储时,空间实际上是分配在虚地址空间的一块连续内存,在物理存储上不一定连续。
物理存储
? 物理存储被认为是计算机上的RAM的多少。在Win32中,Microsoft提供了对硬盘交换文件形式的虚拟内存的支持。但只有在CPU直接支持交换文件的情况下操作系统才能使用它们。从应用程序的角度来说,交换文件透明地增加了应用程序所能使用地RAM(或存储)地数量。如果计算机上有2GB的RAM,在硬盘上还有2GB的交换文件的话,运行的应用程序会认为计算机共有4GB的RAM。
并不是实际上有4GB的RAM。相反,操作系统同CPU一起合作,把RAM的部分内容保存在交换文件中,而在运行程序需要的时候又把交换文件中的内容装回到RAM中。如果不适用交换文件,系统只是认为应用程序能使用的RAM较少一些。
物理存储 = RAM + 页面文件/虚拟内存 + 不包含在页面文件中的物理存储
? 当进程中的线程试图访问进程的地址空间中的一块数据时,必然会发生两件事情中的一件,如图:
? 第1种可能,线程试图访问的数据在RAM中。这时,CPU把数据的虚内存地址映射到内存中的物理地址,然后进行直接访问。 ? 第2种可能,线程试图访问的数据不在RAM而在页面文件(虚拟内存)中。这时,访问会产生一个页面错误,CPU通知操作系统试图进行的访问。操作系统就从RAM找一页空闲内存。如果找不到,系统就必须释放一页。如果页面还没有被修改(脏),系统就能直接释放它。否则,系统首先要把该页从RAM拷贝到页面文件中。接下来,系统在页面文件中找到所要访问的数据库,把它加载到内存的空闲页中。操作系统然后把数据的虚地址映射到RAM中适当的物理地址。 ? 当操作系统需要把内存和页面中的数据来回交换的时候,硬盘就会越多地咔咔作响,系统也会越慢。因为操作系统把时间都花在把页面换入和换出,而不是运行程序上了。其实这也是触发缺页异常,然后置换需要访问的数据内存。
堆
? 在Win32中操作内存的第3种方法是使用堆。堆非常适合于分配很多小块的数据。例如,使用堆来管理链表和树要比使用虚拟内存或内存映射文件好很多。
堆的特性:
-
堆是属于进程的,一个进程的堆种的内容不能被其他进程种的线程访问 -
在通常的程序中,很多数据默认存放在缺省堆,例如:new、malloc等申请的内存空间 -
堆是虚拟内存上的一种特殊内存管理方式 -
dll没有自己的堆,它使用的堆是进程地址空间的一部分 -
什么是Win32的堆? ? 一个Win32堆是一块保留地址空间。起初,该保留区域中的大部分页都没有用物理存储提交。当从堆中分配空间时,堆管理器会向堆提交更多的物理存储。当堆中空间被释放时,堆管理器会从堆中释放物理存储。物理存储是按页提交给堆的。
1.1 进程的缺省堆
? 当一个Win32进程被初始化时,系统在它的地址空间中创建了一个堆。该堆被称为进程的缺省堆。缺省时,该堆的区域是1MB。不贵,系统能增大进程的缺省堆。在编译程序时,可以使用/HEAP链接器开关来改变缺省的堆大小。DLL没有自己的堆,不应使用/HEAP开关。
? 进程的缺省堆被很多Win32函数使用。例如:new、malloc等默认都是从缺省堆上分配空间。 缺省堆的访问被序列化了,系统确保在任一时刻只可能有一个线程在缺省堆中分配或释放内存块。
1.2 创建自己的Win32堆
? 除了进程的缺省堆外,还能再进程的地址空间中创建更多的堆。一般来说,想要在应用程序中创建其他的堆主要有3个原因:
-
部件保护 -
更有效的内存管理 -
局部访问 -
部件保护 ? 在缺省堆中的内存混在一起,可能由于其中一个内存块访问异常导致其他内存块错误。可创建独立的堆,使问题局部化。 -
更有效的内存管理 ? 如果堆中包含的对象都是同样大小,在释放一个对象后,就能保证另一个对象能合适地得到这个空闲对象的空间。 -
局部访问 ? 在设计应用程序时,最好把要同时访问的对象放在一起。若对象的内存分布在多个内存页面中,可能会因为缺页,需要系统在RAM和页面文件之间交换页面,导致性能降低。而将同时访问的对象放在一起将降低这种可能性。
以上就把内存相关的概念介绍的差不多了,主要参考了
https://blog.csdn.net/zj510/article/details/39400087
https://blog.csdn.net/lwwl12/article/details/88859044
https://blog.csdn.net/lwwl12/article/details/89926414相关文章,如果有兴趣可以详读或者了解相关内存的系统api
malloc如何申请内存
1.申请内存的开始
001B5308 8B F4 mov esi,esp
001B530A 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
92:
93: char* data = (char*)malloc(count);
001B530D 50 push eax
001B530E FF 15 80 01 1C 00 call dword ptr ds:[1C0180h] //调用malloc函数
001B5314 83 C4 04 add esp,4
001B5317 3B F4 cmp esi,esp
001B5319 E8 CB BF FF FF call __RTC_CheckEsp (01B12E9h) //检查缓冲区溢出
001B531E 89 45 E8 mov dword ptr [ebp-18h],eax
2.malloc的实现
位于C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\dbgmalloc.c目录
extern "C" _CRTIMP void * __cdecl malloc (
size_t nSize
)
{
void *res = _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);
RTCCALLBACK(_RTC_Allocate_hook, (res, nSize, 0));
return res;
}
汇编
50: *******************************************************************************/
51:
52: extern "C" _CRTIMP void * __cdecl malloc (
53: size_t nSize
54: )
55: {
0FEEE590 55 push ebp
0FEEE591 8B EC mov ebp,esp
0FEEE593 51 push ecx
56: void *res = _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);
0FEEE594 6A 00 push 0
0FEEE596 6A 00 push 0
0FEEE598 6A 01 push 1
0FEEE59A A1 8C C1 F5 0F mov eax,dword ptr ds:[0FF5C18Ch]
0FEEE59F 50 push eax
0FEEE5A0 8B 4D 08 mov ecx,dword ptr [nSize]
0FEEE5A3 51 push ecx
0FEEE5A4 E8 A7 F5 FF FF call _nh_malloc_dbg (0FEEDB50h)
3._nh_malloc_dbg的实现
函数实现在C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\dbgheap.c
/***
*void * _nh_malloc_dbg() - Get a block of memory from the debug heap
*
*Purpose:
* Allocate of block of memory of at least size bytes from the debug
* heap and return a pointer to it. Assumes heap already locked.
*
* If no blocks available, call new handler.
*
* Allocates any type of supported memory block.
*
*Entry:
* size_t nSize - size of block requested
* int nhFlag - TRUE if new handler function
* int nBlockUse - block type
* char * szFileName - file name
* int nLine - line number
*
*Exit:
* Success: Pointer to (user portion of) memory block
* Failure: NULL
*
*Exceptions:
*
*******************************************************************************/
extern "C" void * __cdecl _nh_malloc_dbg (
size_t nSize,
int nhFlag,
int nBlockUse,
const char * szFileName,
int nLine
)
{
int errno_tmp = 0;
void * pvBlk = _nh_malloc_dbg_impl(nSize, nhFlag, nBlockUse, szFileName, nLine, &errno_tmp);
if ( pvBlk == NULL && errno_tmp != 0 && _errno())
{
errno = errno_tmp; // recall, #define errno *_errno()
}
return pvBlk;
}
汇编如下
293: extern "C" void * __cdecl _nh_malloc_dbg (
294: size_t nSize,
295: int nhFlag,
296: int nBlockUse,
297: const char * szFileName,
298: int nLine
299: )
300: {
0FEEDB50 55 push ebp
0FEEDB51 8B EC mov ebp,esp
0FEEDB53 83 EC 08 sub esp,8
301: int errno_tmp = 0;
0FEEDB56 C7 45 FC 00 00 00 00 mov dword ptr [errno_tmp],0
302: void * pvBlk = _nh_malloc_dbg_impl(nSize, nhFlag, nBlockUse, szFileName, nLine, &errno_tmp);
0FEEDB5D 8D 45 FC lea eax,[errno_tmp]
0FEEDB60 50 push eax
0FEEDB61 8B 4D 18 mov ecx,dword ptr [nLine]
0FEEDB64 51 push ecx
0FEEDB65 8B 55 14 mov edx,dword ptr [szFileName]
0FEEDB68 52 push edx
0FEEDB69 8B 45 10 mov eax,dword ptr [nBlockUse]
0FEEDB6C 50 push eax
0FEEDB6D 8B 4D 0C mov ecx,dword ptr [nhFlag]
0FEEDB70 51 push ecx
0FEEDB71 8B 55 08 mov edx,dword ptr [nSize]
0FEEDB74 52 push edx
302: void * pvBlk = _nh_malloc_dbg_impl(nSize, nhFlag, nBlockUse, szFileName, nLine, &errno_tmp);
0FEEDB75 E8 36 00 00 00 call _nh_malloc_dbg_impl (0FEEDBB0h)
4._nh_malloc_dbg_impl的实现
函数实现在C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\dbgheap.c
extern "C" static void * __cdecl _nh_malloc_dbg_impl (
size_t nSize,
int nhFlag,
int nBlockUse,
const char * szFileName,
int nLine,
int * errno_tmp
)
{
void * pvBlk;
for (;;)
{
/* do the allocation
*/
pvBlk = _heap_alloc_dbg_impl(nSize, nBlockUse, szFileName, nLine, errno_tmp);
if (pvBlk)
{
return pvBlk;
}
if (nhFlag == 0)
{
if (errno_tmp)
{
*errno_tmp = ENOMEM;
}
return pvBlk;
}
/* call installed new handler */
if (!_callnewh(nSize))
{
if (errno_tmp)
{
*errno_tmp = ENOMEM;
}
return NULL;
}
/* new handler was successful -- try to allocate again */
}
}
汇编
224: extern "C" static void * __cdecl _nh_malloc_dbg_impl (
225: size_t nSize,
226: int nhFlag,
227: int nBlockUse,
228: const char * szFileName,
229: int nLine,
230: int * errno_tmp
231: )
232: {
0FEEDBB0 55 push ebp
0FEEDBB1 8B EC mov ebp,esp
0FEEDBB3 51 push ecx
233: void * pvBlk;
234:
235: for (;;)
236: {
237: /* do the allocation
238: */
239: pvBlk = _heap_alloc_dbg_impl(nSize, nBlockUse, szFileName, nLine, errno_tmp);
0FEEDBB4 8B 45 1C mov eax,dword ptr [errno_tmp]
0FEEDBB7 50 push eax
0FEEDBB8 8B 4D 18 mov ecx,dword ptr [nLine]
0FEEDBBB 51 push ecx
0FEEDBBC 8B 55 14 mov edx,dword ptr [szFileName]
0FEEDBBF 52 push edx
0FEEDBC0 8B 45 10 mov eax,dword ptr [nBlockUse]
0FEEDBC3 50 push eax
0FEEDBC4 8B 4D 08 mov ecx,dword ptr [nSize]
0FEEDBC7 51 push ecx
0FEEDBC8 E8 63 F9 FF FF call _heap_alloc_dbg_impl (0FEED530h)
5._heap_alloc_dbg_impl的实现
函数实现在C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\dbgheap.c
extern "C" static void * __cdecl _heap_alloc_dbg_impl(
size_t nSize,
int nBlockUse,
const char * szFileName,
int nLine,
int * errno_tmp
)
{
long lRequest;
size_t blockSize;
int fIgnore = FALSE;
_CrtMemBlockHeader * pHead;
void *retval=NULL;
_mlock(_HEAP_LOCK);
__try {
if (check_frequency > 0)
if (check_counter == (check_frequency - 1))
{
_ASSERTE(_CrtCheckMemory());
check_counter = 0;
}
else
check_counter++;
lRequest = _lRequestCurr;
if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
_CrtDbgBreak();
if ((_pfnAllocHook) && !(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, (const unsigned char *)szFileName, nLine))
{
if (szFileName)
_RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d.\n",
szFileName, nLine);
else
_RPT0(_CRT_WARN, "Client hook allocation failure.\n");
}
else
{
if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK &&
!(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))
fIgnore = TRUE;
if (nSize > (size_t)(_HEAP_MAXREQ - nNoMansLandSize - sizeof(_CrtMemBlockHeader)))
{
_RPT1(_CRT_ERROR, "Invalid allocation size: %Iu bytes.\n", nSize);
if (errno_tmp)
{
*errno_tmp = ENOMEM;
}
}
else
{
if (!_BLOCK_TYPE_IS_VALID(nBlockUse))
{
_RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");
}
blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;
RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));
pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);
if (pHead == NULL)
{
if (errno_tmp)
{
*errno_tmp = ENOMEM;
}
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
}
else
{
++_lRequestCurr;
if (fIgnore)
{
pHead->pBlockHeaderNext = NULL;
pHead->pBlockHeaderPrev = NULL;
pHead->szFileName = NULL;
pHead->nLine = IGNORE_LINE;
pHead->nDataSize = nSize;
pHead->nBlockUse = _IGNORE_BLOCK;
pHead->lRequest = IGNORE_REQ;
}
else {
if (SIZE_MAX - _lTotalAlloc > nSize)
{
_lTotalAlloc += nSize;
}
else
{
_lTotalAlloc = SIZE_MAX;
}
_lCurAlloc += nSize;
if (_lCurAlloc > _lMaxAlloc)
_lMaxAlloc = _lCurAlloc;
if (_pFirstBlock)
_pFirstBlock->pBlockHeaderPrev = pHead;
else
_pLastBlock = pHead;
pHead->pBlockHeaderNext = _pFirstBlock;
pHead->pBlockHeaderPrev = NULL;
pHead->szFileName = (char *)szFileName;
pHead->nLine = nLine;
pHead->nDataSize = nSize;
pHead->nBlockUse = nBlockUse;
pHead->lRequest = lRequest;
_pFirstBlock = pHead;
}
memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);
memset((void *)pbData(pHead), _bCleanLandFill, nSize);
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
retval=(void *)pbData(pHead);
}
}
}
}
__finally {
_munlock(_HEAP_LOCK);
}
return retval;
}
6._heap_alloc_base的实现
#define _heap_alloc _heap_alloc_base
__forceinline void * __cdecl _heap_alloc (size_t size)
{
if (_crtheap == 0) {
#if !defined (_CRT_APP) || defined (_DEBUG)
_FF_MSGBANNER(); /* write run-time error banner */
_NMSG_WRITE(_RT_CRT_NOTINIT); /* write message */
#endif /* !defined (_CRT_APP) || defined (_DEBUG) */
__crtExitProcess(255); /* normally _exit(255) */
}
return HeapAlloc(_crtheap, 0, size ? size : 1);
}
7.HeapAlloc
待补充
new 到 malloc
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{ // try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{ // report no memory
_THROW_NCEE(_XSTD bad_alloc, );
}
return (p);
}
可以看出,new其实底层就是调用malloc,然后会再调用构造函数。
|