SYStemV 共享内存
传统的 SYStemV 共享内存是指 shm
那一伙 API:
1 | int shmget(key_t key, size_t size, int shmflg); /* 获取一个新的共享内存段 */ |
在早期版本的内核中,“共享内存”,“信号量”,“消息队列” 都使用通用的 ipcget
函数完成创建,只是 ipc_ops
结构体的初始化不同
其实 do_shmat
底层申请内存的部分和 mmap
一样,都是调用 do_mmap_pgoff
,效果就是映射一片由 VMA 组织起来的共享内存段
之后的 shmget
通过相同的 key
,就可以获取同一片共享内存区域
POSIX 共享内存
传统的 SYStemV shm 共享内存有个升级版的 POSIX API:
1 | static void shm_open(struct vm_area_struct *vma); /* 在/dev/shm/下建立一个文件,作为该进程的共享内存 */ |
/dev/shm/
是一个使用就是 tmpfs 文件系统的设备,可以理解为只存在于内存上的文件
还有个更经典的 POSIX API 就是 mmap:
1 | void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); |
- mmap 需要和磁盘进行交互(非匿名映射),导致效率没有 shm 好,但它能够存储的空间更大
memfd_create 共享内存
函数 memfd_create
可以创建一个“虚拟文件”,它映射到一片物理内存而不是磁盘
1 | int memfd_create(const char *name, unsigned int flags); |
- 创建基于
tmpfs
的匿名文件(返回文件描述符)
函数 memfd_create
本身并没有共享内存的能力,但是通过之前的 FD 转移技术可以实现共享内存(把 memfd_create
生成的 FD 转移到其他进程中,就实现共享内存了)
使用案例如下:
- 发送共享内存句柄:
1 |
|
- 接收共享内存句柄:
1 |
|
- 结果:
1 | ➜ exp ./send |
dma_buf 共享内存
dma_buf 可以实现 buffer
在多个设备的共享,如果设备驱动想要共享 DMA 缓冲区,可以让一个驱动来导出,一个驱动来使用:
- 可以把一片底层驱动 A 的 buffer 导出到用户空间成为一个 fd
- 也可以把 fd 导入到底层驱动 B
- 如果进行
mmap
得到虚拟地址,CPU 也是可以在用户空间访问到已经获得用户空间虚拟地址的底层 buffer 的
类似于消费者生产者模型,Linux DMA-BUF 就是基于这种方式来实现的
dma_buf 在内核中的结构体如下:
1 | struct dma_buf { |
当用户 call VIDIOC_EXPBUF
这个 IOCTL
的时候,可以把 dma_buf 转化为 fd:
1 | int ioctl(int fd, VIDIOC_EXPBUF, struct v4l2_exportbuffer *argp); |
想要把 dma_buf 的导入侧设备驱动,则会用到如下这些API:
1 | /* 导出缓冲 */ |
可以通过如下方式获取文件描述符:
1 | int buffer_export(int v4lfd, enum v4l2_buf_type bt, int index, int *dmafd) |