OceanBase 源码中 static 变量析构顺序导致的 coredump

问题背景

在 OceanBase 开源代码中,有这样一段代码,它可能会导致在系统退出时发生 coredump:

oceanbase::sql::ObSQLSessionInfo &session()
{
  static oceanbase::sql::ObSQLSessionInfo SESSION;
  return SESSION;
}

ObArenaAllocator &session_alloc()
{
  static ObArenaAllocator SESSION_ALLOC;
  return SESSION_ALLOC;
}

int ObTableApiProcessorBase::init_session()
{
  int ret = OB_SUCCESS;
  static const uint32_t sess_version = 0;
  static const uint32_t sess_id = 1;
  static const uint64_t proxy_sess_id = 1;
  if (OB_FAIL(session().test_init(sess_version, sess_id, proxy_sess_id, &session_alloc()))) {
    LOG_WARN("init session failed", K(ret));
  }
  // more ...
  return ret;
}

利用 ASAN 诊断发现,静态对象 SESSION 析构时会引用一个 SESSION_ALLOC,而 SESSION_ALLOC 也是一个静态对象,当 SESSION_ALLOC 先于 SESSION 析构时,SESSION 析构时就会访问到非法内存(因为 SESSION_ALLOC 已经析构)。

C 语言中,对于 static 变量,析构规则是:先构造者后析构 可以用下面的程序来验证:

[xiaochu.yh ~] $cat test_destroy.cpp
// Copyright 1999-2021 Alibaba Inc. All Rights Reserved.
// Author:
//   xiaochu.yh@alipay.com
//
// test static variable destory order

#include <iostream>

using namespace std;

class A
{
public:
  A() { cout << "construct A" << endl; }
  ~A() { cout << "deconstruct A" << endl; }
  void init() {}
};

class B
{
public:
  B() { cout << "construct B" << endl; }
  ~B() { cout << "deconstruct B" << endl; }
  void init(A &a) { a.init(); }
};

A &getA() {
  static A a;
  return a;
}

B &getB() {
  static B b;
  return b;
}

void func()
{
  getB().init(getA());
}

int main(int argc, const char *argv[])
{
  func();
  return 0;
}

[xiaochu.yh ~] $g++ test_destroy.cpp  -o test_destroy
[xiaochu.yh ~] $./test_destroy
construct A
construct B
deconstruct B
deconstruct A

修复方法

既然先构造者后析构 ,那么我们可以在 SESSION 构造之前,主动调用一次 session_alloc,使得 SESSION_ALLOC 先构造即可。

代码如下:

oceanbase::sql::ObSQLSessionInfo &session()
{
  static oceanbase::sql::ObSQLSessionInfo SESSION;
  return SESSION;
}

ObArenaAllocator &session_alloc()
{
  static ObArenaAllocator SESSION_ALLOC;
  return SESSION_ALLOC;
}

int ObTableApiProcessorBase::init_session()
{
  int ret = OB_SUCCESS;
  static const uint32_t sess_version = 0;
  static const uint32_t sess_id = 1;
  static const uint64_t proxy_sess_id = 1;
  
  // ensure allocator is constructed before session to
  // avoid coredump at observer exit
  ObArenaAllocator &dummy_allocator = session_alloc();
  UNUSED(dummy_allocator);
  
  if (OB_FAIL(session().test_init(sess_version, sess_id, proxy_sess_id, &session_alloc()))) {
    LOG_WARN("init session failed", K(ret));
  }
  // more ...
  return ret;
}
### 关于Linux环境下的Coredump机制源码 在Linux环境中,coredump机制允许当程序崩溃时保存内存映像到文件中以便后续分。此功能由内核实现,并且涉及多个部分的工作。 核心逻辑位于`fs/binfmt_elf.c` 文件中的 `elf_core_dump()` 函数[^1]。该函数负责创建ELF格式的核心转储文件。它会遍历进程地址空间并收集必要的信息来建有效的 ELF 文件结。具体来说: - 需要处理不同类型的段(segments),比如栈、堆以及加载的共享库等。 - 对于每个线程组成员都会调用一次这个函数以确保多线程应用能够正确生成 core file。 此外,在 `kernel/sys.c` 中定义了系统调用接口 `do_coredump()` ,用于触发实际的 coredump 过程[^2]。这里包含了对各种条件检查的支持,例如权限验证和大小限制设置等功能。 对于配置选项而言,可以通过 `/proc/sys/kernel/core_pattern` 来指定如何命名及存储产生的 core dump 文件;而通过修改 `/proc/<pid>/limits` 可调整单个进程中所能占用的最大资源量,从而间接影响能否成功生成完整的 core dump 数据[^3]。 ```c // fs/binfmt_elf.c static int elf_core_dump(long signr, struct pt_regs *regs) { ... } // kernel/sys.c SYSCALL_DEFINE0(coredump) { return do_coredump(current->signal->group_exit_code); } ``` 为了更深入理解整个流程,建议阅读官方文档中有关 "Core Dumps" 的章节[^4],这提供了更多背景知识和技术细节方面的指导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值