I'm working on Exercise 27.4 from the 4th edition of Programming in Lua. I am using Lua 5.4.4, and my solution is the following.
/*
Exercise 27.4: Write a library that allows a script to limit the total amount of memory used
by its Lua state. It may offer a single function, `setlimit`, to set that limit.
The library should set its own allocation function. This function, before calling the
original allocator, checks the total memory in use and returns NULL if the requested memory
exceeds the limit.
(Hint: the library can use the user data of the allocation function to keep its state:
the byte count, the current memory limit, etc.; remember to use the original user data
when calling the original allocation function.)
*/
#include "lauxlib.h"
#include <stdlib.h>
typedef struct {
void *o_ud;
lua_Alloc o_alloc;
size_t mem;
size_t limit;
} memlimit_ud;
static void *memlimit_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
memlimit_ud *m_ud = ud;
size_t mem = m_ud->mem;
if (mem + nsize - osize > m_ud->limit) {
return NULL;
}
void *res = m_ud->o_alloc(m_ud->o_ud, ptr, osize, nsize);
if (res) m_ud->mem += nsize - osize;
return res;
}
static int l_setlimit(lua_State *L) {
int n = luaL_checkinteger(L, 1) * 1024;
memlimit_ud *ud;
lua_getallocf(L, (void **) &ud);
ud->limit = n;
return 0;
}
static const struct luaL_Reg memlimit[] = {
{"setlimit", l_setlimit},
{ NULL, NULL},
};
int luaopen_memlimit(lua_State *L) {
memlimit_ud *ud = lua_newuserdata(L, sizeof(memlimit_ud));
luaL_ref(L, LUA_REGISTRYINDEX);
ud->o_alloc = lua_getallocf(L, &ud->o_ud);
ud->mem = 0;
ud->limit = -1;
lua_setallocf(L, memlimit_alloc, ud);
luaL_newlib(L, memlimit);
return 1;
}
However, it throws a segment fault when the script ends. I found this question, and have tried to change lua_newuserdata to malloc, but it still doesn't work. While debugging, I noticed that it was trying to call memlimit_alloc before it crashed, but the function seemed like didn't exist (the former calls to this function were successful).
How can I fix this?
Related
I'm trying to complete an assignment for an operating systems course, Here.
I had a question from the assignment:
What is the purpose of userptr_t?
When I searched the source code for userptr_t, Here, I found this:
/*
* Define userptr_t as a pointer to a one-byte struct, so it won't mix
* with other pointers.
*/
struct __userptr { char _dummy; };
typedef struct __userptr *userptr_t;
typedef const struct __userptr *const_userptr_t;
I can't get to understand completely the use of it, can anyone explain what is the purpose of this type?
It's used here for example in the file copyinout.c in the functions copyin, copyout, copyinstr, copyoutstr and other functions:
#include <types.h>
#include <kern/errno.h>
#include <lib.h>
#include <setjmp.h>
#include <thread.h>
#include <current.h>
#include <vm.h>
#include <copyinout.h>
/*
* User/kernel memory copying functions.
*
* These are arranged to prevent fatal kernel memory faults if invalid
* addresses are supplied by user-level code. This code is itself
* machine-independent; it uses the machine-dependent C setjmp/longjmp
* facility to perform recovery.
*
* However, it assumes things about the memory subsystem that may not
* be true on all platforms.
*
* (1) It assumes that user memory is mapped into the current address
* space while running in the kernel, and can be accessed by just
* dereferencing a pointer in the ordinary way. (And not, for example,
* with special instructions or via special segment registers.)
*
* (2) It assumes that the user-space region of memory is contiguous
* and extends from 0 to some virtual address USERSPACETOP, and so if
* a user process passes a kernel address the logic in copycheck()
* will trap it.
*
* (3) It assumes that access to user memory from the kernel behaves
* the same way as access to user memory from user space: for
* instance, that the processor honors read-only bits on memory pages
* when in kernel mode.
*
* (4) It assumes that if a proper user-space address that is valid
* but not present, or not valid at all, is touched from the kernel,
* that the correct faults will occur and the VM system will load the
* necessary pages and whatnot.
*
* (5) It assumes that the machine-dependent trap logic provides and
* honors a tm_badfaultfunc field in the thread_machdep structure.
* This feature works as follows: if an otherwise fatal fault occurs
* in kernel mode, and tm_badfaultfunc is set, execution resumes in
* the function pointed to by tm_badfaultfunc.
*
* This code works by setting tm_badfaultfunc and then copying memory
* in an ordinary fashion. If these five assumptions are satisfied,
* which is the case for many ordinary CPU types, this code should
* function correctly. If the assumptions are not satisfied on some
* platform (for instance, certain old 80386 processors violate
* assumption 3), this code cannot be used, and cpu- or platform-
* specific code must be written.
*
* To make use of this code, in addition to tm_badfaultfunc the
* thread_machdep structure should contain a jmp_buf called
* "tm_copyjmp".
*/
/*
* Recovery function. If a fatal fault occurs during copyin, copyout,
* copyinstr, or copyoutstr, execution resumes here. (This behavior is
* caused by setting t_machdep.tm_badfaultfunc and is implemented in
* machine-dependent code.)
*
* We use the C standard function longjmp() to teleport up the call
* stack to where setjmp() was called. At that point we return EFAULT.
*/
static
void
copyfail(void)
{
longjmp(curthread->t_machdep.tm_copyjmp, 1);
}
/*
* Memory region check function. This checks to make sure the block of
* user memory provided (an address and a length) falls within the
* proper userspace region. If it does not, EFAULT is returned.
*
* stoplen is set to the actual maximum length that can be copied.
* This differs from len if and only if the region partially overlaps
* the kernel.
*
* Assumes userspace runs from 0 through USERSPACETOP-1.
*/
static
int
copycheck(const_userptr_t userptr, size_t len, size_t *stoplen)
{
vaddr_t bot, top;
*stoplen = len;
bot = (vaddr_t) userptr;
top = bot+len-1;
if (top < bot) {
/* addresses wrapped around */
return EFAULT;
}
if (bot >= USERSPACETOP) {
/* region is within the kernel */
return EFAULT;
}
if (top >= USERSPACETOP) {
/* region overlaps the kernel. adjust the max length. */
*stoplen = USERSPACETOP - bot;
}
return 0;
}
/*
* copyin
*
* Copy a block of memory of length LEN from user-level address USERSRC
* to kernel address DEST. We can use memcpy because it's protected by
* the tm_badfaultfunc/copyfail logic.
*/
int
copyin(const_userptr_t usersrc, void *dest, size_t len)
{
int result;
size_t stoplen;
result = copycheck(usersrc, len, &stoplen);
if (result) {
return result;
}
if (stoplen != len) {
/* Single block, can't legally truncate it. */
return EFAULT;
}
curthread->t_machdep.tm_badfaultfunc = copyfail;
result = setjmp(curthread->t_machdep.tm_copyjmp);
if (result) {
curthread->t_machdep.tm_badfaultfunc = NULL;
return EFAULT;
}
memcpy(dest, (const void *)usersrc, len);
curthread->t_machdep.tm_badfaultfunc = NULL;
return 0;
}
/*
* copyout
*
* Copy a block of memory of length LEN from kernel address SRC to
* user-level address USERDEST. We can use memcpy because it's
* protected by the tm_badfaultfunc/copyfail logic.
*/
int
copyout(const void *src, userptr_t userdest, size_t len)
{
int result;
size_t stoplen;
result = copycheck(userdest, len, &stoplen);
if (result) {
return result;
}
if (stoplen != len) {
/* Single block, can't legally truncate it. */
return EFAULT;
}
curthread->t_machdep.tm_badfaultfunc = copyfail;
result = setjmp(curthread->t_machdep.tm_copyjmp);
if (result) {
curthread->t_machdep.tm_badfaultfunc = NULL;
return EFAULT;
}
memcpy((void *)userdest, src, len);
curthread->t_machdep.tm_badfaultfunc = NULL;
return 0;
}
/*
* Common string copying function that behaves the way that's desired
* for copyinstr and copyoutstr.
*
* Copies a null-terminated string of maximum length MAXLEN from SRC
* to DEST. If GOTLEN is not null, store the actual length found
* there. Both lengths include the null-terminator. If the string
* exceeds the available length, the call fails and returns
* ENAMETOOLONG.
*
* STOPLEN is like MAXLEN but is assumed to have come from copycheck.
* If we hit MAXLEN it's because the string is too long to fit; if we
* hit STOPLEN it's because the string has run into the end of
* userspace. Thus in the latter case we return EFAULT, not
* ENAMETOOLONG.
*/
static
int
copystr(char *dest, const char *src, size_t maxlen, size_t stoplen,
size_t *gotlen)
{
size_t i;
for (i=0; i<maxlen && i<stoplen; i++) {
dest[i] = src[i];
if (src[i] == 0) {
if (gotlen != NULL) {
*gotlen = i+1;
}
return 0;
}
}
if (stoplen < maxlen) {
/* ran into user-kernel boundary */
return EFAULT;
}
/* otherwise just ran out of space */
return ENAMETOOLONG;
}
/*
* copyinstr
*
* Copy a string from user-level address USERSRC to kernel address
* DEST, as per copystr above. Uses the tm_badfaultfunc/copyfail
* logic to protect against invalid addresses supplied by a user
* process.
*/
int
copyinstr(const_userptr_t usersrc, char *dest, size_t len, size_t *actual)
{
int result;
size_t stoplen;
result = copycheck(usersrc, len, &stoplen);
if (result) {
return result;
}
curthread->t_machdep.tm_badfaultfunc = copyfail;
result = setjmp(curthread->t_machdep.tm_copyjmp);
if (result) {
curthread->t_machdep.tm_badfaultfunc = NULL;
return EFAULT;
}
result = copystr(dest, (const char *)usersrc, len, stoplen, actual);
curthread->t_machdep.tm_badfaultfunc = NULL;
return result;
}
/*
* copyoutstr
*
* Copy a string from kernel address SRC to user-level address
* USERDEST, as per copystr above. Uses the tm_badfaultfunc/copyfail
* logic to protect against invalid addresses supplied by a user
* process.
*/
int
copyoutstr(const char *src, userptr_t userdest, size_t len, size_t *actual)
{
int result;
size_t stoplen;
result = copycheck(userdest, len, &stoplen);
if (result) {
return result;
}
curthread->t_machdep.tm_badfaultfunc = copyfail;
result = setjmp(curthread->t_machdep.tm_copyjmp);
if (result) {
curthread->t_machdep.tm_badfaultfunc = NULL;
return EFAULT;
}
result = copystr((char *)userdest, src, len, stoplen, actual);
curthread->t_machdep.tm_badfaultfunc = NULL;
return result;
}
This looks like a strong typedef, i.e. a typedef intended to increase type safety by avoiding unintended uses/conversions of the wrapped data.
In your context, most likely intended to differentiate kernel pointers from user space pointers (usually mapped via the MMU).
I'm trying to use a "fixed memory scheme" and pre-allocate memory & reuse it via alloc, init, free fashion as many times as possible.
free() will called at shutdown only, but I want to test many iterations.
Although I call my alloc function bn_tree_alloc_node_space_heap() & init function bn_tree_init_node_heap(), I can only call free function bn_tree_free_node_space once.
Below is a complete reproducible snippet of my memory management, maint_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#define BN_TREE_HEAP_SIZE 100
/*variables internal*/
typedef struct bntree_internals;
/*bn_tree_node is single bntree_t leaf*/
typedef struct bn_tree_node {
struct bn_tree_node* left;
struct bn_tree_node* right;
float* dataset;
float distance_to_neighbor;
int visited;
int heap_index;
} bn_tree_node;
/*tree*/
typedef struct {
/*in order to keep track of the bn-tree root*/
bn_tree_node* _root;
/*pointer to internal variables struct*/
struct bntree_internals* _internals;
} bntree_t;
/*bn tree leaf nodes heap*/
bn_tree_node* node_processing_space = NULL;
/*leaf nodes*/
void bn_tree_alloc_node_space_heap(int max_dimensions);
bn_tree_node*
get_pre_allocated_bn_tree_node_heap();
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions);
void bn_tree_free_node_space(bn_tree_node* nodes);
int main(int argc, char** argv) {
/*PROBLEM:called the alloc,init,free cycle several times, problem,
getting seg fault on 2nd call of free()*/
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
return (EXIT_SUCCESS);
}
void bn_tree_alloc_node_space_heap(int max_dimensions) {
if (NULL == node_processing_space) {
node_processing_space = (bn_tree_node*) calloc(BN_TREE_HEAP_SIZE, sizeof (bn_tree_node));
//TODO: bn_tree_set_k_dimensions (max_dimensions);
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
node_processing_space[i].dataset = (float*) calloc(max_dimensions, sizeof (float));
}
//bn_heap_tail_index = bn_heap_head_index = 0;
}
}
bn_tree_node* get_pre_allocated_bn_tree_node_heap() {
return node_processing_space;
}
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions) {
int i = 0;
int c = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
/*reset values */
if (NULL != nodes[i].dataset) {
c = 0;
for (; c < max_dimensions; c++) {
nodes[i].dataset[c] = FLT_MIN;
}
}
nodes[i].visited = 0;
nodes[i].distance_to_neighbor = FLT_MAX;
nodes[i].left = NULL;
nodes[i].right = NULL;
nodes[i].heap_index = -1;
}
}
/*PROBLEM is subsequent call to free(), but if I alloc again why cant I free again?*/
void bn_tree_free_node_space(bn_tree_node* nodes) {
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if (nodes[i].dataset) {
free(nodes[i].dataset);
}
}
free(nodes);
nodes = NULL;
}
Here is the output that I expect/want:
alloc
init
free
alloc
init
free
alloc
init
free
alloc
init
free
But Im getting this output/error:
alloc
init
free
alloc
init
double free or corruption (!prev)
Aborted (core dumped)
How can fix this?
Can't I do alloc,init,free as many times as I want (as long as I called alloc before free) OR I can do only alloc() once, then many init(), free() once?
Thanks a million & please be kind enough to provide concise answers with minimal changes.
The problem is that your bn_tree_free_node_space function takes, as its argument, a copy of the pointer variable - that is, you are passing the pointer by value - thus, the line nodes = NULL; at the end of that function only sets the local variable to NULL and does not change the value of the node_processing_space variable.
To fix this (with minimal changes to your code logic1), you need to pass that function a pointer to the pointer, and dereference that in the function. So, your function should look like this:
void bn_tree_free_node_space(bn_tree_node** nodes) // Argument is pointer-to-pointer
{
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if ((*nodes)[i].dataset) { // Now we need to use (*nodes) to get the underlying pointer
free((*nodes)[i].dataset); // ... same here
}
}
free(*nodes); /// ... and here
*nodes = NULL;
}
You will, of course, also need to change the function prototype (just before your main) to match the new definition:
void bn_tree_free_node_space(bn_tree_node** nodes); // Must match definition!
Fruther, you will (clearly) need to change the calls to that function to pass the address of the node_processing_space pointer:
bn_tree_free_node_space(&node_processing_space); // Likewise for the other 3 calls!
Feel free to ask for further clarification and/or explanation.
1 EDIT: There are other ways (some may argue better ways) to implement your system, and also other 'minor' issues in your code. However, you did explicitly ask for "concise answers with minimal changes," so I have endeavoured to comply with that request!
I have a function that initializes a struct that contains nested structs and arrays.
While initializing the struct I have multiple calls to calloc.
Refer to code bellow:
typedef struct
{
int length;
uint8_t *buffer;
} buffer_a;
typedef struct
{
int length;
uint8_t *buffer;
int *second_buffer_size;
uint8_t **second_buffer;
} buffer_b;
typedef struct
{
int max_length;
buffer_a *buffer_in;
buffer_b *buffer_out;
} state_struct;
state_struct *init(int size, int elements) {
size_t struct_size = sizeof(state_struct);
state_struct *s = (state_struct*) calloc(struct_size, struct_size);
log("Building state with length %d", size);
s->max_length = size;
size_t buffer_in_size = s->max_length * sizeof(buffer_a);
s->buffer_in = (buffer_a*) calloc(buffer_in_size, buffer_in_size);
size_t buffer_out_size = s->max_length * sizeof(buffer_b);
s->buffer_out = (buffer_b*) calloc(buffer_out_size, buffer_out_size);
log("Allocated memory for both buffers structs");
for (int i = 0; i < s->max_length; ++i) {
size_t buf_size = elements * sizeof(uint8_t);
s->buffer_in[i].buffer = (uint8_t*) calloc(buf_size, buf_size);
s->buffer_in[i].length = -1;
log(s, "Allocated memory for in buffer");
s->buffer_out[i].buffer = (uint8_t*) calloc(buf_size, buf_size);
s->buffer_out[i].length = -1;
log(s, "Allocated memory for out buffer");
size_t inner_size = elements * elements * sizeof(uint8_t);
size_t inner_second_buffer_size = elements * sizeof(int);
s->buffer_out[i].second_buffer = (uint8_t**) calloc(inner_size, inner_size);
s->buffer_out[i].second_buffer_size = (int*) calloc(inner_second_buffer_size, inner_second_buffer_size);
log(s, "Allocated memory for inner buffer");
}
return s;
}
Logs just before the for loop are printed but the program crashes and the first log statement inside the loop does not get printed out.
Why is this happening?
So this may not be an answer to your question, but here goes:
When I ran this code (on Ubuntu, gcc 7.4), and replaced all the log functions with printf, it finished succesfuly. I suspect the problem might be in the way you use the log function. You specify that it works up until the first log call inside the loop. You didn't specify what the log function does, or whether it is a function or just a macro wrapper for printf, but you call it in a different manner inside the loop - the first parameter is *state_struct rather than a format string.
Also, the way you call calloc seems to be semantically incorrect. The first parameter should be the number of blocks of second parameter size you want to allocate (presumably 1 in this case)
const static int VECTOR_BASIC_LENGTH = 20;
struct m_vector
{
void* my_vector;
size_t my_capacity;
size_t my_head;
};
typedef struct m_vector Vector;
Vector creat_Vector(size_t size,void *judge)
{
Vector _vector;
size = size?size:VECTOR_BASIC_LENGTH;
_vector.my_capacity = size;
_vector.my_head = 0;
//How I write the following two lines
_vector.my_vector = malloc(sizeof(*judge) * size);
return _vector;
}
The type of judge is uncertain,so I pass a void pointer as a parameters.I need the size of *judge to allocate memory to _vector.my_vector,for example if I use:
int *a;
creat_Vector(5,a);
I want the following line:
_vector.my_vector = malloc(sizeof(*judge)*size);
is equal to:
_vector.my_vector = malloc(sizeof(*a)*5);
How could I achieve this function.Using pure C
There is a forbidden thing done in your code.
You statically (at compile time) allocate/declare a local _vector of type Vector in your function creat_Vector. Then you return this object to the outside world. However, when you are exiting your function, all local data is dead. So, you should absolutely rethink this.
One suggestion would be:
int init_Vector(Vector* _vect, size_t size, unsigned int ptr_size)
{
size = size?size:VECTOR_BASIC_LENGTH;
_vect->my_capacity = size;
_vect->my_head = 0;
_vect->my_vector = malloc(size*ptr_size);
if (_vect->my_vector) {
return 0;
}
return 1;
}
Then:
Vector _vector;
char *a;
if (init_Vector(&_vector, 5, sizeof(char)) == 0) {
printf("Success!\n");
}
else {
printf("Failure!\n");
/* treat appropriately (return error code/exit) */
}
/* do whatever with a (if needed) and _vector*/
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I have a homework for tomorrow, and am asked to make a dynamic (resizable) stack, that saves chars.
This thing have been driving me crazy, been on it all day. I did it using the stdlib and it was done. But can't seem to figure out how to allocate memory without malloc.. help would be really appreciated. These are some code snippets that I have used (with stdlib):
struct STACK
{
int size;
int capacity;
char *memory;
int folder_number;
};
typedef struct STACK stack;
My main starts like this:
int main()
{
stack mystack;
stack_init(&mystack);
Initializing the stack function:
void stack_init(stack *s)
{
s->size=1;
s->capacity=INITIAL_CAPACITY;
s->memory=malloc(s->capacity);
s->memory[0]='\0';
s->folder_number=1;
}
I have all kind of functions for my program, and when I insert new char into the stack, I check if I have reached the max capacity, if so , I call the following func:
void double_memory(stack* s)
{
char *tmp = malloc((s->capacity)*2);
for (int i=0; i<(s->capacity); i++)
tmp[i]=s->memory[i];
free(s->memory);
s->capacity *= 2;
s->memory=tmp;
}
Now, I have been trying for at least 6 hours in a row, to try and figure out how to do it otherwise (without using stdlib.h), searched a lot on google, with no success. any help or advice would be really!! appreciated.
Thank you very much in advance.
EDIT: I have started at university around 2 months ago, I don't know about platforms..etc, the last 2 things we learned were pointers, and small information about malloc, b4 that we simply learned about functions hhh..., and before that, VERRY basic coding..
Use a file:
#include <stdio.h>
struct stack {
FILE * fp;
} stack = {NULL} ;
#define ZENAME "zestack"
void stack_init (struct stack *sp);
void stack_exit (struct stack *sp);
void stack_push (struct stack *sp, int ch);
int stack_pop (struct stack *sp);
void stack_init (struct stack *sp)
{
if (sp->fp) fclose(sp->fp);
sp->fp = fopen(ZENAME , "wb+" );
if (!sp->fp) fprintf(stderr, "Fopen(%s) failed\n", ZENAME );
}
void stack_exit (struct stack *sp)
{
if (sp->fp) fclose(sp->fp);
sp->fp = NULL;
}
void stack_push (struct stack *sp, int ch)
{
fputc(ch, sp->fp);
}
int stack_pop (struct stack *sp)
{
int ch;
long int oldpos, newpos;
if (!sp->fp) return -1;
oldpos = fseek(sp->fp, -1, SEEK_CUR);
if (oldpos < 0) { stack_exit (sp); return EOF; }
ch = fgetc(sp->fp);
newpos = fseek(sp->fp, -1, SEEK_CUR);
fprintf(stderr, "Oldpos = %ld Newpos = %ld\n", oldpos, newpos );
return ch;
}
int main(void)
{
int ch;
stack_init ( & stack);
stack_push ( & stack, '1');
stack_push ( & stack, '2');
stack_push ( & stack, '3');
stack_push ( & stack, '4');
while(1) {
ch = stack_pop( &stack);
fprintf(stdout, "Pop = '%c' (0x%x)\n" , ch, (unsigned) ch) ;
if (ch < 0) break;
}
return 0;
}
malloc on most POSIX-compliant platforms simply uses mmap with anonymous mapping under the hood ... so you could call that function instead using the MAP_ANONYMOUS flag to allocate memory into a memory pool for use by your stack implementation. Here is a link to the LINUX man-page for mmap: http://www.kernel.org/doc/man-pages/online/pages/man2/mmap.2.html
For efficient use of your allocated memory pool, I would suggest setting up some type of simple linked-list memory manager ... in other words you want to call mmap once to allocate a large chunk of memory, and then use your own user-defined malloc and free calls to manage the memory pool.
UPDATE: From your comments you're now saying that you can't use any external libraries. Therefore your only other option is to designate a static array for your memory pool since allocating memory dynamically from the heap at runtime requires intervention from the OS, and that can't be done without a system call.
Here is a simple linked-list memory manager system you could use (note: I haven't debugged it, but since it's homework, that's your job :-)
static unsigned char heap[MEMORY_POOL_SIZE];
typedef struct memory_block
{
unsigned long size_bytes;
unsigned char block[];
} memory_block;
typedef struct free_block
{
unsigned long size_bytes;
struct free_block* next;
} free_block;
//initialize our memory pool free-store
static char free_list_initialized = 0;
static free_block* free_list_head = NULL;
void* malloc(unsigned long size_bytes)
{
//initialize the free-store if it's never been used before
if (!free_list_initialized)
{
free_list_head = (free_block*)&heap[0];
free_list_head->size_bytes = MEMORY_POOL_SIZE - sizeof(memory_block);
free_list_head->next = NULL;
free_list_initialized = 1;
}
//search the free-list for a memory block that is at least size_bytes
free_block* current = free_list_head;
free_block* prev = NULL;
while (current != NULL)
{
if (current->size_bytes >= (size_bytes + sizeof(free_block)))
break;
prev = current;
current = current->next;
}
//did we reach the end of the list without finding anything?
if (current == NULL)
return NULL; //out-of-memory!
memory_block* temp = NULL;
//trim the block of memory if the one we found is larger than the requested size
if (current->size_bytes > (size_bytes + sizeof(free_block)))
{
temp = (memory_block*)current;
current = (free_block*)((unsigned char*)current + size_bytes + sizeof(memory_block));
current->size_bytes = current->size_bytes - (size_bytes + sizeof(memory_block));
temp->size_bytes = size_bytes;
if (prev != NULL)
prev->next = current;
}
else
{
prev->next = current->next;
temp = (memory_block*)current;
}
return (void*)&temp->block;
}
void free(void* ptr)
{
free_block* temp = (free_block*)((unsigned char*)ptr - sizeof(unsigned long));
temp->next = free_list_head;
free_list_head = temp;
return;
}