第七章
7-1 修改程序清单7-1中的程序,在每次malloc后打印program break 的值。指定一个较小的内存分配尺寸,观察现象,证明的每次malloc后program break分配了超过所需的内存区域。
我们可以通过一个更简单的程序来实现观察所需的功能。
#include <unistd.h>
#include <malloc.h>
int main(void){
printf("now : %10p\n", sbrk(0));
char * a;
for (int i = 0; i<10; i++)
{
a = malloc(50000);
printf("%d : %10p\n",i, sbrk(0));
}
}
运行程序输出如下:
now : 0xaf060fc000
0 : 0xaf0611d000
1 : 0xaf0611d000
2 : 0xaf06141000
3 : 0xaf06141000
4 : 0xaf06141000
5 : 0xaf06166000
6 : 0xaf06166000
7 : 0xaf06166000
8 : 0xaf0618b000
9 : 0xaf0618b000
我们可以看到每次实际上边界分配了空间大于我们所需的空间。
7-2 实现malloc()和free()。
根据课本可以知道,malloc和free是对堆操作的包装,也就是brk函数的包装,在堆空间进行的数据结构的操作。下面是一个简单实现:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
typedef struct mallocBlock{
struct mallocBlock *before;
int size;
int use;
} mb;
static mb * mb_last = NULL;
void * my_malloc(int size){
mb * mbp;
//将mbp放置在一个可以容纳下的空间的第一个空块上
for(mbp = mb_last; mbp != NULL; mbp = mbp->before){
if(mbp->use == 0 && mbp->size >= size){
break;
}
}
//如果没有找到这样的块,那么mbp就是链表的最后一个指针是 NULL
//此时我们要在边界处加上一个新块
if(mbp == NULL){
mbp = sbrk(sizeof(mb) + size);
if (mbp ==(void *) -1) return (void *)0;
mbp->size = size;
mbp->before = mb_last;
mb_last = mbp;
}
//将该块记为1标识已经使用。
mbp->use = 1;
return mbp + 1;
}
int my_free(void * p){
if(p == NULL) return 0;
mb * mbp = (mb *)p - 1;
mbp->use = 0;
return 0;
}
int main(void)
{
char * a = my_malloc(1);
char * b = my_malloc(2);
char * c = my_malloc(3);
my_free(a);
my_free(b);
my_free(c);
printf("%p,%p,%p\n",a,b,c);
b = my_malloc(1);
printf("%p,%p,%p\n",a,b,c);
return 0;
}
这里的malloc实现是和课本实现基本一致,但是没有对大块内存的再分配进行切割。另外,free函数仅仅是将该块标记为可重用而已,并没有对多余空闲的堆进行释放。不过基本上可以满足实现。
在我的电脑上,实现了完整版的free函数,但是在运行过程中,free函数的brk函数会出现异常,导致段错误,尽管检查了brk函数所释放的堆的位置是合法的,但是仍然出错,并且,sbrk所返回的虚拟内存的位置不是连续的,这个和课本上所述有些区别,不清楚是什么原因导致的。