【问题标题】:How to copy a large portion of a raw filesystem to a file?如何将大部分原始文件系统复制到文件中?
【发布时间】:2018-08-26 04:18:59
【问题描述】:

我正在使用一个神秘的数据收集文件系统。它有一个块描述文件及其在磁盘上的确切偏移量,所以我知道每个文件的起始字节、结束字节和字节长度。目标是从物理磁盘中获取一个文件。它们是大文件,因此性能至关重要。

以下是“有效”的方法,但效率非常低:

import shutil, io
def start_copy(startpos, endpos, filename="C:\\out.bin"):
    with open(r"\\.\PhysicalDrive1", 'rb') as src_f:
        src_f.seek(startpos)
        flength = endpos - startpos
        print("Starting copy of "+filename+" ("+str(flength)+"B)")
        with open(filename, 'wb') as dst_f:
            shutil.copyfileobj( io.BytesIO(src_f.read(flength)), dst_f )
        print("Finished copy of "+filename)

这很慢:io.BytesIO(src_f.read(flength)) 在技术上有效,但它会在写入目标文件之前将整个文件读入内存。因此,它需要的时间比应有的要长得多。

直接使用dst_f 复制将不起作用。 (我假设)不能指定结束位置,所以复制不会停止。

这里有一些问题,每个问题都可以解决这个问题:

  • 是否有接受开始/结束字节参数的复制库(或适用于 Windows 7 的外部实用程序,可与 subprocess 一起使用)?
  • 是否可以创建copyfileobj 可以使用的类文件对象,该对象仅引用另一个类文件对象的一部分?
  • io 对象搜索超过某个端点时是否会引发异常?
  • 是否可以强制 copyfileobj 在驱动器的给定字节偏移处自然停止(一种“假 EOF”)?

【问题讨论】:

  • 为什么需要使用copyfileobj 而不仅仅是write

标签: python windows filesystems buffer disk


【解决方案1】:

执行此操作的明显方法是将write 写入文件。

copyfileobj 的全部意义在于它为您缓冲数据。如果您必须将整个文件读入BytesIO,那么您只是在缓冲BytesIO,这是没有意义的。

所以,只需在 read 周围循环一个大小合适的缓冲区,从 src_fwritedst_f,直到达到 flength 字节。

如果您查看the shutil source(从the shutil docs 链接),copyfileobj 内部没有魔法;这是一个微不足道的功能。从 3.6 开始(我认为自从在 2.1 左右添加 shutil 以来它完全没有改变......),它看起来像这样:

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)

您可以做同样的事情,只需跟踪读取的字节并停止在flength

def copypartialfileobj(fsrc, fdst, size, length=16*1024):
    """copy size bytes from file-like object fsrc to file-like object fdst"""
    written = 0
    while written < size:
        buf = fsrc.read(min(length, size - written))
        if not buf:
            break
        fdst.write(buf)
        written += len(buf)

【讨论】:

  • @charjabug 它不能异步工作,但是操作系统(以及驱动程序和驱动电路)完成的缓冲意味着只要您以合适的大小顺序读取它,它就会很好地流水线化块。
  • @charjabug 如果您需要进一步加快速度,异步可能不是关键,至少不是直接的。如果其中一个磁盘是 SSD 或宽 RAID 条带(因此寻道时间不会比吞吐量慢得多,就像使用普通硬盘驱动器一样),只需执行 4 个线程,每个线程复制 1/4 的文件可能会加快速度事情起来了。或者,您可以使用pywin32 调用一些适当的 Win32 API 函数,尽管我认为您必须处理重叠 I/O 才能以这种方式获得任何加速。
  • @charjabug 此外,大量 Python 的 stdlib 模块旨在用作示例代码,而不仅仅是开箱即用的工具。如果您访问任何模块的文档,并且有一个指向源的链接,请单击它,您可以确切地看到它是如何工作的。
  • @charjabug 我认为 read-once-write-twice 应该更快......但不会那么快。毕竟,这是优化过程中最快的部分(读取 SSD,甚至可能从其缓存中读取),而完全不会影响最慢的部分(写入机械驱动器)。但是,并行执行两个副本(在两个线程上同时执行 A->a 和 B->b,然后在两个线程上执行 A->b 和 B->a)可以将您的时间缩短近一半。 (取决于总线争用是否与机械高清速度一样成为瓶颈,但我会尝试一下。)
  • 鉴于您已经知道目标文件的大小,您可以使用fdst.truncate(size);通过mdst = mmap.mmap(fdst.fileno(), 0) 对目标进行内存映射;然后通过fsrc.readinto(mdst)复制数据。
猜你喜欢
  • 2012-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 2013-07-24
  • 2018-01-27
相关资源
最近更新 更多