Конфликт между libc!malloc и [int 80h].brk

Тема в разделе "WASM.UNIX", создана пользователем Quantum, 10 июл 2006.

  1. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Есть приложение (Linux/BSD), которое использует системный вызов brk для динамического выделения памяти в хипе. Приложение НЕ использует LIBC. Примером такого приложения может служить линукс-версия компилятора fasm.

    Если оформить это приложение в DLL, т.е. SO, то нет гарантии, что приложение-клиент, которое будет обращаться к этой DLL не будет использовать хип через LIBC!malloc или прямо через brk.

    Как избежать конфликта в данной ситуации? mmap не годится. Использовать malloc в DLL тоже не катит. Хотелось бы, чтобы хип DLL и хип клиента соответсвовали разным блокам памяти, по типу приватного хипа в винде.

    ЗЫ: Если я резервирую блок через mmap, как его потом можно растянуть, вроде realloc?
    ЗЫЫ: Как можно зарезервировать n-ное количество страниц памяти, но не выделять их физически? Или такого понятия в Линуксе вообще нет?
     
  2. int_0dh

    int_0dh New Member

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    21
    Адрес:
    Russia
    1) конфликта быть не должно
    2) насколько я знаю, размер области памяти, выделенной через mmap() можно изменить через mmap(), указав в качестве адреса - адрес, который вернул предыдущий mmap(), а в качестве размера - желаемый размер (большой риск обломаться, если кусок за текущим mmap - отображением уже используется)
    3) mmap() не будет выделять страницы физически (по крайней мере все) - он просто зарезервирует определенное число входов в PDE/PTE процесса
     
  3. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    int_0dh
    Вот пример:

    1. Клиент вызывает функцию mylib.f()
    2. mylib.f() выделяет блок памяти от 0x80000000 до 0x80000010 и запоминает, что метка break изначально была по адресу 0x80000000.
    3. Далее, клиент тоже выделяет себе кусок памяти из хипа. Если не учитывать выравнивание в различных BSD, то память он выделит по адресу 0x80000010 до 0x80000020, например.
    4. Теперь либе захотелось освободить память, которую зарезервировала функция mylib.f() в п2. Как это сделать? - вернуть break на оригинальный адрес, т.е. на 0x80000000. Но в данном случае вылетит память, которую потом выделила себе клиентская программа.

    Чтобы избежать segfault'а достаточно не освобождать хиповую память, но она потом может кончиться... Вот в чём конфликт :dntknw:

    Как раз риск облома меня и не устраивает. В принципе, можно заюзать потом mremap, если память кем-то занята, но такой изврат сродни написанию своего менеджера памяти.

    Это обнадёживает. Спасибо за ответы.
     
  4. int_0dh

    int_0dh New Member

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    21
    Адрес:
    Russia
    а почему бы в библиотеке просто и тупо не выделять память на стеке?

    кроме того в библиотеке можно mmapить /dev/zero (ala SystemV) (память на которую /dev/zero отобразиться не имеет отношения к хипу), и реализовать что-то вроде своего malloc, (следует заметить что 1) mmap как и brk() не сможет выделить память меньше размера страницы, 2) естественно размер памяти, который она выделит кратен размеру страницы)

    использовать в библиотеке brk() для выделения памяти - имхо маразм :)
     
  5. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    После возврата из функции обратно в клиентскую прогу, нет гарантии что клиент не перезапишет эту память на стеке. Есть вариант фиксировать esp, но возникает риск потери данных, которые клиент положил на стек перед вызовом библиотечной функции. И ещё один минус: когда я вызываю brk и памяти в хипе больше нет, получаю ошибку (<0), но когда я выделяю память на стеке и стек уже врезался в границу хипа, вместо ошибки я получу исключение. Так что, приходится ещё и исключения обрабатывать... И в таких условиях становится накладно использовать стек внутри библиотеки по прямому назначению (для временного хранения переменных, например). Придётся создавать новый стек фиксированного размера. В общем, "просто и тупо" оборачивается кучей дополнительных затрат и нет гарантии, что будет работать во всех случаях :)

    А чем этот /dev/zero лучше анонимного mmap?

    Такая особенность brk проявляется только в BSD. В Линуксе нет никакой гранулярности.
     
  6. int_0dh

    int_0dh New Member

    Публикаций:
    0
    Регистрация:
    12 окт 2005
    Сообщения:
    21
    Адрес:
    Russia
    в Linux есть такая гранулярность (см mm/mmap.c sys_brk(), kernel 2.2/2.4/2.6 ). фактически brk() это просто обертка над mmap(). но это нас увело от первоначальной проблемы :)

    ЗЫ: если не секрет почему неприемлимо использовать libc унд её malloc соответственно?
     
  7. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    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 имеют разные ординалы, но это можно разрулить.