9.11
A.虚拟地址0x027c
13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
B.地址翻译
参数 | 值 |
VPN | 0x09 |
TLB索引 | 0x01 |
TLB标记 | 0x02 |
TLB命中 | No |
缺页 | No |
PPN | 0x17 |
C.物理地址格式
11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
D.物理地址引用
参数 | 值 |
字节偏移 | 0x0 |
缓存索引 | 0xF |
缓存标记 | 0x17 |
缓存命中 | No |
返回缓存字节 | - |
9.12
A.虚拟地址0x03a9
13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 |
B.地址翻译
参数 | 值 |
VPN | 0x0E |
TLB索引 | 0x02 |
TLB标记 | 0x03 |
TLB命中 | No |
缺页 | No |
PPN | 0x11 |
C.物理地址格式
11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
D.物理地址引用
参数 | 值 |
字节偏移 | 0x1 |
缓存索引 | 0xA |
缓存标记 | 0x11 |
缓存命中 | No |
返回缓存字节 | - |
9.13
A.虚拟地址0x0040
13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
B.地址翻译
参数 | 值 |
VPN | 0x01 |
TLB索引 | 0x01 |
TLB标记 | 0x00 |
TLB命中 | No |
缺页 | Yes |
PPN | - |
9.14
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
int main()
{
int fd;
char *start;
fd = open("hello.txt", O_RDWR, 0); //打开文件
start = mmap(NULL, 1, PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if(start == MAP_FAILED) return -1;//判断是否映射成功
(*start) = 'J';
munmap(start, 1);
return 0;
}
注:代码经过测试,必须使用MAP_SHARED,不能使用MAP_PRIVATE,文件异常并未检测。
9.15
请求 | 块大小 | 块头部 |
malloc(3) | 8 | 0x9 |
malloc(11) | 16 | 0x11 |
malloc(20) | 24 | 0x19 |
malloc(21) | 32 | 0x21 |
9.16
怎么感觉答案都是16呢。。。
空闲块至少是16字节。已分配块不需要pred和succ,所以这八个字节可以装数据,加上头和脚,就是16字节,而且满足单字双字对齐。
求鉴定。
9.17
显然,我们需要修改find_fit函数(之前的版本在practise习题中写过,书后有答案)。
代码可以参考9.18。
为此需要先定义一个全局的cur_point指针,表示上次搜索之后指向哪个块(有效载荷首地址)。
static void *find_fit(size_t asize)
{
void *bp = cur_point;
do{
if( !GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp))) )
return cur_point = bp;
bp = (GET_SIZE(HDRP(bp)) == 0) ? heap_listp : NEXT_BLKP(bp);
}while(bp != cur_point);
return NULL;
}
另外,需要释放cur_point指向的指针时,它可能和前面的空闲块合并,我们应该将cur_point指向前一个空闲块首地址。在coalesce()函数中需要做如下修改:
else if (!prev_alloc && next_alloc) { /* Case 3 */
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
if(cur_point == bp) cur_point = PREV_BLKP(bp);
bp = PREV_BLKP(bp);
}
else { /* Case 4 */
size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
if(cur_point == bp) cur_point = PREV_BLKP(bp);
bp = PREV_BLKP(bp);
}
9.18
需要考虑四个问题:
1. 初始化的时候,序言块和结尾块是怎么样的。
序言块八字节8|0x11。
结尾块四字节0|0x11。
2. 为堆申请更多空间的时候(sbrk),如何更改结尾块
记录最后一个块的alloc。
结尾块向后延伸申请的空间,并将刚多出的空间作为一个空闲块。设置为size|(alloc<<1)。再合并该空闲块。这里如何合并呢?需要判断最后一块是否已分配,可通过epilogue来判断。
3. 某个空闲块匹配的时候,如何设置头和下一块的头。
我们基于以下假设:某个空闲块匹配,上一个和下一个一定不是空闲块(否则可以合并)。
所以头部就设置为(asize|0x011)。
如果要分割,则下一块的头部设置为(size-asize|0x010),不用合并,因为再下一块肯定是已经分配。
4. 释放某个块时,如何合并。
检查头部,alloc_prev = 上一块是否分配
检查下一个块的头部,alloc_next = 下一个块是否分配。
再根据那四种情况分别设置。
最后,如果下一块已分配,则需要将下一块头部设置为(原头部&(~0x010))。
另外,在mm_malloc中,分配多大的块也要发生相应的变化,因为现在最小块大小可以是DSIZE,而不是2*DSIZE。
以下是我的完整代码:代码并没有进行严格测试,如果有问题可以一起讨论。
memlib.c
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MAX_HEAP (1<<24)
/* Private global variables */
static char *mem_heap; /* Points to first byte of heap */
static char *mem_brk; /* Points to last byte of heap plus 1 */
static char *mem_max_addr; /* Max legal heap addr plus 1*/
/*
* mem_init - Initialize the memory system model
*/
void mem_init(void)
{
mem_heap = (char *)malloc(MAX_HEAP);
mem_brk = (char *)mem_heap;
mem_max_addr = (char *)(mem_heap + MAX_HEAP);
}
/*
* mem_sbrk - Simple model of the sbrk function. Extends the heap
* by incr bytes and returns the start address of the new area. In
* this model, the heap cannot be shrunk.
*/
void *mem_sbrk(int incr)
{
char *old_brk = mem_brk;
if ( (incr < 0) || ((mem_brk + incr) > mem_max_addr)) {
errno = ENOMEM;
fprintf(stderr, "ERROR: mem_sbrk failed. Ran out of memory...\n");
return (void *)-1;
}
mem_brk += incr;
return (void *)old_brk;
}
void mem_end(void)/*added by zhanyu*/
{
free(mem_heap);
}
mm.c
#include <stdio.h>
/* Basic constants and macros */
#define WSIZE 4 /* Word and header/footer size (bytes) */
#define DSIZE 8 /* Double word size (bytes) */
#define CHUNKSIZE (1<<12) /* Extend heap by this amount (bytes) */
#define MAX(x, y) ((x) > (y)? (x) : (y))
/* Pack a size and allocated bit into a word */
//#define PACK(size, alloc) ((size) | (alloc))
#define PACK(size, prev_alloc, alloc) ((size)|(prev_alloc<<1)|(alloc))
/* Read and write a word at address p */
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
/* Read the size and allocated fields from address p */
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
#define GET_PREVALLOC(p) ((GET(p) & 0x2)>>1)
/* Given block ptr bp, compute address of its header and footer */
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
typedef unsigned size_t;
int mm_init(void);
static void *extend_heap(size_t words);
void *mm_malloc(size_t size);
void mm_free(void *bp);
static void *coalesce(void *bp);
static void *find_fit(size_t asize);
static void place(void *bp, size_t asize);
void mm_end(); /*added by zhanyu*/
extern void *mem_sbrk(int incr); //memlib.c
extern void mem_init(void); //memlib.c
extern void mem_end(void); //memlib.c
void *heap_listp;
int mm_init(void)
{
mem_init(); /* added by zhanyu */
/* Create the initial empty heap */
if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) return -1;
PUT(heap_listp, 0); /* Alignment padding */
PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1, 1)); /* Prologue header */
PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1, 1)); /* Prologue footer */
PUT(heap_listp + (3*WSIZE), PACK(0, 1, 1)); /* Epilogue header */
heap_listp += (2*WSIZE);
/* Extend the empty heap with a free block of CHUNKSIZE bytes */
if (extend_heap(CHUNKSIZE/WSIZE) == NULL) return -1;
return 0;
}
static void *extend_heap(size_t words)
{
char *bp;
size_t size;
int prev_alloc;
/* Allocate an even number of words to maintain alignment */
size = (words % 2) ? (words+1) * WSIZE : words * WSIZE;
if ((long)(bp = mem_sbrk(size)) == -1) return NULL;
prev_alloc = GET_PREVALLOC(HDRP(bp));
/* Initialize free block header/footer and the epilogue header */
PUT(HDRP(bp), PACK(size, prev_alloc, 0)); /* Free block header */
PUT(FTRP(bp), PACK(size, prev_alloc, 0)); /* Free block footer */
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 0, 1)); /* New epilogue header, the prev_alloc is 0*/
/* Coalesce if the previous block was free */
return coalesce(bp);
}
void mm_free(void *bp)
{
size_t size = GET_SIZE(HDRP(bp));
size_t nsize = GET_SIZE(HDRP(NEXT_BLKP(bp)));
int prev_alloc = GET_PREVALLOC(HDRP(bp));
int next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, prev_alloc, 0));
PUT(FTRP(bp), PACK(size, prev_alloc, 0));
PUT(HDRP(NEXT_BLKP(bp)), PACK(nsize, 0, next_alloc));
if(!next_alloc) PUT(FTRP(NEXT_BLKP(bp)), PACK(nsize, 0, next_alloc));
coalesce(bp);
}
static void *coalesce(void *bp)
{
size_t prev_alloc = GET_PREVALLOC(HDRP(bp));
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
if (prev_alloc && next_alloc) { /* Case 1 */
return bp;
}
else if (prev_alloc && !next_alloc) { /* Case 2 */
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
PUT(HDRP(bp), PACK(size, 1, 0));
PUT(FTRP(bp), PACK(size, 1, 0));
/**then the next block is surely nonempty and it's prev_alloc is 0*/
}
else if (!prev_alloc && next_alloc) { /* Case 3 */
/*the prev prev block is surely nonempty*/
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
PUT(FTRP(bp), PACK(size, 1, 0));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 1, 0));
bp = PREV_BLKP(bp);
}
else { /* Case 4 */
/*the prev prev block is surely nonempty*/
size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
GET_SIZE(FTRP(NEXT_BLKP(bp)));
PUT(HDRP(PREV_BLKP(bp)), PACK(size, 1, 0));
PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 1, 0));
bp = PREV_BLKP(bp);
/**then the next block is surely nonempty and it's prev_alloc is 0*/
}
return bp;
}
static void *find_fit(size_t asize)
{
// First fit search
void *bp;
for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) {
if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp))))
return bp;
}
return NULL; // No fit
}
void *mm_malloc(size_t size)
{
size_t asize; /* Adjusted block size */
size_t extendsize; /* Amount to extend heap if no fit */
char *bp;
/* Ignore spurious requests */
if (size == 0) return NULL;
/* Adjust block size to include overhead and alignment reqs. */
if (size <= DSIZE) asize = DSIZE;
else asize = DSIZE * ((size + (WSIZE) + (DSIZE-1)) / DSIZE);
/* Search the free list for a fit */
if ((bp = find_fit(asize)) != NULL) {
place(bp, asize);
return bp;
}
/* No fit found. Get more memory and place the block */
extendsize = MAX(asize,CHUNKSIZE);
if ((bp = extend_heap(extendsize/WSIZE)) == NULL) return NULL;
place(bp, asize);
return bp;
}
static void place(void *bp, size_t asize)
{
size_t csize = GET_SIZE(HDRP(bp));
size_t nsize = GET_SIZE(HDRP(NEXT_BLKP(bp)));
int prev_alloc = GET_PREVALLOC(HDRP(bp));
int next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
if ((csize - asize) >= (2*DSIZE)) {
PUT(HDRP(bp), PACK(asize, prev_alloc, 1));
PUT(FTRP(bp), PACK(asize, prev_alloc, 1));
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(csize-asize, 1, 0));
PUT(FTRP(bp), PACK(csize-asize, 1, 0));
}
else {
PUT(HDRP(bp), PACK(csize, prev_alloc, 1));
PUT(HDRP(NEXT_BLKP(bp)), PACK(nsize, 1, next_alloc));
}
}
void mm_end() /*added by zhanyu*/
{
mem_end();
}
9.19
1) a; 对于伙伴系统,如果要申请大小为33的空间,那么需要分配64个空间。如果申请大小为65的空间,那么块大小就需要128,所以最多可能有约50%的空间被浪费。b中,最佳适配要搜索所有空间,所以肯定比首次适配要慢一些。c,边界标记主要功能是释放一个块时,能立即和前后空闲块合并。如果空闲块不按顺序排列的话,其实也能够和前一个或者后一个空闲块进行合并,但如果要和前后一起合并,可能会有些困难,那需要搜索前后块在空闲链表中的位置,并且删除一个再进行合并。可以参考P576,LIFO方法。d,其实任何分配器都可能有外部碎片,只要剩余的空闲块大小和足够但是单个都不够,就会产生外部碎片。
2) d; 块大小递增,那么最佳适配法找到的块和首次适配找到的块是同一个,因为最佳适配总是想找一个刚好大于请求块大小的空闲块。a,块大小递减,首次适配很容易找到,所以分配性能会很高。b,最佳适配方法无论怎样,都要搜索所有的链表(除非维护成块大小递增的链表)。c,是匹配的最小的。
3) c; 保守的意思就是所有可能被引用的堆都会被标记,int像指针,所以可能认为它表示的地址是正在被引用的(实际上它只是个int)。
9.20
首先,没有什么头绪~除了写好分配器,还需要考虑存储器映射吧。
真的写起来不是两三个小时就能搞定的,还可能要测试各种bug,最后性能测试也挺麻烦的。
于是乎……写不出来了。