为什么继承会导致共享内存分段错误?

时间:2019-08-16 11:29:50

标签: c++11 inheritance ipc shared-memory

我试图在两个进程之间共享的共享内存中使用类实例。我创建和使用的基类都没有问题。但是,当该类从另一个类继承时,共享内存实例将导致segfaults。

为什么使用继承的类会导致段错误?

我正在使用GCC 4.8.5在Redhat Linux VM(RHEL 7.6)上构建/运行/调试代码。

到目前为止,我已经建立了共享内存并使用char(一个简单的类,其中包含一个带有setter / getter的char)以及一个纯抽象类(它由简单char类继承)来测试了共享内存的用法。在这两种情况下,共享内存都可以正常工作,但在第三种情况下,则存在段错误。

在崩溃的情况下,第一个过程使用ftok创建一个密钥,获取一块与我使用的类大小相等的共享内存,然后将其附加到该内存,并使用placement new运算符创建一个类实例在共享内存中。然后,第二个过程创建相同的密钥,获取内存块,将其附加到该内存块,然后尝试访问它。

这时,如果第二个进程尝试使用内存(该类的调用方法),它将发生段错误。但是,它可以在共享内存中的某个位置创建一个新对象,但这将导致第一个进程在访问时出现段错误。

这是课程设置:

class MyClassInterface {
 public:
};

class MyClass : public MyClassInterface {
 private:
  char val;
 public:
  MyClass() {
    val = 'a';
  }
  ~MyClass() {
    std::cout << "MyClass destructor called" << std::endl;
  };

  void SetVal(char c) {
    val = c;
  }

  char GetVal() {
    return val;
  }
};

这是过程1中共享内存控件的外观:

MyClass *test_val;

int main(int argc, char **argv) {
  ...

  key_t key = ftok("/tmp", 127);
  if (key < 0) {
    exit(EXIT_FAILURE);
  }

  int shmid = shmget(key, sizeof(MyClass), 0666 | IPC_CREAT);
  MyClass *shmem = (MyClass *) shmat(shmid, nullptr, 0);
  if (shmem == (void *) -1) {
    exit(EXIT_FAILURE);
  }

  test_val = new(shmem) MyClass;
  test_val->SetVal('b');


  std::cout << "Val: " << test_val->GetVal() << std::endl;

这是过程2中的共享内存控件:

int main(int argc, char **argv) {
  ...

  key_t key = ftok("/tmp", 127);
  if (key < 0) {
    exit(EXIT_FAILURE);
  }

  int shmid = shmget(key, sizeof(MyClass), 0666 | IPC_CREAT);
  MyClass *shmem = (MyClass *) shmat(shmid, nullptr, 0);
  if (shmem == (void *) -1) {
    exit(EXIT_FAILURE);
  }

  MyClass *ipc_dc = new(shmem) MyClass;
  ipc_dc->SetVal('z');

  std::cout << "Val: " << ipc_dc->GetVal() << std::endl;

我想要的是能够使用从共享内存中的纯抽象类继承的类。实际使用将包括使用信号量来保护类的成员。

我的期望是,初始化之后,两个进程可以使用类对象并自由更新其值(当然,受信号量保护)。但是,尝试以任何方式访问该类(使用继承时)只会导致分段错误。似乎有些东西阻止两个进程同时访问该内存。

我在继承方面做错了吗?我在类中使用共享内存的方式有问题吗?都是吗?

1 个答案:

答案 0 :(得分:0)

正如一些程序员不曾指出的那样,问题是纯抽象类使用虚函数,虚函数在访问对象的每个进程中不会映射到相同的位置。尝试在错误的地址使用映射到vtable的函数会导致段错误,这很有意义。

就我而言,我需要与GMock一起使用的基类,但对于实际应用程序则不需要。围绕类定义添加的预处理器指令允许在构建GTest / GMock(实际上不使用共享内存)时实现和使用“接口”,但在为目标可执行文件构建时将其省略。

因此,答案是不要对将占用共享内存的对象使用继承。