Hey, Mom! The Explanation.

Here's the permanent dedicated link to my first Hey, Mom! post and the explanation of the feature it contains.

Saturday, August 10, 2019

A Sense of Doubt blog post #1633 - Memory Management - a program written in the C language


A Sense of Doubt blog post #1633 - Memory Management - a program written in the C language

Hi there reader,

New feature. Posting my code to my blog to show off both coding skills and my ability to write documentation for code.

In fact, to get all the samples up in time, I may have to post extra posts rather than one a day. I will also have posts for collected code samples and collected writing samples.

I chose this program as my first sample because it's my favorite. As the documentation explains, I wrote a memory management program in the C programming language.

One caveat with this sample: the code is OVER-DOCUMENTED. The documentation here is as much for me as part of my study, to understand all the functions and concepts even years later (as my memory is faulty and C can be somewhat cryptic) as well as helping a user or a future coder who must maintain this code to understand it.

I am considering trimming some of the extra comments that may not be needed or moving them around so that the code is more readable.

For now, this is what I am sharing as it's the original state of the program.

For readers, good news, I managed to preserve the original text editor's state of the code with everything color-identified. The documentation comments are in green. Commands are in blue. Basic code is in black.

For now, this is what it is. And it runs (with the edits for "includes" as noted. Try it.)


// WHAT: ASSIGNMENT TWO - MY MALLOC, CALLOC, REALLOC, AND FREE
// MORE WHAT: a program written in the C programming language
// WHERE: For CS2240 - Trenary
// WHO: Chris Tower and Spider Friends
// WHEN: It is now Monday March, 2 2015 but I have been working on this for weeks.
// HOW: A mix of super soldier serum, mutant x genes, and single malt scotch.
// WHAT THIS PROGRAM DOES: Using system call sbreak, this program replicates versions of the standard library functions malloc, calloc, realloc, and free. Ultimately, using an mreplace program, these functions substitute for the standrad library functions and perform via a series of tester programs. Mine passed! :-)
//DESIGN NOTES AS OF 1503.02 SUBMISSION: Right now, after many hours and weeks of labor, this thing successfully performs all functions but is not as efficient as it could be. Time permitting, I would add a function to split big blocks and fuse together smaller blocks. Originally, I wanted to do a bin system to sort blocks by size, but this is too ambitious at this stage with a third assignment (SHELL) to work on.

// ****************** INCLUDES **********************
//Many includes. All needed? Possibly.
// Includes use "<" and ">" around dot h files but here in html land these disappear, so an edit will be needed to run the program as I substituted "" for the brackets.
#include "stdio.h"
#include "unistd.h"
#include  "sys/types.h"
#include "stdlib.h" // For use of "atoi" and calloc etc.
#include  "fcntl.h"
#include  "sys/stat.h"
#include  "errno.h"
#include  "string.h"
#include  "assert.h"
#include  "apue.h" // except this one uses quotes apparently
// compile program with "error.c"

//******************* Meta Data Header Struct ***********

// Structure for meta-data (header) information added to start of each memory block for record keeping. Size as a type of size_t holds size of block, an int for a boolean flag holds a 0 (zero) when block is free and a 1 (one) when block is used. Two pointers *next and *prev hold pointers to start of next block and previous block. A one element char data [1] array provides a pointer to the end of the metadata header or rather the start of the data block to be used, iE the allocated memory space, but this char is not calculated as part of the size of the meta-data header.
// The struct is type defined to be referred to as simply "meta."

typedef struct _meta {
size_t        size;
int           frflag;
struct _meta         *next;
struct _meta         *prev;
char data [1];
} meta;

// ************************* Variables *******************
// Best to initialize meta head pointer (the pointer to the start of the meta data header at the start of the block to NULL as first time through it is NULL (no allocated blocks yet; no written meta data).
// HEADPTR serves as a pointer of type meta (my struct) to give me a ptr to the first block in the linked list. Could also be global or base pointer.

meta *headptr = NULL;

//********* DEFINES **********************************
/* This is the header size in bytes. */
# define META_SIZE 32

//Explanation of META_SIZE: *  two 8 byte structs, one 8 byte size_t (unsigned long) and a 4 byte int ...not including char data [1]; However, PLEASE NOTE, Meta Size should be 24. If I arranged the variables differently, I could get away with 24. With this ordering of variables, C pads out all the elements to the longest type, which is 8 bytes. I had many alignment problems until I reset META_SIZE this way. And I am leaving it this way because it works, though at some point I may do the science to check the other arranegement, which I believe has to do with moving the int for the free flag (frflag).

// When changing this for the tester, change void *my_free(void*); to void free(void*); so lose the star before the free because the library function does not return a void *free(void*).

//***** MODULE DECLARATIONS**********************
void *my_malloc(size_t);
void *my_free(void*);
void *my_calloc(size_t, size_t);
void *my_realloc(void*, size_t);

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

// The following statements, all of MAIN, run tests on malloc, free, calloc, and realloc using strings, which are copied into the memory space allocated. PLEASE NOTE USE OF STRNCOPY, needs to keep the NULL at the end of the string, so each size of the string given to strncopy is 1 more than the length of the string. For instance, "did it work" is 12 characters in length including spaces but 13 is passed to strncopy so that it grabs the NULL at the end of the string.

//Simply, malloc 32, copy 13 character string into space (12+NULL) and write out string. If this works, I have a successful Malloc at least the first time through the list.
char *string = my_malloc(32);
strncpy(string, "did it work?", 13);
printf("\n The string is = %s\n", string);

//Let's try malloc again with a bigger string. NOTE: need to use a different variable for the string.
char *nextstring = my_malloc(55);
strncpy(nextstring, "it worked again.", 17);
printf("\n The string is = %s\n", nextstring);

//Now, I will perform magic and free the previously allocated block of 55 into which I wrote nextstring. I will pass nextstring to free and free it. I will than malloc 12 into that block, which is 55 as in the above. I should see 55 again in my tester print statement as this is the blocksize.

my_free(nextstring);
char *xtstring = my_malloc(12);
strncpy(xtstring, "bork", 5);
printf("\n The string is = %s\n", xtstring);

//REalloc next. Take the xtstring used above and send it to realloc and ask for another 20 bytes. Then when I test I send in a 21 length string (though the block is much bigger).

xtstring = my_realloc(xtstring, 20);
strncpy(xtstring, "Did the realloc work", 21);
printf("\n The string is = %s\n", xtstring);

// Just another malloc test. I originally had four of them before I added the other functions.
char *tstring = my_malloc(20);
strncpy(tstring, "fourth test!", 13);
printf("\n The string is = %s\n", tstring);

//Lastly, testing calloc, which takes number of elements and blocksize. Elements is just one, for a char, then size is the previous block in tstring, which is 20. Calloc then writes zeroes in that space, so my print tester should have nothing after the equals sign, which it does.
tstring = my_calloc(1, 20);
printf("\n The calloc string is = %s\n", tstring);


} // end bracket of main

void *my_malloc(size_t Blocksize)
{

meta *ptr; // Setting up a ptr to type meta (the struc) in my malloc for use in the malloc process.

if (headptr == NULL) // Checks headptr, if it's NULL then this is the first use of malloc and so I can go ahead and malloc the first block in the list, which is a different process than adding a block to the end of the list or writing into a free block found while examining the list.
{
headptr = sbrk(0); //sbrk(0) gives me a pointer to the current position on the heap, which here is the start of the memory area, which is what I want in a headptr, a pointer to the beginning of the linked list.

ptr = sbrk (META_SIZE + Blocksize); // here's the sbrk of the memory for malloc
if (ptr == (void *) -1) //*Error check on the system call, sbrk fails/
{
return (NULL );
}
// Now, I write all the fields in the meat data header using the meta *ptr I created above. Size is Blocksize. This is the value passed in from main. Frflag is the boolean marking free (0) or used (1), and so I mark used here. Then the next and previous ptrs are empty as this is the first memory block created.

ptr -> size = Blocksize;
ptr -> frflag = 1;
ptr -> next = NULL;
ptr -> prev = NULL;

} // end bracket for if statement checking headptr for FIRST TIME THROUGH


else          // ELSE if not first time through then do the following instead.
{

// *select pointer is created here simply to step through blocks from meta data header to header, so headptr is selected to start at the beginning of the linked list. *temp is created to store each select as I iterate through the list to ensure I examine the last block of memory. I had trouble with this in my initial design because my while loop checked for next pointer being NULL, which it will be with the last item in the list. But in that design, the while bailed out before ite checked that memory block, which is bad. So I switched to a do-while loop, which doesn't check test to exit the loop until after iterating.

meta *select = headptr;
meta *temp;

do {

if (select -> size > Blocksize && select -> frflag == 0) 
// Yay!! Free block found because frflag is zero. Also, checking for size being big enough. Did not yet bother with equals to or greater than.
{
      
select -> frflag = 1; // Since I found a free block, I am using it, so I set the flag to used. I do this in my SELECT ptr as this is the struc I am using as I iterate.

// Printer test statement. Here I print the pointer address, as in the memory location of the beginning of the entire block of memory. Then I print the position of the beginning of the data area allocated, after the meta data header. I use that char data[1] that I created for this position. Then just for fun, I print the blocksize.

fprintf(stderr, "pointer address %lu\t data start %lu\t size %d\n", (unsigned long int)select, (unsigned long int)select -> data, (int)select -> size);

return select -> data; // Returns a pointer to start of the data block for use (after the meta-data header). Again, this is that char data[1].

} // end bracket of if checking size & for checking free flag

temp = select; // Store select in temp to ensure I look at all blocks in the list.
select = select -> next; // iterate, move select to the next block of data

} while (select != NULL); // end bracket for while loop
// The while loop iterates until the select is empty, which is will be after I have checked all blocks to find free ones. If I find no free blocks, then I have to sbrk a new one at the end of the list, which is what the next statements do. If the program finds a free block, it returns with the pointer so it exits the while loop thta way and does not get here.

temp -> next = sbrk(0); // Use sbrk(0) to get current heap position and put that in the next field of the temp struc.

//sbrk some memory just like before with meta size added for header data. I use ptr here as before because this the generic reusable ptr for allocating and filling the header with data.

ptr = sbrk (META_SIZE + Blocksize);

if (ptr == (void *) -1) //*Error check on the system call, sbrk fails/
{
return (NULL );
}

// Fill data fields as before though this time I put temp into previous because I was saving that value gained from sbrk(0). Adding to the end of the list, so next is NULL.

ptr -> size = Blocksize;
ptr -> frflag = 1;
ptr -> next = NULL;
ptr -> prev = temp;

} // end bracket of else not first time statement

// Tester as before.
fprintf(stderr, "pointer address %lu\t data start %lu\t size %d\n", (unsigned long int)ptr, (unsigned long int)ptr -> data, (int)ptr -> size);

return ptr -> data; // Returns a pointer to start of the data block for use (after the meta-data header).

} // end bracket of my_malloc

void *my_free(void *ptr)
{

// Though complicated looking, this next statement simply backs up the pointer by the size of the meta data; however, it uses complicated casting of the ptr into a char * and that plus the subtraction all into a meta * and all that in a meta *p. Then I access just p (that meta *p) to switch the free flag to zero for free. That's it for FREE!
meta *p = (meta *)((char *)ptr - META_SIZE);

p -> frflag = 0;

} // end bracket for free module

void *my_calloc(size_t Elements, size_t Blocksize)
{

//Calloc receives number of Elements, I pas just one char, and the blocksize to then write zeroes. Size is modied with multiplication of these two values and passed to malloc, which returns a ptr to the memory. Then I use  memset to write zeroes of that size to that space starting at ptr and then return the ptr to the calling function, which is main.

  size_t size = Elements * Blocksize;
  void *ptr = my_malloc(size);
  memset(ptr, 0, size);
  return ptr;

} // end bracket for calloc module

void *my_realloc(void *ptr, size_t Blocksize)
{
if (!ptr){
return my_malloc(Blocksize);
// pointer is NULL (no pointer); there's no memory to realloc, so just malloc.
}
//This next statement is the same as the one in free to move the pointer back by the meta data size.
meta *p = (meta *)((char *)ptr - META_SIZE);

//Here I did do a greater or equals for reallocing.
if (p -> size >= Blocksize)
{
       //print tester as before but note use of p here as that's the current pointer I am working with.
       fprintf(stderr, "pointer address %lu\t data start %lu\t size %d\n", (unsigned long int)p, (unsigned long int)p -> data, (int)p -> size);
       return ptr;
       // Enough space exists, so return the pointer.
}

//Otherwise, if not enough space, then I need to realloc. Malloc new space and then free old space. then copy old data to new space.
void *new_ptr; // Need a new ptr for this work.
new_ptr = my_malloc(Blocksize); //Do the malloc.
if (!new_ptr) //If there is no newptr then error.
{
return NULL;  // need to set errono on failure.
}
// copy the memory new ptr is the new space or destination, ptr is the old space and it gets freed next, then use p, which is set all the way at the top of the function with the meta *p to set the new size of the realloced space.
memcpy(new_ptr, ptr, p -> size);
free(ptr);
return new_ptr; // returns the new ptr for the new space.
}
// END of the CODE for "memmanage.c"


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

- Bloggery committed by chris tower - 1908.10 - 10:10

- Days ago = 1498 days ago

- New note - On 1807.06, I ceased daily transmission of my Hey Mom feature after three years of daily conversations. I plan to continue Hey Mom posts at least twice per week but will continue to post the days since ("Days Ago") count on my blog each day. The blog entry numbering in the title has changed to reflect total Sense of Doubt posts since I began the blog on 0705.04, which include Hey Mom posts, Daily Bowie posts, and Sense of Doubt posts. Hey Mom posts will still be numbered sequentially. New Hey Mom posts will use the same format as all the other Hey Mom posts; all other posts will feature this format seen here.

No comments: