深入理解计算机系统_3e 第九章家庭作业 CS:APP3e chapter 9 homework

9.11

A.

00001001 111100

B.

+----------------------------+
|  Parameter          Value  |
+----------------------------+
|  VPN                0x09   |
|  TLB index          0x01   |
|  TLB tag            0x02   |
|  TLB hit?           N      |
|  Page fault?        N      |
|  PPN                0x17   |
+----------------------------+

C.

010111 111100

D.

+-----------------------------+
| Parameter             Value |
+-----------------------------+
| Byte offset           0x00  |
| Cache index           0x0F  |
| Cache tag             0x17  |
| Cache hit?            N     |
| Cahe byte returned    ---   |
+-----------------------------+

9.12

A.

00001110 101001

B.

+----------------------------+
|  Parameter          Value  |
+----------------------------+
|  VPN                0x0E   |
|  TLB index          0x02   |
|  TLB tag            0x03   |
|  TLB hit?           N      |
|  Page fault?        N      |
|  PPN                0x11   |
+----------------------------+

C.

010001 101001

D.

+-----------------------------+
| Parameter             Value |
+-----------------------------+
| Byte offset           0x01  |
| Cache index           0x0A  |
| Cache tag             0x11  |
| Cache hit?            N     |
| Cahe byte returned    ---   |
+-----------------------------+

9.13

更新:计算VPN时0x4写成0010,本错误由孙月晴指出,已更正。

A.

000000001 000000

B.

+----------------------------+
|  Parameter          Value  |
+----------------------------+
|  VPN                0x01   |
|  TLB index          0x01   |
|  TLB tag            0x00   |
|  TLB hit?           N      |
|  Page fault?        Y      |
|  PPN                ---    |
+----------------------------+

C.

Page fault

D.

Page fault


9.14

这个题要注意将open的flag设置为O_RDWR ,因为我们要从这个文件进行读写操作,同时mmap的prot也要设置为PROT_WRITE (否则会出现Segmentation fault),最后一点是要将mmap的flags设置为MAP_SHARED ,因为我们需要将更新直接更改于物理内存/磁盘上,如果设置为MAP_PRIVATE 更改将不会最终改写磁盘上的数据(copy-on-write)。参见man 2 mmap

MAP_SHARED
          Share this mapping.  Updates to the mapping are visible to other
          processes  that  map  this  file, and are carried through to the
          underlying file.  (To precisely control when updates are carried
          through to the underlying file requires the use of msync(2).)

MAP_PRIVATE
          Create  a private copy-on-write mapping.  Updates to the mapping
          are not visible to other processes mapping the  same  file,  and
          are  not carried through to the underlying file.  It is unspeci‐
          fied whether changes made to the file after the mmap() call  are
          visible in the mapped region.

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
	int fd;

	if ((fd = open("hello.txt", O_RDWR)) == -1)
	{
		perror("Failed to open hello.txt");
		exit(EXIT_FAILURE);
	}

	struct stat stat_of_file;

	if (fstat(fd, &stat_of_file) == -1)
	{
		perror("Failed to get stat of hello.txt");
		exit(EXIT_FAILURE);
	}
	
	char *p;
	if ((p = mmap(NULL, stat_of_file.st_size, PROT_WRITE, MAP_SHARED,
	                  fd, 0)) == (void *)-1)
	{
		perror("Failed to mmap");
		exit(EXIT_FAILURE);
	}
	
	p[0] = 'J';

	return 0;
}

运行输出:


9.15

+-------------------------------------+
|Request    Block size    Block header|
+-------------------------------------+
|malloc(3)       8            0x9     |
|malloc(11)      16           0x11    |
|malloc(20)      24           0x19    |
|malloc(21)      32           0x21    |
+-------------------------------------+

9.16
更新:这个题我之前是按照Min(Min(Allocated block), Min(Free block))来做的,但是由于allocate操作后剩余的空间要大于一个Min(Free block)才会有split发生——产生一个比原来的Free block更小的两个块。所以最小的块应该是Max(Min(Allocated block), Min(Free block)),本错误由孙月晴指出,已更正。

 Alignment    Allocated block    Free block   Minimum block size
+---------------------------------------------------------------+
 Single word  Header and footer Header and footer     4*4
 Single word  Header no footer  Header and footer     4*4
 Double word  Header and footer Header and footer     4*4
 Double word  Header no footer  Header and footer     4*4


9.17

本题要求将9.9.12节的“first-fit”策略改为“next-fit”策略,主要改动的函数是find_fit ,同时注意以下两点:

  1. 用一个静态存储的指针“rover”保存上一次搜索的结果地址(第一次是堆的起始地址),作为下一次搜索的起点。
  2. 释放(free)节点并且有合并(coalesce)的时候,如果rover正好指向的就是合并的节点中那个高地址的部分,需要将其指向合并后的节点的起始位置。

**下面的代码改编自CS:APP3e官网的Code examplesmm.c(完整代码),如需使用请联系Randy Bryant and Dave O'Hallaron ** 。

只需要改变mm_init find_fit coalesce 这三个模块,注释写的非常清楚,就不细说了,宏定义请参考上面链接中的完整代码。

/* Global variables */
static char *heap_listp = 0;  /* Pointer to first block */  
static char *rover;           /* Next fit rover */

static void *find_fit(size_t asize);
static void *coalesce(void *bp);

/* 
 * mm_init - Initialize the memory manager 
 */
/* $begin mminit */
int mm_init(void) 
{
    /* Create the initial empty heap */
    if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) //line:vm:mm:begininit
        return -1;
    PUT(heap_listp, 0);                          /* Alignment padding */
    PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1)); /* Prologue header */ 
    PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1)); /* Prologue footer */ 
    PUT(heap_listp + (3*WSIZE), PACK(0, 1));     /* Epilogue header */
    heap_listp += (2*WSIZE);                     //line:vm:mm:endinit  
    /* $end mminit */

    rover = heap_listp;
    /* $begin mminit */

    /* Extend the empty heap with a free block of CHUNKSIZE bytes */
    if (extend_heap(CHUNKSIZE/WSIZE) == NULL) 
        return -1;
    return 0;
}
/* $end mminit */

/*
 * coalesce - Boundary tag coalescing. Return ptr to coalesced block
 */
/* $begin mmfree */
static void *coalesce(void *bp) 
{
    size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(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, 0));
        PUT(FTRP(bp), PACK(size,0));
    }

    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));
        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));
        bp = PREV_BLKP(bp);
    }
    /* $end mmfree */
    /* Make sure the rover isn't pointing into the free block */
    /* that we just coalesced */
    if ((rover > (char *)bp) && (rover < NEXT_BLKP(bp))) 
        rover = bp;
    /* $begin mmfree */
    return bp;
}
/* $end mmfree */

/* 
 * find_fit - Find a fit for a block with asize bytes 
 */
static void *find_fit(size_t asize)
{
    /* Next fit search */
    char *oldrover = rover;

    /* Search from the rover to the end of list */
    for ( ; GET_SIZE(HDRP(rover)) > 0; rover = NEXT_BLKP(rover))
        if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover))))
            return rover;

    /* search from start of list to old rover */
    for (rover = heap_listp; rover < oldrover; rover = NEXT_BLKP(rover))
        if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover))))
            return rover;

    return NULL;  /* no fit found */
}
/* $end mmfirstfit */

9.18

由于存在内存对齐,Header的最低三位将是000,其中的最低位已经被用来判断这个块是否是空块(free or allocated),我们将倒数第二个位用来判断前一个块是否是空块,例如010代表这个空块的前一个块已经被占用了。

我这里只利用了Header的倒数第二位,Free block的Boundary tag没有发生改变。要注意的是,由于allocated块不需要Boundary tag,所以最小的块是一个DWORD(一个Header和一个四字节的数据),最小的存储单位还是WORD。 最小的free块1D:

    +-----+-----+
1D  | 8/0 | 8/0 |
    +-----+-----+
2D
3D
........

每一个free块的Boundary tag被申请后可以存数据(与优化前相比多存一个WORD),所以一个1D的free块可以存1W,2D的块可以存23W,3D的块可以存45W,4D的块可以存6~7W,根据这个对应关系更改mm_malloc函数。

另外要注意的是申请和释放一个块以后记得保存Header中关于前一个块free or allocated的数据,同时要更新下一个块Header中free or allocated的数据。

**下面的代码改编自CS:APP3e官网的Code examplesmm.c(完整代码),并去掉了一些检查模块,改变的地方用“! CHANGED !”注释标出,如需使用请联系Randy Bryant and Dave O'Hallaron ** 。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "mm.h"
#include "memlib.h"

/* $begin mallocmacros */
/* Basic constants and macros */
#define WSIZE       4       /* Word and header/footer size (bytes) */ //line:vm:mm:beginconst
#define DSIZE       8       /* Double word size (bytes) */
#define CHUNKSIZE  (1<<12)  /* Extend heap by this amount (bytes) */  //line:vm:mm:endconst

#define MAX(x, y) ((x) > (y)? (x) : (y))

/* Pack a size and allocated bit into a word */
#define PACK(size, alloc)  ((size) | (alloc)) //line:vm:mm:pack

/* Read and write a word at address p */
#define GET(p)       (*(unsigned int *)(p))            //line:vm:mm:get
#define PUT(p, val)  (*(unsigned int *)(p) = (val))    //line:vm:mm:put

/* Read the size and allocated fields from address p */
#define GET_SIZE(p)  (GET(p) & ~0x7)                   //line:vm:mm:getsize
#define GET_ALLOC(p) (GET(p) & 0x1)                    //line:vm:mm:getalloc
#define GET_PREVIOUS_ALLOC(p) (GET(p) & 0x2) /* ! CHANGED ! */

/* Given block ptr bp, compute address of its header and footer */
#define HDRP(bp)       ((char *)(bp) - WSIZE)                      //line:vm:mm:hdrp
#define FTRP(bp)       ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) //line:vm:mm:ftrp

/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp)  ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) //line:vm:mm:nextblkp
#define PREV_BLKP(bp)  ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) //line:vm:mm:prevblkp
/* $end mallocmacros */

/* Global variables */
static char *heap_listp = 0;  /* Pointer to first block */
static char *rover;           /* Next fit rover */

/* Function prototypes for internal helper routines */
static void *extend_heap(size_t words);
static void place(void *bp, size_t asize);
static void *find_fit(size_t asize);
static void *coalesce(void *bp);

/*
 * mm_init - Initialize the memory manager
 */
/* $begin mminit */
int mm_init(void)
{
    /* Create the initial empty heap */
    if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) //line:vm:mm:begininit
        return -1;
    PUT(heap_listp, 0);                          /* Alignment padding */
    PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 0x1 | 0x2)); /* Prologue header */
    PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 0x1 | 0x2)); /* Prologue footer */
    PUT(heap_listp + (3*WSIZE), PACK(0, 0x1 | 0x2));     /* Epilogue header */
    heap_listp += (2*WSIZE);                     //line:vm:mm:endinit
    /* $end mminit */

    rover = heap_listp;
    /* $begin mminit */

    /* Extend the empty heap with a free block of CHUNKSIZE bytes */
    if (extend_heap(CHUNKSIZE/WSIZE) == NULL)
        return -1;
    return 0;
}
/* $end mminit */

/*
 * mm_malloc - Allocate a block with at least size bytes of payload
 */
/* $begin mmmalloc */
void *mm_malloc(size_t size)
{
    size_t Wsize;      /* round up to an WSIZE */ /* ! CHANGED ! */
    size_t Dsize;      /* DSIZE after align WSIZE */ /* ! CHANGED ! */

    size_t asize;      /* Adjusted block size */
    size_t extendsize; /* Amount to extend heap if no fit */
    char *bp;

    /* $end mmmalloc */
    if (heap_listp == 0){
        mm_init();
    }
    /* $begin mmmalloc */
    /* Ignore spurious requests */
    if (size == 0)
        return NULL;

    Wsize = (size + (WSIZE-1)) / WSIZE; /* ! CHANGED ! */
    Dsize = Wsize/2 + 1; /* ! CHANGED ! */
    asize = DSIZE * Dsize; /* ! CHANGED ! */

    /* Search the free list for a fit */
    if ((bp = find_fit(asize)) != NULL) {  //line:vm:mm:findfitcall
        place(bp, asize);                  //line:vm:mm:findfitplace
        return bp;
    }

    /* No fit found. Get more memory and place the block */
    extendsize = MAX(asize,CHUNKSIZE);                 //line:vm:mm:growheap1
    if ((bp = extend_heap(extendsize/WSIZE)) == NULL)
        return NULL;                                  //line:vm:mm:growheap2
    place(bp, asize);                                 //line:vm:mm:growheap3
    return bp;
}
/* $end mmmalloc */

/*
 * mm_free - Free a block
 */
/* $begin mmfree */
void mm_free(void *bp)
{
    /* $end mmfree */
    if (bp == 0)
        return;

    /* $begin mmfree */
    size_t size = GET_SIZE(HDRP(bp));
    /* $end mmfree */
    if (heap_listp == 0){
        mm_init();
    }
    /* $begin mmfree */

    if (GET_PREVIOUS_ALLOC(HDRP(bp))) /* ! CHANGED ! */
    {
        PUT(HDRP(bp), PACK(size, 0x2));
    }
    else
    {
        PUT(HDRP(bp), PACK(size, 0));
    }
    PUT(FTRP(bp), PACK(size, 0)); /* ! CHANGED ! */
    /* pack next block */
    *HDRP(NEXT_BLKP(bp)) &= ~0x2; /* ! CHANGED ! */
    coalesce(bp);
}

/* $end mmfree */
/*
 * coalesce - Boundary tag coalescing. Return ptr to coalesced block
 */
/* $begin mmfree */
static void *coalesce(void *bp)
{
    size_t prev_alloc = GET_PREVIOUS_ALLOC(HDRP(bp)); /* ! CHANGED ! */
    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, 0x2)); /* ! CHANGED ! */
        PUT(FTRP(bp), PACK(size,0)); /* ! CHANGED ! */
    }

    else if (!prev_alloc && next_alloc) {      /* Case 3 */
        size += GET_SIZE(HDRP(PREV_BLKP(bp)));
        PUT(FTRP(bp), PACK(size, 0));

        if (GET_PREVIOUS_ALLOC(HDRP(PREV_BLKP(bp)))) /* ! CHANGED ! */
        {
            PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0x2));
        }
        else
        {
            PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
        }
        bp = PREV_BLKP(bp); /* ! CHANGED ! */
    }

    else {                                     /* Case 4 */
        size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
            GET_SIZE(FTRP(NEXT_BLKP(bp)));

        if (GET_PREVIOUS_ALLOC(HDRP(PREV_BLKP(bp)))) /* ! CHANGED ! */
        {
            PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0x2));
        }
        else
        {
            PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
        }
        PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
        bp = PREV_BLKP(bp);
    }
    /* $end mmfree */
    /* Make sure the rover isn't pointing into the free block */
    /* that we just coalesced */
    if ((rover > (char *)bp) && (rover < NEXT_BLKP(bp)))
        rover = bp;
    /* $begin mmfree */
    return bp;
}
/* $end mmfree */

/*
 * mm_realloc - Naive implementation of realloc
 */
void *mm_realloc(void *ptr, size_t size)
{
    size_t oldsize;
    void *newptr;

    /* If size == 0 then this is just free, and we return NULL. */
    if(size == 0) {
        mm_free(ptr);
        return 0;
    }

    /* If oldptr is NULL, then this is just malloc. */
    if(ptr == NULL) {
        return mm_malloc(size);
    }

    newptr = mm_malloc(size);

    /* If realloc() fails the original block is left untouched  */
    if(!newptr) {
        return 0;
    }

    /* Copy the old data. */
    oldsize = GET_SIZE(HDRP(ptr));
    if(size < oldsize) oldsize = size;
    memcpy(newptr, ptr, oldsize);

    /* Free the old block. */
    mm_free(ptr);

    return newptr;
}

/*
 * The remaining routines are internal helper routines
 */

/*
 * extend_heap - Extend heap with free block and return its block pointer
 */
/* $begin mmextendheap */
static void *extend_heap(size_t words)
{
    char *bp;
    size_t size;

    /* Allocate an even number of words to maintain alignment */
    size = (words % 2) ? (words+1) * WSIZE : words * WSIZE; //line:vm:mm:beginextend
    if ((long)(bp = mem_sbrk(size)) == -1)
        return NULL;                                        //line:vm:mm:endextend

    /* Initialize free block header/footer and the epilogue header */
    if (GET_PREVIOUS_ALLOC(HDRP(bp))) /* ! CHANGED ! */
    {
        PUT(HDRP(bp), PACK(size, 0x2));
    }
    else
    {
        PUT(HDRP(bp), PACK(size, 0));
    }
    PUT(FTRP(bp), PACK(size, 0));         /* Free block footer */   //line:vm:mm:freeblockftr
    PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* New epilogue header */ //line:vm:mm:newepihdr
    /* Coalesce if the previous block was free */
    return coalesce(bp);                                          //line:vm:mm:returnblock
}
/* $end mmextendheap */

/*
 * place - Place block of asize bytes at start of free block bp
 *         and split if remainder would be at least minimum block size
 */
/* $begin mmplace */
/* $begin mmplace-proto */
static void place(void *bp, size_t asize)
/* $end mmplace-proto */
{
    size_t csize = GET_SIZE(HDRP(bp));

    if ((csize - asize) >= DSIZE) { /* ! CHANGED ! */
        if (GET_PREVIOUS_ALLOC(HDRP(bp))) /* ! CHANGED ! */
        {
            PUT(HDRP(bp), PACK(asize, 0x2|0x1));
        }
        else
        {
            PUT(HDRP(bp), PACK(asize, 0x1));
        }
        PUT(FTRP(bp), PACK(asize, 1));
        bp = NEXT_BLKP(bp);
        PUT(HDRP(bp), PACK(csize-asize, 0x2)); /* ! CHANGED ! */
        PUT(FTRP(bp), PACK(csize-asize, 0));
    }
    else {
        if (GET_PREVIOUS_ALLOC(HDRP(bp))) /* ! CHANGED ! */
        {
            PUT(HDRP(bp), PACK(csize, 0x2|0x1));
        }
        else
        {
            PUT(HDRP(bp), PACK(csize, 0x1));
        }
        PUT(FTRP(bp), PACK(csize, 1));  /* ! CHANGED ! */
        *HDRP(NEXT_BLKP(bp)) |= 0x2; /* ! CHANGED ! */
    }

}
/* $end mmplace */

/*
 * find_fit - Find a fit for a block with asize bytes
 */
static void *find_fit(size_t asize)
{
    /* Next fit search */
    char *oldrover = rover;

    /* Search from the rover to the end of list */
    for ( ; GET_SIZE(HDRP(rover)) > 0; rover = NEXT_BLKP(rover))
        if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover))))
            return rover;

    /* search from start of list to old rover */
    for (rover = heap_listp; rover < oldrover; rover = NEXT_BLKP(rover))
        if (!GET_ALLOC(HDRP(rover)) && (asize <= GET_SIZE(HDRP(rover))))
            return rover;

    return NULL;  /* no fit found */
}
/* $end mmfirstfit */

用这一章实验的工具检测正确性:


9.19

答案:adb

下面是错误陈述的错误原因:

1
(b) "best fit" 可能(看free list是怎么安排的,例如第二问的d项)更慢,因为可能每一次都需要遍历一遍free list

(c) 如果free block是按照地址高低链接的,那么每次插入都需要线性的时间(扫描恰当的地址)

(d) 不一定,例如2^m次方这个链表上的free block都不能合并(地址不连续),但是现在需要申请一个2m+1的块,即使2m次方这个链表上的空间够,我们也可能需要向堆申请新的空间,构成了“external fragmentation”


2
(a) 这话看着想打人,空间浪费得有多大。。。这并不能避免“external fragmentation”,因为链表上的free block并没有合并

(b) 应该按照从小到大的顺序排列,因为这样能够在线性时间内获取到对应的块(d选项)

(c) 应该是选择能够放下的最小块


3
书上9.10.3节原话:

The fundamental reason that Mark&Sweep collectors for C programs must
be conservative is that the C language does not tag memory locations with type
information. Thus, scalars like ints or floats can masquerade as pointers. For
example, suppose that some reachable allocated block contains an int in its
payload whose value happens to correspond to an address in the payload of some
other allocated block b. There is no way for the collector to infer that the data is
really an int and not a pointer. Therefore, the allocator must conservatively mark
block b as reachable, when in fact it might not be.


9.20

这个题包含在本章对应的Malloc Lab (Self-Study Handout )实验中,我做完后会放上来的。
更新:参见 CS:APP3e 深入理解计算机系统_3e MallocLab实验

posted @ 2018-01-04 21:07  QiuhaoLi  阅读(3999)  评论(0编辑  收藏  举报