Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2 3   вперед  Ctrl      все
 Файлы, отображаемые в память.  [new]
Ежов Дмитрий Сергеевич
Member

Откуда: Новокузнецк-Москва, Россия
Сообщений: 100
День добрый.
Есть ли в Дельфи компоненты, позволяющие работать с маппированными файлами.
Желательно, наследники TFileStream

?
12 ноя 20, 14:58    [22230794]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 51775

Работа с такими файлами принципиально не укладывается в интерфейс TStream.

Posted via ActualForum NNTP Server 1.5

12 ноя 20, 15:03    [22230801]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 26836
Мой гугл сразу выдал ссылку на https://torry.net/pages.php?id=228
12 ноя 20, 15:06    [22230805]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 12319
Dimitry Sibiryakov
Работа с такими файлами принципиально не укладывается в интерфейс TStream.
Да ладно, TIBBlobStream укладывается, а MMF нет
12 ноя 20, 15:33    [22230831]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 51775

_Vasilisk_
TIBBlobStream укладывается, а MMF нет

BLOB - поток данных неизвестного размера, последовательно читаемый кусками. MMF - кусок
данных заданного размера. Ничего общего.

Posted via ActualForum NNTP Server 1.5

12 ноя 20, 15:57    [22230853]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
alekcvp
Member

Откуда:
Сообщений: 2494
Dimitry Sibiryakov

Работа с такими файлами принципиально не укладывается в интерфейс TStream.

Серьёзно?..
Я в своё время пытался прикрутить TSteam к MMF ради эксперимента, но за ненадобностью не доделал.
Но ничего "принципиального" я там не вижу.
+
unit smMappedFile;

interface

uses
  System.SysUtils, System.Classes;

type
  TMappedFileStream = class(TStream)
  private
    FFileName: string;
    FHandle: THandle;       // file handle
    FMappedBase: Int64;     // mapping start position
    FMappedSize: NativeInt; // current size of mapping window
    FMapping: THandle;      // handle of file mapping
    FBufferSeek: NativeInt; // file pointer in mapped view
    FDefMapSize: NativeInt; // default size of mapping window
    FMemory: PByte;         // address of mapping window
    FFileSize: Int64;       // available file size
  protected
    function GetSize: Int64; override;
    procedure SetSize(const NewSize: Int64); override;
    procedure RemapAtPosition(RemapBase: Int64);
  public
    constructor Create(const AFileName: string; MapWindowSize: NativeInt = 16777216);
    destructor Destroy; override;
    function Read(var Buffer; Count: Integer): Longint; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
    function Write(const Buffer; Count: Integer): Longint; override;
    property FileName: string read FFileName;
  end;

implementation

uses
  Winapi.Windows, System.RTLConsts;

resourcestring
  sMappedFileIsEmpty = 'Can not map view of zero-length file %s.';
  sSeekBeyondRange   = 'File pointer moved beyond file bounds.';
  sFileMappingError  = 'Error mapping view to file.';
  sStreamIsReadOnly  = 'Stream is read-only.';

function Min(A, B: Cardinal): Cardinal;
begin
  Result := A;
  if B < A then
    Result := B
end;

{ TMappedFileStream }

constructor TMappedFileStream.Create(const AFileName: string; MapWindowSize: NativeInt = 16777216);
begin
  inherited Create;
  FHandle := FileOpen(AFileName, fmOpenRead or fmShareDenyWrite);
  if FHandle = INVALID_HANDLE_VALUE then
    raise EFOpenError.CreateResFmt(@SFOpenErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
  FFileName := AFileName;
  PCardinal(@FFileSize)^ := GetFileSize(FHandle, PByte(@FFileSize) + 4);
  if FFileSize = 0 then
    raise EFileStreamError.CreateResFmt(@sMappedFileIsEmpty, [ExpandFileName(AFileName)]);
  FMapping := CreateFileMapping(FHandle, nil, PAGE_READONLY, 0, 0, nil);
  RemapAtPosition(FMappedBase);
end;

destructor TMappedFileStream.Destroy;
begin
  if FMemory <> nil then
    UnmapViewOfFile(FMemory);
  CloseHandle(FMapping);
  FileClose(FHandle);
  inherited;
end;

procedure TMappedFileStream.RemapAtPosition(RemapBase: Int64);
var
  OldBase: Int64;
begin
  OldBase := FMappedBase;
  if FMemory <> nil then
    UnmapViewOfFile(FMemory);
  if FFileSize < FDefMapSize then 
  begin
    FMappedSize := FFileSize;
    FMappedBase := 0;
  end else 
  begin
    FMappedSize := FDefMapSize;
    if FFileSize - RemapBase < FDefMapSize then
      FMappedBase := FFileSize - FDefMapSize
    else 
      FMappedBase := RemapBase;
  end;
  FBufferSeek := FBufferSeek - (OldBase - FMappedBase);
  FMemory := MapViewOfFile(FMapping, PAGE_READONLY, Int64Rec(FMappedBase).Hi, Int64Rec(FMappedBase).Lo, FMappedSize);
  if FMemory = nil then
    raise EStreamError.CreateRes(@sFileMappingError);
end;

function TMappedFileStream.GetSize: Int64;
begin
  Result := FFileSize;
end;

function TMappedFileStream.Read(var Buffer; Count: Integer): Longint;
var
  BytesToRead: Integer;
begin
  Result := 0;
  while (Result < Count) and (FMappedBase + FBufferSeek < FFileSize) do 
  begin
    if FBufferSeek >= FMappedSize then
      RemapAtPosition(FMappedBase + FMappedSize);
    BytesToRead := Min(FMappedSize - FBufferSeek, Count - Result);
    Move((FMemory + FBufferSeek)^, (PByte(Buffer) + Result)^, BytesToRead);
    Inc(Result, BytesToRead);
  end;
end;

function TMappedFileStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
var
  FilePointer: Int64;
begin
  case Origin of
    soCurrent: FilePointer := FMappedBase + FBufferSeek + Offset;
    soEnd: FilePointer := FFileSize + Offset;
  else
    FilePointer := Offset; // soBeginning
  end;
  if (FilePointer < 0) or (FilePointer > FFileSize) then
    raise EStreamError.CreateRes(@sSeekBeyondRange);
  if (FilePointer < FMappedBase) or ((FilePointer - FMappedBase) >= FMappedSize) then 
  begin
    FMappedBase := FilePointer;
    FBufferSeek := 0;
    RemapAtPosition(FMappedBase);
  end else 
    FBufferSeek := FilePointer - FMappedBase;
  Result := FilePointer;
end;

procedure TMappedFileStream.SetSize(const NewSize: Int64);
begin
  raise EStreamError.CreateRes(@sStreamIsReadOnly);
end;

function TMappedFileStream.Write(const Buffer; Count: Integer): Longint;
begin
  raise EStreamError.CreateRes(@sStreamIsReadOnly);
end;

end.
12 ноя 20, 16:18    [22230878]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
defecator
Member

Откуда:
Сообщений: 39394
alekcvp
Dimitry Sibiryakov

Работа с такими файлами принципиально не укладывается в интерфейс TStream.

Серьёзно?..


сибиряков любит поумничать
в то время, как существует пара десятков реализаций mmf поверх TStream
12 ноя 20, 16:47    [22230924]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Vizit0r
Member

Откуда: Одесса
Сообщений: 855
моя реализация, проверенная годами работы.
На маке и линухе работает, на андроиде тоже.

Проверок на права доступа к файлу и т.д. - тут нет, они делаются до этого момента.

Первоначально где-то слизал, потом перепилил. Где - уже не вспомню, очень уж давно это было.

P.S. Меня до сих пор удивляет, что таких вещей нет в базовой поставке.

К сообщению приложен файл (MemMapFile.pas - 5Kb) cкачать

Сообщение было отредактировано: 12 ноя 20, 16:49
12 ноя 20, 16:52    [22230934]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
DmSer
Member

Откуда: Пенза
Сообщений: 1246
Vizit0r
P.S. Меня до сих пор удивляет, что таких вещей нет в базовой поставке.


Какие преимущества оно даёт по сравнению с TFileStream?
12 ноя 20, 17:07    [22230948]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
rgreat
Member

Откуда:
Сообщений: 6312
Угу. Я тоже не понимаю зачем в реальной жизни нужны "маппированные" файлы.
12 ноя 20, 17:12    [22230952]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 51775

DmSer
Какие преимущества оно даёт по сравнению с TFileStream?

Никаких.

Posted via ActualForum NNTP Server 1.5

12 ноя 20, 17:13    [22230957]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Ежов Дмитрий Сергеевич
Member

Откуда: Новокузнецк-Москва, Россия
Сообщений: 100
Мне надо, чтобы файлик записался, даже несмотря на то, что процесс умер.
12 ноя 20, 17:17    [22230962]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Ежов Дмитрий Сергеевич
Member

Откуда: Новокузнецк-Москва, Россия
Сообщений: 100
Нашел компонент с Торри по ссылке выше, допилил под использование с файлами, созданными через CreateFile, а не просто область в памяти, все заработало.
12 ноя 20, 17:18    [22230963]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
alekcvp
Member

Откуда:
Сообщений: 2494
rgreat
Угу. Я тоже не понимаю зачем в реальной жизни нужны "маппированные" файлы.

На самом деле, именно в реализации TStream - пользы никакой, а вообще если работать через API, то доступ будет быстрее т.к., как я понимаю, MapViewOfFile() тупо возвращает адрес данных в файловом кэше и исключается лишнее копирование памяти туда-сюда, особенно в случае последовательного доступа.
12 ноя 20, 17:18    [22230964]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
alekcvp
Member

Откуда:
Сообщений: 2494
Ежов Дмитрий Сергеевич
Мне надо, чтобы файлик записался, даже несмотря на то, что процесс умер.

Это можно решить через FlushFileBuffers после записи данных
12 ноя 20, 17:19    [22230966]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Ежов Дмитрий Сергеевич
Member

Откуда: Новокузнецк-Москва, Россия
Сообщений: 100
alekcvp,

Это может решить только венда. Никаких FlushBuffer нет, процесс умер.
12 ноя 20, 17:20    [22230968]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Ежов Дмитрий Сергеевич
Member

Откуда: Новокузнецк-Москва, Россия
Сообщений: 100
На самом деле, маппированный файл глубоко используется в нашей 1С-очке.
https://its.1c.ru/db/metod8dev/content/5860/hdoc

Пользователи сидят клиентами на серверном процессе rphost, который хранит всю их актуальную инфу в сеансовых данных, по факту в памяти. Серверный процесс падает (либо его рубит админ), менеджер сервера обнаруживает это, создает новый серверный процесс, который подтягивает сеансовые, которые уже записала венда.
12 ноя 20, 17:23    [22230969]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 51775

alekcvp
как я понимаю, MapViewOfFile() тупо возвращает адрес данных в файловом кэше

Судя по "A mapped view of a file is not guaranteed to be coherent with a file that is
being accessed by the ReadFile or WriteFile function." это не так.

Ежов Дмитрий Сергеевич
Мне надо, чтобы файлик записался, даже несмотря на то, что процесс умер.

Тогда просто всегда пиши WriteLn в паре с Flush.

Ну или почини процесс, чтобы не умирал.

Posted via ActualForum NNTP Server 1.5

12 ноя 20, 17:26    [22230975]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Ежов Дмитрий Сергеевич
Member

Откуда: Новокузнецк-Москва, Россия
Сообщений: 100
мммм, неумирающий серверный процесс в сложных системах, без утечек памяти и кривых сторонних компонентов и кривого легаси от девопса. Что может быть естественней...
12 ноя 20, 17:29    [22230977]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 51775

Ежов Дмитрий Сергеевич
Что может быть естественней...

Использование инструментов тестирования и культуры программирования. Кто из дельфинов
нынче на это способен?..

Posted via ActualForum NNTP Server 1.5

12 ноя 20, 17:32    [22230984]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Vizit0r
Member

Откуда: Одесса
Сообщений: 855
DmSer
Vizit0r
P.S. Меня до сих пор удивляет, что таких вещей нет в базовой поставке.


Какие преимущества оно даёт по сравнению с TFileStream?


насколько я помню из результатов изначального сравнения - MMF дает большой прирост к скорости непоследовательного чтения (записи нет). Ну и плюс доступ по указателю упрощает код.
12 ноя 20, 17:58    [22231004]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
alekcvp
Member

Откуда:
Сообщений: 2494
Dimitry Sibiryakov

Судя по "A mapped view of a file is not guaranteed to be coherent with a file that is
being accessed by the ReadFile or WriteFile function." это не так.

Вообще не вижу взаимосвязи.
ReadFile читает данные в буфер и после этого они там не меняются, при изменении файла.
MMF, как я понимаю, отображает всё в живую.
12 ноя 20, 18:14    [22231020]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 51775

alekcvp
Вообще не вижу взаимосвязи.
ReadFile читает данные в буфер и после этого они там не меняются, при изменении файла.

WriteFile пишет данные с системный буфер. Если бы этот буфер отображался сразу на память -
данные в нём должны всегда соответствовать тому, что записало WriteFile. И наоборот: что
записано в MMF, всегда должно получиться из ReadFile.

Posted via ActualForum NNTP Server 1.5

12 ноя 20, 18:26    [22231033]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
rgreat
Member

Откуда:
Сообщений: 6312
Ежов Дмитрий Сергеевич
мммм, неумирающий серверный процесс в сложных системах, без утечек памяти и кривых сторонних компонентов и кривого легаси от девопса. Что может быть естественней...
Даже если так, хранить важные данные надо в транзакционно безопасной среде.
12 ноя 20, 18:30    [22231038]     Ответить | Цитировать Сообщить модератору
 Re: Файлы, отображаемые в память.  [new]
kealon(Ruslan)
Member

Откуда: Нижневартовск
Сообщений: 6176
Ежов Дмитрий Сергеевич
Мне надо, чтобы файлик записался, даже несмотря на то, что процесс умер.
если у вас программа обвалится на каком-то "странном" состоянии, файлы, отображаемые в память, в этом случае не помогут

Ежов Дмитрий Сергеевич
alekcvp,

Это может решить только венда. Никаких FlushBuffer нет, процесс умер.
это не цель FlushBuffer, его цель - зафиксировать какое-то "устойчивое" состояние. Если операция прошла, то как минимум, она зафиксирована в журнале файловой системы
например, классический подход - это "ведение журнала операций + периодическое сохранение всего состояния"

в случае обвала
1. берётся последнее сохранённое состояние,
2. ищется запись в журнале, которая соответствует этому состоянию, и накатываются все операции, сохранённые в журнале после неё

NTFS, кстати, практически по такому же принципу работает
12 ноя 20, 19:48    [22231072]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3   вперед  Ctrl      все
Все форумы / Delphi Ответить