LINUX.ORG.RU

Два философских вопроса по malloc/free

 , , ,


0

2

Здравствуйте. За месяц читания чужого Си-кода (и писания своего), меня не покидают два концептуальных вопроса про кучу

  1. Много где вижу конструкцию
    if(pth != NULL) free(pth);
    
    Несмотря на то, что в мане написано If ptr is NULL, no operation is performed. Откуда такое недоверие к манам?
  2. Каждый раз когда делаю malloc/free для локальных переменных (особенно если в цикле), в одном месте появляется неприятный зуд на тему «Память же фрагментируется. Нельзя часто выделять освобождать. Особенно много маленьких кусочков.» Оправдан ли этот зуд или все там норм с кучей?
★★★★★

Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от uuwaan

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

Есть более веский аргумент: если куча закончилась, то поздно пить боржоми, программа все равно уже не сможет сделать ничего более полезного, чем грохнуться в корку

annulen ★★★★★
()

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от annulen

Есть более веский аргумент: если куча закончилась, то поздно пить боржоми, программа все равно уже не сможет сделать ничего более полезного, чем грохнуться в корку

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

tailgunner ★★★★★
()
Ответ на: комментарий от Iron_Bug

Может тогда правильнее на попытку освобождения нулевого указателя выводить в stderr грязные ругательства в адрес разработчика?

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

если спецом погуглить можно найти признания Хоара(который быстрая сортировка) - его признание о том что он несёт отвественность за многомилиарДНОе за приравнивание null 0

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

Погоди-ка. Но ведь 0 - это существо из другого, непересекающегося мира значений. А в мире адресов и указателей, ноль не имеет особого смысла на существование. Или я не прав?

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

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

по мнению рыцаря(хиханьки по смаллиана).

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

т.е. вариант критерия валидности указателя можно ведь много придумать

например использовать его нечётность как признак невалидности (для другой железки знаковый бит - али вообще какую логическую_агрегацию над битовым представлением)

выбор в этом смысле все биты нули так же условен как и предпочтение real против surreal (https://www.youtube.com/watch?v=mPn2AdMH7UQ?t=

при том что хохма что если полистать Ньтонову(про бесконечные) его тезисы просто не исключают модель surreal :)

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

Ну просто правило, что если объект находится по адресу 0, значит всё плохо. Не лучше и не хуже других критериев, как мне кажется

Много ли объектов могут быть в нулевом адресе и что им там делать

makoven ★★★★★
() автор топика
Последнее исправление: makoven (всего исправлений: 1)

Уважаемые знатоки, подскажите, правильно ли я понимаю, что в:

int main(){
  char s[] = "Ima string";
  char *p = s - 1000;
  char cp = *(s - 1000);
  return 0;
}

присваивание p никогда не поломает программу, а cp может и поломать? Или оба случая могут поломать?

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

от знатока слышу.

char *p = s - 1000;

вычитание из адресса(константного и известного на момент компиляции так что в коде вообще может быть просто напросто размещение на стеке константы -) даёт некоторое число которое может и быть не валидным адрессом но как число - а чё нет то!

char cp = s[-1000];

да ты (Х|Ф)акир!

qulinxao ★★☆
()
Ответ на: от знатока слышу. от qulinxao

вычитание из адресса

Это к вопросу о твоих отрицательных адресах. Не поломается ли, если адрес «a» будет, скажем, 900?

да ты (Х|Ф)акир!

Ну это для наглядности и единообразия) Вопрос, валидно ли чтение вообще любого адреса? Видимо, нет..

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

тут(у именно этой абстрактной железки) ведь адреса как типа - адрес есть в момент чтения по адрессу - и тогда таки может случится всё то что может случится у железки с апаратным mmu и защитой страниц и прочего

и как это обработается зависит от полной композиции железки+её микрокода+супервизора ака ядро_ос + компилятора и его нюансов.

-----------------

по стандарту чтение(а запись тем более) допустимо только в границах последовательсти(массива,строки и т.п) по факту много нюансов.

qulinxao ★★☆
()
Ответ на: комментарий от makoven

кстати отчего в младших разрядах последние 2 числа отличаются? ведь судя по числу разрядов и l и ll одного числа байтов.

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

Короче, как я понял, адрес это просто число пока его не используешь. А адресная арифметика - обычная арифметика и за использование не считается. Ок.

по стандарту чтение(а запись тем более) допустимо только в границах

По крайней мере, шланг со всеми ворнингами не ругается на этот код

makoven ★★★★★
() автор топика
Последнее исправление: makoven (всего исправлений: 1)
Ответ на: комментарий от anonymous

Хотя указатель на элемент сразу после последнего разрешено получать, т.к. удобно для всяких полуинтервалов, опять же см. сноску. Именно поэтому в примере arr + 5, а не arr + 4.

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

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от makoven

если это хороший ЯП, а не какой-нибудь пых позорный, то его менеджер памяти делает mmap и забирает огромные страницы, которыми сам управляет и даже умеет отдавать обратно.

В куче давно уже никто особо не хранит ничего.

max_lapshin ★★★★★
()

Кули, создай для словоблудия другую тему.

tailgunner ★★★★★
()
Ответ на: комментарий от makoven

Надо читать не про ммап, а про модель памяти в современном процессоре/ос. Тогда будет понятно что и почему. http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ - «первая ссылка в гугле»

Если грубо - модель памяти представляет из себя key-value отображение виртуальных блоков(страниц) в реальные(физические блоки(страницы)).

Аллокации памяти - это просто создание в ядре «доступа» процесса к диапазону адресов.

Связывание визических/виртуальных страниц происходит лениво, т.е. при доступе к какому-то адресу из доступного диапазона(на самом деле после, но не важно) - этот адрес обрезается до адреса странице - это key и по нему записывается value(нечто с адресом физической страницы).

Что такое key-value адрес в адрес ты понимаешь и все фичи его то же.

Если задампить /proc/self/maps и вывести адрес аллокации.

void dump_maps(void) {
  splice(open("/proc/self/maps", O_RDONLY), NULL, STDERR_FILENO, NULL, ~0u, 0);
}

0x7fe52c01a000//адрес аллокации.
///proc/self/maps
...
7fe52c01a000-7fe90dd5e000 rw-p 00000000 00:00 0//rw - это ставилось в ммап, p(private) так же ставилось в ммап//диапазон равен ~15.5гигам, сколько собственно я и выделял.
...

get_total_ram() - кол-во рамы на твоей тачке.

__unlink() - отключает страницы.

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

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

reset_infvector_size() - отключает все страницы после определённого диапазона равного new_size.

Память подключается сама по мере использования.

Т.е. это самореаллоцируемый бесконечный вектор с усечением по реальной памяти, а сама «память» никуда не деётся.

Мой пример аллоцирует «бесконечный вектор», записывает туда мегабайт, гигабайт, усекает его до 500мегабайт, после до 1мегайбайт, а после до нуля. Попутно выводя сколько «памяти» съедает процесс.

Мы же спокойно можем взять любую последовательность ключей и

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

Для того, чтобы быстро преобразовывать адреса(не ходя в ядро) процессор кеширует некое кол-во пар страниц. Дефолтная страницы 4к. Объём кеша на современной тачке в районе 1к записей. Т.е. это всего 4метра памяти.

Это всё работает хорошо до тех пор, пока у тебя не происходит рандомных общений в объём памяти больше этих самых 4-х метров - тогда ядру на каждое общение, которое не попало в кеш - надо ходить в ядро.

Для решения этой проблемы запилили большие страницы 2/4/1024мегабайта. Теперь 1к 2-х метровых страниц - это уже 2гига. Кеш не будет промахиваться пока твои обращения в районе 2-х гигов.

Естественно, используя эти страницы вся память выделяется блоками по 2метра, что не рационально. Поэтому повсеместно они не используются.

Для того же хипа, который не уменьшается - это хорошее решение, поэтому в жабке так(со слов пацана).

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

Понимай под ядром структуры ядра. В данном случае это не имеет значения.

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

Почему бы и нет. Главное не перегибать с обертками, а то получится код systemd.

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