Вопрос

У меня странная ошибка в моей программе, мне кажется, что malloc() вызывает SIGSEGV, что, насколько я понимаю, не имеет никакого смысла.Я использую библиотеку под названием simclist для динамических списков.

Вот структура, на которую мы будем ссылаться позже:

typedef struct {
    int msgid;
    int status;
    void* udata;
    list_t queue;
} msg_t;

И вот этот код:

msg_t* msg = (msg_t*) malloc( sizeof( msg_t ) );

msg->msgid = msgid;
msg->status = MSG_STAT_NEW;
msg->udata = udata;
list_init( &msg->queue );

list_init вот где происходит сбой программы, вот код для list_init:

/* list initialization */
int list_init(list_t *restrict l) {
    if (l == NULL) return -1;

    srandom((unsigned long)time(NULL));

    l->numels = 0;

    /* head/tail sentinels and mid pointer */
    l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
    l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
    l->head_sentinel->next = l->tail_sentinel;
    l->tail_sentinel->prev = l->head_sentinel;
    l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL;
    l->head_sentinel->data = l->tail_sentinel->data = NULL;

    /* iteration attributes */
    l->iter_active = 0;
    l->iter_pos = 0;
    l->iter_curentry = NULL;

    /* free-list attributes */
    l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *));
    l->spareelsnum = 0;

#ifdef SIMCLIST_WITH_THREADS
    l->threadcount = 0;
#endif

    list_attributes_setdefaults(l);

    assert(list_repOk(l));
    assert(list_attrOk(l));

    return 0;
}

линия l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * это место, где вызывается SIGSEGV в соответствии с трассировкой стека.Я использую gdb / nemiver для отладки, но нахожусь в растерянности.При первом вызове этой функции она работает нормально, но во второй раз всегда выходит из строя.Как malloc() может вызвать SIGSEGV?

Это трассировка стека:

#0  ?? () at :0
#1  malloc () at :0
#2  list_init (l=0x104f290) at src/simclist.c:205
#3  msg_new (msg_switch=0x1050dc0, msgid=8, udata=0x0) at src/msg_switch.c:218
#4  exread (sockfd=8, conn_info=0x104e0e0) at src/zimr-proxy/main.c:504
#5  zfd_select (tv_sec=0) at src/zfildes.c:124
#6  main (argc=3, argv=0x7fffcabe44f8) at src/zimr-proxy/main.c:210

Любая помощь или понимание очень ценятся!

Это было полезно?

Решение

malloc может вызвать ошибку, например, когда куча повреждена. Убедитесь, что вы не пишете ничего, выходящего за пределы какого-либо предыдущего выделения.

Другие советы

Возможно, нарушение памяти происходит в другой части вашего кода. Если вы работаете в Linux, вам обязательно стоит попробовать valgrind. Я бы никогда не поверил своим собственным программам на Си, если бы они не проходили через valgrind.

РЕДАКТИРОВАТЬ: еще один полезный инструмент - Электрический забор . Glibc также предоставляет MALLOC_CHECK_ переменную среды, чтобы помочь отладить проблемы с памятью. Эти два метода не влияют на скорость бега так сильно, как valgrind.

Возможно, вы где-то перед этим вызовом испортили кучу из-за переполнения буфера или вызова free с указателем, который не был выделен malloc (или который уже был освобождена).

Если внутренние структуры данных, используемые malloc, будут повреждены таким образом, malloc использует недопустимые данные и может произойти сбой.

Существует множество способов запуска дампа ядра из malloc()realloc() и calloc()).К ним относятся:

  • Переполнение буфера:запись за пределы выделенного пространства (стирание управляющей информации, которая malloc() хранился там).
  • Недостаточный расход буфера:запись перед началом выделенного пространства (стирание управляющей информации, которая malloc() хранился там).
  • Освобождение памяти, которая не была выделена malloc().В смешанной программе на C и C ++ это включало бы освобождение памяти, выделенной на C ++, с помощью new.
  • Освобождение указателя, который частично указывает на блок памяти, выделенный malloc() - что является частным случаем предыдущего случая.
  • Освобождение указателя, который уже был освобожден - пресловутое "двойное освобождение".

Использование диагностической версии malloc() или включение диагностики в стандартной версии вашей системы может помочь выявить некоторые из этих проблем.Например, он может быть способен обнаруживать небольшие неполучения и переполнения (поскольку он выделяет дополнительное пространство для обеспечения буферной зоны вокруг запрошенного вами пространства), и он, вероятно, может обнаруживать попытки освободить память, которая не была выделена или которая уже была освобождена, или указатели на часть выделенного пространства - потому что он будет хранить информацию отдельно от выделенного пространства.Стоимость заключается в том, что отладочная версия занимает больше места.Действительно хороший распределитель сможет записать трассировку стека и номера строк, чтобы сообщить вам, где в вашем коде произошло выделение или где произошло первое освобождение.

Вы должны попытаться отладить этот код изолированно, чтобы увидеть, действительно ли проблема обнаружена там, где генерируется ошибка. (Подозреваю, что это не так).

Это значит:

# 1: скомпилируйте код с -O0, чтобы убедиться, что GDB получает правильную информацию о нумерации строк.

# 2. Напишите модульный тест, который вызывает эту часть кода.

Я предполагаю, что код будет работать правильно при отдельном использовании. Затем вы можете протестировать другие модули таким же образом, пока не выясните причину ошибки.

Использование Valgrind, как предлагали другие, также очень хорошая идея.

Код проблематичен. Если malloc возвращает NULL, этот случай неправильно обрабатывается в вашем коде. Вы просто предполагаете, что память была выделена для вас, когда это на самом деле не было. Это может привести к повреждению памяти.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top