读取大文件使用 100% 内存,我的整个电脑都死机了

reading large file uses 100% memory and my whole pc frozes

我制作了一个简单的应用程序来加密和解密文件。但是当我加载一个像 2gb 这样的大文件时,我的程序使用了 100% 的内存。我使用多处理和多线程。

poolSize = min(cpu_count(), len(fileList))
process_pool = Pool(poolSize)
thread_pool = ThreadPool(len(fileList))

lock = Lock()
worker = partial(encfile, process_pool, lock)

thread_pool.map(worker, fileList)
def encfile(process_pool, lock, file):
    with open(file, 'rb') as original_file:
        original = original_file.read()

    encrypted = process_pool.apply(encryptfn, args=(key, original,))

    with open (file, 'wb') as encrypted_file:
        encrypted_file.write(encrypted)

这是我的总体思路:

由于内存是个问题,你必须以更小的块读取文件,比如 64K 块并加密每个 64K 块并将它们写出。当然,加密块的长度不是 64K,所以问题就变成了如何解密。因此每个加密块必须以 fixed-length header 为前缀,它只不过是编码为 4 字节无符号整数的后续加密块的长度(这应该绰绰有余)。解密算法循环首先读取下一个4字节长度,然后从中知道后面的加密块有多少字节。

顺便说一下,如果您不使用 encfile 一个 lock,则不需要传递给它,例如计算已处理的文件数。

from tempfile import mkstemp
from os import fdopen, replace


BLOCKSIZE = 64 * 1024
ENCRYPTED_HEADER_LENGTH = 4

def encfile(process_pool, lock, file):
    """
    Encrypt file in place.
    """

    fd, path = mkstemp() # make a temporary file

    with open(file, 'rb') as original_file, \
    fdopen (fd, 'wb') as encrypted_file:
        while True:
            original = original_file.read(BLOCKSIZE)
            if not original:
                break
            encrypted = process_pool.apply(encryptfn, args=(key, original))
            l = len(encrypted)
            l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
            encrypted_file.write(l_bytes)
            encrypted_file.write(encrypted)
    replace(path, file)


def decfile(file):
    """
    Decrypt files in place.
    """

    fd, path = mkstemp() # make a temporary file

    with open(file, 'rb') as encrypted_file, \
    fdopen (fd, 'wb') as original_file:
        while True:
            l_bytes = encrypted_file.read(ENCRYPTED_HEADER_LENGTH)
            if not l_bytes:
                break
            l = int.from_bytes(l_bytes, 'big')
            encrypted = encrypted_file.read(l)
            decrypted = decryptfn(key, encrypted)
            original_file.write(decrypted)
    replace(path, file)

说明

块大小越大,需要的内存就越多(您的原始程序读取整个文件;该程序一次只能读取 64K)。但我假设块大小太小会导致对加密的调用太多,这是通过多处理完成的,这将需要更多 CPU 开销——所以这是一个权衡。 64K 是 任意 。如果你有记忆,可以增加很多。您甚至可以尝试 1024 * 1024 (1M)。

我之前试图解释以下内容,但让我详细说明一下:

假设当您加密一个 64K 块时,该特定 64K 块的加密大小最终为 67,986 字节长(不同的 64K 块加密通常具有不同的长度,除非其未加密的值恰好已被加密相同)。如果我只写出没有其他信息的数据,我需要一些方法来知道要解密文件,首先需要读回 67,986 字节的数据并将其传递给解密方法(使用正确的密钥,当然)因为你必须解密加密内容的精确结果,不能少也不能多。换句话说,您不能只读回任意块中的加密文件并将这些块传递给解密方法。但那样会怎样?因此,了解每个加密块有多大的唯一方法是在这些块前面加上一个 header 前缀,它给出了下一个块的长度。

l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big') 获取存储在变量 l 中的整数长度,并将其编码为大小为 ENCRYPTED_HEADER_LENGTH 的字节数组,采用“大端”顺序,这意味着字节从高位开始排列字节到低位字节:

>>> ENCRYPTED_HEADER_LENGTH = 4
>>> l = 67986
>>> l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'big')
>>> l_bytes
b'\x00\x01\t\x92'
>>> l_bytes = l.to_bytes(ENCRYPTED_HEADER_LENGTH, 'little')
>>> l_bytes
b'\x92\t\x01\x00'
>>>

\t 是值为 \x09 的制表符,因此我们将写出 0010992,这是 67986

的 4 字节十六进制值