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

Why does inheritance cause shared memory segmentation fault?

我正在尝试在两个进程之间共享的共享内存中使用一个 class 实例。我创建和使用的基础 class 没有问题。但是,当 class 从另一个 class 继承时,共享内存实例会导致段错误。

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

我是 运行 building/running/debugging Redhat Linux VM (RHEL 7.6) 上的代码,使用 GCC 4.8.5。

到目前为止,我设置了共享内存并使用 char 测试了我的使用情况,一个简单的 class 包含一个带有 setter/getter 的 char,以及一个纯抽象 class,简单的 char class 继承了它。共享内存在前两种情况下工作正常,但在第三种情况下出现段错误。

在崩溃的情况下,第一个进程使用 ftok 创建一个密钥,获得一块大小等于我正在使用的 class 的共享内存,然后附加到该内存并使用新的位置运算符在共享内存中创建一个 class 实例。然后,第二个进程创建相同的键,获取内存块,附加到该内存块,并尝试访问它。

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

这是 class 设置:

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;

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

的成员

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

我在继承方面做错了什么吗?我在 class 中使用共享内存的方式有问题吗?两个?

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

在我的例子中,我需要基础 class 来与 GMock 一起使用,而不是用于实际的应用程序。在 class 定义周围添加预处理器指令允许 "interface" 在构建 GTest/GMock 时实现和使用(共享内存实际上并未使用),但在构建时被遗漏目标可执行文件。

那么,这里的答案是不要对占用共享内存的对象使用继承。