Публикации FreePascal

Разделяемая память в 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.

Примечание

Надо заметить, что таким образом мы сделали общим только менеджер динамической памяти. Структура классов, обработка исключений и т.д. по прежнему остаются раздельными.
Актуальные версии
FPC3.2.2release
Lazarus3.2release
MSE5.10.0release
fpGUI1.4.1release
links