MySQL-8.0.x启动过程
背景
阅读 MySQL 的源代码,分析 MySQL 的启动过程 。
第 001 步执行 main 函数
main 函数是 C/C++ 软件的入口函数,MySQL 也是从这个函数开始执行的。
extern int mysqld_main(int argc, char **argv);
int main(int argc, char **argv) { return mysqld_main(argc, argv); }
位于文件 sql/main.cc
,可以看到这里直接调用了 mysqld_main
这个函数就完事了。
第 002 步执行 main 函数
mysqld_main 这个函数就非常长了,宏观上来看只做 3 件事, 1. 初始化 2.启动监听以接收连接 3. 若 mysqld 服务要退出(有可能是被 kill 或 shutdown) 执行相应的清理动作.
#ifdef _WIN32
int win_main(int argc, char **argv)
#else
int mysqld_main(int argc, char **argv)
#endif
{
// 1. 初始化
// 2.启动监听以接收连接
// 3. 若 mysqld 服务要退出(有可能是被 kill 或 shutdown) 执行相应的清理动作
}
位于文件 sql/maind.cc
7200 行。 由于这是一个非常关键的函数,下面我们详细的展开一下。
// a: 初始化-加载配置项
if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv,
&argv_alloc)) {
flush_error_log_messages();
return 1;
}
// a: 初始化
sys_var_init();
init_variable_default_paths();
init_server_psi_keys();
if (init_server_components()) unireg_abort(MYSQLD_ABORT_EXIT);
// b: 启动监听 (第 7935 行)
set_ports();
if (init_ssl_communication()) unireg_abort(MYSQLD_ABORT_EXIT);
if (network_init()) unireg_abort(MYSQLD_ABORT_EXIT);
init_status_vars();
server_components_initialized();
// 进入 accept 循环
mysqld_socket_acceptor->connection_event_loop()
// c:退出逻辑
// ...
clean_up(true);
mysqld_exit(signal_hand_thr_exit_code);
第 003 步执行 connection_event_loop 函数
void connection_event_loop() {
Connection_handler_manager *mgr =
Connection_handler_manager::get_instance();
while (!connection_events_loop_aborted()) {
Channel_info *channel_info = m_listener->listen_for_connection_event();
if (channel_info != nullptr) mgr->process_new_connection(channel_info);
}
}
位于文件 sql/connection_acceptor.h
60 行。
第 004 步执行 process_new_connection 函数
void Connection_handler_manager::process_new_connection(
Channel_info *channel_info) {
// 其它逻辑
// 准备创建进程
if (m_connection_handler->add_connection(channel_info)) {
inc_aborted_connects();
delete channel_info;
}
}
位于文件 sql/connection_handler_manager.cc
。
第 005 步执行 add_connection 函数
bool Per_thread_connection_handler::add_connection(Channel_info *channel_info) {
int error = 0;
my_thread_handle id;
DBUG_TRACE;
// Simulate thread creation for test case before we check thread cache
DBUG_EXECUTE_IF("fail_thread_create", error = 1; goto handle_error;);
if (!check_idle_thread_and_enqueue_connection(channel_info)) return false;
/*
There are no idle threads available to take up the new
connection. Create a new thread to handle the connection
*/
channel_info->set_prior_thr_create_utime();
error =
mysql_thread_create(key_thread_one_connection, &id, &connection_attrib,
handle_connection, (void *)channel_info);
#ifndef NDEBUG
handle_error:
#endif // !NDEBUG
if (error) {
connection_errors_internal++;
if (!create_thd_err_log_throttle.log())
LogErr(ERROR_LEVEL, ER_CONN_PER_THREAD_NO_THREAD, error);
channel_info->send_error_and_close_channel(ER_CANT_CREATE_THREAD, error,
true);
Connection_handler_manager::dec_connection_count();
return true;
}
Global_THD_manager::get_instance()->inc_thread_created();
DBUG_PRINT("info", ("Thread created"));
return false;
}
位于文件 sql/connection_handler_per_thread.cc
。
第 006 步执行 handle_connection 函数
static void *handle_connection(void *arg) {
Global_THD_manager *thd_manager = Global_THD_manager::get_instance();
Connection_handler_manager *handler_manager =
Connection_handler_manager::get_instance();
Channel_info *channel_info = static_cast<Channel_info *>(arg);
bool pthread_reused [[maybe_unused]] = false;
if (my_thread_init()) {
connection_errors_internal++;
channel_info->send_error_and_close_channel(ER_OUT_OF_RESOURCES, 0, false);
handler_manager->inc_aborted_connects();
Connection_handler_manager::dec_connection_count();
delete channel_info;
my_thread_exit(nullptr);
return nullptr;
}
// 第一层这个循环会导致线程
for (;;) {
THD *thd = init_new_thd(channel_info);
if (thd == nullptr) {
connection_errors_internal++;
handler_manager->inc_aborted_connects();
Connection_handler_manager::dec_connection_count();
break; // We are out of resources, no sense in continuing.
}
mysql_thread_set_psi_id(thd->thread_id());
mysql_thread_set_psi_THD(thd);
MYSQL_SOCKET socket = thd->get_protocol_classic()->get_vio()->mysql_socket;
mysql_socket_set_thread_owner(socket);
thd_manager->add_thd(thd);
if (thd_prepare_connection(thd))
handler_manager->inc_aborted_connects();
else {
while (thd_connection_alive(thd)) {
//
// 可以看到这里的死循环作用是持续的处理 client 来的请求
//
if (do_command(thd)) break;
}
end_connection(thd);
}
close_connection(thd, 0, false, false);
// ...
// ...
} // for 循环结点的位置
my_thread_end();
my_thread_exit(nullptr);
return nullptr;
}
位于 sql/connection_handler_per_thread.cc
第 245 行 。
就到这里把已经开始处理请求了,这个启动算是完成了。