快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
Cygwin checkout-2008-09-28
vs2008
欢迎转载,但请保留作者信息
cygwin支持所谓的user heap,这个heap将用于malloc / realloc / free操作。当然,malloc操作并不一定要使用这个user heap,经过适当配置也可以直接使用VirtualAlloc这样的API来完成内存分配。但如果希望用cygwin模拟出sbrk的操作,那么只能使用user heap做为malloc操作的heap。
Cygwin使用了下面的结构体描述heap信息:
struct user_heap_info
{
void *base;
void *ptr;
void *top;
void *max;
unsigned chunk;
unsigned slop;
};
这几个成员很直观地描述了一个heap所必须的几个信息。这些信息将放在cygheap里面。
当cygwin.dll初始化的时候,它将调用一个叫heap_init的函数:
/* Initialize the heap at process start up. */
void
heap_init ()
{
const DWORD alloctype = MEM_RESERVE;
/* If we're the forkee, we must allocate the heap at exactly the same place
as our parent. If not, we don't care where it ends up. */
page_const = system_info.dwPageSize;
if (!cygheap->user_heap.base)
{
cygheap->user_heap.chunk = cygwin_shared->heap_chunk_size ();
/* For some obscure reason Vista and 2003 sometimes reserve space after
calls to CreateProcess overlapping the spot where the heap has been
allocated. This apparently spoils cyg_fork. The behaviour looks quite
arbitrary. Experiments on Vista show a memory size of 0x37e000 or
0x1fd000 overlapping the usual heap by at most 0x1ed000. So what
we do here is to allocate the heap with an extra slop of (by default)
0x200000 and set the appropriate pointers to the start of the heap
area + slop. A forking child then creates its heap at the new start
address and without the slop factor. Since this is not entirely
foolproof we add a registry setting "heap_slop_in_mb" so the slop
factor can be influenced by the user if the need arises. */
cygheap->user_heap.slop = cygwin_shared->heap_slop_size ();
while (cygheap->user_heap.chunk >= MINHEAP_SIZE)
{
/* Initialize page mask and default heap size. Preallocate a heap
* to assure contiguous memory. */
cygheap->user_heap.base =
VirtualAlloc (NULL, cygheap->user_heap.chunk
+ cygheap->user_heap.slop,
alloctype, PAGE_NOACCESS);
if (cygheap->user_heap.base)
break;
cygheap->user_heap.chunk -= 1 * 1024 * 1024;
}
if (cygheap->user_heap.base == NULL)
api_fatal ("unable to allocate heap, heap_chunk_size %p, slop %p, %E",
cygheap->user_heap.chunk, cygheap->user_heap.slop);
cygheap->user_heap.base = (void *) ((char *) cygheap->user_heap.base
+ cygheap->user_heap.slop);
cygheap->user_heap.ptr = cygheap->user_heap.top = cygheap->user_heap.base;
cygheap->user_heap.max = (char *) cygheap->user_heap.base
+ cygheap->user_heap.chunk;
}
else
{
DWORD chunk = cygheap->user_heap.chunk; /* allocation chunk */
/* total size commited in parent */
DWORD allocsize = (char *) cygheap->user_heap.top -
(char *) cygheap->user_heap.base;
/* Loop until we've managed to reserve an adequate amount of memory. */
char *p;
DWORD reserve_size = chunk * ((allocsize + (chunk - 1)) / chunk);
while (1)
{
p = (char *) VirtualAlloc (cygheap->user_heap.base, reserve_size,
alloctype, PAGE_READWRITE);
if (p)
break;
if ((reserve_size -= page_const) < allocsize)
break;
}
if (!p && in_forkee && !fork_info->handle_failure (GetLastError ()))
api_fatal ("couldn't allocate heap, %E, base %p, top %p, "
"reserve_size %d, allocsize %d, page_const %d",
cygheap->user_heap.base, cygheap->user_heap.top,
reserve_size, allocsize, page_const);
if (p != cygheap->user_heap.base)
api_fatal ("heap allocated at wrong address %p (mapped) != %p (expected)", p, cygheap->user_heap.base);
if (allocsize && !VirtualAlloc (cygheap->user_heap.base, allocsize, MEM_COMMIT, PAGE_READWRITE))
api_fatal ("MEM_COMMIT failed, %E");
}
debug_printf ("heap base %p, heap top %p", cygheap->user_heap.base,
cygheap->user_heap.top);
page_const--;
// malloc_init ();
}
对于父进程而言,cygheap->user_heap.base的值为NULL,而对于fork出来的子进程,整个cygheap的内容都是从父进程复制而来的,因此这个值将不为NULL,所以父子进程将执行两个不同的分支。
对于父进程,chunk的初始值为0x1800 0000(这是cygwin的保留值,384M),而slop的值则为0。Cygwin尝试使用VirtualAlloc分配尽可能大的空间,如果不能满足则以1M为单位向下递减直到满足要求为止,当然,它的大小不能小于MINHEAP_SIZE,即4M。
而对于子进程,它将尝试在和父进程相同的地址上分配一块同等大小的空间。
在初始化了user heap之后,就可以正常使用sbrk函数了,其代码如下:
CYG_API void *
sbrk (int n)
{
char *newtop, *newbrk;
unsigned commitbytes, newbrksize;
if (n == 0)
return cygheap->user_heap.ptr; /* Just wanted to find current cygheap->user_heap.ptr address */
newbrk = (char *) cygheap->user_heap.ptr + n; /* Where new cygheap->user_heap.ptr will be */
newtop = (char *) pround (newbrk); /* Actual top of allocated memory -
on page boundary */
if (newtop == cygheap->user_heap.top)
goto good;
if (n < 0)
{ /* Freeing memory */
assert (newtop < cygheap->user_heap.top);
n = (char *) cygheap->user_heap.top - newtop;
if (VirtualFree (newtop, n, MEM_DECOMMIT)) /* Give it back to OS */
goto good; /* Didn't take */
else
goto err;
}
assert (newtop > cygheap->user_heap.top);
/* Find the number of bytes to commit, rounded up to the nearest page. */
commitbytes = pround (newtop - (char *) cygheap->user_heap.top);
/* Need to grab more pages from the OS. If this fails it may be because
we have used up previously reserved memory. Or, we're just plumb out
of memory. Only attempt to commit memory that we know we've previously
reserved. */
if (newtop <= cygheap->user_heap.max)
{
if (VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL)
goto good;
}
/* Couldn't allocate memory. Maybe we can reserve some more.
Reserve either the maximum of the standard cygwin_shared->heap_chunk_size ()
or the requested amount. Then attempt to actually allocate it. */
if ((newbrksize = cygheap->user_heap.chunk) < commitbytes)
newbrksize = commitbytes;
if ((VirtualAlloc (cygheap->user_heap.top, newbrksize, MEM_RESERVE, PAGE_NOACCESS)
|| VirtualAlloc (cygheap->user_heap.top, newbrksize = commitbytes, MEM_RESERVE, PAGE_NOACCESS))
&& VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL)
{
cygheap->user_heap.max = (char *) cygheap->user_heap.max + pround (newbrksize);
goto good;
}
err:
set_errno (ENOMEM);
return (void *) -1;
good:
void *oldbrk = cygheap->user_heap.ptr;
cygheap->user_heap.ptr = newbrk;
cygheap->user_heap.top = newtop;
return oldbrk;
}
这段代码简洁明了,从中可以很清楚地看出user_heap_info中各个成员的作用。
cygwin下的cygheap:从父进程到子进程的复制(2009-9-7)
cygwin下的/etc/fstab(2009-9-7)
cygwin关键技术:fork(2009-9-4)
cygwin关键技术:设备模拟(2009-9-4)
cygwin关键技术:cygheap(2009-9-2)
cygwin关键技术:tls(2009-8-24)