linux 共享内存方式

linux 共享内存实现方式

引言

最近看了宋宝华老师写的《世界上最好的共享内存》,宋宝华老师文章链接: https://cloud.tencent.com/developer/article/1551288,深有感触,正好也在接触这块东西,迫不及待的想要深入学习和复盘下这块知识,所以这里简单写个文章总结下linux共享内存的几种实现方式。

早期传统的共享内存,强调把同一片内存,map到多个进程的虚拟地址空间(在相应进程找到一个VMA区域),通过这种方式CPU可以在各个进程访问到这片内存。
在这里插入图片描述

而现在很多场景下其实不再强调映射到进程虚拟地址空间的概念,而是更强调以某种“句柄”的形式,让大家都知道这一句柄的存在,并且可以借助此“句柄”来跨进程引用这片内存。
在这里插入图片描述

共享内存的方式有很多种,目前主流的方式有:

  • 基于传统SYS V的共享内存;
  • 基于POSIX mmap文件映射实现共享内存;
  • 通过memfd_create()和fd跨进程共享实现共享内存;

SYSV共享内存

sysv由内核提供,对应内核代码在linux/ipc/shm.c,废话不多说,直接上代码:

  • 写端:
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>

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 <sys/shm.h>
#include <unistd.h>
#include <stdio.h>

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;
}

运行结果:
在这里插入图片描述
通过ipcs可以看到刚才创建的共享内存:
在这里插入图片描述

POSIX共享内存

posix通过shm_open 和mmap来实现对共享内存的操作,从API上看比sysv的更易于使用。
直接上代码:

  • 写端:
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


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 <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>


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;
}

运行结果:
在这里插入图片描述
在/dev/shm下可以看到:
在这里插入图片描述

共享句柄

共享句柄主要是通过:

  1. memfd_create 创建匿名fd
  2. 将匿名fd 通过unix socket domain进行各个进程共享fd操作
  • memfd_create
    这个接口比较有趣,本意是创建匿名fd,它会返回一个“匿名”内存“文件”的fd,而它本身并没有实体的文件系统路径,你通过这个接口创建了一个文件,这个匿名文件不存在路径,只存在内存中。

使用方式:

    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);
  • unix socket domain
    如何跨进程分享fd?我们得通过unix socket domain,我们都知道linux的fd本身是个进程级别的东西,从/proc/pid/fd可以看到每个进程自己的fd列表。
    在这里插入图片描述
    但是进程A的 0 1 2…等fd和进程B的0 1 2…等fd并不是一个概念。简单举个例子,你有个孩子叫狗蛋,你的同事有个孩子也叫狗蛋,但是这两个狗蛋并不是同一个狗蛋…好了,言归正传,linux提供了一种机制,可以把进程A的fd发给进程B。比如进程A有个2的fd,发给进程B的时候,进程B自己本身已经有2这个fd了,那进程B就会把进程A发过来的fd存为100(也是举例)。相当于建立了一个映射机制。
    在这里插入图片描述
    进程A有个2的fd,发给进程B,但是进程B已经有2的fd了,所以进程B把进程A的2fd映射成fd100,这个100进程B本身没有。我们把这种发送称为共享,那如何共享fd,linux提供了unix socket domain机制。

那这种memfd_create+unix socket domain有什么好处呢?
安全!它并不像sysv这种通过key就能访问那片共享内存,这种共享handle的方式通过memfd_create将fd匿名起来。接口上也比sysv更加简洁。至于posix本质也是通过shm_open或者open来打开一个映射到硬盘的文件,本质也存在不安全性。

这种方式的示例过长,就不展示了。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值