C程序中的存储分配
(刘爱贵 - Aiguille.LIU)
(刘爱贵 - Aiguille.LIU)
C程序设计中,经常需要使用malloc/free动态管理内存,在需要的时候向操作系统申请空间,适合的时候释放不再使用的空间。那么,C库中malloc/free是如何实现的呢?参考"The C Programming Language",我们设计了自己的存储分配程序。
由于程序中某些地方可能不通过malloc调用申请空间,因此,malloc管理的空间不一下是连续的。这样,空闲存储空间以空闲链表的方式组织,每个块包含一个长度、一个指向下一块的指针以及一个指向自身的存储空间的指针。这些块按照存储地址的升序组织,最后一块指向第一块。
当有申请请求时,malloc将扫描空闲块链表,直到找到一个足够大的块为止。这种算法称为“首次适应(first fit)”。与之相对的算法是“最佳适应(best fit)”,它寻找满足条件的最小块。如果该块恰好与请求的大小相符合,则将它从链表中移走并返回用户。如果该块太大,则将它分成两部分:大小合适的块返回给用户,剩下的部分留在空闲块链表中。如果找不到一个足够大的块,则向操作系统申请一个大块并加入到空闲块链表中。
释放过程也是首先搜索空闲块链表,以找到可以插入被释放块的合适位置。如果与被释放块相邻的任一边是一个空闲块,则将这两个块合成一个更大的块,这样存储空间不会有太多的碎片。因为空闲块链表是以地址的递增顺序链接在一起的,所以很容易判断相相邻的块是否空闲。
malloc函数返回的存储空闲要求满足将要保存的对象的对齐要求。虽然机器类型各异,但是,每个特定的机器都有一个最受限的类型:如果最受限的类型可以存储在某个特定的地址中,则其他所有的类型也可以存放在此地址中。在某些机器中,最受限的类型是double;而在另外一些机器中,最受限的类型是int或long类型。
下面的my_malloc.c程序,简化了块的对齐,所有块的大小都是头部大小的整数倍,且头部已正确地对齐。空闲块开始处的控制信息称为“头部”。假定long类型为最受限的类型:
#include
<
stdio.h
>
#include < string .h >
#define NALLOC 1024 /* 最小申请单元数 */
typedef long Align; /* 按照long类型的边界对齐 */
union header /* 块的头部 */
{
struct {
union header *ptr; /* 空闲块的链表的下一块 */
unsigned size; /* 本块的大小 */
}s;
Align x; /* 强制块的对齐 */
} ;
typedef union header Header;
static Header base ; /* 从空链表开始 */
static Header * freep = NULL; /* 空闲链表的初始指针 */
/* free 函数:将块ap放入空闲块链表中 */
void free( void * ap)
{
Header *bp, *p;
bp = (Header*)ap -1;
for ( p = freep ; !(bp>p &&bp < p->s.ptr); p = p->s.ptr)
if ( p >= p->s.ptr && (bp >p ||bp <p->s.ptr))
break; /* 被释放的块在链表的开头或未尾 */
if ( bp + bp->s.size == p->s.ptr) { /* 与上一相邻块合并 */
bp ->s.size +=p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
}else
bp->s.ptr= p->s.ptr;
if ( p+ p->s.size == bp) { /* 与下一相邻块合并 */
p->s.size +=bp->s.size;
p ->s.ptr = bp ->s.ptr;
} else
p->s.ptr = bp;
freep = p;
}
/* morecore: 向系统申请更多的存储空间 */
static Header * morecore( unsigned nu)
{
char *cp, *sbrk(int );
Header *up;
if ( nu <NALLOC)
nu = NALLOC;
cp = sbrk(nu * sizeof(Header));
if ( cp == (char *) -1) /* 没有空闲空间 */
return NULL;
up = (Header *) cp;
up ->s.size = nu;
free ((void*)(up+1));
return freep;
}
/* mumalloc: 存储分配函数 */
void * mymalloc (unsigned nbytes)
{
Header *p, *prevp;
Header *morecore(unsigned);
unsigned nunits;
nunits = (nbytes +sizeof(Header)-1)/sizeof(Header)+1;
if ((prevp = freep)==NULL) { /* 没有空闲链表 */
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for ( p = prevp ->s.ptr; ; prevp = 0, p = p->s.ptr) {
if ( p ->s.size >= nunits) { /* 足够大 */
if ( p->s.size == nunits) /* 正好 */
prevp ->s.ptr= p->s.ptr;
else { /* 分配未尾部分 */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void*)(p+1);
}
if ( p == freep) /* 闭环的空闲链表 */
if ((p = morecore(nunits)) ==NULL)
return NULL; /* 没有剩余的存储空间 */
}
}
/* 主函数:测试mymalloc函数 */
int main( void )
{
char * p ;
p = (char *)mymalloc (30);
strcpy(p, "hello world");
printf("%s ", p);
}
#include < string .h >
#define NALLOC 1024 /* 最小申请单元数 */
typedef long Align; /* 按照long类型的边界对齐 */
union header /* 块的头部 */
{
struct {
union header *ptr; /* 空闲块的链表的下一块 */
unsigned size; /* 本块的大小 */
}s;
Align x; /* 强制块的对齐 */
} ;
typedef union header Header;
static Header base ; /* 从空链表开始 */
static Header * freep = NULL; /* 空闲链表的初始指针 */
/* free 函数:将块ap放入空闲块链表中 */
void free( void * ap)
{
Header *bp, *p;
bp = (Header*)ap -1;
for ( p = freep ; !(bp>p &&bp < p->s.ptr); p = p->s.ptr)
if ( p >= p->s.ptr && (bp >p ||bp <p->s.ptr))
break; /* 被释放的块在链表的开头或未尾 */
if ( bp + bp->s.size == p->s.ptr) { /* 与上一相邻块合并 */
bp ->s.size +=p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
}else
bp->s.ptr= p->s.ptr;
if ( p+ p->s.size == bp) { /* 与下一相邻块合并 */
p->s.size +=bp->s.size;
p ->s.ptr = bp ->s.ptr;
} else
p->s.ptr = bp;
freep = p;
}
/* morecore: 向系统申请更多的存储空间 */
static Header * morecore( unsigned nu)
{
char *cp, *sbrk(int );
Header *up;
if ( nu <NALLOC)
nu = NALLOC;
cp = sbrk(nu * sizeof(Header));
if ( cp == (char *) -1) /* 没有空闲空间 */
return NULL;
up = (Header *) cp;
up ->s.size = nu;
free ((void*)(up+1));
return freep;
}
/* mumalloc: 存储分配函数 */
void * mymalloc (unsigned nbytes)
{
Header *p, *prevp;
Header *morecore(unsigned);
unsigned nunits;
nunits = (nbytes +sizeof(Header)-1)/sizeof(Header)+1;
if ((prevp = freep)==NULL) { /* 没有空闲链表 */
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for ( p = prevp ->s.ptr; ; prevp = 0, p = p->s.ptr) {
if ( p ->s.size >= nunits) { /* 足够大 */
if ( p->s.size == nunits) /* 正好 */
prevp ->s.ptr= p->s.ptr;
else { /* 分配未尾部分 */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void*)(p+1);
}
if ( p == freep) /* 闭环的空闲链表 */
if ((p = morecore(nunits)) ==NULL)
return NULL; /* 没有剩余的存储空间 */
}
}
/* 主函数:测试mymalloc函数 */
int main( void )
{
char * p ;
p = (char *)mymalloc (30);
strcpy(p, "hello world");
printf("%s ", p);
}
更详细的程序说明请直接参考"The C Programming Language"一书162 ~ 166页。