最近看了宋宝华老师写的《世界上最好的共享内存》,宋宝华老师文章链接: https://cloud.tencent.com/developer/article/1551288,深有感触,正好也在接触这块东西,迫不及待的想要深入学习和复盘下这块知识,所以这里简单写个文章总结下linux共享内存的几种实现方式。
早期传统的共享内存,强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),通过这种方式CPU可以在各个进程访问到这片内存。
而现在很多场景下其实不再强调映射到进程虚拟地址空间的概念,而是更强调以某种“句柄”的形式,让大家都知道这一句柄的存在,并且可以借助此“句柄”来跨进程引用这片内存。
共享内存的方式有很多种,目前主流的方式有:
sysv由内核提供,对应内核代码在linux/ipc/shm.c,废话不多说,直接上代码:
#include
#include
#include
int main() {
key_t key = ftok("/dev/shm/testshm", 0);
int id = shmget(key, 0x400000, IPC_CREAT | 0666);
char *p = (char*)shmat(id, nullptr, 0);
memset(p, 'T', 0x400000);
shmdt(p);
return 0;
}
#include
#include
#include
int main() {
key_t key = ftok("/dev/shm/testshm", 0);
int id = shmget(key, 0x400000, 0666);
char *p = (char*)shmat(id, nullptr, 0);
printf("%c %c %c %c\n", p[0], p[1], p[2], p[3]);
shmdt(p);
return 0;
}
posix通过shm_open 和mmap来实现对共享内存的操作,从API上看比sysv的更易于使用。
直接上代码:
#include
#include
#include
#include
#include
#include
#include
int main() {
int fd = shm_open("testposix", O_CREAT | O_RDWR, 0666);
ftruncate(fd, 0x400000);
char *p = (char*)mmap(nullptr, 0x400000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
memset(p, 'T', 0x400000);
munmap(p, 0x400000);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
int main() {
int fd = shm_open("testposix", O_RDONLY, 0666);
ftruncate(fd, 0x400000);
char *p = (char*)mmap(nullptr, 0x400000, PROT_READ, MAP_SHARED, fd, 0);
printf("%c %c %c %c\n", p[0], p[1], p[2], p[3]);
munmap(p, 0x400000);
return 0;
}
共享句柄主要是通过:
使用方式:
int fd = memfd_create("memfd", 0);
ftruncate(fd, size);
void *p = mmap(nullptr, 0x400000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// do someting for p
munmap(p, size);
那这种memfd_create+unix socket domain有什么好处呢?
安全!它并不像sysv这种通过key就能访问那片共享内存,这种共享handle的方式通过memfd_create将fd匿名起来。接口上也比sysv更加简洁。至于posix本质也是通过shm_open或者open来打开一个映射到硬盘的文件,本质也存在不安全性。
这种方式的示例过长,就不展示了。。。