ROS系统学习8—节点间的内存共享(初级篇)

168
0
2021年1月27日 09时14分

 

上一篇文章我们介绍了怎么在ROS中应用LCM与外部进行通信。本篇我们回到ROS的内部,看看如何实现ROS节点间的内存共享,以实现更加快速的内部通信。

 

首先,我们需要清楚为什么要用内存共享?

 

答案是ROS提供的服务或者话题都是通过网络来实现的。这样做虽然更具普遍性,照顾到ROS节点可能架设在不同的硬件上这一点,然对于在同一台设备的两个节点间传输数据是非常不友好的。因为其既没必要性又浪费大量资源,且效率低下,容易造成网络堵塞,延时严重。

 

那么怎么改善最好呢,自然是进程间的内存共享。我们直接让两个节点共享一片物理内存,在里面做一个队列数据结构,一个往里面写,一个去里面读。

 

下面我们就来实现这一功能。跟上篇文章一样,我们先实现纯净的Linux C++版本,然后再把代码移植到ROS的节点中。

 

实现Linux进程间的内存共享,主要参考这篇文章(因为写的很好,运行顺利,这部分只做了一点点修改,其他照抄,见谅见谅):

 

“写”进程,流程如下:

 

1) 获得key, ftok()

2) 使用key来创建一个共享内存 shmget()

3) 映射共享内存(得到虚拟地址), shmat()

4) 使用共享内存, 往共享内存中写入数据

5) 解除映射 shmdt()

6) 如果共享内存不再使用,可以使用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;
}

 

“读”进程,流程如下:

 

1) 获得key, ftok()

2) 使用key来获得一个共享内存 shmget()

3) 映射共享内存(得到虚拟地址), shmat()

4) 使用共享内存, 读取共享内存中的数据

5) 解除映射 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;
}

 

运行结果如下:

 

1611560459(1)

 

下面我们直接把代码移植到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();
}

 

运行结果如下:

 

微信图片_20210125154236

 

到此就实现了最简单的节点间的内存共享,当然它离实用还十万八千里,后面有机会会介绍如何改造它,让它实用起来。

 

发表评论

后才能评论