510 likes | 633 Views
Heap Management. What is really stored on the heap?. 0x7000. Housekeeping Users Data Buffer Next Block Data Housekeeping. 0x7008. int main() { int *x,*y; x=(int*)malloc(2*sizeof(int)); //new heap
E N D
What is really stored on the heap? 0x7000 Housekeeping Users Data Buffer Next Block Data Housekeeping 0x7008 int main() { int *x,*y; x=(int*)malloc(2*sizeof(int)); //new heap assert(x); printf("%p\n",x); //How doesmallocfunction know where to put y? y=(int*)malloc(sizeof(int)); assert(y); //How does free function know how much to free? free(x);
What is really stored on the heap? 0x7000 Housekeeping Users Data Buffer Next Block Data Housekeeping 0x7008 "housekeeping data" stored in area before your data: size field- holds the number of bytes of users data in this block (user's request + some buffer) next field- if block in "use" it will be null.. otherwise holds the address of the next free block.. Thus a "linked list" of free blocks is formed inside the heap. Starting at address given by the "free list pointer".. malloc can search through free blocks.
What is really stored on the heap? 0x7000 Housekeeping Users Data Buffer Next Block Data Housekeeping 0x7008 A "reference count" of number of pointers pointing to this block in user's program could also be in the housekeeping data. What would we use the reference count for?
What does the heap look like? malloc() searches the free list for a block that is big enough. If none is found, more memory is requested from the operating system. free() checks if the blocks adjacent to the freed block are also free If so, adjacent free blocks are merged in to a single, larger free block. Otherwise, the freed block is just added to the free list.
What does the heap look like? • If there are multiple free blocks of memory that are big enough for some request, how do we choose which one to use? • best-fit: choose the smallest block that is big enough for the request • first-fit: choose the first block we see that is big enough • next-fit: like first-fit but remember where we finished searching and resume searching from there
int main() { int *x,*y,*z; x=(int*)malloc(20*sizeof(int)); //new heap assert(x); y=(int*)malloc(10*sizeof(int)); assert(y); free(x); z=(int*)malloc(10*sizeof(int)); assert(z); What does our heap look like if we use first fit? What if we use next fit? What does the heap look like?
20 integers and 12 bytes of buffer... 10 integers and 12 bytes of buffer... Heap Free List Pointer = 7160 the heap starts here at 7000 x=7008 this is 92 bytes here size=92 bytes next free block 20*4 + 12 buffer NULL 7100 y=7108 this is 52 bytes here size=52 bytes next free block 10*4 + 12 buffer NULL 7160 this is heap size - 168 bytes here size=xxxx bytes next free block (heap size-160) NULL The Heap looks like this after malloc'ing x and y before x is freed.
Heap Free List Pointer = 7000 FREE FREE FREE FREE FREE FR 10 integers and 12 bytes of buffer... the heap starts here at 7000 this is 92 bytes here size=92 bytes next free block 20*4 + 12 buffer NULL 7100 y=7108 this is 52 bytes here size=52 bytes next free block 10*4 + 12 buffer NULL 7160 this is heap size - 168 bytes here size=xxxx bytes next free block (heap size-160) NULL The Heap looks like this x is freed before z is malloc'ed.
10 integers and 12 bytes of buffer... Heap Free List Pointer = 7060 FREE FREE FREE FREE FREE 10 integers and 12 bytes of buffer... the heap starts here at 7000 z=7008 this is 52 bytes here size=52 bytes next free block 20*4 + 12 buffer NULL 7060 this is 32 bytes here size=32 bytes next free block 7160 7100 y=7108 this is 52 bytes here size=52 bytes next free block 10*4 + 12 buffer NULL 7160 this is heap size - 168 bytes here size=xxxx bytes next free block (heap size-160) NULL
Heap Free List Pointer = 7000 FREE FREE FREE FREE FREE 10 integers and 12 bytes of buffer... 10 integers and 12 bytes of buffer... the heap starts here at 7000 this is 92 bytes here size=92 bytes next free block 7220 7100 y=7108 this is 52 bytes here size=52 bytes next free block 10*4 + 12 buffer NULL 7160 z=7168 this is 52 bytes here size=52 bytes next free block 10*4 + 12 buffer NULL 7220 this is heap size - 220 bytes here size=xxxx bytes next free block (heap size-220) NULL
Data Structure for malloc.c _fragblocks HEAP = 222 bytes _heapbase _fraghead BLOCKSIZE = 212 bytes
_fragblocks • A global array of 12 integers holds the number of 4096 bytes blocks containing fragments of a certain size that have been allocated. [11] is number of 4096 byte blocks containing 2048 byte fragments… [10] is number of 4096 bytes blocks containing for 1024 byte fragments • Note that frag here is allocated frag, not free frag. int _fragblocks[BLOCKLOG]; //BLOCKLOG = log2(BLOCKSIZE)
_fradhead • A global array of 12 structures holds the pointer to the first and the last frag block of a given size: 2log • Note that frag here is free frag. struct list _fraghead[BLOCKLOG]; //list{int *next; int prev;} _fraghead[log] NEXT PREV NEXT PREV NEXT PREV NEXT PREV 0 0
How the real malloc does it… • The real malloc actually uses blocks of standardized size to fill your request. • The standard block is 4096 bytes. • Standard blocks can be broken up in to smaller “frag blocks” of sizes 8, 16, 32, 64, 128, 256, 512, 1024, 2048 bytes long. • For example: • If you ask for 13 bytes you’ll get a 16 byte fragment. • This is a trade off between speed and space wastage. Why the smallest free frag size is 8? Why not 4 or other number?
_heapinfo • busy • fragmented • type: frag size • num of free frag • first free frag • allocated as whole • type: a block • the num of contiguous blocks which are allocated • free • the num of contiguous blocks which are free • next free cluster • prev free cluster
Look at source code – malloc.h • $tar –xvf malloc_debug.tar • $cd malloc_debug/malloc • $vim malloc.h
extern What if I want global variables to be accessible for use in another file (section of my code)? extern int flag; extern- lets the compiler know that it won’t be able to figure out where the variable is located at compile time. (unresolved reference) Why? The compiler only looks at the file you are trying to compile- and doesn’t have the “big picture” yet about the other files you’re compiling and putting together. NACHOS will have about 30 source files that you will compile to build the OS. (LINUX kernel has 100s)
extern continued The Linker does have the “big picture” and responsible for “linking” together all of the object files made by the compiler and making a single executable. In the case of extern the compiler will send a message to the linker saying “I’m not really sure where this variable is located- it may be in another file - can you find it?”
Function Pointers typedef enum bool {FALSE, TRUE} bool; bool greaterthan(int x,int y) { return (x>y); } bool lessthan(int x, int y) { return (x<y); } bool foo(bool (*compare)(int,int), int x, int y) { return( (*compare)(x,y)); } int main() { if (foo(lessthan,3,5)) printf("3 is less than 5\n"); }
Function Pointers #include <stdio.h> void function (int x) {printf("%d",x);} void (*func_pointer)(int); int main() { int choice; int x = 5; void (*func_pointer)(int) = function; (*func_pointer)(int); }
Function Pointers To declare a function pointer : return type (*function_ptr) (function inputs); extern void *(*_morecore)(long); extern- this function may be defined or used outside this file The return type is a void* The input to the function must be a long. The function is referred to via the pointer _morecore
This is a function prototype: /* Default value of previous. */ extern void *_default_morecore(long); • This function takes in a long and returns a void* and is called _default_morecore • This matches the type of a _morecore function pointer. • The _default_morecore function actually goes and gets more memory for the heap.
Function Pointers cont’d So far we’ve declared a function pointer extern void *(*_morecore)(long); We’ve given a function prototype extern void *_default_morecore(long) And then we eventually assign the pointer… Code from malloc.c /* How to really get more memory. */ void *(*_morecore)(long) = _default_morecore;
What do these do? #define INT_BIT (CHAR_BIT * sizeof (int)) You may assume that CHAR_BIT is 8 in our architecture. #define BLOCKLOG (INT_BIT > 16 ? 12 : 9) What value does BLOCKLOG get? #define BLOCKSIZE (1 << BLOCKLOG) What value does BLOCKSIZE get? Shift left 1 how many times? #define BLOCKIFY(SIZE) (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)
Heap Initial Size /* Determine the amount of memory spanned by the initial heap table (not an absolute limit). */ #define HEAP (INT_BIT > 16 ? 4194304 : 65536)
Unions • Most of the programmers in the world think that Unions were a bad idea. That’s probably why Java got rid of them. Unfortunately- there’s lots of poorly written code out there with unions in it. Including malloc() • Unions allow you to store one of several different types in a given storage location. • Unfortunately- you have to remember which type that you stored in the location.
Thus in a Number_T we can store either an int or a double and can change back and forth Union Example typedef union Number { int x; double y; } Number_T; int main() { Number_T value; value.x=100; printf ("X is %d and Y is %g\n", value.x,value.y); value.y=100.0; printf ("X is %d and Y is %g\n", value.x,value.y); } X is 100 and Y is 6.15928e-306 X is 0 and Y is 100.000000
An info is either: union info { struct { int type; union { struct { int nfree; int first; } frag; int size; } info; } busy; struct { int size; int next; int prev; } free; }; This (busy struct) Or This (free struct)
A busy is: an integer And either: frag structure or another integer union info { struct { int type; union { struct { int nfree; int first; } frag; int size; } info; } busy; struct { int size; int next; int prev; } free; };
What does this do? #define BLOCK(A) (((char *) (A) - _heapbase) / BLOCKSIZE + 1) #define ADDRESS(B) ((void *) (((B) - 1) * BLOCKSIZE + _heapbase))
Debug malloc.c • $ cd ~/malloc_debug • $ddd main • Menu ‘data’->’Status Display’ • Backtrace of the stack • Menu ‘Edit’->’Preferences…’->’Source’ • Display Source Line Numbers
main.c • //Allocate the first memory under your control. • int *x; //new heap • x=(int*)malloc(20*sizeof(int)); • free(x);
Step into malloc func _fragblocks HEAP = 222 bytes _heapbase _fraghead BLOCKSIZE = 212 bytes
Step into initialize() • heapsize = HEAP / BLOCKSIZE; • Figures out how many blocks we have • We have 210 blocks. • In other words, 210 _heapinfo elements.
align(heapsize * sizeof (union info)); _fragblocks HEAP = 222 bytes _heapbase _fraghead BLOCKSIZE = 212 bytes
_heapbase = (char *) _heapinfo; _fragblocks HEAP = 222 bytes _heapbase _fraghead BLOCKSIZE = 212 bytes
Go back to malloc 126: Check for request for 0 bytes 129: Check to see if size requested is < 8 Bytes 133: Is the request <= 2048 bytes?
136: --size;137: for (log=1; (size>>=1)!=0; ++log) • At the end the following will be true: • 2log >= size > 2log-1 • For example: • size = 80 • in binary size = 1010000 • --size = 1001111 • log = 7 • Why --size?
142: if ((next = _fraghead[log].next) != 0) • We check to see if there are any frag blocks of our size available. (Initially no frag blocks. _fraghead[log].next is all 0’s because…)
158: result = malloc(BLOCKSIZE) • We make a recursive call to malloc requesting a block of 4096 bytes. • Step into malloc again. • We do same checks (initialize, size, etc.) • We go in to large block routine • 179: else
184: blocks = BLOCKIFY(size) • We get the number of blocks this size corresponds to. • In our case: • size = 4096 • blocks = 1
187: start = block = _heapindex • _heapindex points the free block which is in the free block link. _heapinfo[3] _heapinfo[7] _heapinfo[0] _heapinfo[9] _heapinfo[17] _heapinfo[12] _heapindex
188: if (block == start) • We searched through the linked list, and did not find any memory block large enough to meet our request. • We need to get more memory from system.
line 193 -- 203 Suppose we need 4 more blocks We just need to ask for 2 more.
204: result = morecore(blocks * BLOCKSIZE) _fragblocks _heapbase _fraghead result
Go back to malloc again! • Now we are at line 158 • 161: ++_fragblocks[log] • We increase the count of our fragment sized blocks by one.
NEXT NEXT NEXT NEXT NEXT PREV PREV PREV PREV PREV 0 0 Line 164 -171 1st block _fraghead 31 frags each frag has 128 bytes
Line 174 - 177 • Fill in _heapinfo. • Return the result. • Done!