C : 将主函数移动到新文件时出现分段错误

Trầ*_* Dự 4 c segmentation-fault

我实现了一个自定义内存分配器。这个内部文件的所有主要代码memory.c我都在这个文件中创建了一个主函数来测试函数。一切正常。但是当我将这些测试代码移动到另一个文件(调用main.c并运行它)时,我遇到了分段错误。

int main (int argc, char *argv []) {
   allocator_init();
   char* ptr = (char*) allocate(4096);   // csutom function. that on `memory.c` 
   strcpy(ptr, "this is the test one");// segmentation fault here
   printf("print: %s\n", ptr);
   deallocate(ptr);
}
Run Code Online (Sandbox Code Playgroud)

这是主要代码:

volatile Memory memory;


/* allocated memory to memory variable by assign /dev/zero to 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 to optimize
    PAGE_SIZE = getPageSize();

    // fd = open("ZEROES", O_RDWR);

    if(fd == -1) {
        perror("File open failed");
        exit(0);
    }

    // Initialize the region list
    memory.region = NULL;

    int i;
    /// Initialize the caches
    /// size of each cache is 16 * 2^i => 16, 32, 64, 128, 256, 512, 1024, 2048
    for (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 from mapped address */
      region = mapped_addr;
      region->size = size;
      region->addr = mapped_addr + REGION_SIZE;
      printf("allocated region: %p\n", region->addr);

      /* assign this region to memory */
      memory.region = region;

      /* just simple append algorithm in linked list. so new region will be appended to 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);
}
Run Code Online (Sandbox Code Playgroud)

这是memory.h定义我使用过的所有结构:

#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 up to the 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 for 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 for caller (can calc by allocated address + Region size)
  int size;                 /// size of this allocated Region (not include size Region)
  struct __Region *next;
} Region;

/// size of Slab equals to size of System Page
typedef struct __Slab {
  void *addr;               /// address of this slab (exclude header)
  char *bitmap;             /// started address can use for caller (can calc by allocated address + slab size)
  int size;
  int slots;                /// number of slots in maximum has been used
  int cache_size;            /// size of cache that contains this slab
  int bitmap_size;          /// size of bitmap. so, can count address bit by bitmap + bitmap_size
  int currentSize;          /// current allocated elements of this slab. currentSize = 0. deallocated this slab
  int bit;                  /// bit to marked which 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
Run Code Online (Sandbox Code Playgroud)

上面的代码可以正常工作,allocate函数返回一个地址。我只是在移动到另一个文件时遇到错误。在我的中memory.c,我有一些全局变量来控制分配的地址和内存。当我将代码移动到新文件时它会影响吗?我无法解释这一点。

谢谢 :)

Jon*_*ler 5

有一些值得观察的地方:

\n\n
    \n
  1. 您的标头应该只声明代码的使用者需要知道的内容。这是三个函数:allocator_init()allocate()deallocate_cache()。文件中的其他所有内容都是实现细节,应该对代码的使用者隐藏。
  2. \n
  3. 标头不应定义变量 \xe2\x80\x94 ,甚至暂时也不应如此。如果您没有extern在标头中的变量前面编写,则应该以 为前缀,static并且您最好有一个很好的理由。(有关完整的讨论,请参阅如何在 C 中的源文件之间共享变量。)
  4. \n
  5. 以 开头的名称_保留供实现使用。这有点过于简化,但它涵盖了所有基础,并且比形式上正确的规则更容易记住。以 开头的名称__绝对是禁止的(100%;不涉及简化)。不要为自己定义此类名称,并且在代码中使用此类名称时要格外小心(它们通常是您可能不应该使用的类型)。我只是从结构标签中删除了前导下划线;它工作得很愉快。
  6. \n
  7. 您的标头包含"bits.h",但幸运的是您的代码没有使用其中的任何内容,因此注释掉它效果很好。
  8. \n
  9. 您的代码使用getPageSize()但未定义它。也许是情有可原的;我用4096代替。追踪发现这是一个在标题中定义的变量,导致了这场解释该做什么的谩骂PAGE_SIZE争论的开始。它应该是文件中的变量。从源文件外部可见的唯一内容应该是消费者需要使用的那些函数(也许还有全局变量)。其他一切都应该是不可见的\xe2\x80\x94,没有名称冲突,没有误用或滥用的可能性。staticmemory.cstatic
  10. \n
  11. 当它被设为静态变量时,编译器会抱怨PAGE_SIZE未使用。顺便说一句,当代码可以在源代码外部访问时,因为它不是静态的,所以将所有可以静态的东西都变成静态定义 \xe2\x80\x94 是另一个优点,编译器无法警告未使用的变量或功能。
  12. \n
  13. MAX_SIZE您的标头为;定义了两个不同的值 不!
  14. \n
  15. 您的标头定义了未使用的值,例如#define TRUE 1; 再说一次,不要。(MAX_SIZE也没有使用,也没有FALSE,也没有SLAB_SIZE,\xe2\x80\xa6)
  16. \n
  17. 你的ALIGN宏有问题:

    \n\n
     #define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    按照目前的情况,ALIGNMENT 为 8,并且宏有效。但假设我将 ALIGNMENT 更改为 16;那么面具是错误的。应该是吧& ~(ALIGNMENT - 1)。请注意明显不完整的参数化。

  18. \n
  19. 如果由于错误而退出,则不应以状态 0 退出;这意味着成功。使用 1 或EXIT_FAILURE\xe2\x80\x94 或遵循其他一些本地相关方案。
  20. \n
  21. 您的标头已声明,deallocate_cache()但您的主程序调用了deallocate(),并且没有给出两者的代码。命名不一致是有问题的。我使用了一个虚拟实现。
  22. \n
\n\n

显然,还必须添加必要的标头。好的; 修复了该批次后,编译并链接了代码。然后我遇到了:

\n\n
Mapping failed: Operation not supported by device\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是 Mac OS X 10.9.2 Mavericks。仅供参考。我通过创建一个空文件./dev.zero并引用它来解决这个问题。当心可移植性假设。

\n\n

然后它因总线错误(信号 10)而崩溃。它没有打印消息allocated region。这将损坏限制在 3 行代码内。

\n\n

你不能对void *标准 C 中的类型进行算术运算。GCC 允许你这样做;但是,不要在具有任何可移植性的代码中利用这一点。

\n\n

当我创建一个空文件时,程序崩溃了。当我将dd if=/dev/zero of=dev.zero bs=1k count=1024文件初始化为全零时,程序不再崩溃。我添加了一堆调试打印代码。

\n\n

我建议不要使用/dev/zero您的映射文件。

\n\n
Mapping succeeded: 0x10ca74000\nregion = 0x10ca74000\nsize = 4096\nallocated region: 0x10ca74018\nMemory: allocate_region (0x10ca40080)\nRegion: Base (0x10ca74000)\nAddress: 0x10ca74018, size:   4096, next = 0x0\nCache: line 0 (0x10ca40088)\nSize: 16\nCache: line 1 (0x10ca40098)\nSize: 32\nCache: line 2 (0x10ca400a8)\nSize: 64\nCache: line 3 (0x10ca400b8)\nSize: 128\nCache: line 4 (0x10ca400c8)\nSize: 256\nCache: line 5 (0x10ca400d8)\nSize: 512\nCache: line 6 (0x10ca400e8)\nSize: 1024\nCache: line 7 (0x10ca400f8)\nSize: 2048\nptr = 0x10ca74018\nprint: this is the test one\ndeallocate called for 0x10ca74018: unimplemented\n
Run Code Online (Sandbox Code Playgroud)\n\n

代码

\n\n

内存.h

\n\n
#ifndef MEMORY_H\n#define MEMORY_H\n\nextern void  allocator_init(void);\nextern void  deallocate(void *ptr);\nextern void *allocate(unsigned int size);\n\n#endif // MEMORY_H\n
Run Code Online (Sandbox Code Playgroud)\n\n

主程序

\n\n
#include <stdio.h>\n#include <string.h>\n#include "memory.h"\n\nint main(void)\n{\n    allocator_init();\n    char *ptr = (char *) allocate(4096); // custom function. that on `memory.c`\n    printf("ptr = %p\\n", ptr);\n    strcpy(ptr, "this is the test one"); // segmentation fault here\n    printf("print: %s\\n", ptr);\n    deallocate(ptr);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

内存.c

\n\n
#include "memory.h"\n#include <assert.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\n#define REGION_SIZE sizeof(Region)\n\n#define ALIGNMENT   8\n#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1))\n\n#if defined(DEV_ZERO_MEMORY_MAPPING)\n#define DEV_ZERO "/dev/zero"\n#else\n#define DEV_ZERO "./dev.zero"\n#endif\n\nenum { NUM_CACHES = 8 };\n\nstatic int fd = -1;\n\ntypedef struct Region\n{\n    void *addr;\n    int size;\n    struct Region *next;\n} Region;\n\ntypedef struct Slab\n{\n    void *addr;\n    char *bitmap;\n    int size;\n    int slots;\n    int cache_size;\n    int bitmap_size;\n    int currentSize;\n    int bit;\n    struct Slab *next;\n} Slab;\n\ntypedef struct Cache\n{\n    int size;\n    Slab *S;\n} Cache;\n\ntypedef struct Memory\n{\n    Region *region;\n    Cache cache[NUM_CACHES];\n} Memory;\n\nstatic Memory memory = { 0 };\n\nstatic void dump_slab(FILE *fp, const char *tag, const Slab *slab)\n{\n    fprintf(fp, "Slab: %s (%p)\\n", tag, slab);\n    if (slab != 0)\n    {\n        fprintf(fp, "addr: %p, ", slab->addr);\n        fprintf(fp, "bitmap: %p, ", (void *)slab->bitmap);\n        fprintf(fp, "size: %6d, slots %3d, ", slab->size, slab->slots);\n        /*\n        int cache_size;\n        int bitmap_size;\n        int currentSize;\n        int bit;\n        struct Slab *next;\n        */\n    }\n}\n\nstatic void dump_cache(FILE *fp, const char *tag, const Cache *cache)\n{\n    fprintf(fp, "Cache: %s (%p)\\n", tag, cache);\n    if (cache != 0)\n    {\n        fprintf(fp, "Size: %d\\n", cache->size);\n        Slab *slab = cache->S;\n        while (slab != 0)\n        {\n            dump_slab(fp, "", slab);\n            slab = slab->next;\n        }\n    }\n}\n\nstatic void dump_region(FILE *fp, const char *tag, const Region *reg)\n{\n    fprintf(fp, "Region: %s (%p)\\n", tag, reg);\n    if (reg != 0)\n    {\n        fprintf(fp, "Address: %p, size: %6d, next = %p\\n",\n                reg->addr, reg->size, reg->next);\n    }\n}\n\nstatic void dump_memory(FILE *fp, const char *tag, const Memory *mem)\n{\n    fprintf(fp, "Memory: %s (%p)\\n", tag, mem);\n    if (mem != 0)\n    {\n        Region *reg = mem->region;\n        dump_region(fp, "Base", reg);\n        while (reg->next != 0)\n        {\n            dump_region(fp, "Next", reg->next);\n            reg = reg->next;\n        }\n        for (int i = 0; i < NUM_CACHES; i++)\n        {\n            char line[32];\n            snprintf(line, sizeof(line), "line %d", i);\n            dump_cache(fp, line, &memory.cache[i]);\n        }\n    }\n}\n\nvoid allocator_init(void)\n{\n    fd = open(DEV_ZERO, O_RDWR|O_CREAT, 0600);\n    if (fd == -1)\n    {\n        perror("File open failed");\n        exit(0);\n    }\n\n    if (fd == -1)\n    {\n        perror("File open failed");\n        exit(0);\n    }\n\n    memory.region = NULL;\n\n    for (int i = 0; i < NUM_CACHES; i++)\n    {\n        memory.cache[i].size = 16 << i;\n        memory.cache[i].S = NULL;\n    }\n}\n\nstatic void *allocate_region(unsigned int size)\n{\n    assert(fd != -1);\n\n    Region *temp = memory.region;\n    void *mapped_addr = mmap(NULL, size + REGION_SIZE, PROT_READ | PROT_WRITE,\n                             MAP_PRIVATE, fd, 0);\n\n    if (mapped_addr == MAP_FAILED)\n    {\n        perror("Mapping failed");\n        exit(0);\n    }\n    printf("Mapping succeeded: %p\\n", mapped_addr);\n\n    Region *region = mapped_addr;\n    printf("region = %p\\n", region);\n    region->size = size;\n    printf("size = %d\\n", region->size);\n    region->addr = (char *)mapped_addr + REGION_SIZE;\n    printf("allocated region: %p\\n", region->addr);\n\n    memory.region = region;\n    region->next = temp;\n\n    dump_memory(stderr, __func__, &memory);\n\n    return region->addr;\n}\n\nvoid *allocate(unsigned int size)\n{\n    size = ALIGN(size);\n    return allocate_region(size);\n}\n\nvoid deallocate(void *ptr)\n{\n    fprintf(stderr, "%s called for %p: unimplemented\\n", __func__, ptr);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我强烈建议创建函数来沿着所示的dump_memory()dump_region()dump_cache()dump_slab()函数转储复杂结构;它们通常非常有帮助,尽管它们实际上在调试过程中起到了转移注意力的作用。

\n