Есть приложение (Linux/BSD), которое использует системный вызов brk для динамического выделения памяти в хипе. Приложение НЕ использует LIBC. Примером такого приложения может служить линукс-версия компилятора fasm. Если оформить это приложение в DLL, т.е. SO, то нет гарантии, что приложение-клиент, которое будет обращаться к этой DLL не будет использовать хип через LIBC!malloc или прямо через brk. Как избежать конфликта в данной ситуации? mmap не годится. Использовать malloc в DLL тоже не катит. Хотелось бы, чтобы хип DLL и хип клиента соответсвовали разным блокам памяти, по типу приватного хипа в винде. ЗЫ: Если я резервирую блок через mmap, как его потом можно растянуть, вроде realloc? ЗЫЫ: Как можно зарезервировать n-ное количество страниц памяти, но не выделять их физически? Или такого понятия в Линуксе вообще нет?
1) конфликта быть не должно 2) насколько я знаю, размер области памяти, выделенной через mmap() можно изменить через mmap(), указав в качестве адреса - адрес, который вернул предыдущий mmap(), а в качестве размера - желаемый размер (большой риск обломаться, если кусок за текущим mmap - отображением уже используется) 3) mmap() не будет выделять страницы физически (по крайней мере все) - он просто зарезервирует определенное число входов в PDE/PTE процесса
int_0dh Вот пример: 1. Клиент вызывает функцию mylib.f() 2. mylib.f() выделяет блок памяти от 0x80000000 до 0x80000010 и запоминает, что метка break изначально была по адресу 0x80000000. 3. Далее, клиент тоже выделяет себе кусок памяти из хипа. Если не учитывать выравнивание в различных BSD, то память он выделит по адресу 0x80000010 до 0x80000020, например. 4. Теперь либе захотелось освободить память, которую зарезервировала функция mylib.f() в п2. Как это сделать? - вернуть break на оригинальный адрес, т.е. на 0x80000000. Но в данном случае вылетит память, которую потом выделила себе клиентская программа. Чтобы избежать segfault'а достаточно не освобождать хиповую память, но она потом может кончиться... Вот в чём конфликт Как раз риск облома меня и не устраивает. В принципе, можно заюзать потом mremap, если память кем-то занята, но такой изврат сродни написанию своего менеджера памяти. Это обнадёживает. Спасибо за ответы.
а почему бы в библиотеке просто и тупо не выделять память на стеке? кроме того в библиотеке можно mmapить /dev/zero (ala SystemV) (память на которую /dev/zero отобразиться не имеет отношения к хипу), и реализовать что-то вроде своего malloc, (следует заметить что 1) mmap как и brk() не сможет выделить память меньше размера страницы, 2) естественно размер памяти, который она выделит кратен размеру страницы) использовать в библиотеке brk() для выделения памяти - имхо маразм
После возврата из функции обратно в клиентскую прогу, нет гарантии что клиент не перезапишет эту память на стеке. Есть вариант фиксировать esp, но возникает риск потери данных, которые клиент положил на стек перед вызовом библиотечной функции. И ещё один минус: когда я вызываю brk и памяти в хипе больше нет, получаю ошибку (<0), но когда я выделяю память на стеке и стек уже врезался в границу хипа, вместо ошибки я получу исключение. Так что, приходится ещё и исключения обрабатывать... И в таких условиях становится накладно использовать стек внутри библиотеки по прямому назначению (для временного хранения переменных, например). Придётся создавать новый стек фиксированного размера. В общем, "просто и тупо" оборачивается кучей дополнительных затрат и нет гарантии, что будет работать во всех случаях А чем этот /dev/zero лучше анонимного mmap? Такая особенность brk проявляется только в BSD. В Линуксе нет никакой гранулярности.
в Linux есть такая гранулярность (см mm/mmap.c sys_brk(), kernel 2.2/2.4/2.6 ). фактически brk() это просто обертка над mmap(). но это нас увело от первоначальной проблемы ЗЫ: если не секрет почему неприемлимо использовать libc унд её malloc соответственно?
int_0dh Реализации этих 2х системных вызовов сходны, но brk проще, т.к. память выделяется строго секвенциально. Я бы не назвал его обёрткой над mmap, хотя исходники смотрел только для 2.2 и 2.4. В BSD гранулярность проявляется следующим образом: brk(0x87654321) = 0x87654321, но фактически метка break установится на адрес следующей страницы и повторный вызов brk(0) вернёт настоящий адрес. А в Линуксе такого поведения не наблюдается, т.е. оба вызова возвращают 0x87654321. Физически, конечно, должна быть гранулярность и страницы выделяются только тогда, когда break переваливает за границу кратную 4К, но на юзере это никак не сказывается. Недавно обнаружил, что во FreeSBIE 1.1 не работает вызов libc!lseek и, глянув в исходники, понял почему не работает. А системный вызов lseek работает Но это к слову. Вообще, если не использовать LIBC, то можно собирать экзешники для Linux/BSD без необходимости перекомпиляции на машине пользователя, отсутствие интерпретатора ускоряет загрузку на порядок, да и смысла мало писать оптимизированный ассемблерный код, который постоянно будет вызывать тормозную сишную либу - лучше вообще тогда на С программить. К сожалению, brk и mmap на разных сборках Linux/BSD имеют разные ординалы, но это можно разрулить.