Насколько большой слишком большой для кучи

Моя программа написана на C, и я компилирую ее с помощью gcc. Я читаю файл и сохраняю содержимое файла в буфер. Для этого мне нужно, чтобы буфер был такого же размера, как файл. Я использую malloc() для выделения памяти для буфера. К сожалению, я столкнулся с файлом размером 277 МБ. Это слишком много для кучи? Я получаю ошибку seg во время выполнения, но не более того. Это работало для файлов размером до 160 МБ, но этот единственный выброс файла размером 277 МБ ломает его.

РЕДАКТИРОВАТЬ: valgrind дает мне

@0xC0000022L valgrind дает мне

==6380== Warning: set address range perms: large range [0x8851028, 0x190e6102) (undefined)
==6380== Warning: set address range perms: large range [0x8851028, 0x190e6028) (defined)
==6380== Warning: set address range perms: large range [0x190e7028, 0x2997c108) (undefined)
==6380== Warning: set address range perms: large range [0x190e7028, 0x2997c028) (defined)
==6380== Warning: silly arg (-1737565464) to malloc()
==6380== Invalid write of size 4
==6380==    at 0x8048A49: main (newanalyze.c:85)
==6380==  Address 0x4a00 is not stack'd, malloc'd or (recently) free'd
==6380==
==6380==
==6380== Process terminating with default action of signal 11 (SIGSEGV)
==6380==  Access not within mapped region at address 0x4A00
==6380==    at 0x8048A49: main (newanalyze.c:85)

но в строке 85 это всего лишь небольшая переменная, на которую не должен влиять размер файла.


person jbf81tb    schedule 12.07.2012    source источник
comment
Что было бы полезно, так это strace ‹ваша программа› и информация об ОС. Размер не слишком велик для современной кучи, и я почти уверен, что malloc в Linux со стандартным GCC легко выделяет пространство.   -  person Sam    schedule 12.07.2012
comment
Проверьте возвращаемое значение malloc. Корректной программе может не хватить памяти, но она никогда не выйдет из строя.   -  person Kerrek SB    schedule 12.07.2012
comment
Почему бы не использовать mmap для таких случаев? Кроме того, чтобы получить быстрый анализ, запустите программу под Valgrind (отладочная сборка).   -  person 0xC0000022L    schedule 12.07.2012
comment
mediafire.com/?y667x54elblca0a это информация о трассировке. вот информация об ОС от uname Linux ubuntu 3.2.0-26-generic-pae #41-Ubuntu SMP Thu 14 Jun 16:45:14 UTC 2012 i686 i686 i386 GNU/Linux   -  person jbf81tb    schedule 12.07.2012
comment
я думаю, что выделять весь файл в памяти нехорошо, если вы можете делать это по частям   -  person mulax    schedule 12.07.2012
comment
Спасибо за страйк! Перевариваю последние 3 строчки.   -  person Sam    schedule 12.07.2012
comment
Что ж, вы можете использовать gdb, чтобы проверить, не связана ли ошибка seg с чем-то еще, что ломается.   -  person noMAD    schedule 12.07.2012
comment
@0xC0000022L libc malloc переводит большие запросы на выделение в mmap внутренние вызовы.   -  person tdenniston    schedule 12.07.2012
comment
Да, это будет зависеть от реализации, хотя. Однако я хочу сказать, что mmap уже предоставляет средства для получения указателя на содержимое файла. Так зачем обходить это стороной?   -  person 0xC0000022L    schedule 12.07.2012
comment
@ 0xC0000022L Извините, я слишком быстро прочитал вопрос и упустил тот факт, что он отображал файл в память. Ты прав.   -  person tdenniston    schedule 12.07.2012
comment
Предупреждение: глупый аргумент (-1737565464) для malloc() выглядит как раздача. Вы вызвали malloc со значением, которого не должны. Возможно, ваш расчет запутался в знаковом/беззнаковом арифметическом переполнении, или вы делаете что-то еще глупое. Отследите этот вызов malloc, покажите нам вычисление и все типы, используемые для этого вызова.   -  person nos    schedule 12.07.2012


Ответы (3)


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

munmap(0xb7706000, 4096)                = 0
mmap2(NULL, 2557403136, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1  ENOMEM (Cannot allocate memory)

В качестве контрпримера у меня есть игрушечная программа, которая успешно работает:

mmap(NULL, 283652096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2d00994000

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

person Sam    schedule 12.07.2012
comment
Проблема в том, что программа не может выделить достаточно памяти даже для того, чтобы начать что-то делать, так что утечки быть не может. Но я получаю нулевой указатель, возвращаемый malloc при попытке выделить место для очень большой структуры, но не для буфера. Странно то, что я думаю, что структура должна быть в 36 раз больше размера буфера, но это означает, что она не должна работать с файлом размером 160 МБ, потому что у меня всего 4 ГБ ОЗУ. - person jbf81tb; 12.07.2012
comment
На самом деле это будет зависеть от ограничений ресурсов, наложенных системным администратором, а также оболочки, вызывающей команду (ulimit -a). Кроме того, учитывая, что ваш адрес уже находится в диапазоне адресов за пределами 32-битного диапазона, в то время как все адреса в вопросе ОП находятся в пределах 32-битного диапазона, у вас есть важное отличие. - person 0xC0000022L; 12.07.2012
comment
На самом деле, это имеет смысл как объем памяти в этой функции mmap2, потому что после небольшого пересчета структура чуть меньше чем в 10 раз превышает размер буфера. но тем не менее, моя системная информация говорит, что у меня 5,8 ГБ памяти, что, я думаю, я ошибаюсь, и у меня 6 ГБ ОЗУ. поэтому я не знаю, почему выделяется 2,5 ГБ памяти. - person jbf81tb; 12.07.2012
comment
Этот ответ рассказал мне, насколько велика была вещь, которая все испортила, и научил меня strace. Это закончилось проблемой с очень большой структурой, которую нельзя было сильно изменить. К счастью, я только что по ошибке сделал два массива больше, чем мне было нужно, поэтому изменил их, и размер структуры уменьшился чуть меньше чем наполовину. - person jbf81tb; 12.07.2012

Пожалуйста, обратите внимание на вывод Valgrind,

==6380== Предупреждение: глупый аргумент (-1737565464) для malloc()

-1737565464 — это целое число со знаком, а 2557401832 (>2G), если оно взято как целое число без знака. Вы передаете в malloc параметр ›2G вместо 277M.

И из следующей информации мы знаем, что вы пытаетесь записать по адресу 0x4a00, который является недопустимым адресом, в этом сценарии вы ожидаете SEGV. Пожалуйста, проверьте newanalyze.c:85 в своем коде, чтобы увидеть, что там есть.

==6380== Недопустимая запись размера 4

==6380== at 0x8048A49: основной (newanalyze.c:85)

==6380== Адрес 0x4a00 не стек, malloc или (недавно) освобожден

person Derui Si    schedule 12.07.2012

Следуя одному из комментариев, вот как открыть файл с помощью mmap(2). Это при условии, что вы используете Unix.

int fd;
struct stat S;
const char* file_base;

if ((fd = open(filename, O_RDONLY)) < 0) {
    fprintf(stderr, "Can't open file %s to read\n", filename);
    return NULL;
}
if (fstat(fd, &S) != 0) {
    fprintf(stderr, "Can't stat file %s!\n", filename);
    close(fd);
    return NULL;
}
if ((file_base = mmap(NULL, S.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
    fprintf(stderr, "Unable to map file %s\n", filename);
    close(fd);
    return NULL;
}

После этого file_base указывает на часть памяти, которая содержит все содержимое файла.

Преимущества этого способа:

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

Этот бит памяти помечен только для чтения; вы также можете проделать этот трюк с файлом для чтения-записи, хотя это означает, что если вы записываете в память, вы одновременно изменяете файл.

Если вы не используете Unix, вам может быть доступна функция mmap. Если нет, то будет какой-то родной для Windows способ сделать то же самое (CreateFileMapping).

person Norman Gray    schedule 12.07.2012
comment
Отличное и краткое объяснение файлов с отображением памяти. Избавил меня от написания моего ответа, который я только что начал. Добавил ссылку на справочную страницу к вашему ответу. +1 - person 0xC0000022L; 12.07.2012
comment
Этот ответ научил меня чему-то новому, но в итоге это оказалось не тем, что мне нужно было делать. Я бы дал ему +1, если бы мог, но мне нужно больше репутации. - person jbf81tb; 12.07.2012