Мегаобучалка Главная | О нас | Обратная связь


Совместное использование общей памяти



2020-02-03 189 Обсуждений (0)
Совместное использование общей памяти 0.00 из 5.00 0 оценок




Традиционным является метод межзадачного взаимодействия при помощи совместно используемой памяти. В DOS и 16-разрядной Windows он был простым и не требующим пояснений — у всех задач, в том числе у операционной системы, было общее адресное пространство. Но именно из этого проистекали все беды и проблемы данных ОС. В 32x разрядных версиях Windows у каждого приложения свое адресное пространство, недоступное другим приложениям. Тем не менее, способ обмена данными через память существует.

Разделение данных осуществляется посредством отображения некоторого объема совместно используемой памяти в адресное пространство каждого приложения, участвующего в обмене. Память может быть разделяемой потому, что она фактически располагается вне адресного пространства приложения. Вообще говоря, в 32-разрядной Windows под "памятью" подразумевается не только оперативная память (ОЗУ), но также и память, резервируемая операционной системой на жестком диске. Этот вид памяти называется виртуальной памятью. Код и данные отображаются на жесткий диск посредством страничной системы (paging system) подкачки. Страничная система использует для отображения страничный файл (win386.swp в Windows 95 и pagefile.sys в Windows NT). Необходимый фрагмент виртуальной памяти переносится из страничного файла в ОЗУ и, таким образом, становится доступным.

Для выделения фрагмента совместно используемой памяти должен быть создан специальный системный объект Win 32, называемый отображаемым файлом. Этот объект "знает", как соотнести файл, находящийся на жестком диске, с памятью, адресуемой процессами. В нашем случае файл, который мы посредством объекта файлового отображения хотим сделать доступным нашему приложению — это не файл с расширением ТХТ, DLL. или ЕХЕ. Это страничный файл подкачки, используемый системой. Отобразив фрагмент страничного файла в память, адресуемую нашим приложением, мы, по сути дела, получили фрагмент памяти, на который в дальнейшем будем ссылаться, как на данные объекта файлового отображения.

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

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

Разделение данных между приложениями осуществляется посредством использования функций API, предоставляемых Win 32 для создания и использования объектов файлового отображения. Вот наиболее важные из этих функций:

CreateFileMapping
MapViewOfFile
UnMapViewOfFiie

Создание объекта файлового отображения

Отображаемый файл создается операционной системой при вызове функции CreateFileMapping. Этот объект поддерживает соответствие между содержимым файла и адресным пространством процесса, использующего этот файл. В нашем случае целью будет совместное использование данных. Страничный файл операционной системы используется как расширение памяти. Таким образом, создав отображаемый файл, связанный со страничным файлом операционной системы, мы имеем в качестве результата выделение глобально доступной памяти. Так как эта память является общедоступной, то другой экземпляр объекта файлового отображения, открытый программой, запущенной в качестве другого процесса, может иметь к ней доступ.

Функция CreateFileMapping имеет шесть параметров:

function CreateFileMapping (hFile: THandle;
ipFileMappingAttributes: PSecurityAttributes;
flProtect,
dwMaximumSizeHign,
dwMaximurnSizeLow: DWORD;
lpName: PChar): THandle; stdcall;

Первый параметр имеет тип THandle. Для наших целей значение этого параметра всегда должно быть $FFFFFFFF (значок "$" указывает на то, что значение шестнадцатеричное). В принципе на этом месте должен стоять дескриптор уже открытого при помощи функции CreateFile файла; но, поскольку мы имеем дело с необычным файлом, указание значения $FFFFFFFF приводит к связыванию объекта файлового отображения именно со страничным файлом операционной системы.

Второй параметр — указатель на запись типа TSecurityAttributes. В нашем случае значение этого параметра всегда равно nil. Третий параметр имеет тип dword. Он определяет атрибут защиты. Чтобы при помощи отображаемого файла организовать совместное использование данных, третьему параметру следует присвоить значение PAGE_READWRITE.

Четвертый и пятый параметры также имеют тип dword. В 32-разрядной операционной системе значение dword имеет дайну 32 бита. Когда выполняется функция CreateFileMapping, значение типа dword четвертого параметра сдвигается влево на четыре байта и затем объединяется со значением пятого параметра посредством операции and. Проще говоря, значения объединяются в одно 64-разрядное число, равное объему памяти, выделяемой объекту файлового отображения из страничного файла операционной системы.

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

Шестой параметр имеет тип pСhar. Это значение представляет собой имя объекта файлового отображения, которое должно быть уникальным.

Функция СreateFileMapping возвращает значение типа THandle. В случае успешного завершения возвращаемое функцией значение представляет собой дескриптор созданного объекта файлового отображения. В случае возникновения какой-либо ошибки возвращаемое значение будет равно 0.

Следующий фрагмент кода демонстрирует использование функции СreateFileMapping:

var
hMappedFiie: THandie;
Begin
hMappedFile:=CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,
25, 'SharedBiock');
if (hMappedFiie = 0) then ShowMessage('Mapping error ! ');
End;

В этом примере функция выделяет 25 байт. езультирующий отображаемый файл называется SharedBlock. В случае успеха функция вернет описатель текущего объекта файлового отображения в переменную hFileMapObj. Переменная hFileMapObj имеет тип THandie. Если переменная hFileMapObj равна нулю, то это значит, что функция завершилась с ошибкой, о чем будет выведено соответствующее сообщение.

Подключение объекта файлового отображения к адресному пространству

При помощи функции CreateFileMapping мы только создали объект типа "отображаемый файл"; следующая задача — спроецировать данные файла в адресное пространство нашего процесса. Этой цели служит функция MapViewOfFile. Функция MapViewOfFile имеет пять параметров:

function MapViewOfFile(hFileMappingObject: THandie;
dwDesiredAccess: DWORD;
dwFileOffsetHigh,
dwFiieOffsetLow,
dwIMumberOfBytesToMap: DWORD): Pointer; stdcall;

Первый параметр имеет тип THandle. Его значением должен быть дескриптор созданного объекта файлового отображения — тот, который возвращает функция CreateFileMapping. Второй параметр имеет тип dword. Его значение должно быть установлено в FILE_MAP_WRITE (или. что то же самое, в FILE_MAP_ALL_ACCESS); это означает, что данные объекта файлового отображения будут доступны как по считыванию, так и по записи.

Третий и четвертый параметры также имеют тип dword. Это — смещение отображаемого участка относительно начала файла в байтах. В нашем случае эти параметры должны быть установлены в нуль, поскольку значение, которое мы даем пятому (последнему) параметру функции MapViewOfFile, также равно нулю.

Пятый и последний параметр функции MapViewOfFile, как и предыдущие параметры, имеет тип dword. Он используется для определения (в байтах) количества данных объекта файлового отображения, которые надо отобразить в процесс (сделать доступными для вас). Для достижения наших целей это значение должно быть установлено в нуль, что означает автоматическое отображение в процесс всех данных, выделенных перед этим функцией CreateFileMapping.

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

Следующий фрагмент кода демонстрирует вызов функции MapViewOfFile:

var
hMappedFile: THandie;
pSharedBuf: PChar;
Begin
hMappedFile:=CreateFileMapping ($FFFFFFFF, nil, PAGE_READWRITE, 0,
25, 'SharedBlock');
if (hMappedFile = 0) then ShowMessage ('Mapping error !') else begin
pSharedBuf:=MapViewOfFile(hMappedFile,FILE_MAP_ALL_ACCESS,0,0,0);
if (pSharedBuf = nil) then ShowMessage ('MapView error');
end;
End;

В этом примере выделяется 25 байт разделяемой памяти при помощи функции CreateFileMapping. Имя результирующего объекта файлового отображения будет SharedBlock. Если функция завершится успешно, то ссылка на новый отображаемый файл будет возвращена в переменную hMappedFile. Переменная hMappedFile имеет тип Thandle, если значение переменной hMappedFile равно нулю, то это означает, что функция отработала с ошибкой, о чем будет выведено соответствующее сообщение. Если значение не нулевое, то будет вызвана функция MapViewOfFile. Дескриптор hMappedFile является первым параметром этой функции и связывает созданный отображаемый файл с текущим процессом. Если функция завершится успешно, то в переменную pSharedBuf будет помещен начальный адрес памяти, по которому располагаются данные объекта файлового отображения с именем SharedBlock. Если функция отработает с ошибкой, то значение, возвращаемое функцией MapViewOfFile будет равно nil, в этом случае будет показано сообщение, констатирующее этот факт.

Совместное использование отображаемых данных

Чтобы разделить между процессами память, выделенную из страничного файла операционной системы, каждый процесс, участвующий в разделении, должен отобразить один и тот же фрагмент одного и того же файла. Такой результат может быть достигнут передачей во всех указанных процессах одинаковых параметров для функций CreateFileMapping и MapViewOfFile. Указатель, возвращаемый последней функцией, вы можете впоследствии считывать или записывать согласно своим потребностям. Если данные, связанные с этим указателем, доступны одному из процессов, то они также доступны и другим процессам.

Займемся очередным "клонированием" нашего примера с передачей данных о времени и свободной памяти. Общим для клиента и сервера теперь является описанный выше код по открытию и отображению файла. Он описан как реакция на событие формы onCreate. Когда запускается программа и создается форма, соответственно создается и подключается отображаемый файл. Код выглядит так:

procedure TSrvForm.FormCreate (Sender: TObjecr);
begin
hFileMapObj:=CreateFiieMapping(MAXDWORD, nil, PAGE_READWRITE, 0, 25,
'SharedMem');
if (hFiieMapObj:=0) then ShowMessage('Error: cannot map this file')
else begin
pSharedBuf:=MapViewOfFile(hFiieMapObj,FILE_MAP_WRITE,0,0,0);
if (pSharedBuf = nil) then
ShowMessage ('Error: pSharedBuf is nil');
end;
end;

Приведенный выше код работает точно так, как описано выше в разделе "Подключение объекта файлового отображения к адресному пространству". Единственным отличием является лишь то, что объявления переменных hFileMapObj и pSharedBuf перенесены в раздел var секции interface так, как это показано ниже:

interface
var Form1: TForm1;
hFileMapObj: THandle;
pSharedBuf; Pointer;
implementation

Компоненты типа TTimer есть и у клиента и у сервера. При каждом очередном событии от этого компонента сервер помещает нужные данные в совместно используемую область памяти:

type
pDataRecord = ^TDataRecord;
TDataRecord = record
DateTime : TDateTime;
Mem : Cardinal ;
end;

procedure TServerForm.Timer1Timer(Sender: TObject);

var ms : TMemoryStatus;
begin
GlobalMemoryStatus (ms);
pDataRecord(pSharedBuf)^.DateTime:= Now;
pDataRecord(pSharedBuf)^.Mem := ms.dwAvailPageFile + ms.dwAvaiiPhys;
end;

а клиент — читает эти данные, если нажата соответствующая кнопка:

procedure TClientForm.TimerITimerfSender: TObject);
begin
if MemSpeedButton.Down then MemSpeedButton.Caption:=
Format('%8dK',[pDataRecord(pSharedBuf)^.Mem div 1024]);
if TimeSpeedButton.Down then TimeSpeedButton.Caption:=
TimeToStr(pDataRecord(pSharedBuf)^.DateTime);
end;

Последним штрихом станет восстановление надписи на кнопке после того, как она снова нажата пользователем:

procedure TClientForm.TimeSpeedButtonClick (Sender: TObject);
begin
if not TimeSpeedButton.Down then TimeSpeedButton.Caption:='Time';
end;

procedure TClientForm.Mem.SpeedButtonClick(Sender: TObject);
begin
if not MemSpeedButton. Down then MemSpeedButton.Caption:='Memory';
end;

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

Прекращение отображения данных и закрытие объекта файлового отображения

Последние две функции, имеющие отношение к объекту файлового отображения, называются UnMapViewOfFile И CloseHandle. ФУНКЦИЯ UnMapViewOfFile делает то, что подразумевает ее название. Она прекращает отображение в адресное пространство процесса того файла, который перед этим был отображен при помощи функции MapViewOfFile. Функция CloseHandle также делает то, что подразумевает ее название; она закрывает дескриптор, в нашем случае это дескриптор объекта файлового отображения, возвращаемый функцией CreateFileMapping.

Функция UnMapViewOfFile должна вызываться перед функцией CloseHandle Функции UnMapViewOfFile передается единственный параметр типа указатель:

procedure TCIientForm.FormDestroy(Sender: TObject);
begin
UnMapViewOfFiie (pSharedBuf) ;
CloseHandle (hFileMapObj);
end;

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

Каналы

Протокол DDE пережил полосу своего расцвета, и хотя впоследствии появилась его сетевая версия (Network DDE, или NDDE), стандартом де-факто она не стала. Одним из кандидатов на замену DDE стал механизм каналов (pipes). О его роли говорит хотя бы то, что это один из основных протоколов работы такого продукта, как Microsoft SQL Server.

Сразу оговоримся, что каналы бывают двух видов — именованные (named pipes) и безымянные (anonymous pipes). Вторые не предназначены для связи между самостоятельными приложениями и представляют собой, так сказать, рудимент Win32 API. В этой книге рассматриваются только именованные каналы.

И еще одна очень важная оговорка. Каналы рассматривались Microsoft как протокол для организации клиент-серверных приложений. Поэтому их серверная часть реализована только в среде Windows NT и не поддерживается в Windows 95/98; клиенты могут быть созданы во всех этих ОС.

Канал можно представить себе как среду, через которую могут обмениваться данными два приложения. Обмен данными может быть как односторонним, так и двухсторонним. Тем не менее, одно из приложений играет роль сервера (оно создает канал), другое (или другие) лишь подключается к нему. Противоречия с вышесказанной фразой о двух приложениях здесь нет: на сервере канал виден как ресурс с единственным уникальным именем. Когда к серверу подключается очередной клиент, для него создается уникальный экземпляр канала со своими дескриптором, буфером и т, п. Но имя канала для всех клиентов одинаково.

Имя канала записывается в соответствии с так называемым соглашением UNC (Universal Naming Convention). Выглядеть оно должно так:

\servername\pipe\pipename

де servernanie — сетевое имя компьютера — сервера,

ipename — имя канала.

В случае, если клиенту нужно подключиться к серверу на том же компьютере, его сетевое имя заменяется точкой:

\.\pipe\pipename

Максимальная длина имени канала ограничена 256 символами; строчные и прописные буквы не отличаются.

Для программиста алгоритмы создания серверного и клиентского "концов" канала отличаются. Для создания сервера применяются специальные функции Win 32 API; клиентский же конец открывается как обычный файл и работа с ним также напоминает работу с файлом. Неудивительно, что в дальнейшем мы "упрячем" эти функции в класс Delphi — потомок HandleStream, ближайшего родственника файлового потока TFileStream.

Пока же рассмотрим искомые функции API. Главная из них для нас следующая:

function CreateNainedPipe(lpName: PChar;
dwOperiMode,
dwPipeMode,
nMaxInstances,
nOutBufferSize,
nInBufferSize,
nDefauitTimeOut: DWORD;
lpSecurityAttributes:PSecurityAttributes):THandle;

Она создает серверный конец канала с именем lpName. Остальные параметры перечислены в табл. 8.1.

Параметры функции createNamedPipe

Параметр Назначение

dwOpenMode

Режим открытия. Флаги:
PIPE_ACCESS_DUPLEX — двунаправленный обмен данными
PIPE_ACCESS_INBOUND — только от клиента к серверу
PIPE_ACCESS_OUTBOUND — только от сервера к клиенту
FILE_FLAG_WRITE_THROUGH — запись данных, минуя кэш
FILE_FLAG_OVERLAPPED — режим отложенной операции ввода/ вывода

dwPipeMode

Режим работы канала. Флаги:
PIPE_TYPE_BYTE — запись в режиме потока байт
PIPE TYPE_MESSAGE — запись в режиме потока сообщений
PIPE_READMODE_BYTE — чтение в режиме потока байт
PIPE_READMODE_MESSAGE — чтение в режиме потока сообщений
PIPE_WAIT — функции ввода/вывода не будут возвращать управление до завершения операции
PIPE_NOWAIT — функции ввода/вывода возвращают управление немедленно

nMaxinstances

Максимальное количество открываемых экземпляров канала, от 1 до PIPE_UNLIMITED_INSTANCES

nOutBufferSize
Размер буфера для записи

nInBufferSize
Размер буфера для чтения

nDefauitTimeOut
Задает время ожидания конца операции ввода/вывода в канале (в мс)

IpSecurityAttributes
Указатель на структуру Windows NT, содержащую информацию о правах доступа к каналу Pежимы потока байт и потока сообщений не слишком отличаются друг от друга — в первом случае система смешивает данные от различных операций чтения/записи в единый поток, во втором — разделяет их на отдельные порции. Канал в режиме PIPE_TYPE_BYTE может работать на чтение только в режиме чтения PIPE_READMODE_BYTE, канал в режиме PIPE_TYPE_MESSAGE — в обоих режимах чтения.

Функция

function ConnectNainedPipe(hNamedPipe: THandle;
lpOverlapped: POverlapped): BOOL;

позволяет подключиться к уже созданному каналу hNamedPipe, а функция

function DisconnectNaitledPipe (hNamedPipe: THandie): BOOL;

позволяет отключить клиента — она разрывает связь клиентского и серверного концов канала hNamedPipe.

Чтобы собрать информацию о состоянии канала hNamedPipe, нужно вызвать функцию

function GetNamedPipeln.fo(hMamedPipe: THandle;
var ipFlags: DWORD;
lpOutBufferSize,
lpInBufferSize,
lpMaxInstances: Pointer): BOOL;

Указатели lpOutBufferSize, lpInBufferSize, ipMaxInstances должны указывать на переменные, куда будут записаны размеры буферов и число открытых экземпляров канала. Параметр lpFlags указывает на переменную, в которую будут записаны флаги состояния канала. Среди них уже знакомый флаг PIPE_TYPE_MESSAGE, а также флаг PIPE_SERVER_END. Он установлен, если hNamedPipe — серверный конец канала.

function PeekNamedPipe (hNamedPipe: THandle;
lpBuffer: Pointer;
nBufferSize: DWORD;
lpBytesRead,
lpTotalBytesAvail,
lpBytesLeftThisMessage: Pointer): BOOL;

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

Function WaitNamedPipe(lpNamedPipeName: PChar; nTimeOut: DWORD): BOOL;

позволяет организовать ожидание окончания операции в канале. Параметр nTimeOut задает время ожидания в миллисекундах; возможны еще два особых значения — NMPWAIT_USE_DEFAULT_WAIT (ожидать в течение времени, указаного при создании канала) и NMPWAIT_WAIT_FOREVER (ждать бесконечно).

Осталось добавить, что чтение и запись в канал осуществляется так же, как и в обычный файл — функциями ReadFile и WriteFile.

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



2020-02-03 189 Обсуждений (0)
Совместное использование общей памяти 0.00 из 5.00 0 оценок









Обсуждение в статье: Совместное использование общей памяти

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

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



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (189)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.012 сек.)