【发布时间】:2011-03-23 16:01:19
【问题描述】:
请注意,我不想解决我的问题的任何问题 - 我正在考虑事情发生的概率,因此想知道一些事情:
如果你删除对象并使用 gcc 作为编译器会发生什么?
上周我正在调查一次崩溃,其中竞争条件导致对象被双重删除。
调用对象的虚析构函数时发生崩溃,因为指向虚函数表的指针已经被覆盖。
第一次delete会覆盖虚函数指针吗?
如果不是,那么第二次删除是否安全,只要在此期间没有进行新的内存分配?
我想知道为什么我之前没有识别出我遇到的问题,唯一的解释是虚拟函数表在第一次删除期间被立即覆盖,或者第二次删除没有崩溃。
(第一个意味着如果“比赛”发生,崩溃总是发生在同一个位置 - 第二个,当比赛发生时通常什么都不会发生 - 并且只有当第三个线程覆盖删除对象时同时问题发生。)
编辑/更新:
我做了一个测试,以下代码因段错误(gcc 4.4、i686 和 amd64)而崩溃:
class M
{
private:
int* ptr;
public:
M() {
ptr = new int[1];
}
virtual ~M() {delete ptr;}
};
int main(int argc, char** argv)
{
M* ptr = new M();
delete ptr;
delete ptr;
}
如果我从 dtor 中删除 'virtual',程序会被 glibc 中止,因为它检测到双重释放。 使用 'virtual' 在对析构函数进行间接函数调用时会发生崩溃,因为指向虚函数表的指针无效。
在 amd64 和 i686 上,指针都指向一个有效的内存区域(堆),但那里的值无效(计数器?它非常低,例如 0x11 或 0x21)所以“调用”(或“jmp”当编译器进行返回优化时)跳转到无效区域。
节目收到信号SIGSEGV,
分段错误。 0x0000000000000021
在?? () (gdb)
#0 0x0000000000000021 在?? ()
#1 0x000000000040083e in main()
所以在上面提到的条件下,指向虚函数表的指针总是被第一次删除覆盖,所以如果类有虚析构函数,下一次删除将跳转到必杀技。
【问题讨论】:
-
听起来你也需要投资一些互斥体或关键部分
-
0A0D:这是我的初步解决方案(解决方法)。实际上有一个设计缺陷,因为没有意图有两个线程可以删除对象。
标签: c++ gcc probability delete-operator postmortem-debugging