为什么继承会导致共享内存分段错误?
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 时实现和使用(共享内存实际上并未使用),但在构建时被遗漏目标可执行文件。
那么,这里的答案是不要对占用共享内存的对象使用继承。
我正在尝试在两个进程之间共享的共享内存中使用一个 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 时实现和使用(共享内存实际上并未使用),但在构建时被遗漏目标可执行文件。
那么,这里的答案是不要对占用共享内存的对象使用继承。