У (статически связанные) библиотеки DLL используют другую кучу, чем основная программа?

Я новичок в программировании Windows, и я просто "потерял" два часа, охотясь за ошибкой, о которой все, кажется, знают: вы не можете создать объект в куче в DLL и уничтожить его в другой DLL (или в основная программа).

Я почти уверен, что в Linux/Unix это не так (если это так, скажите об этом, но я уверен, что сделал это тысячи раз без проблем...).

На этом этапе у меня есть несколько вопросов:

1) У статически связанных DLL файлов используется другая куча, чем основная программа?

2) Является ли статически связанная DLL сопоставлена ​​в одном и том же пространстве процесса основной программы? (Я вполне уверен, что ответ здесь большой ДА, иначе не было бы смысла передавать указатели от функции в основной программе до функции в DLL).

Я говорю о простой/обычной DLL, а не о службах COM/ATL

EDIT: "статически связанным" я подразумеваю, что я не использую LoadLibrary для загрузки DLL, но я связываюсь с библиотекой заглушек

Ответ 1

DLL/exes необходимо будет связать с реализацией библиотек времени выполнения C.

В случае C Windows Runtime-библиотек вы можете указать, если вы хотите ссылаться на следующее:

  • Однопоточная библиотека времени выполнения C (поддержка однопоточных библиотек теперь прекращена)
  • Многопоточная DLL/многопоточная DLL для отладки
  • Библиотеки времени статического времени.
  • Немного больше (вы можете проверить ссылку)

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

Теперь это зависит от того, с какой библиотекой времени выполнения C связана DLL, о которой вы говорите. Предположим, допустим, что используемая вами DLL связана со статической библиотекой времени выполнения C, а ваш код приложения (содержащий основную функцию) связан с многопотоковой C Runtime DLL, тогда, если вы передадите указатель на память, выделенную в DLL в вашу основную программу и попытайтесь ее освободить или наоборот, это может привести к поведению undefined. Таким образом, основной основной причиной являются библиотеки времени выполнения C. Пожалуйста, выберите их внимательно.

Более подробную информацию о библиотеках времени выполнения C поддерживайте здесь и здесь

Цитата из MSDN:

Внимание Не смешивайте статические и динамические версии библиотек времени выполнения. Наличие нескольких копий библиотек времени выполнения в процессе может вызвать проблемы, поскольку статические данные в одной копии не используются совместно с другой копией. Компилятор не позволяет вам связываться как с статическими, так и с динамическими версиями в одном файле .exe, но вы все равно можете получить две (или более) копии библиотек времени выполнения. Например, библиотека динамической компоновки, связанная со статическими (не DLL) версиями библиотек времени выполнения, может вызвать проблемы при использовании с файлом .exe, который был связан с динамической (DLL) версией библиотек времени выполнения, (Вы также должны избегать смешивания отладочных и не отладочных версий библиотек в одном процессе.)

Ответ 2

Если у меня есть приложение, которое компилируется как .exe, и я хочу использовать библиотеку, я могу либо статически связать эту библиотеку из файла .lib, либо динамически связать эту библиотеку из файла .dll.

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

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

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

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

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

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

Могут существовать и другие проблемы, например, windows определяет определенные действия, например, как сбои выделения обрабатываются для каждого потока, а не для каждого модуля. Это означает, что код, выполняемый в модуле A в потоке, настроенном модулем B, также может работать непредвиденным образом.

Ответ 3

Позволяет сначала понять распределение кучи и стек в ОС Windows по нашим приложениям /DLL. Традиционно в операционную систему и библиотеки времени выполнения входит реализация кучи.

  • В начале процесса ОС создает кучу по умолчанию, называемую Process heap. Куча процесса используется для выделения блоков, если не используется другая куча.
  • Время выполнения языка также может создавать отдельные кучи в процессе. (Например, время выполнения C создает собственную кучу.)
  • Помимо этих выделенных куч, прикладная программа или одна из многих загруженных библиотек динамической компоновки (DLL) могут создавать и использовать отдельные кучи, называемые частными кучами
  • Эта куча находится поверх операционной системы Virtual Memory Manager во всех системах виртуальной памяти.
  • Давайте обсудим больше об ЭЛТ и связанных кучках:
    • Распределитель времени выполнения (CRT) C/С++: предоставляет функции malloc() и free(), а также новые и удаленные операторы.
    • CRT создает такую ​​дополнительную кучу для всех своих распределений (дескриптор этой кучи CRT хранится внутри библиотеки CRT в глобальной переменной с именем _crtheap) как часть ее инициализации.
    • CRT создает свою собственную кучу, которая находится поверх кучи Windows.
    • Куча Windows - это тонкий слой, окружающий диспетчер времени выполнения Windows (NTDLL).
    • Распределитель времени выполнения Windows взаимодействует с Virtual Memory Allocator, который резервирует и фиксирует страницы, используемые ОС.

Ваша DLL и ссылка exe на многопотоковые статические библиотеки CRT. Каждая DLL и exe, которую вы создаете, имеют свою собственную кучу, то есть _crtheap. Выделения и отчисления должны произойти из соответствующей кучи. То, что динамически выделяется из DLL, не может быть выделено из исполняемого файла и наоборот.

Что вы можете сделать? Скомпилируйте наш код в DLL и exes с помощью /MD или/MDd, чтобы использовать многопоточную и DLL-специфичную версию библиотеки времени выполнения. Следовательно, как DLL, так и exe связаны с одной и той же библиотекой времени выполнения C и, следовательно, с одним _crtheap. Выделения всегда сопряжены с де-распределениями в одном модуле.