目录
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;
源码地址