2009-09-17 4 views
8

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

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

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 вызывается согласно t o трассировка стека. Я использую 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 

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

+0

Как произносится 'list_t'? –

+0

В качестве побочного примечания, вызов 'srandom()' более одного раза не рекомендуется. Чтобы избежать ошибок в будущем, даже в 'list_init()', как известно, вызывается только один раз, вы должны переместить этот посев в какое-то место, которое более явно выполняется один раз, например, около вершины 'main()'. – RBerteig

ответ

25

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

+20

И используйте valgrind! Другие плакаты упомянули об этом инструменте, но, к сожалению, не получили много оборотов. Ничего плохого в этом ответе, но любое обсуждение повреждения памяти является неполным без предупреждения использовать valgrind! –

+2

Я получал sigsegv при вызове free(). Я запустил код под valgrind, и он показал, что я написал за буфером и испортил кучу. –

12

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

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

16

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

EDIT: еще один полезный инструмент: Electric fence. Glibc также предоставляет переменную окружения MALLOC_CHECK_, которая помогает отлаживать проблемы памяти. Эти два метода не влияют на скорость работы, как valgrind.

1

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

Это означает:

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

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

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

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

4

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

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

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

0

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

Смежные вопросы