Мобильное программирование приложений реального времени в стандарте POSIX

       

Объекты в типизированной памяти


Объекты в типизированной памяти - это конфигурируемые реализацией именованные пулы памяти, доступные одному или нескольким процессорам системы через один или несколько портов. Пример подобной конфигурации показан на рис. 5.1.


Рис. 5.1.  Пример многопроцессорной системы с несколькими пулами памяти

Здесь пул памяти M4 доступен только процессору P2. Пул M2 включает в себя пулы M2.1 и M2.2, причем доступ к M2.1 возможен только через порт B1. Остальные пулы доступны обоим процессорам через указанные на рисунке порты.

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

Каждая допустимая комбинация пула памяти и порта идентифицируется именем, определяемым при конфигурировании системы способом, зависящим от реализации. Используя это имя, объект в типизированной памяти можно открыть и отобразить в адресное пространство процесса. Реализация должна поддерживать как динамическое выделение (резервирование) памяти из пула (аналог malloc(), когда приложение задает только запрашиваемый объем), так и отображение части пула с заданным в приложении смещением от начала. Зарезервированный фрагмент в последующем подлежит освобождению. Наконец, при доступе через определенный порт нужно иметь возможность выяснить смещение и размер непрерывного участка типизированной памяти.

Согласно стандарту POSIX-2001, для открытия объектов в типизированной памяти служит функция posix_typed_mem_open() (см. листинг 5.11).

#include <sys/mman.h> int posix_typed_mem_open (const char *name, int oflag, int tflag);

Листинг 5.11. Описание функции posix_typed_mem_open(). (html, txt)

Нормальным результатом функции posix_typed_mem_open() является файловый дескриптор объекта в типизированной памяти с именем name.

Аргумент oflag задает один из трех разрешенных видов доступа к объекту - O_RDONLY, O_WRONLY или O_RDWR.

Значение аргумента tflag определяет поведение объекта в типизированной памяти при последующих отображениях посредством функции mmap().
Может быть установлен один из трех флагов:

POSIX_TYPED_MEM_ALLOCATE

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

POSIX_TYPED_MEM_ALLOCATE_CONTIG

При вызове mmap() резервировать и отображать один непрерывный сегмент памяти.

POSIX_TYPED_MEM_MAP_ALLOCATABLE

При вызове mmap() отображать объект в адресное пространство процесса, не воздействуя на возможности резервирования памяти.

Если не установлен ни один из описанных флагов, указанный фрагмент объекта отображается в адресное пространство процесса и становится недоступным для резервирования; после отмены отображения возможность резервирования восстанавливается.

Отметим, что, в отличие от объектов в разделяемой памяти, объекты в типизированной памяти нельзя создать - их можно только открыть. Не применима к ним (точнее, к их открытым дескрипторам) и функция установки размера ftruncate().

После того, как объект в типизированной памяти открыт, посредством функции posix_typed_mem_get_info() (см. листинг 5.12) можно выяснить максимальный объем памяти, доступной для резервирования. Это важно, поскольку типизированная память зачастую является дефицитным ресурсом.

#include <sys/mman.h> int posix_typed_mem_get_info (int fildes, struct posix_typed_mem_info *info);

Листинг 5.12. Описание функции posix_typed_mem_get_info(). (html, txt)

Соответствующая информация возвращается в структуре типа posix_typed_mem_info, на которую указывает аргумент info. Согласно стандарту POSIX-2001, эта структура должна содержать поле

size_t posix_tmi_length; /* Максимальный объем памяти, */ /* доступной для резервирования*/

Функция posix_typed_mem_get_info() принимает во внимание флаг POSIX_TYPED_MEM_ALLOCATE или POSIX_TYPED_MEM_ALLOCATE_CONTIG, если он был установлен при открытии объекта в типизированной памяти. При отсутствии этих флагов результат вызова posix_typed_mem_get_info() не специфицирован.



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

Еще одним проявлением общего принципа "если объект может измениться, должна быть возможность опросить его текущее состояние" является наличие в стандарте POSIX-2001 функции posix_mem_offset() (см. листинг 5.13).

#include <sys/mman.h> int posix_mem_offset ( const void *restrict addr, size_t len, off_t *restrict off, size_t *restrict contig_len, int *restrict fildes);

Листинг 5.13. Описание функции posix_mem_offset(). (html, txt)

Функция posix_mem_offset() позволяет выяснить адрес (смещение от начала), длину и дескриптор объекта (блока) в типизированной памяти, отображенного в адресное пространство процесса, начиная с адреса addr и имеющего длину len. Искомое смещение записывается по указателю off. По указателю contig_len помещается минимум из значения len и длины максимального непрерывного блока типизированной памяти, отображенного, начиная с addr. По указателю fildes выдается дескриптор, использованный в вызове mmap() при задании опрашиваемого отображения (если с тех пор дескриптор закрыли, выдается -1).

Если дескриптор объекта в типизированной памяти был открыт без флагов POSIX_TYPED_MEM_ALLOCATE и POSIX_TYPED_MEM_ALLOCATE_CONTIG, то возвращенные функцией posix_mem_offset() значения, будучи подставлены в вызов mmap(), зададут отображение в точности того же блока типизированной памяти, что и при первоначальном обращении к mmap().

Поведение функции posix_mem_offset() при попытке опросить характеристики отображения объекта, не являющегося объектом в типизированной памяти, зависит от реализации.

Рассмотрим возможную последовательность применения описанных функций. Пусть процессу A1, выполняющемуся на процессоре P1 (см. выше рис. 5.1), требуется зарезервировать блок памяти из пула M2, причем этот блок предполагается использовать совместно с процессом A2, выполняющемся на процессоре P2.


Поскольку P2 имеет доступ только к M2.2, процессы должны использовать именно эту часть M2.

В качестве первого шага процесс A1 вызывает функцию posix_typed_mem_open() с именем объекта "/m2.2-b1" (или каким-то еще в том же духе, включающим имена пула и порта) и установленным в аргументе tflag флагом POSIX_TYPED_MEM_ALLOCATE, получая открытый дескриптор, пригодный для резервирования памяти. Затем A1 обращается с этим дескриптором к функции mmap(), указывая в качестве длины довольно большое значение, например, 1048576. Пусть в ответ на запрос система выделяет два несмежных блока типизированной памяти длиной 524288 байт каждый, отображает их в один непрерывный фрагмент адресного пространства процесса A1 и возвращает указатель на него. Теперь процесс A1 может стандартным образом работать с полученным мегабайтным массивом.

Если процесс A1 пожелает выяснить, какие части пула M2.2 были зарезервированы, он должен обратиться к функции posix_mem_offset() с адресом первого элемента массива и полной длиной (1048576). В ответ он получит смещение и длину первого из выделенных блоков (524288). Поскольку возвращенная длина меньше запрошенной, требуется повторный вызов posix_mem_offset() со смещением в полмегабайта от начала массива и соответственно уменьшенной длиной. (В общем случае, разумеется, следует организовать цикл, вызывая posix_mem_offset() до тех пор, пока не будет исчерпан весь отображенный массив. Чтобы избежать подобных относительно сложных действий, можно при открытии объекта в типизированной памяти установить в tflag флаг POSIX_TYPED_MEM_ALLOCATE_CONTIG, но тогда, возможно, попытка резервирования непрерывного мегабайтного блока закончится неудачей.)

Чтобы обеспечить совместное использование типизированной памяти с процессом A2, A1 должен каким-то образом (очевидно, с помощью средств межпроцессного взаимодействия) передать тому смещения и длины зарезервированных блоков. Получив эти данные, A2 обращается к posix_typed_mem_open() с именем "/m2.2-b2" (доступ через порт B2) и нулевым значением tflag, а затем дважды вызывает mmap() для отображения обоих непрерывных фрагментов типизированной памяти в свое адресное пространство.Вероятно, при втором вызове целесообразно указать соответствующий адрес начала и флаг MAP_FIXED, чтобы "склеить" эти отображения и, как и в случае с A1, получить непрерывный мегабайтный массив.


Содержание раздела