说明:本日志主要用来记录自己的debug过程,对于malloc这种需要对地址空间进行直接分配和管理的实验,必须要debug才能找出错误在哪。一开始真是不知道从何下手,通过自己慢慢摸索,逐渐找到了方法,对gdb的使用也越来越熟练,提高了自己的debug能力,也希望给同样不知道如何开始debug的小白一点参考。
注意:
先根据课本上给的范例实现隐式空闲链表,并且好好理解。
一跑有87分还不错哦
(一)显示空闲链表
在隐式空闲链表的基础上,我们实现显示空闲链表。
以下是设计图:

写完初版代码,一运行,毋庸置疑:Segmentation fault!
注意:
- 每次修改完一定要重新编译:make
- 使用gdb进行调试的时候一定要先修改“Makefile”文件中的“CFLAGS”在参数最后加“-g”,gcc编译的时候要加入-g参数才能使用gdb调试,否则就会报以下错误。
再次运行gdb,我们就可以看到错误发生时所在的源代码了。
但是有个问题是,通过backtrace我们可以看到栈中的内容,但是我们不知道在这之前都运行了哪些函数,在思考这个问题并且无从下手很久之后。
……TWO THOUSAND YEARS LATER……
PRINTF大法来了:在所有函数的开头都输出函数名,涉及指针和SIZE大小相关的也进行输出,在此基础上,配合上gdb,一切都开始变得行云流水了起来。
运行结果如下:
DEBUG1:
发现没有调用insert_to_freelist函数
出现了新的问题,为什么没有用还能找到4024的空间?
DEBUG2:
划重点:
heap_listp: 0xf69ed020
root: 0xf69ed014
*** extend_heap(4096)bp:0xf69ed028
*** coalesce(bp=0xf69ed028)no need coalesce
*** insert_to_freelist(bp=0xf69ed028,size=4096) //插入root的时候有错误,观察insert_to_freelist函数,输出root和bp
*** mm_malloc(size=2040)
*** findfit(size=2048)
找到0xf69ed018
疑点1.
“*** findfit(size=2048)
找到0xf69ed018”
为什么找到的不是root: 0xf69ed028?
检查0xf69ed018是否是正确的空闲块,看其大小csize很大,所以root肯定有问题。
说明在插入root的时候就有错误,观察insert_to_freelist函数(打断点:break insert_to_freelist),输出root和bp
果然root值不对,没有被赋值
bp指针初始化为root肯定是错的,再看看findfit的时候遍历查找空闲块的时候bp指针的变化。
不幸的是查看不了:$1 = < optimized out >
参考word文档——解决
Unfortunately, the variable is still optimized out! While -Og ensures that the information that GDB shows is correct (compared to -O3), there will still be some variables that are optimized out. This makes sense if you think about how assembly works: the value of many variables are often discarded, unless there is a reason to save them. To fix this, go into the Makefile and change the debug optimization level to “COPT_DBG = -O0”. The resulting code is very inefficient, but it should preserve all local variables.
修改Makefile文件中的“CFLAGS”设置,将参数改为“-O0”。
bp=root是root变量本身存储的地址
而不是root指向的地址
仔细检查insert_to_freelist函数
“PUT_PTR(root,bp);”语句没有问题
在此不知何时突然恍然大悟,如果让bp=root的话,那么bp指向的是root指针本身存储的地址,而不是保存的值,正确的方式应该是bp=GET(root),才能获取到root保存的值!!!
检查所有的指针引用,有很多地方需要更正:
In function ‘insert_to_freelist’: GET(root)
In function ‘relink’: GET(PRED(bp))——不是获取PRED(bp),而是前面连接的空闲块的所在地址
In function ‘find_fit’: bp=GET(root)
DEBUG3:
运行gdb看看是在哪一步出了问题,好吧这里应该是把“bp”放到“root”而不是“oldroot”//更改太着急,不仔细啊!
DEBUG4:
依然报错,这次看看又是什么问题:
*** relink(bp=0xf6993028)链接后(前后连上检验-是否指向同一块)0x4 0x0
一眼就可以看出来relink函数出了问题0x4 0x0
看了代码发现这里应该是printf输出的问题,与代码本身无关。
在输出中也加上GET:printf(“链接后(前后连上检验-是否指向同一块)0x%x 0x%x\n”,GET(SUCC(prev_node)),GET(PRED(next_node)));
——重来
又报错了……这次是输出语句的问题,跟代码无关,因为第一次prev_node和next_node都为NULL,经过前面检验,relink的前后连接是没有问题的,因此这里直接删除printf语句
好这一次让我们专注函数的运行来观察一下什么地方出错了
经过一番分析,发生错误的时候,freelist中仅有一块1992字节大小的空闲块,位于0xf69ed860。而此时要分配4080大小的空闲块,错误的关键来了,在findfit中竟然找到了,而此时执行place函数肯定就会报错了。那么明明空间不够的情况下,findfit为什么会找到呢?
gdb调试,打断点在find_fit
为什么此时第一个空闲块大小bpsize这么大啊
输出bp,发现bp还是最开始分配的空闲块指针,root也是,那么此时的root按道理应该存放的是0xf69ed860位置的值
wait wait报错的不是第一个find_fit,继续吧,被自己蠢哭了,但是根据刚才的分析这里也有点问题,等下如果还报错再回来看看。
找出错误,在修改find_fit函数时漏加了一个GET……
改正:bp=GET(SUCC(bp))
DEBUG5:
仅有空闲块1992大小不够,额外申请8192的空间,然后发现和前面一块空闲块的地址是相连的,进入“else if(!prev_alloc&&next_alloc)”条件判断后的then语句。那么就要relink后一块的地址,而0xf69ed860是第一个空闲块的地址
发现代码逻辑错误:
修改前:
bp=PREV_BLKP(bp);
relink(PREV_BLKP(bp));
修改后:
relink(PREV_BLKP(bp));
bp=PREV_BLKP(bp);
DEBUG6:
又报错了,这次问题好像更复杂了,先检查一下是否还有跟刚刚一样的错误,果然有!
在最后一个合并前后块的代码中也出现了逻辑错误
修改前:
bp=PREV_BLKP(bp);
relink(PREV_BLKP(bp));
relink(NEXT_BLKP(bp));
修改后:
relink(PREV_BLKP(bp));
relink(NEXT_BLKP(bp));
bp=PREV_BLKP(bp);
成功了成功了!
注意在这里编译的时候出现了warning,但我们就是利用存储的整数值来获取指针,因此没有问题
但是只有54分……我裂开了……发生甚莫事了……还不如隐式链表……
突然想起来有一大堆printf语句,会影响程序性能,于是删除之后再试试
哦豁不错,这才是它真正的实力👍
当然啦,我们必须要进一步优化我们滴代码啦~
众所周知,分离空闲链表+Best Fit才是坠吊的,那我们来试试。(本来是个正经的实验记录,画风好像逐渐走偏)
(二)分离空闲链表
在隐式空闲链表的基础上起草宏伟蓝图:
DEBUG1:
PRINTF大法好,先了解程序在干什么
分配4096->拿走2048->拿走2048->释放2048->拿走56->还剩1992->拿走4072->扩展8192->合并+1992=10184->拿走4080->还剩6104->释放刚拿走的4080->和后一块合并
*** mm_free(bp=0xf69ed880,size=4080)
*** coalesce(bp=0xf69ed880)
*** relink(bp=0xf69f0048,size=0)
fitsize_root:第0块
合并后一块,bp不变
发现关键语句错误:relink(bp=0xf69f0048,size=0)
后一块6104的地址是0xf69ee870,因此正确的输出应该是relink(bp=0xf69ee870,size=6104)
所以是relink(NEXT_BLKP(bp));
中的NEXT_BLKP(bp)错了
又是coalesce函数中relink的顺序错了……可恶
但是为什么在刚刚的隐式链表的实现中没有报错,这里很奇怪
else if(prev_alloc&&!next_alloc)的then语句:
修改前:
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
relink(NEXT_BLKP(bp));
修改后:
relink(NEXT_BLKP(bp));
PUT(HDRP(bp),PACK(size,0));
PUT(FTRP(bp),PACK(size,0));
DEBUG2:
这次检查了前面的分配没有任何问题,但是在插入空闲块2048的时候又报错,出错在while循环语句
经过一番分析,当前只有第九块中有一个空闲块(bp=0xf6915870,size=6104)
(bp=0xf6914048,size=2048)插入第八条链表,p应该为null,但是其第一块地址为0xf69ed880,0xf69ed880是之前块所在地址,说明1992大小的空闲块在relink的时候出错了,第八条链表的表头没有重置为NULL
relink的时候发生在被后面分配的8192空闲块合并。果然啊,又是在改变size的时候才把指针头塞给relink重置,因此就重置到第九条链表去了,如下所示:
insert_to_freelist(bp=0xf69ed880,size=10184)
fitsize_root:第9块
正确写法:
else{
size+=GET_SIZE(HDRP(PREV_BLKP(bp)))+GET_SIZE(FTRP(NEXT_BLKP(bp)));
relink(PREV_BLKP(bp));
relink(NEXT_BLKP(bp));
PUT(HDRP(PREV_BLKP(bp)),PACK(size,0));
PUT(FTRP(NEXT_BLKP(bp)),PACK(size,0));
bp=PREV_BLKP(bp);
}
运行结果,很奇怪的是,忙活了那么久,没有提升!!!
最后三种实现方式的运行成绩竟然一模一样……
不死心地试了一下优化,但是运行成绩稳如泰山、无动于衷,本实验日志主要记录debug过程,因此这里也不多作赘述了,反正都是无用功。
总结一句:“昨天我还大呼不会debug,今天我就爱上gdb。”