• 通过socket+mmap以及环形缓存buf实现内存共享通信


    目录

    socket建联

    memfd_create句柄传输

    mmap建联

    环形buf共享数据

    大致原理

    数据结构框图

    更新写指针

    更新读指针

    是否可写

    是否可读

    源码地址


    socket建联

    启动两个进程(进程间socket通信的发送端和接收端)分别进行初始化,通过统一的本地文件进行进程间socket通信。

            服务端(接收端)执行socket->bind->listen->accept四步。

            客户端(发送端)执行socket->connect两步。

    若只开了服务端,则一直accept等待客户端连接,若只开了客户端,则connect不上重复connect直至超时。

    memfd_create句柄传输

    通过memfd_create创建内存共享文件句柄,两端需要同时拥有这个文件句柄才能进行内存共享,因此在第一步中client connect成功之后则创建句柄,通过sendmsg发送给server,server accept成功之后则recvfrom等待接收这个文件句柄。

    mmap建联

    两端均拿到文件句柄之后,同时开启mmap,进行内存地址的共享,两个进程共同拿到一个函数指针,指向同一块内存地址。下面再进行指针偏移的操作进行数据的共享传输。

    环形buf共享数据

    大致原理

    通过读写指针的更新在同一块内存地址上进行数据的共享,一边放数据更新写指针,一边读数据更新读指针。读之前根据读写指针判断是否可读,写之前根据读写指针判断是否可写。

    客户端和服务端共享一块地址时会给这块地址分配一个大小,此处比如100M,也即1024*1024*100  字节(Byte),随后再设置一个单块数据的大小限制,如2M,单块数据表示每次入的大小必须小于2M。整个共享内存100M的头部会有一个结构体常驻在此,长期共享读写指针之类的数据,在它的后面才是每一包数据的读写,读写的依据都是头部实时更新的读写指针位置数据。

    数据结构框图

     

     除了头部数据(我们算sizeof(SShareHead))之外,剩余的内存写进程从第一个2M开始放,读进程从第一个2M开始读。

    速度:

    1、若写的比读的快,那么写指针会一直往后,直到耗尽了100M,此时会从第一个2M开始继续覆盖,极端情况下如果读指针还停留在第一个2M的位置,那么就不可写了,若读指针在一半的位置,那么写指针会去追逐它直到追上,那么就会停止写入,读进程读一次,写进程才有空余写一次,速度就完全由慢一些的读进程决定了。

    2、若读的比较快,那么根据读写指针的计算,会判断不可读,直到有写入数据,速度完全由写进程决定。

    最终有一种环形追逐的感觉,所以我们称之为环形buf,其实实质上是一块内存重复读写。

    更新写指针

    1. /*更新写指针*/
    2. //初始化的时候dwRingLen = 100M - sizeof(SShareHead)) - 2M //总长度去掉头部,去掉一包数据
    3. if (dwWidx + dwWriteLen >= dwRingLen)
    4. {
    5. //如果写指针 + 当前要写的数据已经到达最后一个数据块(最后一个2M)的位置,则写指针清零,从零开始写
    6. dwWidx = 0;
    7. }
    8. else
    9. {
    10. //否则写指针正常往后加
    11. dwWidx += dwWriteLen;
    12. }

    更新读指针

    1. if (dwRidx + dwReadLen >= dwRingLen)
    2. dwRidx = 0;
    3. else
    4. dwRidx += dwReadLen;

    是否可写

    1. //dwLen是此次想要写入的长度
    2. bool checkWritable(u32 dwLen)
    3. {
    4. //u32代表unsigned int
    5. //初始化的时候dwRingLen = 100M - sizeof(SShareHead)) - 2M
    6. //总长度去掉头部,去掉一包数据,见数据框图
    7. u32 dwSpace = dwRingLen - (dwWidx - dwRidx + dwRingLen) % dwRingLen;
    8. if (dwWidx > dwRidx && dwRidx > 0)
    9. {
    10. dwSpace += dwTailLen;
    11. }
    12. return (dwSpace > dwLen);
    13. }

    是否可读

    1. dwLen = (dwWidx - dwRidx + dwRingLen) % dwRingLen;
    2. //有数据直接返回,TProcessShareDataHead是自定义的私有数据头
    3. if (dwLen >= sizeof(TProcessShareDataHead))
    4. {
    5. //m_pDataAddr是数据框图的红色箭头指向处
    6. pHead = (TProcessShareDataHead *)(m_pDataAddr + mdwRidx);
    7. return true;
    8. }
    9. //校验拖尾数据是否可读,极端情况读指针在最后,写指针在最前面
    10. //读指针=dwRingLen-1,写指针=0;实际上读指针还能再读最后一包数据
    11. if (dwRidx > dwWidx)
    12. {
    13. //m_pDataAddr是数据框图的红色箭头指向处
    14. pHead = (TProcessShareDataHead *)(m_pDataAddr + dwRidx);
    15. return true;
    16. }
    17. return false;

    源码地址

    码云:memfdshare: 共享内存模型 (gitee.com)

  • 相关阅读:
    海龟作图的简单介绍
    回顾:综合布线与连接部件AEM测试创新品牌开放日(深圳站)
    【数学模型】基于Matlab模拟疫情 SEIRS模型
    Python实现WOA智能鲸鱼优化算法优化随机森林回归模型(RandomForestRegressor算法)项目实战
    如何在Spring JDBC 中获取自动生成的 ID
    网络安全 - ARP 欺骗原理+实验
    多态的理解和使用
    office的excel中使用,告诉我详细的解决方案,如何变成转化为金额格式
    2.4 Sample Moments and Hypothesis Tests
    asisctf 2023 web hello wp
  • 原文地址:https://blog.csdn.net/FlayHigherGT/article/details/126237783