【问题标题】:File Mapping to copy faster?文件映射复制更快?
【发布时间】:2011-07-15 03:24:28
【问题描述】:

我做了一个应用程序,使用线程和TFileStream的方法复制文件,但是我对速度有点失望,尤其是在复制大文件时。然后我听说了文件映射,这显然可以产生一种复制速度更快的方法,因为访问文件会更快。

我是初学者,所以我正在尝试,但我没有设法通过文件映射复制文件。 (该文件是创建 test2.iso 但不是做 0ko 3GB ^ ^。)

这是我的代码。

procedure TForm1.Button1Click(Sender: TObject);
var
  FFilehandle: THANDLE;
  FFileMap: THANDLE;
  FmappingPtr: pchar;
  hFile2:    THANDLE ;
  SizeFile1,BytesWritten: DWORD ;
begin
  FFilehandle := CreateFile('titan.iso',
    GENERIC_WRITE OR GENERIC_READ,
    FILE_SHARE_READ OR FILE_SHARE_WRITE,
    nil,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0);
  if (FFilehandle <> INVALID_HANDLE_VALUE) then
  begin
    FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
      nil, // optional security attributes
      PAGE_READWRITE, // protection for mapping object
      0, // high-order 32 bits of object size
      2*1024, // low-order 32 bits of object size
      0); //
    if (FFileMap <> NULL) then
    begin
      FMappingPtr := MapViewOfFile(FFileMap,
        FILE_MAP_WRITE,
        0,
        0,
        0);
      if Assigned(FMappingPtr)   then
      begin
        // Manipulation de FMappingPtr
        hFile2 := CreateFile('test.iso', GENERIC_WRITE, 0, nil,
          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        if (hFile2 <> INVALID_HANDLE_VALUE) then
        begin
          SizeFile1 := GetFileSize(FFilehandle, NIL); // recupere taille du fichier 1
          WriteFile(hFile2, Fmappingptr, SizeFile1, &BytesWritten, NIL); // Transfert la mémoire mappé dans file 2
          // libere les ressources
        end
        else
          MessageBox(0, 'Impossible de lire le fichier mappé', 'Error', 0);

        UnmapViewOfFile(Fmappingptr);
        CloseHandle(FFileMap);
        CloseHandle(FFilehandle);
        CloseHandle(hFile2);
      end
      else
      begin
        CloseHandle (FFileMap);
        CloseHandle (FFileHandle);
        MessageBox(0, 'Impossible de lire le fichier mappé', 'Error', 0);
      end;
    end
    else
    begin
      CloseHandle (FFilemap);
      MessageBox(0, 'Impossible de mappe le fichier en mémoire', 'Error', 0);
    end;
  end
  else
    MessageBox(NULL, 'Impossible d''ouvrir le fichier', 'Error', NULL);
end;

我的问题在哪里?

【问题讨论】:

  • 你有什么理由在这里重新发明轮子?为什么不直接在 WinAPI 中使用 CopyFile? msdn.microsoft.com/en-us/library/aa363851(v=vs.85).aspx
  • 因为我需要像 Teracopy 或 FastCopy 那样更快地编写系统副本
  • 系统文件复制将很难被击败。如果你有机会,你需要了解它的弱点是什么。你呢?
  • 他们是怎么做到的?他们做了哪些系统副本没有做的事情?这就是你需要找出的,而不是反复试验。
  • 你为什么不呢?因为您自称是初学者,而超越操作系统提供的系统功能并不是初学者的任务。这通常甚至不是专家的任务。

标签: delphi memory-mapped-files file-copying


【解决方案1】:

并不是我不同意 cmets 对您的问题的看法,但您的代码失败的原因有两个。

首先,您使用CreateFileMappingdwFileOffsetLow 指定2Kb,然后将整个文件大小传递给WriteFile。使用此映射视图,对于nNumberOfBytesToWrite,最多应使用 2Kb 调用“WriteFile”。

其次,你没有正确传递文件数据的起始地址,试试这个:

[...]

FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
  nil, // optional security attributes
  PAGE_READWRITE, // protection for mapping object
  0, // high-order 32 bits of object size
  0, // low-order 32 bits of object size
  0); //
if (FFileMap <> NULL) then
begin
  FMappingPtr := MapViewOfFile(FFileMap,
    FILE_MAP_WRITE,
    0,
    0,
    0);
  if Assigned(FMappingPtr)   then
  begin
    // Manipulation de FMappingPtr
    hFile2 := CreateFile('test.iso', GENERIC_WRITE, 0, nil,
      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if (hFile2 <> INVALID_HANDLE_VALUE) then
    begin
      SizeFile1 := GetFileSize(FFilehandle, NIL); // recupere taille du fichier 1
      WriteFile(hFile2, Fmappingptr[0], SizeFile1, &BytesWritten, NIL); // Transfert la mémoire mappé dans file 2
      // libere les ressources
    end

[...]


顺便说一句,代码没有返回任何错误的原因是您没有检查“WriteFile”的返回。

【讨论】:

  • @Sertac 我收到错误消息“无法读取映射的文件”
  • @Reality - 不,如果 'MapViewOfFile' 没有失败,'FmappingPtr' 不为零。我只对代码进行了更改以使其运行。一种是调用“CreateFileMapping”(传递 0 表示最大大小的高低顺序),另一种是在“WriteFile”中传递“FmappingPtr[0]”而不是“FMappingPtr”。
  • 是的,但我有这个错误“无法读取映射的文件”,如果我将值低阶更改为“2k”,例如,我没有这个错误:s
  • @Reality - 如果您将低阶文件大小设置为 2K,则在调用 WriteFile 之前还将“SizeFile1”设置为 2K。如果一切正常,您应该可以复制文件的前 2K。
  • @Sertac 是的,现在我的新文件 test2.iso 执行 3Ko 但我如何为 test2.iso = 3Gb 执行?
【解决方案2】:

系统 FileCopy 确实使用内存映射文件。如果文件很大,您可以看到系统上的虚拟内存量随着映射的发生而减少。最近 Windows Server 中有一个“功能”可以使用所有可用的虚拟 RAM 来构建映射。所以......我会使用 FileCopy(或 FileCopyEx)并让操作系统决定移动数据的最佳方式(它可能最了解)。如果您在单独的线程中执行此操作,您甚至可以在不停止程序的情况下执行此操作 - 这将是一个非常快速的副本,因为大多数 CPU 将花费大部分时间来打磨他们的指甲等待磁盘/网络。

在您的示例中,您不应该是使用 PAGE_READONLY 的 CreateFileMapping 和使用 FILE_MAP_READ 的 MapViewOfFile 吗?返回的指针 (FMappingPtr) 应该指向有效在调试器中可见 - 它应该看起来像你的文件。

【讨论】:

  • 它是 CopyFile 而不是 FileCopy。 Ex 版本有进度回调,所以不需要线程。
  • 这取决于您还想做什么。如果你想做一些有用的事情,那么让它在单独的线程上运行可能正是需要的——它可能会在单独的核心上运行。
猜你喜欢
  • 2010-10-04
  • 2015-02-16
  • 2018-06-25
  • 1970-01-01
  • 2019-07-27
  • 2012-03-02
  • 2021-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多