通过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,其实实质上是一块内存重复读写。

更新写指针

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

更新读指针

    if (dwRidx + dwReadLen >= dwRingLen)
        dwRidx = 0;
    else
        dwRidx += dwReadLen;

是否可写

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

是否可读

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

源码地址

码云:mytest-projects: 收集一些c/c++的技术点 (gitee.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值