【问题标题】:C : Segmentation Fault when move main function to new fileC:将主要功能移动到新文件时出现分段错误
【发布时间】:2014-05-18 02:07:01
【问题描述】:

我实现了一个自定义内存分配器。我在这个内部文件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);
}

这里是主要代码:

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);
}

这是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

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

谢谢:)

【问题讨论】:

  • 您是否正确链接/包含文件?尝试删除强制转换,如果这让编译器抱怨它可能意味着它看不到 allocate 的声明/定义。不要忘记使用-Wall进行编译
  • 是的。我试图删除演员,但仍然遇到这个错误。 on memory.c 包含一个全局变量来控制我分配的内存。搬到外面时会影响我的代码吗?谢谢:)
  • 只要该全局变量仅在内部 memory.c 使用,就没有关系。你没有回答我关于你是否包含allocate 的原型以及是否链接到memory.c 的问题。
  • @Kninnug 是的。我有一个memory.h 文件,但它只包含一些结构。我没有在标题中包含 allocatedeallocate 函数。所以,现在,我尝试将这些函数作为原型添加到标题中,但我仍然遇到上述错误。谢谢:)
  • @Kninnug 我已经调试并看到从 allcate() 函数返回时的地址在监视窗口上标记为“超出范围”。这很奇怪吗?谢谢)

标签: c segmentation-fault


【解决方案1】:

有一些值得观察的地方:

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

     #define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
    

    就目前而言,ALIGNMENT 为 8,并且宏有效。但是假设我将 ALIGNMENT 更改为 16;那么面具是错误的。应该是&amp; ~(ALIGNMENT - 1)。当心明显的参数化不完整。

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

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

Mapping failed: Operation not supported by device

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

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

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

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

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

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: this is the test one
deallocate called for 0x10ca74018: unimplemented

代码

内存.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. that on `memory.c`
    printf("ptr = %p\n", ptr);
    strcpy(ptr, "this is the test one"); // segmentation fault here
    printf("print: %s\n", ptr);
    deallocate(ptr);
}

内存.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;
        }
        for (int i = 0; i < 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;

    for (int i = 0; i < 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 for %p: unimplemented\n", __func__, ptr);
}

我强烈建议制作函数来转储沿dump_memory()dump_region()dump_cache()dump_slab() 显示的函数的复杂结构;它们通常非常有帮助,尽管它们实际上是在调试这个问题时的一些红鲱鱼。

【讨论】:

  • 我创建了新项目,并使用了你所有的代码。当我运行时,这里是输出:Mapping succeed : 0x7f65b022000 region = 0x7f65b022000 Bus error (core dumped) 我的机器上是否有任何错误?谢谢:)
  • “总线错误”通常意味着对齐问题。找出导致此错误的行。
  • 在我的 Mac 上,当映射文件 (./dev.zero) 为空时,我得到了核心转储(总线错误)。当我创建一个非空文件(如答案中所述)时,它工作正常。如果您确定您正在使用的平台,这可能会有所帮助。
  • 我刚刚在 Ubuntu 13.10 VM 上编译了代码,当./dev.zero 为空时它崩溃了,并且在它不为空时工作(不需要重新编译)。
  • @JonathanLeffler 我添加了define DEV_ZERO_MEMORY_MAPPING 并且代码运行良好。我使用的是 Ubuntu 13.10,我也将您的源代码与我的源代码进行了比较,并按照您的指导进行操作,现在一切正常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-27
  • 2019-08-27
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 2022-10-04
相关资源
最近更新 更多