深入理解计算机系统(第二版) 家庭作业 第九章


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 
#include 
#include 
#include 
#include 
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 
#include 
#include 

#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 

/* 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,最后性能测试也挺麻烦的。
于是乎……写不出来了。





你可能感兴趣的:(深入理解计算机系统(第二版) 家庭作业 第九章)