mmap基本原理
关于mmap的原理网上有许多资料,这里只做简单的介绍。mmap是内存映射的实现,就是把进程的虚拟地址空间映射到真实的物理内存中,从而,对映射地址的读写相当于对真实物理内存的读写,操作系统负责将写入的内容刷新到磁盘,或者从磁盘加载到内存中(这里是指文件映射,还有一种叫匿名映射)。
与传统IO的其区别
传统的IO调用,例如write(), CPU需要从用户态切换到内核态,操作系统内核负责从用户进程地址空间将数据拷贝到内核空间,再将内核空间的数据刷新到磁盘上,整个过程不仅需要CPU状态的切换,还有不必要的数据拷贝(将用户进程空间的数据拷贝到内核空间); mmap则不然,调用mmap.write()的时候,相当于直接把数据写到了内核空间中,然后由操作系统异步刷新到磁盘中。由此可见,mmap不仅减少了数据拷贝次数,还提高了用户空间和内核空间的数据交换效率,用户进程不必阻塞等待数据刷新到磁盘。对于read()系统调用,也是同样的。
mmap应用场景
mmap的应用之一就是本地进程间通信,进程通信的方式有很多, 例如socket、管道、信号量、消息队列、mmap内存映射等。对于两个本地的独立进程,如果数据量较大,例如,一个进程从摄像头捕获视屏流,将视频流拆分成一张张图片,另外一个进程对图片内容进行识别,一秒可能有几十帧图片,每张图片的大小可能达到几MB,这种场景下用mmap内存映射的方式传输图片是最合适的,效率会比较高。
进程1写入
# 8MB
MAX_IMG_SIZE = 8388608
with open(path, mode='wb+') as file_obj, mmap.mmap(file_obj.fileno(), length=MAX_IMG_SIZE, access=mmap.ACCESS_WRITE) as mmap_obj:
while True:
img = get_image()
if img:
shape = img.shape
# 图片的像素高度
pixel_height_bytes = shape[0].to_bytes(byteorder='big', length=2)
# 图片的像素宽度
pixel_width_bytes = shape[1].to_bytes(byteorder='big', length=2)
img_bytes = img.tobytes()
# 图片的字节长度
img_length = len(img_bytes)
img_length_bytes = img_length.to_bytes(byteorder='big', length=4)
# 写入图片
mmap_obj[0:2] = pixel_height_bytes
mmap_obj[2:4] = pixel_width_bytes
mmap_obj[4:8] = img_length_bytes
mmap_obj[8:8+img_length] = img_bytes
# 通知读取图片
# ...
进程2读取
# 8MB
MAX_IMG_SIZE = 8388608
with open(path, mode='rb') as file_obj, mmap.mmap(file_obj.fileno(), length=MAX_IMG_SIZE, access=mmap.ACCESS_READ) as mmap_obj:
while True:
# 接到读取通知
pixel_height = int.from_bytes(mmap_obj[0:2], byteorder='big')
pixel_width = int.from_bytes(mmap_obj[2:4], byteorder='big')
img_length = int.from_bytes(mmap_obj[4:8], byteorder='big')
img_byte = mmap_obj[8: 8 + img_length]
nparr = np.frombuffer(img_byte, dtype=np.uint8)
img = nparr.reshape((pixel_height, pixel_width, 3))
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。