How can I compile and run this project? - c

https://github.com/joelpet/malloc
i want to learn how to create a more efficient malloc and I found this project. I can run make to compile it:
$ make tstmalloc
make: `tstmalloc' is up to date.
But how can I run and test it? I read the readme file but it is not detailed enough. I want to compile all the programs, test all the programs and understand what the programs do. But if I run just make then it complains in a manner that I don't understand:
$ make
gcc -g -Wall -ansi -DSTRATEGY=2 -c -o malloc.o malloc.c
malloc.c: In function ‘morecore’:
malloc.c:77:3: warning: implicit declaration of function ‘getpagesize’ [-Wimplicit-function-declaration]
noPages = ((nu*sizeof(Header))-1)/getpagesize() + 1;
^
malloc.c:78:84: error: ‘MAP_ANONYMOUS’ undeclared (first use in this function)
cp = mmap(__endHeap, noPages*getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
^
malloc.c:78:84: note: each undeclared identifier is reported only once for each function it appears in
malloc.c:85:5: warning: implicit declaration of function ‘perror’ [-Wimplicit-function-declaration]
perror("failed to get more memory");
^
make: *** [malloc.o] Error 1
And if I try to compile the programs individually I get another error msg that I don't understand:
$ gcc malloc.c
/usr/bin/ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): relocation 0 has invalid symbol index 11
The contents of my malloc.c is:
#include "brk.h"
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#define NALLOC 1024 /* minimum #units to request */
typedef long Align; /* for alignment to long boundary */
union header { /* block header */
struct {
union header *ptr; /* next block if on free list */
unsigned size; /* size of this block - what unit? */
} s;
Align x; /* force alignment of blocks */
};
typedef union header Header;
static Header base; /* empty list to get started */
static Header *freep = NULL; /* start of free list */
/* free: put block ap in the free list */
void free(void * ap)
{
Header *bp, *p;
if(ap == NULL) return; /* Nothing to do */
bp = (Header *) ap - 1; /* point to block header */
for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if(p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* freed block at atrt or end of arena */
if(bp + bp->s.size == p->s.ptr) { /* join to upper nb */
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
}
else
bp->s.ptr = p->s.ptr;
if(p + p->s.size == bp) { /* join to lower nbr */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else
p->s.ptr = bp;
freep = p;
}
/* morecore: ask system for more memory */
#ifdef MMAP
static void * __endHeap = 0;
void * endHeap(void)
{
if(__endHeap == 0) __endHeap = sbrk(0);
return __endHeap;
}
#endif
static Header *morecore(unsigned nu)
{
void *cp;
Header *up;
#ifdef MMAP
unsigned noPages;
if(__endHeap == 0) __endHeap = sbrk(0);
#endif
if(nu < NALLOC)
nu = NALLOC;
#ifdef MMAP
noPages = ((nu*sizeof(Header))-1)/getpagesize() + 1;
cp = mmap(__endHeap, noPages*getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
nu = (noPages*getpagesize())/sizeof(Header);
__endHeap += noPages*getpagesize();
#else
cp = sbrk(nu*sizeof(Header));
#endif
if(cp == (void *) -1){ /* no space at all */
perror("failed to get more memory");
return NULL;
}
up = (Header *) cp;
up->s.size = nu;
free((void *)(up+1));
return freep;
}
void * malloc(size_t nbytes)
{
Header *p, *prevp;
Header * morecore(unsigned);
unsigned nunits;
if(nbytes == 0) return NULL;
nunits = (nbytes+sizeof(Header)-1)/sizeof(Header) +1;
if((prevp = freep) == NULL) {
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for(p= prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if(p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) /* exactly */
prevp->s.ptr = p->s.ptr;
else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void *)(p+1);
}
if(p == freep) /* wrapped around free list */
if((p = morecore(nunits)) == NULL)
return NULL; /* none left */
}
}

66 static Header *morecore(
67 unsigned nu) /* the amount of memory to ask for (in Header-sized units) */
68 {
69 char *cp;
70 Header *up;
71 if (nu < NALLOC)
72 nu = NALLOC;
73 cp = sbrk(nu * sizeof(Header));
74 if (cp == (char *) -1) /* no space at all */
75 return NULL;
76 up = (Header *) cp;
77 up->s.size = nu;
78 free((void *)(up+1));
79 return freep;
80 }
Your error refers to line 77 of malloc.c being no_pages = ... but the current code on GitHub is not that. So post up what you have in your malloc.c or else get the latest code from GitHub.

Related

Having trouble understanding free() from K&R

I'm having a lot of trouble understanding what's going on inside the free function shown on chapter 8.7 from K&R, here's the full code and some information on how the program operates:
The blocks are kept in order of increasing storage address, and the last block (highest address) points to the first.
When a request is made, the free list is scanned until a big-enough block is found.
Freeing also causes a search of the free list, to find the proper
place to insert the block being freed. If the block being freed is adjacent to a free block on either side, it is coalesced with it
into a single bigger block, so storage does not become too
fragmented. Determining adjacency is easy because the free list is
maintained in order of increasing address.
A free block contains:
A pointer to the next block in the chain
A record of the size of the block
The free space itself
The control information at the beginning is called the "header". To simplify alignment, all blocks are multiples of the header size, and the header is aligned properly.
#include <unistd.h>
#define NULL ((void *)0)
typedef long Align; /* for alignment to long boundary */
union header { /* block header */
struct {
union header *ptr; /* next block if on free list */
unsigned size; /* size of this block */
} s;
Align x; /* force alignment of blocks */
};
typedef union header Header;
static Header base; /* empty list to get started */
static Header *freep = NULL; /* start of free list */
/* malloc: general-purpose storage allocator */
void *kr_malloc(unsigned nbytes)
{
Header *p, *prevp;
Header *morecore(unsigned);
unsigned nunits;
nunits = (nbytes+sizeof(Header)-1)/sizeof(Header) + 1;
if ((prevp = freep) == NULL) { /* no free list yet */
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) /* exactly */
prevp->s.ptr = p->s.ptr;
else {
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return p+1;
}
if (p == freep) /* wrapped around free list */
if ((p = morecore(nunits)) == NULL)
return NULL; /* none left */
}
}
#define NALLOC 1024 /* minimum #units to request */
/* morecore: ask system for more memory */
Header *morecore(unsigned nu)
{
char *cp;
void kr_free(void *);
Header *up;
if (nu < NALLOC)
nu = NALLOC;
cp = sbrk(nu * sizeof(Header));
if (cp == (char *) -1) /* no space at all */
return NULL;
up = (Header *) cp;
up->s.size = nu;
kr_free(up+1);
return freep;
}
/* free: put block ap in free list */
void kr_free(void *ap)
{
Header *bp, *p;
bp = (Header *)ap - 1; /* point to block header */
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* freed block at start or end of arena */
if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr */
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
} else
bp->s.ptr = p->s.ptr;
if (p + p->s.size == bp) { /* join to lower nbr */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else
p->s.ptr = bp;
freep = p;
}
/* free: put block ap in free list */
void kr_free(void *ap)
{
Header *bp, *p;
bp = (Header *)ap - 1; /* point to block header */
So.. bp is gonna point to the header of the block passed in as an argument (ap) and p is gonna be the variable used to iterate over the linked list, so far so good.
The first loop inside free is easy, just keep iterating over the linked list until bp is between p and p->s.ptr (p / bp / p->s.ptr) so you can insert bp where it belongs.
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
And here's where I'm starting to have trouble:
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
I understand that p > p->s.ptr is a condition that will only be true if p (the pointer that is being used to iterate over the linked list) is the last free block in the linked list, and
p == p->s.ptr will only happened if p is the only block of free memory available in the linked list, so when you say:
p >= p->s.ptr
you are checking if p is the last or only free block in the linked list why would you want to check for this case? And also what's the point of:
... && (bp > p || bp < p->s.ptr))? What is it that you are checking here? And why do you break out of the loop if the condition is true?
if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr */
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
} else
bp->s.ptr = p->s.ptr;
No problem here, you are just coalescing the block passed in as an argument (ap) with p->s.ptr if ap and p->s.ptr are next to each other (ap / p->s.ptr).
if (p + p->s.size == bp) { /* join to lower nbr */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else
p->s.ptr = bp;
You are doing the same as above but this time coalescing ap with p.
Testing for (bp > p || bp < p->s.ptr) when p points to the last block in the free list is necessary to only break if the block to be freed is after the last block or before the first block of the free list.
This loop starts at freep, which is not necessarily the free block with the lowest address as malloc sets freep = prevp; when it returns a block carved from the free list, and free sets freep = p; the address of the last freed block.
Setting freep this way improves efficiency when blocks are freed in increasing order of addresses, which is the case when they are freed in the same order as allocation in a clean arena assuming no intervening calls to free.

K&R section 8.7 for loop in free()

In K&R(2nd) section 8.7, I think there is an unexpected infinite for loop in free() and its test part seems wrong.
I inserted four // comments. malloc() has one, morecore() has another and free() has the others.
typedef long Align;
union header {
struct {
union header* ptr;
unsigned size;
}s;
Align x;
};
typedef union header Header;
static Header base;
static Header *freep = NULL;
/* malloc: general-purpose storage allocator */
void *malloc(unsigned nbytes)
{
Header *p, *prevp;
Header *morecore(unsigned);
unsigned nunits;
nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;
if ((prevp = freep) == NULL) { /* no free list yet */
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) /* exactly */
prevp->s.ptr = p->s.ptr;
else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void *)(p + 1);
}
if (p == freep) /* wrapped around free list */
// base.s.ptr = &base, freep == &base
if ((p = morecore(nunits)) == NULL)
return NULL; /* none left */
}
}
#define NALLOC 1024 /* minimum #units to requst */
/* morecore: ask system for more memory */
static Header *morecore(unsigned nu)
{
char *cp, *sbrk(int);
Header *up;
if (nu < NALLOC)
nu = NALLOC;
cp = sbrk(nu * sizeof(Header));
if (cp == (char *) -1) /* no space at all */
return NULL;
up = (Header *) cp;
up->s.size = nu;
// base.s.ptr = &base, freep == &base
free((void *)(up+1));
return freep;
}
/* free: put block ap in free list */
void free(void *ap)
{
Header *bp, *p;
bp = (Header *)ap - 1; /* point to block header */
// base.s.ptr = &base, freep == &base
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* freed block at start or end of arena */
// for (p = freep; !(0); )\
if (p >= s.ptr && (0))\
break;
if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr*/
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
} else
bp->s.ptr = p->s.ptr;
if (p + p->s.size == bp) { /* join to lower nbr */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else
p->s.ptr = bp;
freep = p;
}
What I want to say is
void free(void *ap) {
/* ... */
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* freed block at start or end of arena */
/* ... */
}
this for loop is infinite. Because bp > p && bp < p->s.ptr is the same as 0 because each value of p and p->s.ptr is &base. And p = p->s.ptr does nothing. p points to base, base.s.ptr points to itself. So the value of bp > p && bp < p->s.ptr won't be changed.
Moreover, the pointer bp points to the header of memory received by sbrk(). Is it possible to compare bp with p?
I think the function free() only makes sense when we already got a valid memory block using malloc() and call it to free it. The function call free((void *)(up+1)); in morecore() and the body of free() doesn't match(I think).
Q1. Why is the for loop infinite?
Q2. Is it valid to compare bp with p in that for loop? If so, how and where is bp(which points to the memory allocated by sbrk()) placed?
Q1. Why is the for loop infinite?
No, it's not infinite.
void free(void *ap) {
/* ... */
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* freed block at start or end of arena */
/* ... */
}
When base.s.ptr = &base and freep == &base, above code is the same as
void free(void *ap) {
/* ... */
for (p = freep; !(0); p = p->s.ptr)
if (1 && (1))
break; /* freed block at start or end of arena */
/* ... */
}
(I was confused with the meaning of || operator. I thought bp > p || bp < p->s.ptr should be the same as 0 when I asked the question.)
Q2. Is it valid to compare bp with p in that for loop? If so, how and where is bp(which points to the memory allocated by sbrk()) placed?
Yes, comparing those two is valid. sbrk() returns a previous program break value. So bp and p are comparable.

Fragmentation in custom allocator. Unable to order pointers correctly

Context: I have written a best-fit memory allocator. It allocates large blocks of memory, and serves best fitting chunks of it to programs upon request. If memory reserves are exhausted, its asks for OS for more. All free blocks are stored on a linked-list, ordered by increasing pointer value.
Problem:: When memory is released, the program is supposed to link it back into the free list of blocks for recycling, and more crucially merges the given block with its surrounding blocks if possible. Unfortunately, this only works well as long as I do not need to ask the OS for more then one super-block to serve. When this does occur, the new blocks I receive have nonsensical addressing and get inserted between other super-block addressing space. This leads to permanent fragmentation.
Tl;dr: I am being given new super-blocks of memory whose address is within the address space of another super-block, leading to fragmentation when sub-blocks are returned.
Illustration of problem:
Here is a diagram of the problem I described above.
Numbers: Memory addresses (from real execution).
Beige blocks: Free memory.
White blocks: Memory unlinked for use.
The diagram shows the progression of memory usage until the fragmentation catalyst occurs. You can see once the block gets inserted, merging will never be possible up for the busy blocks once they are checked back in.
Code: Reproducible Example:
The following includes the allocator and a small test program. It must be compiled with at least C99.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/*****************************************************************************/
/* SYMBOLIC CONSTANTS */
/*****************************************************************************/
#define NEXT(hp) ((hp)->key.next)
#define UNITS(hp) ((hp)->key.units)
#define UNIT_SIZE sizeof(Header)
#define MIN_UNITS_ALLOC 64
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/*****************************************************************************/
/* TYPE DEFINITIONS */
/*****************************************************************************/
typedef union header {
intmax_t align;
struct {
union header *next;
unsigned units;
} key;
} Header;
/*****************************************************************************/
/* GLOBAL VARIABLES */
/*****************************************************************************/
Header base = {.key = { NULL, 0 }};
Header *list;
/*****************************************************************************/
/* PROTOTYPES */
/*****************************************************************************/
/* Allocates a 'bytes' size block of memory. On success, returns pointer to
* the block. On error, NULL is returned. */
void *alloc (size_t bytes);
/* Returns a 'bytes' size block of allocated memory for reuse */
void release (void *bytes);
/* Attempts to reserve memory from the operating system via a system call */
static Header *reserve (unsigned units);
/*****************************************************************************/
/* FUNCTION IMPLEMENTATIONS */
/*****************************************************************************/
void *alloc (size_t bytes) {
size_t units, diff;
Header *block, *lastBlock, *best, *lastBest;
if (bytes == 0) {
return NULL;
}
if (list == NULL) {
list = base.key.next = &base;
}
best = lastBest = NULL;
units = (bytes + UNIT_SIZE - 1) / UNIT_SIZE + 1;
diff = SIZE_MAX;
for (lastBlock = list, block = NEXT(list); ; lastBlock = block, block = NEXT(block)) {
/* Loop across list, find closest fitting block */
if (UNITS(block) >= units && UNITS(block) - units < diff) {
diff = UNITS(block) - units;
best = block;
lastBest = lastBlock;
}
/* Upon cycle completion */
if (block == list) {
/* If no block available, reserve some. */
if (best == NULL) {
if ((lastBest = reserve(units)) == NULL) {
return NULL;
} else {
fprintf(stderr, "\nalloc: Out of memory, linking new block %lld of size %u.\n\n", (long long)lastBest, UNITS(lastBest));
release((void *)(lastBest + 1));
}
/* If block of perfect size, return. Else slice and return */
} else {
if (diff == 0) {
NEXT(lastBest) = NEXT(best);
} else {
UNITS(best) = diff;
best += diff;
UNITS(best) = units;
}
fprintf(stderr, "alloc: Unlinked block %lld of %u units.\n", (long long)best, UNITS(best));
return (void *)(best + 1);
}
}
}
}
void release (void *bytes) {
Header *p, *block;
block = (Header *)bytes - 1;
/* Choose p such that: p -> block -> NEXT(p) */
for (p = list; !(p < block && NEXT(p) > block); p = NEXT(p)) {
if (p >= NEXT(p) && (block > p || block < NEXT(p))) {
break;
}
}
/* Merge block with NEXT(p) if adjacent */
if (block + UNITS(block) == NEXT(p)) {
NEXT(block) = NEXT(NEXT(p));
UNITS(block) += UNITS(NEXT(p));
} else {
NEXT(block) = NEXT(p);
}
/* Merge block with p if adjacent */
if (p + UNITS(p) == block) {
NEXT(p) = NEXT(block);
UNITS(p) += UNITS(block);
} else {
NEXT(p) = block;
}
}
static Header *reserve (unsigned units) {
char *bytes, *sbrk(int);
Header *block;
units = MAX(units, MIN_UNITS_ALLOC);
if ((bytes = sbrk(units)) == (char *)-1) {
return NULL;
} else {
block = (Header *)bytes;
UNITS(block) = units;
}
return block;
}
/*****************************************************************************/
/* TESTING FUNCTIONS (DELETE) */
/*****************************************************************************/
void printFreeList (unsigned byAddress) {
Header *lp = list;
if (lp == NULL) {
fprintf(stdout, "List is NULL\n");
return;
}
do {
fprintf(stdout, "[ %lld ] -> ", (byAddress ? (long long)lp : (long long)UNITS(lp)));
lp = NEXT(lp);
} while (lp != list);
putc('\n', stdout);
}
void releaseItemAtIndex (int i, int k, long long *p[]) {
fprintf(stderr, "Releasing block %d/%d\n", i, k);
release(p[i]);
for (int j = i; j < k; j++) {
if (j + 1 < k) {
p[j] = p[j + 1];
}
}
}
#define MAX_TEST_SIZE 5000
int main (int argc, const char *argv[]) {
/* Seed PRNG: For removed random deletion (now manual) */
srand(time(NULL));
/* Array of pointers to store allocated blocks, blockSize we want to allocate. */
long long *p[MAX_TEST_SIZE], blockSize = 256;
/* Number of blocks we choose to allocate */
int k = 6;
/* Allocate said blocks */
for (int i = 0; i < k; i++) {
p[i] = alloc(blockSize * sizeof(char));
printFreeList(0); printFreeList(1); putchar('\n');
}
fprintf(stderr, "\n\n");
int idx;
while (k > 0) {
fprintf(stderr, "Delete an index between 0 up to and including %d:\n", k - 1);
scanf("%d", &idx);
releaseItemAtIndex(idx, k, p);
printFreeList(0); printFreeList(1); putchar('\n');
k--;
}
return 0;
}
Miscellaneous Details:
I am running a 64 bit operating system.
I do not know if pointer comparison on the heap is guaranteed to be valid. This is not guaranteed by the standard according to K&R.
Well I eventually wrote one a couple years later that seems to work alright. Bonus: It's using static memory.
Header File
#if !defined(STATIC_ALLOCATOR_H)
#define STATIC_ALLOCATOR_H
/*
*******************************************************************************
* (C) Copyright 2020 *
* Created: 07/04/2020 *
* *
* Programmer(s): *
* - Jillian Oduber *
* - Charles Randolph *
* *
* Description: *
* Static first-fit memory allocator *
* *
*******************************************************************************
*/
#include <iostream>
#include <cstddef>
#include <cstdint>
#include "dx_types.h"
extern "C" {
#include "stddef.h"
}
/*
*******************************************************************************
* Type Definitions *
*******************************************************************************
*/
// Defines the header block used in the custom allocator
typedef union block_h {
struct {
union block_h *next;
size_t size; // Size is in units of sizeof(block_h)
} d;
max_align_t align;
} block_h;
/*
*******************************************************************************
* Class Definitions *
*******************************************************************************
*/
class Static_Allocator {
public:
/*\
* #brief Configures the class with the given static memory capacity
* #param memory Pointer to static array (must support address comparison)
* #param size Total number of bytes alloted for distribution
\*/
Static_Allocator(uint8_t *memory, size_t size);
/*\
* #brief Returns a memory block of the desired size
* #param size Number of bytes to allocate
* #return
* - valid pointer if memory is available
* - NULL otherwise
\*/
uint8_t *alloc (size_t size);
/*\
* #brief Releases the allocated block of memory
* #param ptr Pointer to allocated block of memory
* #return
* - STATUS_OK on success
* - STATUS_ERR if invalid block
* - STATUS_BAD_PARAM on NULL parameter
\*/
dx::status_t free (uint8_t *ptr);
/*\
* #brief Returns the amount of free memory
* #return size_t Bytes (of available free memory)
\*/
size_t free_memory_size ();
/*\
* #brief Debug utility which shows what is on the free-list
* #return None
\*/
void show ();
/*\
* #brief Debug utility which asserts there is only a single
* memory block
\*/
bool unified ();
private:
// Fixed allocator capacity
size_t d_capacity;
// Fixed memory
uint8_t *d_memory;
// Pointer to head of the linked list
block_h *d_free_list;
// Available memory
size_t d_free_memory_size;
// Unit size
static const size_t mem_unit_size = sizeof(block_h);
};
#endif
Source File
#include "static_allocator.h"
Static_Allocator::Static_Allocator (uint8_t *memory, size_t size):
d_capacity(0),
d_memory(NULL),
d_free_list(NULL),
d_free_memory_size(0)
{
// Assign memory array
d_memory = memory;
// Trim off two mem_unit_size for head + init-block + spillover
d_capacity = size - (size % mem_unit_size) - 2 * mem_unit_size;
// Ensure free memory is set to capacity
d_free_memory_size = d_capacity;
}
uint8_t * Static_Allocator::alloc (size_t size)
{
block_h *last, *curr;
// Number of blocks sized units to allocate
size_t nblocks = (size + mem_unit_size - 1) / mem_unit_size + 1;
// If larger than total capacity or invalid size; immediately return
if ((nblocks * sizeof(block_h)) > d_capacity || size == 0) {
return NULL;
}
// If uninitialized, create initial list units
if ((last = d_free_list) == NULL) {
block_h *head = reinterpret_cast<block_h *>(d_memory);
head->d.size = 0;
block_h *init = head + 1;
init->d.size = d_capacity / sizeof(block_h);
init->d.next = d_free_list = last = head;
head->d.next = init;
}
// Problem: You must not be allowed to merge the init block
// Look for space - stop if wrapped around
for (curr = last->d.next; ; last = curr, curr = curr->d.next) {
// If sufficient space available
if (curr->d.size >= nblocks) {
if (curr->d.size == nblocks) {
last->d.next = curr->d.next;
} else {
curr->d.size -= nblocks;
curr += curr->d.size; // Suspect
curr->d.size = nblocks;
}
// Reassign free list head
d_free_list = last;
// Update amount of free memory available
d_free_memory_size -= nblocks * sizeof(block_h);
return reinterpret_cast<uint8_t *>(curr + 1);
}
// Otherwise not sufficient space. If reached
// the head of the list again, there is no more
// memory left
if (curr == d_free_list) {
return NULL;
}
}
}
dx::status_t Static_Allocator::free (uint8_t *ptr)
{
block_h *b, *p;
// Check if parameter is valid
if (ptr == NULL) {
std::cerr << "Null pointer!";
return dx::STATUS_BAD_PARAM;
}
// Check if memory is in range
if (!(ptr >= (d_memory + 2 * mem_unit_size)
&& ptr < (d_memory + d_capacity + 2 * mem_unit_size))) {
std::cerr << "Pointer out of range!";
return dx::STATUS_ERR;
}
// Obtain block header (ptr - sizeof(block_h))
b = reinterpret_cast<block_h *>(ptr) - 1;
// Update available memory size
d_free_memory_size += b->d.size;
// Find insertion location for block
for (p = d_free_list; !(b >= p && b < p->d.next); p = p->d.next) {
// If the block comes at the end of the list - break
if (p >= p->d.next && b > p) {
break;
}
// If at the end of the list, but block comes before next link
if (p >= p->d.next && b < p->d.next) {
break;
}
}
// [p] <----b----> [p->next] ----- [X]
// Check if we can merge forwards
if (b + b->d.size == p->d.next) {
b->d.size += (p->d.next)->d.size;
b->d.next = (p->d.next)->d.next;
} else {
b->d.next = p->d.next;
}
// Check if we can merge backwards
if (p + p->d.size == b) {
p->d.size += b->d.size;
p->d.next = b->d.next;
} else {
p->d.next = b;
}
d_free_list = p;
return dx::STATUS_OK;
}
size_t Static_Allocator::free_memory_size ()
{
return d_free_memory_size;
}
void Static_Allocator::show ()
{
std::cout << "Block Size: " << sizeof(block_h) << "\n"
<< "Capacity: " << d_capacity << "\n"
<< "Free Size (B): " << d_free_memory_size << "\n";
if (d_free_list == NULL) {
std::cout << "<uninitialized>\n";
return;
}
// Show all blocks
block_h *p = d_free_list;
do {
std::cout << "-------------------------------\n";
std::cout << "Block Address: " << (uint64_t)(p) << "\n";
std::cout << "Blocks (32B): " << p->d.size << "\n";
std::cout << "Next: " << (uint64_t)(p->d.next) << "\n";
std::cout << "{The next block is " << (uint64_t)((p->d.next) - (p)) << " bytes away, but we claim the next " << p->d.size * mem_unit_size << " bytes\n";
p = p->d.next;
} while (p != d_free_list);
std::cout << "-------------------------------\n\n\n";
}
bool Static_Allocator::unified () {
if (d_free_list == NULL) {
return false;
}
return ((d_free_list->d.next)->d.next == d_free_list);
}

Confused about K&R implementation of malloc

The code for function malloc() in K&R section 8.7 is below
void *malloc(unsigned nbytes) {
Header *p, *prevp;
Header *moreroce(unsigned);
unsigned nunits;
nunits = (nbytes+sizeof(Header)-1)/sizeof(header) + 1;
if ((prevp = freep) == NULL) { /* no free list yet */
base.s.ptr = freeptr = prevptr = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) { /* exactly */
prevp->s.ptr = p->s.ptr;
} else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void *)(p+1);
}
if (p == freep) /* wrapped around free list */
if ((p = morecore(nunits)) == NULL)
return NULL; /* none left */
}
}
I'm mainly confused by the "allocate tail end" part.
Suppose p->s.size = 5 and nunits = 2. According to the code, we first subtract 2 from p->s.size, advance p by 3, record allocated size at that address and return (void *)(p+1).
Let p' denote p after self-increment and * denote free space. The memory after above operations should look like this:
p * * p' * *
We've actually allocated 2 units of memory, but the remaining free space for p should be 2 instead of 3, since one unit is occupied by the header information for the allocated tail end.
So I think the line
p->s.size -= nunits;
should be replaced by
p->s.size -= nunits + 1;
Have I missed anything?
The answer is in this line
nunits = (nbytes+sizeof(Header)-1)/sizeof(header) + 1;
That line takes the number of bytes requested nbytes, adds sizeof(Header)-1 to round up, divides by sizeof(header) to get the number of units needed to hold nbytes. And finally, it adds 1 to make room for the header. So all the code after that assumes that you're reserving space for nbytes (plus padding if needed) plus the header.

Seemingly unneeded line in K&R C example for malloc?

You can find this code in K&R's The C Programming Language:
void *malloc(unsigned nbytes) {
Header *p, *prevp;
Header *moreroce(unsigned);
unsigned nunits;
nunits = (nbytes+sizeof(Header)-1)/sizeof(header) + 1;
if ((prevp = freep) == NULL) { /* no free list yet */
base.s.ptr = freeptr = prevptr = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) { /* exactly */
prevp->s.ptr = p->s.ptr;
} else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits; /* STRANGE LINE???????? */
}
freep = prevp;
return (void *)(p+1);
}
if (p == freep) /* wrapped around free list */
if ((p = morecore(nunits)) == NULL)
return NULL; /* none left */
}
}
(from http://pelusa.fis.cinvestav.mx/tmatos/LaSumA/LaSumA2_archivos/Supercomputo/The_C_Programming_Language.pdf)
There's basically a linked list, and each node in the list begins with a header indicating the next node and the amount of memory the node has allocated after the header (in multiples of the header size). This linked list tracks where there is free memory.
What I don't understand is why the line at "STRANGE LINE?????" is needed. I understand the first two. We want to give the end of the node to the user, so we shrink the size, and advance p with the new size and give that to the user (+1). But then the third line wants to set the size of the place p points at to the number of units. Why? After the second line p points at a spot in the free memory which doesn't seem to have any meaning. Is this some sort of tricksy optimization I'm unaware of, or is it actually necessary?
Thanks for any help!
The reason you did not understand this line is that it cannot be understood in isolation: from the point of view of malloc, it is useless. This assignment is done in order to support functionality of the corresponding free function, which needs to know the allocated size to add the memory chunk to the free list.
void free(void *ap)
{
Header *bp, *p;
bp = (Header *)ap - 1; /* point to block header */
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
break; /* freed block at start or end of arena */
/* Here is where the saved size comes into play */
if (bp + bp->size == p->s.ptr) { /* join to upper nbr */
bp->s.size += p->s.ptr->s.size;
bp->s.ptr = p->s.ptr->s.ptr;
} else
bp->s.ptr = p->s.ptr;
if (p + p->size == bp) { /* join to lower nbr */
p->s.size += bp->s.size;
p->s.ptr = bp->s.ptr;
} else
p->s.ptr = bp;
freep = p;
}
As you can see, the size is squirreled away in the bytes just prior to what's returned to the caller. This is the header portion that malloc and free use for their "bookkeeping".

Resources