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 行 。


就到这里把已经开始处理请求了,这个启动算是完成了。