本文的所有的分析仅基于个人理解,代码基于ceph nautilus版本
MDSDaemon Start
首先一切的开始自然是mds进程的上电过程:
src/ceph_mds.cc
int main(int argc, const char **argv)
{
// 全局初始化,任何类型ceph daemon启动时都需要调用global_init来完成一些初始化工作
auto cct = global_init(NULL, args,
CEPH_ENTITY_TYPE_MDS, CODE_ENVIRONMENT_DAEMON,
0, "mds_data");
// 初始化堆栈分析器,如果设置了CEPH_HEAP_PROFILER_INIT的环境变量后才会启动,且需要google-perftools才能使用
// 默认情况下是不启动的,后续可以通过ceph tell mds.$name heap start_profiler来启动
ceph_heap_profiler_init();
// 对fork的封装
Preforker forker;
entity_addrvec_t addrs;
pick_addresses(g_ceph_context, CEPH_PICK_ADDRESS_PUBLIC, &addrs);
// Normal startup
if (g_conf()->name.has_default_id()) {
derr << "must specify '-i name' with the ceph-mds instance name" << dendl;
exit(1);
}
if (g_conf()->name.get_id().empty() ||
(g_conf()->name.get_id()[0] >= '0' && g_conf()->name.get_id()[0] <= '9')) {
derr << "MDS id '" << g_conf()->name << "' is invalid. "
"MDS names may not start with a numeric digit." << dendl;
exit(1);
}
// fork子进程并等待子进程退出??
if (global_init_prefork(g_ceph_context) >= 0) {
std::string err;
int r = forker.prefork(err);
if (r < 0) {
cerr << err << std::endl;
return r;
}
if (forker.is_parent()) {
if (forker.parent_wait(err) != 0) {
return -ENXIO;
}
return 0;
}
// 为子进程执行global_init
global_init_postfork_start(g_ceph_context);
}
common_init_finish(g_ceph_context);
global_init_chdir(g_ceph_context);
auto nonce = ceph::util::generate_random_number<uint64_t>();
// 创建一个Messenger实例并进行相关配置
std::string public_msgr_type = g_conf()->ms_public_type.empty() ? g_conf().get_val<std::string>("ms_type") : g_conf()->ms_public_type;
Messenger *msgr = Messenger::create(g_ceph_context, public_msgr_type,
entity_name_t::MDS(-1), "mds",
nonce, Messenger::HAS_MANY_CONNECTIONS);
if (!msgr)
forker.exit(1);
msgr->set_cluster_protocol(CEPH_MDS_PROTOCOL);
cout << "starting " << g_conf()->name << " at " << msgr->get_myaddrs()
<< std::endl;
uint64_t required =
CEPH_FEATURE_OSDREPLYMUX;
msgr->set_default_policy(Messenger::Policy::lossy_client(required));
msgr->set_policy(entity_name_t::TYPE_MON,
Messenger::Policy::lossy_client(CEPH_FEATURE_UID |
CEPH_FEATURE_PGID64));
msgr->set_policy(entity_name_t::TYPE_MDS,
Messenger::Policy::lossless_peer(CEPH_FEATURE_UID));
msgr->set_policy(entity_name_t::TYPE_CLIENT,
Messenger::Policy::stateful_server(0));
int r = msgr->bindv(addrs);
if (r < 0)
forker.exit(1);
// get monmap
/* monmap内容如下,记录了mon节点的ip/port,名称等信息
fsid d60bfa64-9f2c-42c4-ad33-d964eab9e7fc
last_changed 2019-10-10 10:16:41.683766
created 2019-10-10 10:16:41.683766
0: 192.168.2.41:6789/0 mon.um1
1: 192.168.2.42:6789/0 mon.um2
2: 192.168.2.43:6789/0 mon.um3
具体解释见https://docs.ceph.com/docs/master/architecture/
*/
MonClient mc(g_ceph_context);
if (mc.build_initial_monmap() < 0)
forker.exit(1);
global_init_chdir(g_ceph_context);
msgr->start();
// start mds
// 新建一个mds实例并初始化,至此mds常规启动完成,可接受消息并分发处理
mds = new MDSDaemon(g_conf()->name.get_id().c_str(), msgr, &mc);
r = mds->init();
if (r < 0) {
msgr->wait();
goto shutdown;
}
msgr->wait();
shutdown:
// 优雅退出流程
return 0;
}
至此,新建了一个MDSDaemon实例,MDSDaemon是mds进程的抽象,继承自Dispatcher类,负责mds和外界(其他ceph daemon、cephfs client)的交互,下面重点关注MDSDaemon的初始化
MDSDaemon Init
src/mds/MDSDaemon.cc
int MDSDaemon::init()
{
// 注册到Messenger的订阅者列表中,ceph网络模块的代码此次不关注
// 只要知道这里是和收发消息有关即可
messenger->add_dispatcher_tail(&beacon);
messenger->add_dispatcher_tail(this);
// 初始化monclient
r = monc->init();
// 初始化mgrclient
mgrc.init();
messenger->add_dispatcher_head(&mgrc);
// 想要订阅mdsmap和mgrmap,mdsmap用户维护状态,mgrmap还未研究
monc->sub_want("mdsmap", 0, 0);
monc->sub_want("mgrmap", 0, 0);
monc->renew_subs();
mds_lock.Unlock();
/*
admin socket相关,完成的工作有:
|__创建AdminSocket类实例
|__创建MDSSocketHook类实例
|__调用AdminSocket->register_command()来注册MDSDaemon支持的命令
*/
set_up_admin_socket();
g_conf().add_observer(this);
mds_lock.Lock();
if (beacon.get_want_state() == MDSMap::STATE_DNE) {
suicide(); // we could do something more graceful here
dout(4) << __func__ << ": terminated already, dropping out" << dendl;
mds_lock.Unlock();
return 0;
}
// 初始化SafeTimer定时器
timer.init();
// 初始化Beacon
beacon.init(*mdsmap);
// 设置Messenger名称
messenger->set_myname(entity_name_t::MDS(MDS_RANK_NONE));
// schedule tick
// 开始定时器调度
reset_tick();
mds_lock.Unlock();
return 0;
}
定时器
从MDSDaemon的init过程可以看到,会开启一个定时器,之后在mds的生命周期内,该定时器会不断触发并进行一些工作,这里再简单介绍下ceph中定时器的用法:
void MDSDaemon::reset_tick()
{
// cancel old
if (tick_event) timer.cancel_event(tick_event);
// schedule
// 开启定时器,当定时器时间到后调用MDSDaemon::tick()
tick_event = timer.add_event_after(
g_conf()->mds_tick_interval,
new FunctionContext([this](int) {
ceph_assert(mds_lock.is_locked_by_me());
tick();
}));
}
// 定时器时间到时执行该函数:先重置定时器,再调度MDSRank::tick()
// 默认定时器时间为mds_tick_interval:5s
void MDSDaemon::tick()
{
// reschedule
reset_tick();
// Call through to subsystems' tick functions
if (mds_rank) {
mds_rank->tick();
}
}
消息接收和处理
mds收到外部消息后通过MDSDaemon::ms_dispatch2来进行分发处理,这里又分为core msg和client msg两类。
bool MDSDaemon::ms_dispatch2(const Message::ref &m)
{
std::lock_guard l(mds_lock);
if (stopping) {
return false;
}
// Drop out early if shutting down
// 如果mds进程已经处于shutdown状态,则不能处理消息
if (beacon.get_want_state() == CEPH_MDS_STATE_DNE) {
dout(10) << " stopping, discarding " << *m << dendl;
return true;
}
// First see if it's a daemon message
// 优先处理其他daemon的msg(mon,osd等)
const bool handled_core = handle_core_message(m);
if (handled_core) {
return true;
}
// Not core, try it as a rank message
// 如果不是daemon msg,则交由MDSRankDispatcher::ms_dispatch()来处理
if (mds_rank) {
return mds_rank->ms_dispatch(m);
} else {
return false;
}
}
MDS消息处理流程总结概括为下图:
总结
- MDS的启动过程大致如下:
- 命令行参数解析
- 全局初始化工作
- 初始化堆栈分析器,需设置CEPH_HEAP_PROFILER_INIT的env
- fork子进程并等待子进程退出
- 创建一个Messenger实例并进行相关配置,用于消息的收发
- monmap的获取
- 新建一个MDSDaemon实例并初始化
- mds启动后默认开一个启定时器,间隔为mds_tick_interval(默认5s)
- mds的消息处理入口为ms_dispatch2