mmap用于本地进程间通信

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))

你可能感兴趣的:(mmap用于本地进程间通信)