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
,同時注意以下兩點:
- 用一個靜態存儲的指針“rover”保存上一次搜索的結果地址(第一次是堆的起始地址),作為下一次搜索的起點。
- 釋放(free)節點並且有合並(coalesce)的時候,如果rover正好指向的就是合並的節點中那個高地址的部分,需要將其指向合並后的節點的起始位置。
**下面的代碼改編自CS:APP3e官網的Code examples 的mm.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 examples 的mm.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實驗