Python工具箱系列(四十五)

发布时间 2023-11-06 16:43:39作者: 西安衍舆航天

内存映射文件

mmap是python内置标准库,提供将文件映射到内存的机制。通过mmap将文件映射到内存之后,我们可以高效并优雅地对文件的内容进行随机访问。通常打开文件后要通过组合各种seek()、read()和write()调用来访问,使用mmap后可以简单将文件映射到内存,然后通过切片操作来访问数据。需要强调的是,对某个文件进行内存映射并不会导致将整个文件读到内存中。也就是说,文件并不会拷贝到某种内存缓冲区或数组上。相反,操作系统只是为文件内容保留一段虚拟内存而已。当访问文件的不同区域时,文件的这些区域将被读取并按照需要映射到内存区域中。但是,文件中从未访问过的部分会简单地留在磁盘上。这一切都是以透明的方式在幕后完成的。

以下代码演示了一个基本的映射操作过程:

import mmap

# 打开一个已经存在的文件并且输出
targetfile = r'gotoolkits\resources\poetry.txt'
with open(targetfile, mode="r", encoding='utf-8') as f:
    print(f.read())

with open(targetfile, "r+b") as f:
    # 在内存中进行映射。0表示文件内容全部映射
    mm = mmap.mmap(f.fileno(), 0)
    end = mm.size()

    # 读一行ls
    print(mm.readline().decode('utf-8'))
    # 分片读
    print(mm[:12].decode('utf-8'))

    # 在文件尾写一句话
    modifystr = " world!\n"
    mm.seek(end-len(modifystr))
    mm.write(modifystr.encode('utf-8'))

    # 关闭映射
    mm.close()

mmap另一个用途就是在多个进程间共享内存映射。也就是说多个进程可以同时打开一个文档,同时进行操作。下面的代码简单示例了2个进程同时操作一个文档的过程。以下代码为mmap1.py:

import mmap
import os

targetfile = r'd:\dev\gotoolkits\resources\mmapshare.txt'


def memory_map(filename, access=mmap.ACCESS_WRITE):
    size = os.path.getsize(filename)
    fd = os.open(filename, os.O_RDWR)
    return mmap.mmap(fd, size, access=access)


mp = memory_map(targetfile)
content = mp.read().decode('utf-8')
mp[:] = b'0'*mp.size()
mp.seek(0)

while True:
    job = input("what you want?:").strip()
    if job == 'quit':
        break
    outstr = f"my choice is {job}\n"
    mp.write(outstr.encode('utf-8'))

mp.close()

另一个进程代码mmap2.py类似mmap1.py。稍有区别在于初始时不覆盖内存的当前内容,同时为了演示方便,将映射操作的起点定在了虚拟内存的一半处。相关代码如下:

import mmap
import os

targetfile = r'd:\dev\gotoolkits\resources\mmapshare.txt'


def memory_map(filename, access=mmap.ACCESS_WRITE):
    size = os.path.getsize(filename)
    fd = os.open(filename, os.O_RDWR)
    return mmap.mmap(fd, size, access=access)


mp = memory_map(targetfile)
mp.seek(round(mp.size()/2))

while True:
    job = input("what you want?:").strip()
    if job == 'quit':
        break
    outstr = f"my choice is {job}\n"
    mp.write(outstr.encode('utf-8'))

mp.close()

运行的效果如下图所示:

从图中可以看出,同时运行mmap1.py与mmap2.py,并且分别输入不同的内容。最终在mmapshare.txt文件中体现了2个进程对它的修改。