Разделяемая память в FPC |
22.05.2005 Иван Шихалев |
При использовании динамических библиотек приходится соблюдать ограничение на типы данных, которые могут передаваться в параметрах процедур и функций. Если попытаться активно передавать динамические массивы и/или длинные строки (тип ansistring), то рано или поздно возникнет AV — ошибка 216. Кроме того, приходится следить, чтобы процедуры выделения/освобождения динамической памяти вызывались из одного и того же модуля.
При написании сложной многомодульной программы эти ограничения весьма неприятны.
Дело в том, что Free Pascal для распределения динамической памяти использует не функции операционной системы, а свои собственные, что позволяет значительно ускорить процесс. Однако это приводит к тому, что данные о состоянии памяти, выделенных и свободных блоках и т.д. хранятся в глобальных переменных исполняемого модуля. Таким образом, пул памяти динамической библиотеки отделен от пула памяти вызывающего ее приложения. А длинные строки и динамические массивы как раз и используют (неявно) динамическую память.
Соответственно, чтобы избавиться от нашей проблемы следует заставить менеджер памяти всех исполняемых модулей работать с одними глобальными данными.
К счастью, Free Pascal позволяет программисту заменить стандартный менеджер памяти любым другим. Для этого используется процедура SetMemoryManager().
Итак, чтобы все модули работали с одним пулом динамической памяти следует выделить один, который за эту память будет отвечать, а в остальных на него сослаться. Конкретно это делается так: пишется библиотека, которая экспортирует функцию, возвращающую ее менеджер памяти; пишется модуль, который в секции инициализации устанавливает этот менеджер памяти в качестве своего; во всех исполняемых файлах большого проекта используется данный модуль. При этом важно, чтобы в первичном файле (том, который начинается с program или library), модуль подключения был первым в списке uses — это нужно для того, чтобы менеджер памяти был установлен до инициализации прочих модулей.
Реализуется это просто:
{$IFDEF WIN32}
{$APPTYPE GUI}
{$ENDIF WIN32}
{$MODE OBJFPC}
{$SMARTLINK OFF}
library fpMem;
procedure GetMemMan (out MemMan : TMemoryManager); stdcall; export;
begin
GetMemoryManager (MemMan)
end;
exports
GetMemMan name 'GetSharedMemoryManager';
end.
{$MODE OBJFPC}
{$SMARTLINK OFF}
unit SharedMemory;
interface
implementation
procedure GetMemMan (out MemMan : TMemoryManager); stdcall;
external 'fpmem' name 'GetSharedMemoryManager';
var
MemMan : TMemoryManager;
initialization
GetMemMan (MemMan);
SetMemoryManager (MemMan)
end.
| FPC | 3.2.2 | release |
| Lazarus | 3.2 | release |
| MSE | 5.10.0 | release |
| fpGUI | 1.4.1 | release |