C : Segmentation Fault when move main function to new file -
i implement custom memory allocator. main code inside file memory.c
create main function in file test function. works fine. when move testing code file (calling main.c
, run it. meet segmentation fault.
int main (int argc, char *argv []) { allocator_init(); char* ptr = (char*) allocate(4096); // csutom function. on `memory.c` strcpy(ptr, "this test one");// segmentation fault here printf("print: %s\n", ptr); deallocate(ptr); }
here main code :
volatile memory memory; /* allocated memory memory variable assign /dev/zero memory */ void allocator_init() { fd = open("/dev/zero", o_rdwr); if(fd == -1) { perror("file open failed"); exit(0); } // page size can different on different platform. customize again optimize page_size = getpagesize(); // fd = open("zeroes", o_rdwr); if(fd == -1) { perror("file open failed"); exit(0); } // initialize region list memory.region = null; int i; /// initialize caches /// size of each cache 16 * 2^i => 16, 32, 64, 128, 256, 512, 1024, 2048 (i=0; i<8; i++) { memory.cache[i].size = 16<<i; memory.cache[i].s = null; } return; } void *allocate_region (unsigned int size) { region *region, *temp; temp = memory.region; void *mapped_addr = mmap(null, size + region_size, prot_read | prot_write, map_private, fd, 0); if(mapped_addr == map_failed) { perror("mapping failed"); exit(0); } /* create region mapped address */ region = mapped_addr; region->size = size; region->addr = mapped_addr + region_size; printf("allocated region: %p\n", region->addr); /* assign region memory */ memory.region = region; /* simple append algorithm in linked list. new region appended head of linked list */ region->next = temp; return region->addr; } /* allocate : if size < 2048 : allocate cache. else allocate region */ void *allocate(unsigned int size) { size = align(size); return allocate_region(size); }
here memory.h
define struct have used:
#ifndef memory_h #define memory_h #define max_size (1024*1024) #define region_size sizeof(region) #define slab_size sizeof(slab) #define word_size 32 #define alignment 8 /* rounds nearest multiple of alignment */ #define align(size) (((size) + (alignment-1)) & ~0x7) #define size_t_size (align(sizeof(size_t))) #define true 1 #define false 0 #define max_size 10000 // caches are: // 16, 32, 64, 128, 256, 512, 1024, 2048 #include "bits.h" int page_size; // file descriptor zeroes file int fd; void allocator_init(); int deallocate_cache(void* ptr); void *allocate(unsigned int size); typedef struct __region { void *addr; /// started address can use caller (can calc allocated address + region size) int size; /// size of allocated region (not include size region) struct __region *next; } region; /// size of slab equals size of system page typedef struct __slab { void *addr; /// address of slab (exclude header) char *bitmap; /// started address can use caller (can calc allocated address + slab size) int size; int slots; /// number of slots in maximum has been used int cache_size; /// size of cache contains slab int bitmap_size; /// size of bitmap. so, can count address bit bitmap + bitmap_size int currentsize; /// current allocated elements of slab. currentsize = 0. deallocated slab int bit; /// bit marked part of slab has been used struct __slab * next; } slab; typedef struct __cache { int size; slab *s; } cache; typedef struct __memory { region *region; cache cache[8]; } memory; #endif // memory_h
above code works fine, allocate
function return address. meet error when move file. in memory.c
, have global variable control allocated address , memory. affect when move code new file ? cannot explain this.
thanks :)
there observations worth making:
- your header should declare consumer of code needs know. that's 3 functions:
allocator_init()
,allocate()
,deallocate_cache()
. else in file implementation detail should hidden consumer of code. - headers should not define variables — not tentatively. if aren't writing
extern
in front of variable in header, should prefixedstatic
, you'd better have reason it. (see how share variable between source files in c complete disquisition.) - names starting
_
reserved implementation use. that's slight over-simplification, covers bases , easier remember formally correct rules. names starting__
off-limits (100%; no simplification involved). don't define such names yourself, , extremely cautious using such names in code (they're types shouldn't using). removed leading underscores structure tags; works happily. - your header included
"bits.h"
, fortunately code didn't use it, commenting out worked fine. - your code uses
getpagesize()
doesn't define it. excusable; used 4096 instead. , tracking down showedpage_size
variable defined in header, leading start ofdiatribepolemic explaining to. shouldstatic
variable inmemory.c
file. things visible outside of source file should functions (and perhaps global variables) consumers need use. else shouldstatic
invisible — no name conflicts, no possibility of misuse or abuse. - when made static variable, compiler complains
page_size
unused. that, incidentally, advantage of making can static static definition — when code accessed outside source because not static, compiler can't warn unused variables or functions. - your header defined 2 different values
max_size
; don't! - your header defined unused values, such
#define true 1
; again, don't. (max_size
wasn't used, either, norfalse
, norslab_size
, …) your
align
macro faulty:#define align(size) (((size) + (alignment - 1)) & ~0x7)
as stands, alignment 8, , macro works. suppose change alignment 16; mask wrong. should
& ~(alignment - 1)
. beware of apparent parameterization incomplete.- if exit because of error, should not exit status 0; means success. either use 1 or
exit_failure
— or follow other locally relevant scheme. - your header declared
deallocate_cache()
main program callsdeallocate()
, code neither given. naming inconsistency problematic. used dummy implementation.
obviously, necessary headers had added, too. ok; lot fixed, code compiled , linked. ran into:
mapping failed: operation not supported device
this mac os x 10.9.2 mavericks. fyi. worked around creating empty file ./dev.zero
, referencing that. beware portability assumptions.
then crashed bus error, signal 10. didn't print allocated region
message. limits damage 3 lines of code.
you cannot arithmetic on void *
types in standard c. gcc allow it; don't take advantage of that, though, in code has pretensions portability.
when created empty file, program crashed. when used dd if=/dev/zero of=dev.zero bs=1k count=1024
initialize file zeros, program no longer crashed. i'd added bunch of debug printing code.
i suggest not using /dev/zero
mapping file.
mapping succeeded: 0x10ca74000 region = 0x10ca74000 size = 4096 allocated region: 0x10ca74018 memory: allocate_region (0x10ca40080) region: base (0x10ca74000) address: 0x10ca74018, size: 4096, next = 0x0 cache: line 0 (0x10ca40088) size: 16 cache: line 1 (0x10ca40098) size: 32 cache: line 2 (0x10ca400a8) size: 64 cache: line 3 (0x10ca400b8) size: 128 cache: line 4 (0x10ca400c8) size: 256 cache: line 5 (0x10ca400d8) size: 512 cache: line 6 (0x10ca400e8) size: 1024 cache: line 7 (0x10ca400f8) size: 2048 ptr = 0x10ca74018 print: test 1 deallocate called 0x10ca74018: unimplemented
code
memory.h
#ifndef memory_h #define memory_h extern void allocator_init(void); extern void deallocate(void *ptr); extern void *allocate(unsigned int size); #endif // memory_h
main.c
#include <stdio.h> #include <string.h> #include "memory.h" int main(void) { allocator_init(); char *ptr = (char *) allocate(4096); // custom function. on `memory.c` printf("ptr = %p\n", ptr); strcpy(ptr, "this test one"); // segmentation fault here printf("print: %s\n", ptr); deallocate(ptr); }
memory.c
#include "memory.h" #include <assert.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> #define region_size sizeof(region) #define alignment 8 #define align(size) (((size) + (alignment - 1)) & ~(alignment - 1)) #if defined(dev_zero_memory_mapping) #define dev_zero "/dev/zero" #else #define dev_zero "./dev.zero" #endif enum { num_caches = 8 }; static int fd = -1; typedef struct region { void *addr; int size; struct region *next; } region; typedef struct slab { void *addr; char *bitmap; int size; int slots; int cache_size; int bitmap_size; int currentsize; int bit; struct slab *next; } slab; typedef struct cache { int size; slab *s; } cache; typedef struct memory { region *region; cache cache[num_caches]; } memory; static memory memory = { 0 }; static void dump_slab(file *fp, const char *tag, const slab *slab) { fprintf(fp, "slab: %s (%p)\n", tag, slab); if (slab != 0) { fprintf(fp, "addr: %p, ", slab->addr); fprintf(fp, "bitmap: %p, ", (void *)slab->bitmap); fprintf(fp, "size: %6d, slots %3d, ", slab->size, slab->slots); /* int cache_size; int bitmap_size; int currentsize; int bit; struct slab *next; */ } } static void dump_cache(file *fp, const char *tag, const cache *cache) { fprintf(fp, "cache: %s (%p)\n", tag, cache); if (cache != 0) { fprintf(fp, "size: %d\n", cache->size); slab *slab = cache->s; while (slab != 0) { dump_slab(fp, "", slab); slab = slab->next; } } } static void dump_region(file *fp, const char *tag, const region *reg) { fprintf(fp, "region: %s (%p)\n", tag, reg); if (reg != 0) { fprintf(fp, "address: %p, size: %6d, next = %p\n", reg->addr, reg->size, reg->next); } } static void dump_memory(file *fp, const char *tag, const memory *mem) { fprintf(fp, "memory: %s (%p)\n", tag, mem); if (mem != 0) { region *reg = mem->region; dump_region(fp, "base", reg); while (reg->next != 0) { dump_region(fp, "next", reg->next); reg = reg->next; } (int = 0; < num_caches; i++) { char line[32]; snprintf(line, sizeof(line), "line %d", i); dump_cache(fp, line, &memory.cache[i]); } } } void allocator_init(void) { fd = open(dev_zero, o_rdwr|o_creat, 0600); if (fd == -1) { perror("file open failed"); exit(0); } if (fd == -1) { perror("file open failed"); exit(0); } memory.region = null; (int = 0; < num_caches; i++) { memory.cache[i].size = 16 << i; memory.cache[i].s = null; } } static void *allocate_region(unsigned int size) { assert(fd != -1); region *temp = memory.region; void *mapped_addr = mmap(null, size + region_size, prot_read | prot_write, map_private, fd, 0); if (mapped_addr == map_failed) { perror("mapping failed"); exit(0); } printf("mapping succeeded: %p\n", mapped_addr); region *region = mapped_addr; printf("region = %p\n", region); region->size = size; printf("size = %d\n", region->size); region->addr = (char *)mapped_addr + region_size; printf("allocated region: %p\n", region->addr); memory.region = region; region->next = temp; dump_memory(stderr, __func__, &memory); return region->addr; } void *allocate(unsigned int size) { size = align(size); return allocate_region(size); } void deallocate(void *ptr) { fprintf(stderr, "%s called %p: unimplemented\n", __func__, ptr); }
i recommend making functions dump complex structures along dump_memory()
, dump_region()
, dump_cache()
, dump_slab()
functions shown; they're helpful, though of red herring in debugging this.
Comments
Post a Comment