本实验的问题在于通过测试,具体的Bug,具体分析,以下仅给出算法与几点建议。
1.void *malloc(size_t size)
- 功能:
- 分配大小为size字节的内存块,并返回块起始地址
- 如果size是0,返回NULL
- 算法:
- 如果size为0,返回NULL
- 根据首次原则找到符合条件的select指针,(select->state == FREE) && ((select->size - sizeof(struct chunk)) >= size)
- 剩余空间不足,返回NULL
- 如果减去参数size+sizeof(struct chunk),剩余空间大于sizeof(struct chunk),创建新的chunk,new
(1)初始化new
(2)修改select - 修改select->state=USED;
- 返回ptr=((uint8_t *)select)+sizeof(struct chunk);
2.void free(void *ptr)
- 功能:
- 释放ptr指向的内存块
- 如果ptr是NULL,直接返回
- 提示:
- 根据ptr得到chunk:
- struct trunk *achunk=(struct chunk *)(((uint8_t *)ptr)-sizeof(struct chunk));
- 要求:
- 必须验证ptr的有效性:判断achunk->signature是否等于“OSEX”
- 必须合并相邻的空闲块
- 算法:
- 如果ptr是NULL,直接返回
- 根据ptr得到chunk地址struct chunk* achunk=(struct chunk *)(((uint8_t *)ptr)-sizeof(struct chunk));
- 检验achunk->signature是否等于“OSEX”,相等时,strncmp(achunk->signature, “OSEX”,4)==0
- 合并相邻的空闲内存块,检查achunk前后是否为FREE:如果为FREE,合并chunk
(1)如果achunk不是最后一个chunk,检查achunk后一个内存块
(2)如果achunk不是chunk_head,检查achunk的前一个内存块 - 修改achunk->state=FREE
3.void *calloc(size_t num, size_t size)
- 功能:
- 为num个元素的数组分配内存,每个元素占size字节
- 把分配的内存初始化成0
- 算法:
- 如果size或num为0,返回NULL
- 根据首次原则找到符合条件的select指针,(select->state == FREE) && ((select->size - sizeof(struct chunk)) >= num*size)
- 剩余空间不足,返回NULL
- 如果减去参数size+sizeof(struct chunk),剩余空间大于sizeof(struct chunk),创建新的chunk,new
(1)初始化new
(2)修改select - 修改select->state=USED;
- 初始化分配的内存为0,memset(ptr, 0, num * size);
- 返回ptr = ((uint8_t *)select) + sizeof(struct chunk);
4.void *realloc(void *oldptr, size_t size)
- 功能:
- 重新分配oldptr指向的内存块,新内存块有size字节
- -如果oldptr是NULL,该函数等价于malloc(size)
- -如果size是0,该函数等价于free(oldptr)
- 把旧内存块的内容复制到新内存块
- -如果新内存块比较小,只复制旧内存块的前面部分
- -如果新内存块比较大,复制整个旧内存块,而且不用初始化多出来的那部分
- 如果新内存块还在原来的地址oldptr,返回oldptr;否则返回新地址
- 要求:
- 必须验证oldptr的有效性
- 必须合并相邻的空闲块
- 算法:
- 如果oldptr是NULL,该函数等价于malloc(size)
- 如果size是0,该函数等价于free(oldptr),但是要注意返回NULL
- 根据oldptr得到chunk地址struct chunk *achunk=(struct chunk *)(((uint8_t *)oldptr)-sizeof(struct chunk));
- 检验achunk->signature是否等于“OSEX”,相等时,strncmp(achunk->signature, “OSEX”,4)==0
- 修改achunk->state=FREE;
- 合并相邻的空闲内存块,检查achunk前后是否为FREE:如果为FREE,合并chunk
(1)如果achunk不是最后一个chunk,检查achunk后一个内存块
(2)如果achunk不是chunk_head,检查achunk的前一个内存块 - 根据首次原则找到符合条件的select指针,(select->state == FREE) && ((select->size - sizeof(struct chunk)) >= size)
- 剩余空间不足,返回NULL
- 如果减去参数size+sizeof(struct chunk),剩余空间大于sizeof(struct chunk),创建新的chunk,new
(1)初始化new
(2)修改select - 修改select->state=USED;
- 计算ptr= ((uint8_t *)select) + sizeof(struct chunk);
- 比较ptr和oldptr,相等则直接返回
- 如果新内存块比较小,只复制旧内存块的前面部分,strncpy((char *)ptr, (char *)oldptr, select->size - sizeof(struct chunk));
- 如果新内存块比较大,复制整个旧内存块,而且不用初始化多出来的那部分,strncpy((char *)ptr, (char *)oldptr, achunk->size - sizeof(struct chunk));
- 返回ptr
温馨提示:
- chunk链表为临界区,在读与写之前需要利用信号量进行保护
- 地址往后增加,在将select内存块分裂成两个chunk时,需要考虑new的值应该是select+sizeof(struct chunk)+size;
- 创建新的chunk时,需要考虑select内存块的剩余空间是否可以允许创建;
- 由于在读chunk链表时,进行了保护,sem_wait()的位置在函数的前面,注意每一次return之前都要调用sem_signal(),避免卡死;
- 合并空闲块时,注意achunk位于链表首部或尾部的情况是否考虑;
- 如果遇到“死机”(“卡住不动”),考虑在sem.c文件的sem_wait()和sem_signal()函数里面添加printk()来打印当前value值,以便观测第几次函数调用导致“死机”,进行后续的Debug工作;
- Debug的时候可以考虑注释掉一些测试,以便观测具体在哪个函数出问题,比如说为了区分Bug位于malloc还是free,可以将无法通过的测试之前的测试全部注释,如果还是无法通过,那么推断Bug很可能在malloc处,此方法可以结合上述的打印信号量当前value一起使用,效果更佳。