我试图在两个进程之间共享的共享内存中使用类实例。我创建和使用的基类都没有问题。但是,当该类从另一个类继承时,共享内存实例将导致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;
我想要的是能够使用从共享内存中的纯抽象类继承的类。实际使用将包括使用信号量来保护类的成员。
我的期望是,初始化之后,两个进程可以使用类对象并自由更新其值(当然,受信号量保护)。但是,尝试以任何方式访问该类(使用继承时)只会导致分段错误。似乎有些东西阻止两个进程同时访问该内存。
我在继承方面做错了吗?我在类中使用共享内存的方式有问题吗?都是吗?
答案 0 :(得分:0)
正如一些程序员不曾指出的那样,问题是纯抽象类使用虚函数,虚函数在访问对象的每个进程中不会映射到相同的位置。尝试在错误的地址使用映射到vtable的函数会导致段错误,这很有意义。
就我而言,我需要与GMock一起使用的基类,但对于实际应用程序则不需要。围绕类定义添加的预处理器指令允许在构建GTest / GMock(实际上不使用共享内存)时实现和使用“接口”,但在为目标可执行文件构建时将其省略。
因此,答案是不要对将占用共享内存的对象使用继承。