LINUX.ORG.RU

Как вызвать exec* без утечки памяти?

 ,


1

1

Когда вызывается функция семейства exec, то параметры копируются в память нового процесса, который замещает текущий без возможности возврата.

Соответственно, если параметры выделены на куче - формально происходит утечка памяти (still reachable в формулировке valgrind). Да, система заберет память назад по завершению процесса (флаг on close). Но ведь формально это неправильно.

Речь про случай, когда мы делаем fork перед exec.

Если выделять на стеке - то тогда есть жесткое ограничение на размер аргументов (плюс стек не резиновый, команда формально может быть до 3.5 Гб длиной, а стек всего метр-4).

Или может можно как-то по другому выделить?

★★★★★

Последнее исправление: PPP328 (всего исправлений: 1)

Когда вызывается функция семейства exec, то параметры копируются в память нового процесса, который замещает текущий без возможности возврата.

Соответственно, если параметры выделены на куче - формально происходит утечка памяти

?????

anonymous
()

Когда ты делаешь exec, процесс замещается новым. Целиком, со всеми потрохами (т. е. адресным пространством). Вся память, выделенная в этом процессе ранее, освобождается.

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

Вся память, выделенная в этом процессе ранее, освобождается.

Но не программистом, а системой. Соответственно все счетчики памяти, пользовательские или valgrind’овские считают что это lost.

PPP328 ★★★★★
() автор топика
Ответ на: комментарий от anonymous
switch(fork()) {
    case -1 : exit(1);
    case 0 :
       char ** args = allocate_args(...);
       execl(args[0], args);
       // never reached if started;
    default:
       ...
}

Формально, ресурс аллоцирован программистом, но не был освобожден им. Если проект использует кастомный аллокатор с подсчетом памяти либо valgrind (что тоже самое), то он заявит об утечке.

PPP328 ★★★★★
() автор топика

У тебя есть форк, который создает новый процесс с той же таблицой страниц. Дальше ты вызываешь exec*, который сначала проверяет останется ли у тебя 3/4 на стеке для запуска бинарника и подсчитывает количество аргументов. Если все ок, то exec копирует параметры себе в структуру(binprm или как-то так называется, лень искать), ну и дальше уже в свою vma начинает копировать бинарник с последующим началом исполнения. Команды на 3.5Гб быть не может, длинна пути файла тоже лимитирована, аргументы окружения тоже лимитированы. После удачного запуска можешь почистить аргументы в родительском процессе, если выделил их в хипе.

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

После удачного запуска можешь почистить аргументы в родительском процессе, если выделил их в хипе.

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

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

Создаст, если освободите в родительском процессе. CoW же.

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

Все аргументы, переменные окружения, путь до файла будут скопированы в новую page table. Это будет сделано вне зависимости от COW или чего-либо еще.

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

Вы из тех, кто перед exit() делаете много операций, чтобы освободить всю выделенную ранее память?

mky ★★★★★
()

команда формально может быть до 3.5 Гб длиной, а стек всего метр-4

Так аргументы команды же на стек в итоге и складываются, лул.

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

Да, потому что см. выше. Во-первых это правильно. Во-вторых это правильно с точки зрения статического анализа. В-третьих это правильно с точки зрения динамического анализа.

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

это правильно

тупняк какой-то

с точки зрения … анализа

с моей точки зрения твои анализаторы говно, тк не учитывают, что exit() всё чистит. кто знает, какими ещё false positive они тебя дурят

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

Я в вашем примере, после комментария:

// never reached if started;

не увидил вызова exit(). Вот мне и стало интерестно. Сначала я подумал, что вы не делете exit() после exec(). А теперь, боюсь подумать, что вы между exec() и exit() в потомке суёте кучу free(), чтобы операционке, которая и так по каким-то причинам не смогла exec() стало ещё веселее от выделения памяти под CoW из-за free().

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

не увидил вызова exit()

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

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

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

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

Это упрощенный пример без обработки ошибок. Речь о нормальном цикле работы программы.

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

Формально, ресурс аллоцирован программистом, но не был освобожден им.

Формально ресурс был освобождён успешным вызовом execl. Баги анализаторов posix не колышут.

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