Related
I'm doing a custom malloc. I did a very simple one but now I'm trying to merge and split blocks in order to improve the efficiency of calls to sbrk(). when I try to execute a custom program with not many mallocs it works perfectly. But as soon as I try more mallocs or for example the command ls after some successful allocations, it ends giving a weird segmentation fault (core dumped) when calling the split function.
Any help or hint would be greatly appreciated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "struct.h"
static p_meta_data first_element = NULL;
static p_meta_data last_element = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define ALIGN8(x) (((((x)-1)>>3)<<3)+8)
#define MAGIC 0x87654321
void *malloc(size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes);
p_meta_data request_space(size_t size_bytes);
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2);
void split(p_meta_data meta_data, size_t size_bytes);
void free(void *ptr);
void *calloc(size_t num_bytes, size_t num_blocs);
void *realloc(void *ptr, size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes) {
p_meta_data current = first_element;
while (current && !(current->available && current->size_bytes >= size_bytes)){
fprintf(stderr, " %zu libre %d\n", current->size_bytes, current->available);
current = current->next;
}
if (current == NULL) {
fprintf(stderr, "null\n" );
} else {
fprintf(stderr, "%zu libre %d\n", current->size_bytes, current->available);
}
return current;
}
p_meta_data request_space(size_t size_bytes) {
if (size_bytes < 122880) {
size_bytes = 122880;
fprintf(stderr, "request %zu\n", size_bytes);
}
p_meta_data meta_data;
meta_data = (void *)sbrk(0);
if (sbrk(SIZE_META_DATA + size_bytes) == (void *)-1)
return (NULL);
meta_data->size_bytes = size_bytes;
meta_data->available = 0;
meta_data->magic = MAGIC;
meta_data->next = NULL;
meta_data->previous = NULL;
return meta_data;
}
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2) {
if (!meta_data1 || !meta_data2) {
return NULL;
}
meta_data1->size_bytes = meta_data1->size_bytes + SIZE_META_DATA + meta_data2->size_bytes;
meta_data1->next = meta_data2->next;
if (last_element == meta_data2) {
fprintf(stderr, "gleich\n");
last_element = meta_data1;
}
meta_data2 = NULL;
return meta_data1;
}
void free(void *ptr) {
p_meta_data meta_data;
if (!ptr)
return;
pthread_mutex_lock(&mutex);
meta_data = (p_meta_data)(ptr - SIZE_META_DATA);
if (meta_data->magic != MAGIC) {
fprintf(stderr, "ERROR free: value of magic not valid\n");
exit(1);
}
meta_data->available = 1;
fprintf(stderr, "Free at %x: %zu bytes\n", meta_data, meta_data->size_bytes);
p_meta_data meta_data_prev, meta_data_next;
meta_data_prev = meta_data->previous;
meta_data_next = meta_data->next;
if (meta_data_prev && meta_data_prev->available) {
meta_data = merge(meta_data_prev, meta_data);
}
if (meta_data_next && meta_data_next->available) {
meta_data = merge(meta_data, meta_data_next);
}
pthread_mutex_unlock(&mutex);
}
void split(p_meta_data meta_data, size_t size_bytes) {
if (!meta_data) {
fprintf(stderr, "no deberia entrar\n");
return;
}
p_meta_data meta_data2;
size_t offset = SIZE_META_DATA + size_bytes;
meta_data2 = (p_meta_data)(meta_data + offset);
fprintf(stderr, "size of metadata %d", meta_data->size_bytes - size_bytes - SIZE_META_DATA);
meta_data2->size_bytes = meta_data->size_bytes - size_bytes - SIZE_META_DATA;
meta_data2->available = 1;
meta_data2->magic = MAGIC;
meta_data2->previous = meta_data;
meta_data2->next = meta_data->next;
if (meta_data == last_element) {
last_element = meta_data2;
}
meta_data->size_bytes = size_bytes;
meta_data->next = meta_data2;
return;
}
void *malloc(size_t size_bytes) {
void *p, *ptr;
p_meta_data meta_data;
if (size_bytes <= 0) {
return NULL;
}
size_bytes = ALIGN8(size_bytes);
fprintf(stderr, "Malloc %zu bytes\n", size_bytes);
// Bloquegem perque nomes hi pugui entrar un fil
pthread_mutex_lock(&mutex);
meta_data = search_available_space(size_bytes);
if (meta_data) { // free block found
fprintf(stderr, "FREE BLOCK FOUND---------------------------------------------------\n");
meta_data->available = 0; //reservamos el bloque
} else { // no free block found
meta_data = request_space(size_bytes); //pedimos más espacio del sistema
if (!meta_data) //si meta_data es NULL (es decir, sbrk ha fallado)
return (NULL);
if (last_element) // we add the new block after the last element of the list
last_element->next = meta_data;
meta_data->previous = last_element;
last_element = meta_data;
if (first_element == NULL) // Is this the first element ?
first_element = meta_data;
}
fprintf(stderr, "die differenz %zu\n", meta_data->size_bytes - size_bytes);
if ((meta_data->size_bytes - size_bytes) > 12288) {
split(meta_data, size_bytes);
fprintf(stderr,"call split\n");
}
p = (void *)meta_data;
// Desbloquegem aqui perque altres fils puguin entrar
// a la funcio
pthread_mutex_unlock(&mutex);
// Retornem a l'usuari l'espai que podra fer servir.
ptr = p + SIZE_META_DATA; //p es puntero al inicio de meta_data, y ptr es el puntero al inicio del bloque de datos en sí (justo después de los metadatos)
return ptr;
}
void *calloc(size_t num_bytes, size_t num_blocs) {
size_t mem_to_get = num_bytes * num_blocs;
void *ptr = malloc(mem_to_get);
if (ptr == NULL) {
return ptr;
} else {
memset(ptr, 0, mem_to_get);
return ptr;
}
}
void *realloc(void *ptr, size_t size_bytes) {
fprintf(stderr, "realloc\n");
if (ptr == NULL) {
return malloc(size_bytes);
} else {
p_meta_data inic_bloc = (p_meta_data )(ptr - SIZE_META_DATA);
if (inic_bloc->size_bytes >= size_bytes) {
return ptr;
} else {
void *new_p = malloc(size_bytes);
memcpy(new_p, ptr, inic_bloc->size_bytes);
inic_bloc->available = 1;
return new_p;
}
}
}
where struct.h is:
#include <stddef.h>
#include <unistd.h>
#define SIZE_META_DATA sizeof(struct m_meta_data)
typedef struct m_meta_data *p_meta_data;
/* This structure has a size multiple of 8 */
struct m_meta_data {
size_t size_bytes;
int available;
int magic;
p_meta_data next;
p_meta_data previous;
};
Here are some remarks about your code:
it is confusing for the reader to hide pointers behind typedefs. Why not define m_meta_data as a typedef for struct m_meta_data and use m_meta_data * everywhere?
are you sure sbrk() is properly defined? The cast (void *)sbrk(0) seems to indicate otherwise. sbrk() is declared in <unistd.h> on POSIX systems.
BUG in split(), the computation meta_data2 = (p_meta_data)(meta_data + offset); is incorrect. It should be:
meta_data2 = (p_meta_data)((unsigned char *)meta_data + offset);
you should define strdup() and strndup() as the definitions from the C library might not call your redefined malloc():
char *strdup(const char *s) {
size_t len = strlen(s);
char *p = malloc(len + 1);
if (p) {
memcpy(p, s, len + 1);
}
return p;
}
char *strndup(const char *s, size_t n) {
size_t len;
char *p;
for (len = 0; len < n && s[n]; len++)
continue;
if ((p = malloc(len + 1)) != NULL) {
memcpy(p, s, len);
p[len] = '\0';
}
return p;
}
blocks allocated with malloc() should be aligned on 16-byte boundaries on 64-bit intel systems. As a matter of fact, the m_meta_data structure has a size of 32 bytes on 64-bit systems, but 20 bytes on 32-bit systems. You should adjust your m_meta_data structure for 32-bit systems.
you should check for overflow in size_t mem_to_get = num_bytes * num_blocs;
you should not rely on void * arithmetics, it is a gcc extension. Write this instead:
p_meta_data inic_bloc = (p_meta_data)ptr - 1;
in realloc(), when extending the size of the block, you just make the original block available but you do not coalesce it with adjacent blocks as you do in free(). You might just call free(ptr), especially since modifying inic_bloc->available = 1; without getting the lock seems risky.
you should check meta_data->available in free() and realloc() to detect invalid calls and prevent arena corruption.
in malloc(), you forget to release the lock in case of allocation failure.
calling fprintf while the lock is set is risky: if fprintf calls malloc, you would get a deadlock. You might assume that printing to stderr does not call malloc() because stderr is unbuffered, but you are taking chances.
when allocating a new block with sbrk(), you should use sbrk(0) after the allocation to determine the actual size made available as it may have been rounded up to a multiple of PAGE_SIZE.
you should split blocks if (meta_data->size_bytes - size_bytes) > SIZE_META_DATA. The current test is far too loose.
I'm developing a kernel memory checker to find memory leaks in kernel space.
I have two functions profile_vmalloc and profile_vfree, profile_vmalloc uses vmalloc to allocate memory and adds memory allocated info to a list, profile_vfree frees the allocated memory and removes that info from the list. How to replace vmalloc and vfree so that when I compile and run my module any kernel module allocating and freeing memory will use my functions ?
How do I use sysfs to create a read only file that will allow the user to see a summary of memory leaks?
This is what my code looks like so far
// Linux Kernel headers
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/vmalloc.h>
#define FILE_NAME_LENGTH 256
#define vmalloc(size) profile_malloc (block_size, __FILE__, __LINE__)
#define vfree(mem_ref) profile_free(mem_ref)
struct _MEM_INFO
{
const void *address;
size_t size;
char file_name[FILE_NAME_LENGTH];
size_t length;
};
typedef struct _MEM_INFO MEM_INFO;
struct _MEM_LEAK {
MEM_INFO mem_info;
struct _MEM_LEAK * next;
};
typedef struct _MEM_LEAK MEM_LEAK;
#undef vmalloc
#undef vfree
static MEM_PROFILER_LIST * ptr_start = NULL;
static MEM_PROFILER_LIST * ptr_next = NULL;
/*
* adds allocated memory info. into the list
*
*/
void add(MEM_INFO alloc_info)
{
MEM_PROFILER_LIST * mem_leak_info = NULL;
mem_leak_info = (MEM_PROFILER_LIST *) malloc (sizeof(MEM_PROFILER_LIST));
mem_leak_info->mem_info.address = alloc_info.address;
mem_leak_info->mem_info.size = alloc_info.size;
strcpy(mem_leak_info->mem_info.file_name, alloc_info.file_name);
mem_leak_info->mem_info.line = alloc_info.line;
mem_leak_info->next = NULL;
if (ptr_start == NULL)
{
ptr_start = mem_leak_info;
ptr_next = ptr_start;
}
else {
ptr_next->next = mem_leak_info;
ptr_next = ptr_next->next;
}
}
/*
* remove memory info. from the list
*
*/
void erase(unsigned pos)
{
unsigned int i = 0;
MEM_PROFILER_LIST * alloc_info, * temp;
if(pos == 0)
{
MEM_PROFILER_LIST * temp = ptr_start;
ptr_start = ptr_start->next;
free(temp);
}
else
{
for(i = 0, alloc_info = ptr_start; i < pos;
alloc_info = alloc_info->next, ++i)
{
if(pos == i + 1)
{
temp = alloc_info->next;
alloc_info->next = temp->next;
free(temp);
break;
}
}
}
}
/*
* deletes all the elements from the list
*/
void clear()
{
MEM_PROFILER_LIST * temp = ptr_start;
MEM_PROFILER_LIST * alloc_info = ptr_start;
while(alloc_info != NULL)
{
alloc_info = alloc_info->next;
free(temp);
temp = alloc_info;
}
}
/*
* profile of vmalloc
*/
void * profile_vmalloc (unsigned int size, const char * file, unsigned int line)
{
void * ptr = vmalloc (size);
if (ptr != NULL)
{
add_mem_info(ptr, size, file, line);
}
return ptr;
}
/*
* profile of free
*/
void profile_free(void * mem_ref)
{
remove_mem_info(mem_ref);
free(mem_ref);
}
/*
* gets the allocated memory info and adds it to a list
*
*/
void add_mem_info (void * mem_ref, unsigned int size, const char * file, unsigned int line)
{
MEM_INFO mem_alloc_info;
/* fill up the structure with all info */
memset( &mem_alloc_info, 0, sizeof ( mem_alloc_info ) );
mem_alloc_info.address = mem_ref;
mem_alloc_info.size = size;
strncpy(mem_alloc_info.file_name, file, FILE_NAME_LENGTH);
mem_alloc_info.line = line;
/* add the above info to a list */
add(mem_alloc_info);
}
/*
* if the allocated memory info is part of the list, removes it
*
*/
void remove_mem_info (void * mem_ref)
{
unsigned int i;
MEM_PROFILER_LIST * leak_info = ptr_start;
/* check if allocate memory is in our list */
for(i = 0; leak_info != NULL; ++i, leak_info = leak_info->next)
{
if ( leak_info->mem_info.address == mem_ref )
{
erase ( i );
break;
}
}
}
/*
* writes a memory leak summary to a file
*/
void mem_leak_summary(void)
{
unsigned int i;
MEM_PROFILER_LIST * mem_output;
FILE * fp_write = fopen (SUMMARY_FILE, "wt");
char info[1024];
if(fp_write != NULL)
{
fwrite(info, (strlen(info) + 1) , 1, fp_write);
sprintf(info, "%s\n", "-----------------------------------");
fwrite(info, (strlen(info) + 1) , 1, fp_write);
for(mem_output= ptr_start; mem_output!= NULL; mem_output= mem_output->next)
{
sprintf(info, "address : %d\n", leak_info->mem_output.address);
fwrite(info, (strlen(info) + 1) , 1, fp_write);
sprintf(info, "size : %d bytes\n", leak_info->mem_output.size);
fwrite(info, (strlen(info) + 1) , 1, fp_write);
sprintf(info, "line : %d\n", leak_info->mem_output.line);
fwrite(info, (strlen(info) + 1) , 1, fp_write);
sprintf(info, "%s\n", "-----------------------------------");
fwrite(info, (strlen(info) + 1) , 1, fp_write);
}
}
clear();
}
static int __init profiler_init(void)
{
return 0;
}
static void __exit profiler_cleanup(void)
{
printk("profiler module uninstalled\n");
}
module_init(profiler_init);
module_exit(profiler_cleanup);
MODULE_LICENSE("GPL");
How to replace vmalloc and vfree so
that when I compile and run my module any kernel module allocating and
freeing memory will use my functions ?
Step 1. In the files that call vmalloc & vfree, define macros:
#define vmalloc(x) profile_vmalloc(x)
#define vfree(x) profile_vfree(x)
This will ensure that your functions are called.
Step 2: In your definition of the profile_* functions, do you accounting and call the actual vmalloc/vfree functions.
You are already doing the step 2, so you just need to take care of step 1.
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;
}
Could someone check my code and tell me if I am on the right track.. It seems like I am a bit lost.. if you see my errors, please let me know them..
What I am trying to do is to solve bounded buffer using my own semaphores as well as GCD.
Thanks in advance..
sema.c
void procure( Semaphore *semaphore ) {
pthread_mutex_lock(semaphore->mutex1);
while(semaphore->value <= 0)
pthread_cond_wait(&semaphore->condition, semaphore->mutex1);
semaphore->value--;
pthread_mutex_unlock(semaphore->mutex1);
}
void vacate( Semaphore *semaphore ) {
pthread_mutex_lock(semaphore->mutex1);
semaphore->value++;
pthread_cond_signal(&semaphore->condition);
pthread_mutex_unlock(semaphore->mutex1);
}
void init ( Semaphore *semaphore ){
semaphore->value = 1;
pthread_mutex_t myMutex;
semaphore->mutex1 = &myMutex;
pthread_mutex_init( semaphore->mutex1, NULL);
}
void destroy ( Semaphore *semaphore ) {
pthread_mutex_destroy(semaphore->mutex1);
}
and main.c
struct variables {
Semaphore *sem;
};
struct variables vars;
void constructer (int *buffer, int *in, int *out) {
init(vars.sem);
}
void deconstructer () {
destroy(vars.sem);
}
int rand_num_gen() {
uint_fast16_t buffer;
int file;
int *rand;
file = open("/dev/random", O_RDONLY);
while( 1 ) {
read(file, &buffer, sizeof(buffer));
printf("16 bit number: %hu\n", buffer );
*rand = (int) buffer;
close(file);
break;
}
return *rand;
}
void put_buffer( int* buffer, int* in, int* out ) {
buffer[*in] = rand_num_gen(); // produce
procure(vars.sem); // wait here
*in = (*in + 1) % BUF_SIZE;
vacate(vars.sem);
}
void get_buffer( int* buffer, int* in, int* out ) {
int value;
procure(vars.sem);
value = buffer[*out];
vacate(vars.sem);
*out = (*out + 1) % BUF_SIZE;
}
int main (void) {
int *in, *out, *buffer;
constructer(buffer, in, out);
dispatch_queue_t producer, consumer;
producer = dispatch_queue_create("put_buffer", NULL);
consumer = dispatch_queue_create("get_buffer", NULL);
dispatch_async(producer,
^{
int i;
do
{
put_buffer( buffer, in, out );
dispatch_async(consumer,
^{
get_buffer( buffer, in, out );
if (i == RUN_LENGTH) exit(EXIT_SUCCESS);
});
}
while (i < RUN_LENGTH);
});
dispatch_main();
deconstructer();
exit (0);
}
Your code has a bug. In the init function you assign the address of a local variable to semaphore->mutex1, and when the function returns this address will be invalid. Later you still use this address, so this leads to undefined behavior.
You must either allocate the memory for the mutex directly in the semaphore (without a pointer) or allocate the memory via malloc.
Update:
Your program has so many bugs that you should definitely pick an easier topic to learn the basic concepts about memory management, how to allocate, use and reference a buffer, do proper error handling, etc. Here is a slightly edited version of your code. It still won't work, but probably has some ideas that you should follow.
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void procure(Semaphore *semaphore) {
pthread_mutex_lock(semaphore->mutex1);
while (semaphore->value <= 0)
pthread_cond_wait(&semaphore->condition, semaphore->mutex1);
semaphore->value--;
pthread_mutex_unlock(semaphore->mutex1);
}
void vacate(Semaphore *semaphore) {
pthread_mutex_lock(semaphore->mutex1);
semaphore->value++;
pthread_cond_signal(&semaphore->condition);
pthread_mutex_unlock(semaphore->mutex1);
}
struct variables {
mutex_t sem_mutex;
Semaphore sem;
};
struct variables vars;
void constructor(int *buffer, int *in, int *out) {
vars.sem.value = 1;
vars.sem.mutex1 = &vars.sem_mutex;
pthread_mutex_init(vars.sem.mutex1, NULL);
}
void deconstructor() {
pthread_mutex_destroy(&semaphore->mutex1);
}
int rand_num_gen() {
const char *randomfile = "/dev/random";
unsigned char buffer[2]; // Changed: always treat files as byte sequences.
FILE *f = fopen(randomfile, "rb");
// Changed: using stdio instead of raw POSIX file access,
// since the API is much simpler; you don't have to care
// about interrupting signals or partial reads.
if (f == NULL) { // Added: error handling
fprintf(stderr, "E: cannot open %s\n", randomfile);
exit(EXIT_FAILURE);
}
if (fread(buffer, 1, 2, f) != 2) { // Added: error handling
fprintf(stderr, "E: cannot read from %s\n", randomfile);
exit(EXIT_FAILURE);
}
fclose(f);
int number = (buffer[0] << CHAR_BIT) | buffer[1];
// Changed: be independent of the endianness of the system.
// This doesn't matter for random number generators but is
// still an important coding style.
printf("DEBUG: random number: %x\n", (unsigned int) number);
return number;
}
void put_buffer( int* buffer, int* in, int* out ) {
buffer[*in] = rand_num_gen(); // produce
procure(&vars.sem); // wait here
*in = (*in + 1) % BUF_SIZE;
vacate(&vars.sem);
}
void get_buffer( int* buffer, int* in, int* out ) {
int value;
procure(&vars.sem);
value = buffer[*out];
vacate(&vars.sem);
*out = (*out + 1) % BUF_SIZE;
}
int main (void) {
int inindex = 0, outindex = 0;
int buffer[BUF_SIZE];
constructor(buffer, &inindex, &outindex);
// Changed: provided an actual buffer and actual variables
// for the indices into the buffer.
dispatch_queue_t producer, consumer;
producer = dispatch_queue_create("put_buffer", NULL);
consumer = dispatch_queue_create("get_buffer", NULL);
dispatch_async(producer, ^{
int i;
do {
put_buffer(buffer, &inindex, &outindex);
dispatch_async(consumer, ^{
get_buffer(buffer, &inindex, &outindex);
if (i == RUN_LENGTH) exit(EXIT_SUCCESS);
});
} while (i < RUN_LENGTH);
});
dispatch_main();
deconstructor();
exit (0);
}
As I said, I didn't catch all the bugs.
I'm having trouble with what should be a simple program.
I've written a single linked list implementation in C using void* pointers. However, I have a problem, as there is a possible memory leak somewhere, however I checked the code using valgrind and it detected no such errors.
But when all the memory is free'd there is still some memory un-freed (see comments)... I tried passing everything to the add function by reference too, but this didn't fix the issue either.
I just wondered if anyone here had any comments from looking at the code. (This should be simple!, right?)
/*
Wrapping up singley linked list inside a struct
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* Needed for: memcpy */
void waitKey(){
printf("Press any key to continue...");
getchar();
}
/* Define a structure for a list entry */
struct ListEntry {
void* data;
struct ListEntry* pNext;
};
/* Struct for list properties */
struct ListProperties {
struct ListEntry* g_pLast;
struct ListEntry* g_pHead;
struct ListEntry* pCurrent;
unsigned int size;
int getHead;
};
/* Add:
args: list, data, dyn (0 if not, else size of dynamic data)
*/
void add(struct ListProperties* l, void* d, unsigned long dyn) {
struct ListEntry* pNew = malloc(sizeof(struct ListEntry));
/* Set the data */
if (dyn > 0){
/* Allocate and copy array */
pNew->data = malloc(dyn);
pNew->data = memcpy(pNew->data,d,dyn);
} else {
pNew->data = d;
}
/* Set last element to point to new element */
if (l->g_pLast != NULL){
l->g_pLast->pNext = pNew;
/* Get head of list */
if (l->g_pHead == NULL && l->getHead == 0){
l->g_pHead = l->g_pLast;
l->getHead = 1;
}
} else {
/* 1 elem case */
l->g_pHead = pNew;
l->pCurrent = pNew;
}
/* New element points to NULL */
pNew->pNext = NULL;
/* Save last element for setting
pointer to next element */
l->g_pLast = pNew;
/* Inc size */
l->size++;
}
/* Create new list and return a pointer to it */
struct ListProperties* newList(){
struct ListProperties* nList = malloc (sizeof(struct ListProperties));
nList->g_pHead = NULL;
nList->g_pLast = NULL;
nList->getHead = 0;
nList->size = 0;
return nList;
}
/* Reset pointer */
int reset(struct ListProperties *l){
if (l->g_pHead != NULL){
l->pCurrent = l->g_pHead;
return 0;
}
return -1;
}
/* Get element at pointer */
void* get(struct ListProperties *l) {
if (l->size > 0){
if (l->pCurrent != NULL){
return l->pCurrent->data;
}
}
return NULL;
}
/* Increment pointer */
int next(struct ListProperties *l){
if (l->pCurrent->pNext != NULL){
l->pCurrent = l->pCurrent->pNext;
return 1;
}
return 0;
}
/* Get element at n */
void* getatn(struct ListProperties *l, int n) {
if (l->size > 0){
int count = 0;
reset(l);
while (count <= n){
if (count == n){
return l->pCurrent->data;
break;
}
next(l);
count++;
}
}
return NULL;
}
/* Free list contents */
void freeList(struct ListProperties *l){
struct ListEntry* tmp;
/* Reset pointer */
if (l->size > 0){
if (reset(l) == 0){
/* Free list if elements remain */
while (l->pCurrent != NULL){
if (l->pCurrent->data != NULL)
free(l->pCurrent->data);
tmp = l->pCurrent->pNext;
free(l->pCurrent);
l->pCurrent = tmp;
}
}
}
l->g_pHead = NULL;
l->g_pLast = NULL;
l->size = 0;
l->getHead = 0;
free(l);
}
void deleteElem(struct ListProperties *l, int index){
struct ListEntry* tmp;
int count = 0;
if (index != 0)
index--;
reset(l);
while (count <= index){
if (count == index){ // Prev element
if (l->pCurrent != NULL){
if (l->pCurrent->pNext != NULL){
free(l->pCurrent->pNext->data); // Free payload
tmp = l->pCurrent->pNext;
l->pCurrent->pNext = l->pCurrent->pNext->pNext;
free(tmp);
if (l->size > 0)
l->size--;
} else {
// Last element
free(l->pCurrent->data);
free(l->pCurrent);
l->g_pHead = NULL;
l->g_pLast = NULL;
l->getHead = 0;
l->size = 0;
}
}
break;
}
if (next(l) != 1)
break;
count++;
}
}
int size(struct ListProperties *l){
return l->size;
}
int main( int argc, char* argv )
{
int j = 0;
unsigned long sz = 0;
/*=====| Test 1: Dynamic strings |=====*/
/* Create new list */
struct ListProperties* list = newList();
if (list == NULL)
return 1;
char *str;
str = malloc(2);
str = strncat(str,"A",1);
sz = 2;
printf("Dynamic Strings\n===============\n");
/* Check memory usage here (pre-allocation) */
waitKey();
/* Add to list */
for (j = 0; j < 10000; j++){
add(list,(char*)str, sz);
str = realloc(str, sz+2);
if (str != NULL){
str = strncat(str,"a",1);
sz++;
}
}
/* Allocated strings */
waitKey();
/* TESTING */
freeList(list);
free(str);
/* Check memory usage here (Not original size!?) */
waitKey();
return 0;
}
Thanks!
You don't say how you are checking memory usage, but I'm going to guess that you are using ps or something similar to see how much memory the OS has given the process.
Depending on your memory allocator, calling free may or may not return the memory to the OS. So even though you are calling free, you will not see the memory footprint decrease from the OS's point of view.
The allocator may keep a cache of memory that is given to it by the OS. A call to malloc will first look in this cache to see if it can find a big enough block and if so, malloc can return without asking the OS for more memory. If it can't find a big enough block, malloc will ask the OS for more memory and add it to it's cache.
But free may simply add the memory back to the cache and never return it to the OS.
So, what you may be doing is seeing the allocators cache and not any memory leak.
As was mentioned, I would not trust the memory usage reported by the task manager as there are other factors beyond your control that impact it (how malloc/free are implemented, etc).
One way you can test for memory leaks is by writing your own wrapper functions around the existing malloc and free functions similar to:
void* my_malloc(size_t len) {
void* ptr = malloc(len);
printf("Allocated %u bytes at %p\n", len, ptr);
return ptr;
}
void my_free(void* ptr) {
printf("Freeing memory at %p\n", ptr);
free(ptr);
}
Now, you will get a log of all memory that is dynamically allocated or freed. From here, it should be fairly obvious if you leak a block of memory (the more complex your program is, the longer your log will be and the more difficult this task will be).
Your program contains incorrect argv in main, incorrect usage of strncat, and strange memory allocation. Some of these should of shown up as warnings. The argv is a non-issue, but if the others showed up as warning, you needed to heed them. Don't ignore warnings.
These changes clean it up. The biggest thing was that you don't seem to have a good grasp on the NUL ('\0') character (different than NULL pointer) used to terminate C strings, and how that effects str(n)cat.
The mixed usage of str* functions with memory functions (*alloc/free) was likely part of the confusion. Be careful.
#include <assert.h>
...
int main( int argc, char* argv[] ) /* or int main(void) */
...
sz = 2;
str = (char*) malloc(sz); /* allocate 2 bytes, shortest non-trivial C string */
assert(str != NULL);
strncpy(str, "A", sz); /* copy 'A' and '\0' into the memory that str points to */
...
/* Add to list */
for (j = 0; j < 10000; j++){
add(list, str, sz);
str = realloc(str, ++sz); /* realloc str to be one (1) byte larger */
assert(str != NULL);
strncat(str, "a", sz - strlen(str)); /* now insert an 'a' between last 'A' or 'a' and '\0' */
assert(str != NULL);
}