为什么要用内存共享?
答案是ROS提供的服务或者话题都是通过网络来实现的。照顾到ROS节点可能架设在不同的硬件上这一点,这样做更具普遍性,但对于在同一台设备的两个节点间传输数据是非常不友好的。因为其既没必要性又浪费大量资源,且效率低下,容易造成网络堵塞,延时严重。
那么怎么改善最好呢,自然是进程间的内存共享。我们直接让两个节点共享一片物理内存,在里面做一个队列数据结构,一个往里面写,一个去里面读。
下面我们就来实现这一功能。跟上篇文章一样,我们先实现纯净的Linux C++版本,然后再把代码移植到ROS的节点中。
在C++中怎么共享内存?
实现Linux进程间的内存共享,主要参考这篇文章。
写进程
“写”进程,流程如下:
-
获得key, ftok()
-
使用key来创建一个共享内存 shmget()
-
映射共享内存(得到虚拟地址), shmat()
-
使用共享内存, 往共享内存中写入数据
-
解除映射 shmdt()
-
如果共享内存不再使用,可以使用shmctl()销毁共享内存
代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{ // 生成一个key
key_t key = ftok("./", 66);
// 创建共享内存,返回一个id
int shmid = shmget(key, 8, IPC_CREAT|0666|IPC_EXCL);
if(-1 == shmid)
{
perror("shmget failed");
exit(1);
}
// 映射共享内存,得到虚拟地址
void *p = shmat(shmid, 0, 0);
if((void*)-1 == p)
{
perror("shmat failed");
exit(2);
}
// 写共享内存
int *pp = (int *)p;
*pp = 0x12345678;
*(pp + 1) = 0xffffffff;
// 解除映射
if(-1 == shmdt(p))
{
perror("shmdt failed");
exit(3);
}
printf("解除映射成功,点击回车销毁共享内存\n");
getchar();
// 销毁共享内存
if(-1 == shmctl(shmid, IPC_RMID, NULL))
{
perror("shmctl failed");
exit(4);
}
return 0;
}
读进程
“读”进程,流程如下:
-
获得key, ftok()
-
使用key来获得一个共享内存 shmget()
-
映射共享内存(得到虚拟地址), shmat()
-
使用共享内存, 读取共享内存中的数据
-
解除映射 shmdt()
代码如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main()
{
// 生成一个key
key_t key = ftok("./", 66);
// 获取共享内存,返回一个id
int shmid = shmget(key, 0, 0);
if(-1 == shmid)
{
perror("shmget failed");
exit(1);
}
// 映射共享内存,得到虚拟地址
void *p = shmat(shmid, 0, 0);
if((void*)-1 == p)
{
perror("shmat failed");
exit(2);
}
// 读共享内存
int x = *(int *)p;
int y = *((int *)p + 1);
printf("从共享内存中都取了:0x%x 和 0x%x \n", x, y);
// 解除映射
if(-1 == shmdt(p))
{
perror("shmdt failed");
exit(3);
}
return 0;
}
将上述代码移植进ROS
创建writeshmInRos.cpp
#include <ros/ros.h>
#include <iostream>
#include "lcm/lcm-cpp.hpp"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_publisher");
ros::NodeHandle nh;
// 生成一个key
key_t key = ftok("./", 66);
// 创建共享内存,返回一个id
int shmid = shmget(key, 8, IPC_CREAT|0666|IPC_EXCL);
if(-1 == shmid)
{
perror("shmget failed");
exit(1);
}
// 映射共享内存,得到虚拟地址
void *p = shmat(shmid, 0, 0);
if((void*)-1 == p)
{
perror("shmat failed");
exit(2);
}
// 写共享内存
int *pp = (int *)p;
*pp = 0x12345678;
*(pp + 1) = 0xffffffff;
// 解除映射
if(-1 == shmdt(p))
{
perror("shmdt failed");
exit(3);
}
printf("解除映射成功,点击回车销毁共享内存\n");
getchar();
// 销毁共享内存
if(-1 == shmctl(shmid, IPC_RMID, NULL))
{
perror("shmctl failed");
exit(4);
}
ros::spinOnce();}
创建readshmInRos.cpp
#include <ros/ros.h>
#include <iostream>
#include "lcm/lcm-cpp.hpp"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_publisher");
ros::NodeHandle nh;
// 生成一个key
key_t key = ftok("./", 66);
// 获取共享内存,返回一个id
int shmid = shmget(key, 0, 0);
if(-1 == shmid)
{
perror("shmget failed");
exit(1);
}
// 映射共享内存,得到虚拟地址
void *p = shmat(shmid, 0, 0);
if((void*)-1 == p)
{
perror("shmat failed");
exit(2);
}
// 读共享内存
int x = *(int *)p;
int y = *((int *)p + 1);
printf("从共享内存中都取了:0x%x 和 0x%x \n", x, y);
// 解除映射
if(-1 == shmdt(p))
{
perror("shmdt failed");
exit(3);
} ros::spinOnce();
}
运行结果如下:
本文转载自:https://blog.csdn.net/weixinhum/article/details/84585954
来源:CSDN
作者:BM_LM
链接:https://blog.csdn.net/qq_32617285/article/details/104113977