内存映射文件
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个进程对它的修改。