LINUX.ORG.RU

Segmentation fault при выделeнии памяти в malloc()


0

0

Есть программа, которая работает под DOS/Win 9x,NT. Откомпилял тоже самое с небольшими исправлениями под linux. Попрежнему работает под DOS/Win, но под linux выдает сообщение segmentation fault во время инициализации в malloc(). Пытаюсь выделить памать под 3х мерный массив 20х20х20 из double. За одну порцию выделяестя небольшой кусок 248 байт. Памяти достаточно (128MiB со свопом). В чем может быть проблема? Понимаю, что вариантов масса. Но куда хоть копать?

anonymous

какой вопрос, такой ответ: поставь проверенный дистрибутив и поменьше лазай в нём ручками. А для начала отнеси своё чудо к кому-нибудь и убедись что всё не работает именно у тебя, если же и у кого-нибудь не работает, то тебя смело можно объявлять человеком года.

anonymous
()
Ответ на: комментарий от anonymous

Проверенный это который? Сейчас установлен SuSE 7.0 Относил на другую машину, там программа работать не захотела, т.к. не нашла библиотеку libstdc++. Оказывается оно еще и с shared lib скомпилялось.

anonymous
()

А ты хочешь, что-бы она по умолчанию статически линковалась со стандартными либами ? :) На другой машине нет C++ runtime'a. А почему ты уверен, что в malloc'e дело ? Посмотри в дебагере, и если дело действительно в malloc'e (а не в использовании памяти с нулевым указателем например), напиши здесь строчку вызова malloc'a, или лучше кусок исходника. А вообще юзай new - он получше будет, да и портабельней к тому же.

timur
()

честно говоря такой глюк я когда-то наблюдал, и как-то избавился от него. Попробуй отключить оптимизацию (ну это вообще первое дело при debugging'е), проверь библиотечный malloc (и библиотечный ли malloc, может твой личный?) тестовой программой для того, чтобы убедиться "что ты в своём уме", ну а потом придётся обсекать программу до нескольких строк (с "проявляющимся" глюком) и выставлять на суд общественности. А насчёт new -- я в сорсы не лазал, но по опыту могу сказать (efence) что GNU'шный new реализован через тот же malloc (память по Страуструпу не инициализируется, поэтому не calloc), кстати попробуй той же efence подменить malloc'и и посмотреть что получится (очень рекомендую, она ловит _почти_ все ошибки работы с динамической памятью), пока советов, я думаю, хватит :-), потом расскажу несколько правил работы с динамической памятью (выработанных посредством собственных шишек :-)), придерживаясь которых можно избежать (части) segfault'ов и ликвидировать memory leakage.

anonymous
()

Я тоже не смотрел исходников new в g++, но думаю необязательно он реализован через malloc(). На linux-i386 его вполне могут реализовать, используя sys_brk через int 0x80, минуя все либы типа libc, libgcc etc. Да и неважно это - нет гарантии, что new реализован лучше, чем malloc(). Важно другое: new является элементом языка, тогда, как malloc() - библиотечной функцией. И уж если пишешь на С++, то по моему лучше использовать new, не говоря уже о том, что его можно переопределить со всеми вытекающими отсюда удобствами, да и семантика его IMHO как-то ближе к аллокированию памяти.

timur
()

Все попорядку.
1.Слинковано как статик. Но не пробывал на другой машине (не нашел). Может быть завтра удасться.
2.Привожу кусок исходника. Плюс стек после выдачи ошибки.
3.Вся оптимизация отключена (компиляю в Kdevelop, в настройках проекта все чисто).
4.Насчет проверки библиотечного malloc(). Он в программе вызывается неоднократно. И до появления ошибки все работает, память выделяется. Поэтому и простая тестовая программа наверняка будет работать.
5.Что такое efence? Где про нее можно почитать?

double*** allocate3DarrayOfDouble(uint ni, uint nj, uint nk)
{
int i,j;
double*** p;

if( ni < 1 || nj < 1 || nk < 1 )
return NULL;

p = (double***)malloc( sizeof(double**) * ni );

if( p == NULL ) return NULL;

for( i=0; i< ni; i++)
{
p[i] = (double**)malloc( sizeof(double*) * nj );

if( p[i] == NULL )
{
while( --i >= 0 )
free(p[i]);
free(p);
return NULL;
}

for(j=0; j< nj; j++)
{
// i=0, j=7 -> segmentation fault in malloc.c
// see line below
p[i][j] = (double*)malloc( sizeof(double) * nk );

if( p[i][j] == NULL )
{
//free current allocated j-string and i item occoding to j-string
while( --j >= 0 )
free( p[i][j] );
free( p[i] );

//free all other allocted strings
while( --i >= 0 )
{
j = nj;
while( --j >= 0 )
free( p[i][j] );
free( p[i] );
}
free( p );
return NULL;
} // if( p[i][j] == NULL )
} // for(j=0; j< nj; j++)
} // for( i=0; i< ni; i++)

return p;
} // double*** allocate3DarrayOfDouble(uint ni, uint nj, uint nk)

Вот такой функцией я пользуюсь для выделения массивов.
А вот так выглядит стек после получения сообщения segmentation fault.

chunk_alloc(ar_ptr=0x8140ee0, nb=248) at malloc.c:2948
__libc_malloc(bytes=240) at malloc.c:2696
allocate3DarrayOfDouble(ni=29, nj=22, nk=30) at read_tdt.cpp:985
read_tdt(pfilename=0x829d040) at read_tdt.cpp:558
begin_x() at begin_x.c:713
main(argc=3, argv=0xbffff874, envp=0xbffff884) at main.c:292

anonymous
()

Меняй libc :(. У меня стоит 2.1.3 и нормально сработало allocate3DarrayOfDouble(29, 22, 30). Попробуй malloc поменять на new или calloc. efence - это libefence, которая замещает все malloc, calloc etc. и пытается отловить ошибки с памятью. man efence. Просто добавь к параметрам линковки -lefence.
P.S. efence = Electric Fence

timur
()

Менять malloc на new, calloc смысла нет - т.к. они сами пользуются
malloc-ом.
Как правильно заметил предыдущий товарисч - посмотри какая у тебя версия glibc.
А еще попробуй повозится с mallopt - про нее можно прочитать
в info libc, Malloc Tunable Parameters. Если ошибка в glibc то, скорее всего изменением
некоторых параметров mallop - можно будет добится того что ошибка перестанет выскакивать.

Еще одно, что весьма вероятно - это то что ты где-то затираешь
данные самого malloca - выходишь за пределы выделенного блока, посмотри
на сей предмет свой код, я думаю что дело именно в этом. Для отлавливания
такой херни можешь воспользоватся glibc-шной хфункцией mcheck.

temofey
()

Вот кстати пример на вскидку повторяющий твой подвиг, правда
вылетает чуть в другом месте malloca чем у тебя, но
это зависит от того в каком месте ты нагадил.
#include <stdlib.h>


int main()
{
        char *s1,*s2;
        int i;
        s1=malloc(1);
        for(i=-300;i<300;i++)
        {
                s1[i]=-1;
                s2=malloc(10);/*тут вываливается в core*/
        }
        return 0;
}

Так что как говорится ищи на третей планете.

temofey
()

ну efence или mcheck дело вкуса конечно, efence более автоматизированный
вариант проверки, что для начала debugging'а лучше, затем уже, если
потребуется произвести какие-то действия на inconsistency можно и
mcheck попользовать. А bug скорее всего именно в затирании служебной
инфы malloc'а, (у меня всё так и было -- temofey попал в десяточку,
и установил я это после нескольких дней изнурительного трахача :),
потом хороший человек подсказал попробовать efence которая моментально
указала мою ошибку) Теперь о правилах хорошего тона. Для кого-то
они не новость, естественно как и любые правила они налагают ограничения
но если есть возможность лучше их придерживаться (всех ОДНОВРЕМЕННО)
-- плохого не будет:

1. указатели на динамические области (далее ДУ) инициализируются нулями

2. при аллоцировании памяти указатель проверяется на нулёвость

3. при использовании ДУ вначале производится проверка ненулёвости
указателя (отсекается часть segfault'ов, остальные -- выход за
пределы -- проверяется либо во время отладки c помощью efence,
либо, как в любом нормальном контейнере, вводится size
который служит для проверки в run time при доступе к элементам)

4. при освобождении памяти ДУ сразу обнуляется (ликвидируется часть
segfault'ов и устраняется memory leak -- см. п. 2)

как в любом механизме, все части должны работать одновременно -- по
частям, очевидно, работать не будет. Есть проблема с копиями указателей
-- за всё надо платить (вспомните тот же auto_ptr)

anonymous
()

да, забыл добавить, для дюжины динамических объектов и прозрачной
программы такая схема может и не к чему а вот для развесистой
программы с кучей перемежающихся malloc/free самое то.

2temofey: я тут подумал на счёт макросов для переопределения new/delete
и их tracing'а с помощью mtrace -- см. свой вопрос ранее.

anonymous
()

Макросы негодятся, шибко хлипко все получится.
А вот просто переопределить - можно.
Я даже готовое нашел LeakTracer называется - чуть чуть его
подровнял - и получилось пось то что хотелось. Может както на веб выложу.
Правда в отличие от efence - он на чисто программных механизмах основан,
без VM, но принцип тотже что и mtrace.
Кстати не можешь привести пример использования efence.
А то я попробовал у меня ничего не вышло, пробовал так:
...
char *s1=malloc(1);
s1[1]=0; /*тут поидее должен вывалится sefault */
...
$ gcc -g -lefence test.c
$ ./a.out
Нихрена не вываливается.

temofey
()

Или я дурноватый, или лыжи не едут :-(
Диктую большими буквами
#include <stdlib.h>                                                        15 41
int main()
{
        char *s1;
        int i;
        s1=malloc(1);
        if(s1)
        {
                s1[-1]=1;
                s1[1]=1;
                for(i=0;i<3000;i++)
                {
                        printf("%d\n",i);
                        s1[i]=-1;
                }
        }
        return 0;
}

$ gcc -lefence -g test.c
$ export EF_ALIGNMENT=0;./a.out
0
....
2615
2616
Segmentation fault (core dumped)

Но в этом месте и без efence вываливается. Граждане просветите что я делаю не так.

temofey
()

Да святится имя твое Hamster. Получилось.
Видимо это тот варант когда от перестановки слагаемых сумма меняется :-).

temofey
()

А кто скажет почему надо использовать EF_PROTECT_BELOW в efence? Что разве есть принципиальные трудности в размещении запрещенных зон одновременно перед и после выделенного блока? Я имею в виду автора efence. Зачем лишний геморой на голову пользователей. По два раза код тестировать? Зачем же?

temofey
()

Для начала зарегистрируюсь, чтобы было понятно кого пинать :),
и copyright поставлю на вышеописанный механизм чтобы дебилл гейтс
не объявил его своей собственностью, итак прошу считать с сего момента
четвёрку вышеописанных правил под защитой закона об авторских правах: (с) 2000 anonymous :)

2temofey: перечитай внимательно ещё раз пункт WORD-ALIGNMENT AND OVERRUN DETECTION в мане efence.

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

/* array-in-one-string memory allocation model */
double***
allocate3DarrayOfDouble(uint ni, uint nj, uint nk)
{
  double*   p1;
  double**  p2;
  double*** p3 = NULL;
  int i = ni;
  int j = nj*ni;

  if(ni && nj && nk) {

    if ( (p3=(double***)calloc(ni, sizeof(double**))) ) {

      if ( !(p2=(double**)calloc(ni*nj, sizeof(double*))) ) {

	my_free(&p3, ni, nj);

      } else {

	while(--i) p3[i] = p2 + i*nj;
	p3[0] = p2;

	if ( !(p1=(double*)malloc(sizeof(double)*ni*nj*nk)) ) {

	my_free(&p3, ni, nj);

	} else {

	  while(--j) p2[j] = p1 + j*nk;
	  p2[0] = p1;

	}
      }
    }
  }

  return p3;
}

(короче и прозрачнее, на мой взгляд, чем первый вариант)
где my_free -- функция для освобождения всей аллокированной памяти:

/* robustness memory free */
void
my_free(double**** ptr, uint ni, uint nj)
{
  double*** p = *ptr;
/* if an array has been allocated by strings
  int i,j;

  if (!p) return;
  else
    for (i=0; i<ni; i++) {
      if (p[i]) {
	for (j=0; j<nj; j++)
	  if (p[i][j])  free(p[i][j]);
	free(p[i]);
      }
    }
  free(p);
  *ptr = NULL;
*/
/* prevent segfaults and memory leak */
  if (p) {
    if (p[0]) {
      if (p[0][0])
	free(p[0][0]);
      free(p[0]);
    }
    free(p);
    *ptr = NULL;
  }
}

здесь я размещаю весь массив в одну строку. Объяснение такое: аллокировать память
по строкам надо либо когда далее её из каких-то соображений необходимо освобождать
по строкам, либо сегментация виртуального адрессного пространства накладывает ограничения
на размер непрерывного куска памяти, второй случай на мой взгляд практически не реализуем
из-за большого отношения размера доступного виртуального адрессного пространства
к ему же физическому (попробуйте найдите современную машину где оно около единицы)
главное же достоинство размещения в одну строку очевидно всем использующим `езду поинтерами',
пардон за жаргон, последняя позволяет в некоторых ситуациях существенно уменьшить
количество выполняемых операций, пример: три вложенных цикла (по мерностям) в самом внутреннем
используется элемент вышеаллокированного массива. Если доступ к элементам производится
последовательно (циклы по i<ni, j<nj, k<nk), то компилятор легко прооптимизирует такую
ситуацию -- при доступе к элементам a[i][j][k] (массива a[ni][nj][nk]),
что транслируется в *(*(*(a+i*nj*nk)+j*nk)+k), внутренние выражения будут изменяться
только при изменении соответствующих счётчиков. Когда же доступ к элементам производится
в некотором непоследовательном порядке задача компилятора существенно усложняется,
какая-нибудь эвристика может сработать, а может и нет (но разработчики compiler'ов не зря
едят свой хлеб и простые случаи просечены :), в таком случае оптимизация остаётся на совести
прикладного программиста и вот здесь аллокирование в одну строку даёт свои преимущества.

На сегодня хватит, если интересно, то to be continued and comments are welcome.

filin ★★
()

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

filin ★★
()

солнце!!! если ты в таком длинном стиле будешь излагать свои ошибки то на тебя никто не будет обращать внимание. Кто любит чтобы его поучали? Но ты очень умный мужик, раз такое придумал. адназначна. И такой хитрый, с такими подлогами.

temofey
()

С новым Годом и проч...

Всем огромное спасибо за помощь! Баги пофиксены. Программа работает.
Malloc тут ни причем. С памятью программа работает так как надо.
Все баги нашлись "на третьей планете".

Точно сказать в чем же было дело я не могу. Но все segmantation fault
в библиотечных функциях пропали после исправления кода работающего
с текстовыми файлами. Открывал я файл в текстовом режиме

 FILE* f = fopen(fname, "wt");

затем с ним работал. При этом символы <space> и '\n' обрабатывались
особо. И все работало хорошо в win, пока я не осознал, что в linux
на буковку 't' просто забили. Поэтому при считывании из файла в
буфер преобразования последовательности "\r\n\" в '\n'
не производилось. И логика работы программы нарушалась.

2filin: Спасибо за совет. Было у меня нечто подобное.
Только не так красиво, как у тебя. Но при таком выделении памяти
тяжелее обнаружить выход индекса за пределы массива. Безусловно,
вызвать malloc всего лишь 3 раза приятно и работает быстрее.

Еще раз всем спасибо
и С Новым Годом!

She-She
()

Тут я коечто нацарапал по поводу отлавливания всякой туфты,
типа memory leaks - если интересно заходите
http://www.sit.kiev.ua/linux

temofey
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.