实现动态内存管理功能

时间:2018-01-10 04:19:37

标签: c linux gcc

我尝试实现动态内存管理功能并取得成功。然而,当我尝试实现另一个以完全相同的方式打印关于内存的信息的函数时,我得到了分段错误。我正在使用Ubuntu,gcc。

以下是我的相关代码:

在mm_alloc.c中:(其他函数malloc,free等也在这里)

#include "mm_alloc.h"
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"

void mm_print_mem() {

   printf( "\nstart_addr\tsize\tfree\tprev\tnext\n");
   printf("=============================\n");

    printf("HEAD OF LL %p\n", metadata);
    METADATA *currentPtr = metadata;
    int i = 0;
    while (currentPtr!= NULL  && i <= 10) {
        printf("%p\t%d\t%d\t%p\t%p\n", currentPtr, (int)currentPtr-
>size, currentPtr->free,currentPtr->prev,currentPtr->next);
        if (currentPtr->next == NULL) break;
        currentPtr = currentPtr->next;
        i++;
    }
    return;
}

mm_alloc.h:

#pragma once

#include <stdlib.h>

typedef struct METADATA{
    struct METADATA *next;
    struct METADATA *prev;
    int free;
    size_t size;
}METADATA;

METADATA *metadata;

void *mm_malloc(size_t size);
void *mm_realloc(void *ptr, size_t size);
void mm_free(void *ptr);
void mm_print_mem();

和测试功能。在这里,当我调用mm_print_mem函数时,我得到了分段错误,其他人工作得很好。

int main() {
    load_alloc_functions();

    int *dizi = (int *)mm_malloc(5 * sizeof(int));
    dizi[4] = 5;
    dizi[2] = 10;
    printf("%d - %d\n", dizi[4], dizi[2]);
    mm_print_mem();

    return 0;
}

我在Ubuntu上使用这些命令来链接文件并运行测试文件。

gcc -c -Wall -Werror -fpic mm_alloc.c
gcc -shared -o libfoo.so 14011085.so
gcc mm_test.c -o try -ldl
./try

请注意,即使mm_print_mem的内部为空,也会出现分段错误。我在哪里做错了?

2 个答案:

答案 0 :(得分:2)

在全局变量metadata的声明中至少存在问题。它在包含文件中声明为METADATA *metadata;。因此,当一个完整的程序中存在一个单独的定义时,它具有正确的外部链接,但在每个翻译单元中都定义。不这样做是明确的未定义行为,不需要诊断。

有多种方法可以解决这个问题:

  • 在包含文件

    中声明它为extern
    extern METADATA *metadata;
    

    并且只在没有extern说明符的一个翻译单元中定义它:

    #include "mm_alloc.h"    // extern declaration
    ...
    METADATA * metadata;     // single definition in whole program
    
  • 将所有库函数放在一个单独的翻译单元中(如果有意义),在该文件中 define metadata并从mm_alloc.h中删除对它的任何引用文件。毕竟,它是图书馆的私人数据......

  • 从mm_alloc.h包含文件中删除对metadata的引用,该文件应仅声明库的外部接口,在文件的单个文件中定义(在文件级别没有extern说明符)并且在库中的所有其他文件中声明 extern METADATA *metadata;

没有看到其他功能的来源,我不能说是否还有其他问题......

答案 1 :(得分:1)

如果不提供A Minimal, Complete, and Verifiable example,你会让事情变得有点困难,但一个简短的例子会有所帮助。如果没有看到loadalloc函数,就会让我们猜测问题的确切位置或问题的严重程度。

那说一个简短的例子来说明你的mm_print_mem();功能应该至少让这个部分理顺。 (注意:该示例使用圆形链表,其中最后一个节点指向第一个和第一个&gt; prev指向最后一个。您可以调整测试和分配对于metadata->prevlast->next,如果您希望两端都有NULL

虽然您可以使用#pragma once,但您会发现只需使用传统的标头防护来防止多个包含,例如#ifndef HEADERNAME,然后是#define,例如

#ifndef  __mm_alloc_h__
#define  __mm_alloc_h__  1

#include <stdlib.h>

typedef struct METADATA {
    struct METADATA *next;
    struct METADATA *prev;
    int free;
    size_t size;
} METADATA;

METADATA *metadata;

// void *mm_malloc(size_t size);
// void *mm_realloc(void *ptr, size_t size);
// void mm_free(void *ptr);
void mm_print_mem();

#endif

你的mm_alloc.c很好,但你的循环控制有点奇怪,其中包含幻数 10作为条件。使用示例列表,您只需迭代并增加节点,直到currentPtr->next(例如,分配后currentPtr)等于metadata(完成圆圈)

#include "mm_alloc.h"
#include <stdio.h>

void mm_print_mem() {

    METADATA *currentPtr = metadata;

    printf ("\nstart_addr\tsize\tfree\tprev\t\tnext\n"
            "=========================================================\n"
            "HEAD OF LL %p\n", metadata);

    for (;;) {
        printf ("%p\t%d\t%d\t%p\t%p\n", currentPtr, (int)currentPtr->size,
                currentPtr->free,currentPtr->prev,currentPtr->next);
        currentPtr = currentPtr->next;
        if (currentPtr == metadata)
            break;
    }
}

我写了一个简单的测试程序,为你分配和填充20个节点,显示第一个和剩余节点的独立处理,例如,

#include "mm_alloc.h"
#include <stdio.h>

#define MAX 20

int main (void) {

    int i;

    for (i = 0; i < MAX; i++) {
        METADATA *node = malloc (sizeof *node);
        if (!node) {
            perror ("malloc node");
            return 1;
        }
        node->next = node->prev = NULL;
        node->free = MAX - i - 1;
        node->size = i;

        if (!metadata) {        /* 1st node is self-referencing */
            node->prev = node;  /* in circular linked-list */
            node->next = node;
            metadata = node;
        }
        else {  /* add rest at end as metadata->prev */
            node->prev = metadata->prev;
            node->next = metadata;
            metadata->prev->next = node;
            metadata->prev = node;
        }
    }
    mm_print_mem();

    return 0;
}

注意:你完成后应该释放节点)

编译共享对象库&amp;测试计划

$ gcc -Wall -Werror -fPIC -o mm_alloc.o -c mm_alloc.c
$ gcc -shared -o libmm_alloc.so mm_alloc.o

编译测试程序时,请确保您的程序可以找到您的共享库。如果不在标准库搜索路径位置,则使用链接器选项-rpath是指定自定义库的位置的好方法。 (请注意,它也必须在您复制的任何系统上的相同位置)

(下面使用行继续以允许编译字符串适合)

$ gcc -L"/path/to/your/lib/dir" \
  -Wl,-rpath="/path/to/your/lib/dir" \
  -Wall -o mm_alloc_tst mm_alloc_tst.c -lmm_alloc

示例使用/输出

$ ./mm_alloc_tst

start_addr      size    free    prev            next
=========================================================
HEAD OF LL 0x208d010
0x208d010       0       19      0x208d3a0       0x208d040
0x208d040       1       18      0x208d010       0x208d070
0x208d070       2       17      0x208d040       0x208d0a0
0x208d0a0       3       16      0x208d070       0x208d0d0
0x208d0d0       4       15      0x208d0a0       0x208d100
0x208d100       5       14      0x208d0d0       0x208d130
0x208d130       6       13      0x208d100       0x208d160
0x208d160       7       12      0x208d130       0x208d190
0x208d190       8       11      0x208d160       0x208d1c0
0x208d1c0       9       10      0x208d190       0x208d1f0
0x208d1f0       10      9       0x208d1c0       0x208d220
0x208d220       11      8       0x208d1f0       0x208d250
0x208d250       12      7       0x208d220       0x208d280
0x208d280       13      6       0x208d250       0x208d2b0
0x208d2b0       14      5       0x208d280       0x208d2e0
0x208d2e0       15      4       0x208d2b0       0x208d310
0x208d310       16      3       0x208d2e0       0x208d340
0x208d340       17      2       0x208d310       0x208d370
0x208d370       18      1       0x208d340       0x208d3a0
0x208d3a0       19      0       0x208d370       0x208d010

仔细看看,如果您有其他问题,请告诉我。