Использование отображаемых файлов

В Windows под памятью подразумевается не только оперативная память (ОЗУ), но также и память, резервируемая операционной системой на жестком диске. Этот вид памяти называется виртуальной памятью. Посредством страничной системы (paging system) подкачкив такую память на жестком диске отображаются код и данные. По мере необходимости требуемый фрагмент виртуальной памяти переносится из страничного файла в ОЗУ и, таким образом, становится доступным для выполнения. Данный механизм отображения можно применить и для любого другого файла, сделав его содержимое частью адресного пространства.

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

function CreateFileMappingA(hFile: THandle;

lpFileMappingAttributes: PSecurityAttributes;

flProtect, dwMaximumSizeHigh, dwMaximumSizeLow:DWORD;

lpName: PAnsiChar): THandle; stdcall;

Первый параметр соответствует дескриптору открытого при помощи функции CreateFile файла. Второй параметр является указателем на структуру с данными по защите файла. Посредством параметра flProtect указывается тип доступа к файлу, например, PAGE_READWRITE. С помощью параметров dwMaximumSizeHigh, dwMaximumSizeLow задается выделяемое пространство виртуальной памяти. Если отображается весь файл, то их значения можно задать равными нулю (при условии, что файл намного меньше 4 Гбайт). Последний параметр представляет собой имя объекта файлового отображения (применяется при вызовах из разных, но одновременно использующих созданный объект, программ).

Следующий этап построения отображения - спроецировать данные файла в адресное пространство. Этой цели служит следующая функция:

function MapViewOfFile(hFileMappingObject: THandle;

DwDesiredAccess: DWORD; dwFileOffsetHigh,

dwFileOffsetLow, dwNumberOfBytesToMap: DWORD):

Pointer; stdcall;

Первый параметр является дескриптором созданного объекта файлового отображения. С помощью второго параметра определяется режим доступа к объекту (FILE_MAP_WRITE, FILE_MAP_READ, FILE_MAP_ALL_ACCESS). Посредством третьего и четвертого параметров задается смещение отображаемого участка относительно начала файла (Чаще всего оба параметра равны нулю). Пятый параметр определяет количество байт данных файлового отображения – если равно нулю, то отображаются все данные, выделенные при этом функцией CreateFileMappingA.

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

По окончании работы с отображаемым в памяти файлом необходимо вернуть занятое адресное пространство, используя функцию function UnmapViewOfFile(lpBaseAddress:

Pointer): BOOL; stdcall;,где lpBaseAddress – используемый выше указатель. Последняя операция, которую надо выполнить – закрыть полученный выше дескриптор. Эта операция выполняется путем вызова еще одной функции Windows API: function CloseHandle(hObject: THandle): BOOL; stdcall;

ПРИМЕР 26

Анализ качества продукции на некотором производстве проводится путем измерения при некоторых условиях x некоторого признака y. Пусть определенная совокупность (x, y, My, Dy), состоящая из n=8 элементов записана в типизированный файл DatPro.dat, причем первоначально (My, Dy) равны нулю и их предстоит вычислить. Если производство работает нормально, то совокупность точек (x, y) должна ложиться на прямую линию. Методом наименьших квадратов необходимо постоянно контролировать на производстве этот факт, т.е., если ошибка отклонения функции y=f(x) от прямой линии меньше, чем некоторая величина x, то качество выпускаемой продукции в пределах нормы, - иначе необходимо принимать меры по изменению, например, технологического режима. Пусть x=0.08. В данном случае применяются следующие формулы расчета:

использование отображаемых файлов - student2.ru ; использование отображаемых файлов - student2.ru ;

использование отображаемых файлов - student2.ru ; использование отображаемых файлов - student2.ru ;

В этих формулах обозначено, например, использование отображаемых файлов - student2.ru

Расчет ошибки выполняется по следующей формуле:

использование отображаемых файлов - student2.ru

Результаты решения примера представлены на рис. 62.

использование отображаемых файлов - student2.ru

Рис. 62. Вариант решения примера 26.

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

unit Unit25;

Interface

uses Windows, Messages, SysUtils, Variants, Classes,

Graphics, Controls, Forms,

Dialogs, Buttons, StdCtrls;

Type

TForm1 = class(TForm)

Memo1: TMemo;

Button1: TButton;

BitBtn1: TBitBtn;

Memo2: TMemo;

Label1: TLabel;

Label2: TLabel;

procedure Button1Click(Sender: TObject);

public

hFile:THandle;

hFileMapObject: THandle;

PFileMem:Pointer;

end;

datP=record

x,y,My,Dy:real;

end;

const n=8;

var Form1: TForm1;

Implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

type buf=array of datP;

var FilName:string;

r:datP;

s:string;

i:byte;

ff:file of datP;

a,b,Sx,Sy,Sx2,Sxy:real;

begin

FilName:='DatPro.dat';

{$I-}

AssignFile(ff,FilName);

Reset(ff);

{$I+}

if IOResult<>0 then begin

memo1.Lines.Add('Ошибка открытия файла');

Exit;

end;

for i:=1 to n do begin;

Read(ff,r);

s:='';

with r do begin

s:=FormatFloat('0.00',x)+FormatFloat(' 0.00',y)+

FormatFloat(' 0.00',My)+

FormatFloat(' 0.00',Dy);

memo1.Lines.Add(s);

end;

end;

CloseFile(ff);

PFileMem:=nil;

hFile:=CreateFileA(pChar(FilName),GENERIC_READ or

GENERIC_WRITE, FILE_SHARE_READ or

FILE_SHARE_WRITE, nil,OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,0);

if hFile=INVALID_HANDLE_VALUE then begin

memo2.Lines.Add('Ошибка открытия файла Windows');

Exit;

end;

hFileMapObject:=CreateFileMapping(hFile, nil,

PAGE_READWRITE, 0,0,'ShareMem');

if hFileMapObject = INVALID_HANDLE_VALUE then begin

memo2.Lines.Add(

'Ошибка открытия файла отображения');

CloseHandle(hFile);

Exit;

end;

PFileMem := MapViewOfFile(hFileMapObject,

FILE_MAP_ALL_ACCESS,0, 0,n*Sizeof(datP));

if PFileMem = nil then begin

memo2.Lines.Add(

'Ошибка открытия отображения в памяти');

CloseHandle(hFileMapObject);

CloseHandle(hFile);

Exit;

end;

s:='';

Sx:=0.0;

Sy:=0.0;

Sx2:=0.0;

Sxy:=0.0;

FOR I:=0 TO n-1 DO

with buf(PFileMem)[I] do begin

Sx:=Sx+x;

Sx2:=Sx2+sqr(x);

Sy:=Sy+y;

Sxy:=Sxy+y*x;

end;

a:=(Sy*Sx2-Sxy*Sx)/(n*Sx2-sqr(Sx));

b:=(n*Sxy-Sy*Sx)/(n*Sx2-sqr(Sx));

Sy:=0.0;

FOR I:=0 TO n-1 DO

with buf(PFileMem)[I] do begin

My:=a+b*x;

Dy:=My-y;

Sy:=Sy+sqr(Dy);

end;

Sxy:=sqrt(Sy/(n-1));

memo2.Lines.Add(

'Ошибка='+FormatFloat(' 0.00000',Sxy));

if Sxy>0.08 then memo2.Lines.Add(

'В системе производства сбои...')

else memo2.Lines.Add(

'В производстве все в порядке!');

if PFileMem<>nil then UnMapViewOfFile(PFileMem);

if hFileMapObject<>0 then

CloseHandle(hFileMapObject);

if hFile=0 then CloseHandle(hFile);

AssignFile(ff,FilName);

Reset(ff);

s:='';

memo2.Lines.Add(s);

for i:=1 to n do begin;

Read(ff,r);

with r do begin

s:=FormatFloat('0.00',x)+FormatFloat(' 0.00',

y)+FormatFloat(' 0.0000',My)+

FormatFloat(' 0.0000',Dy);

memo2.Lines.Add(s);

end;

end;

CloseFile(ff);

end;

End.

Следует обратить внимание, какое преобразование типов было использовано для работы с выделенной под файл памятью. Самым замечательным является то, что любые изменения в памяти мгновенно отражаются в отображенном файле. Так, если второй раз запустить программу на выполнение, то исходные данные (рис. 62) будут отражать уже не нулевые значения величин My и Dy.

ПРОГРАММНЫЕ ПОТОКИ

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

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

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

ПРИОРИТЕТЫ ПОТОКОВ

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

Операционная система может назначать приоритеты в соответствии со своими установками, или можно задать нужный приоритет, используя специальные системные подпрограммы (например, SetPriorityClass). Система при выполнении приложения будет расставлять подзадачи в соответствии с их приоритетом и в том случае, когда нет доступных для выполнения подзадач с высоким приоритетом, выполняются подзадачи с приоритетом более низкого уровня. Если несколько подзадач имеют один итот же уровень приоритета, то они расставляются системой и выполняются одна за другой по кругу.

Приоритеты присваиваются отдельно процессам и каждому включенному в процесс потоку. Приоритет, устанавливаемый для процесса, известен как базовый приоритет (base priority). Базовый приоритет может изменяться от 0 до 31. Для потоков можно использовать приоритеты от 0 до 15, если процесс не имеет приоритета REALTIME_PRIORITY_CLASS. В табл. 16 приводятся основные сведения по приоритетам прцессов.

Таблица 16

Наши рекомендации