MySQL启动过程详解二:核心模块启动 init_server_components()
阅读原文时间:2023年07月11日阅读:3

mysqld_main() 函数中,init_server_components() 函数负责MySQL核心模块的启动,包括mdl系统,Innodb存储引擎的启动等等:

1. mdl子系统初始化。

2. 初始化 table definition cache 和 hostname cache hash表

3. 初始化 timer组件

4. 初始化 query cache

5. 随机数模块和浮点数计算器初始化

6. 初始化 slave list

7. 启动 error log

8. 初始化各种 xxx_delegate 类型的指针, 为他们分配对象, 对动态插件的支持

9. 初始化 storage engins 之前配置 binlog

10. 初始化 gtid server

11. tc_log 尽早指向 TC_LOG_DUMMY, 以便允许 plugin_init() 在读取 mysql.plugin 表之后提交附加的事务;tc_log为事务两阶段提交过程中的协调者

12. plugin_register_builtin_and_init_core_se(),注册内置插件,初始化 MyISAM、CSV、InnoDB 插件。

13. plugin_register_dynamic_and_init_all(),注册并初始化动态插件,已经注册但尚未初始化的插件也要初始化。

14. 处理命令行选项

15. 打开 slow log file 和 general  log file

16. 设置默认的存储引擎

17. 设置并打开 tc_log

18. xa recovery 操作

19. 打开 binlog

20. 初始化优化器cost 模块

21. 初始化 max_user_conns 和 sql_command_flags & server_command_flags 数组。

/*
* 核心模块
*/
static int init_server_components()
{
DBUG_ENTER("init_server_components");
/*
We need to call each of these following functions to ensure that
all things are initialized so that unireg_abort() doesn't fail
需要调用以下每个函数来确保所有内容都已初始化
*/
// 初始化 mdl 子系统。特别是, 初始化新的全局变量锁和相关的条件变量: LOCK_mdl 和 COND_mdl。
mdl_init();
// 初始化 partitioning, 当前只有 PSI Keys。
partitioning_init();
// 初始化 table definition cache hash表 和 hostname cache hash表
if (table_def_init() | hostname_cache_init(host_cache_size))
unireg_abort(MYSQLD_ABORT_EXIT);
// 初始化 timer 组件
if (my_timer_initialize())
sql_print_error("Failed to initialize timer component (errno %d).", errno);
else
have_statement_timeout = SHOW_OPTION_YES;
// 初始化 query cache
init_server_query_cache();
// 随机数模块初始化
randominit(&sql_rand, (ulong)server_start_time, (ulong)server_start_time / 2);
// 浮点运算器
setup_fpu();
#ifdef HAVE_REPLICATION
// 初始化 slave list
init_slave_list();
#endif

/* Setup logs */

/*
Enable old-fashioned error log, except when the user has requested
help information. Since the implementation of plugin server
variables the help output is now written much later.

log\_error\_dest can be:  
disabled\_my\_option     --log-error was not used or --log-error=  
""                     --log-error without arguments (no '=')  
filename               --log-error=filename  

*/
#ifdef _WIN32
/*
Enable the error log file only if console option is not specified
and --help is not used.
*/
bool log_errors_to_file = !opt_help && !opt_console;
#else
/*
Enable the error log file only if --log-error=filename or --log-error
was used. Logging to file is disabled by default unlike on Windows.
*/
// 是否启用 error log
bool log_errors_to_file = !opt_help && (log_error_dest != disabled_my_option);
#endif
// 启用 error log
if (log_errors_to_file)
{
// Construct filename if no filename was given by the user.
// 如果 没有指定 error log filename, 则自动生成
if (!log_error_dest[0] || log_error_dest == disabled_my_option)
fn_format(errorlog_filename_buff, pidfile_name, mysql_data_home, ".err",
MY_REPLACE_EXT); /* replace '.' by '.err', bug#4997 */
else
fn_format(errorlog_filename_buff, log_error_dest, mysql_data_home, ".err",
MY_UNPACK_FILENAME);
/*
log_error_dest may have been set to disabled_my_option or "" if no
argument was passed, but we need to show the real name in SHOW VARIABLES.
*/
log_error_dest = errorlog_filename_buff;
// open error log
if (open_error_log(errorlog_filename_buff, false))
unireg_abort(MYSQLD_ABORT_EXIT);
}
else
{
// We are logging to stderr and SHOW VARIABLES should reflect that.
// 记录 error log 到 stderr
log_error_dest = "stderr";
// Flush messages buffered so far.
flush_error_log_messages();
}

enter_cond_hook = thd_enter_cond;
exit_cond_hook = thd_exit_cond;
is_killed_hook = (int (*)(const void *))thd_killed;
// transaction_cache init
if (transaction_cache_init())
{
sql_print_error("Out of memory");
unireg_abort(MYSQLD_ABORT_EXIT);
}

/*
initialize delegates for extension observers, errors have already
been reported in the function
初始化各种 xxx_delegate 类型的指针, 为他们分配对象, 对动态插件的支持
*/
if (delegates_init())
unireg_abort(MYSQLD_ABORT_EXIT);

/* need to configure logging before initializing storage engines
在初始化 storage engines 之前需要配置 binlog.
*/
if (opt_log_slave_updates && !opt_bin_log)
{
sql_print_warning("You need to use --log-bin to make "
"--log-slave-updates work.");
}
if (binlog_format_used && !opt_bin_log)
sql_print_warning("You need to use --log-bin to make "
"--binlog-format work.");

/* Check that we have not let the format to unspecified at this point */
assert((uint)global_system_variables.binlog_format <=
array_elements(binlog_format_names) - 1);

#ifdef HAVE_REPLICATION
// replicate_same_server_id
if (opt_log_slave_updates && replicate_same_server_id)
{
if (opt_bin_log)
{
sql_print_error("using --replicate-same-server-id in conjunction with \
--log-slave-updates is impossible, it would lead to infinite loops in this \
server.");
unireg_abort(MYSQLD_ABORT_EXIT);
}
else
sql_print_warning("using --replicate-same-server-id in conjunction with \
--log-slave-updates would lead to infinite loops in this server. However this \
will be ignored as the --log-bin option is not defined.");
}
#endif

opt_server_id_mask = ~ulong(0);
#ifdef HAVE_REPLICATION
// 检查 serverid 超长
opt_server_id_mask = (opt_server_id_bits == 32) ? ~ulong(0) : (1 << opt_server_id_bits) - 1;
if (server_id != (server_id & opt_server_id_mask))
{
sql_print_error("server-id configured is too large to represent with"
"server-id-bits configured.");
unireg_abort(MYSQLD_ABORT_EXIT);
}
#endif
//
if (opt_bin_log)
{
/* Reports an error and aborts, if the --log-bin's path
is a directory.
--log-bin 指向一个目录
*/
if (opt_bin_logname &&
opt_bin_logname[strlen(opt_bin_logname) - 1] == FN_LIBCHAR)
{
sql_print_error("Path '%s' is a directory name, please specify \
a file name for --log-bin option",
opt_bin_logname);
unireg_abort(MYSQLD_ABORT_EXIT);
}

/\* Reports an error and aborts, if the --log-bin-index's path  
   is a directory.  
   --log-bin-index 指向一个目录  
\*/  
if (opt\_binlog\_index\_name &&  
    opt\_binlog\_index\_name\[strlen(opt\_binlog\_index\_name) - 1\] == FN\_LIBCHAR)  
{  
  sql\_print\_error("Path '%s' is a directory name, please specify \\  

a file name for --log-bin-index option",
opt_binlog_index_name);
unireg_abort(MYSQLD_ABORT_EXIT);
}

char buf\[FN\_REFLEN\];  
const char \*ln;  
ln = mysql\_bin\_log.generate\_name(opt\_bin\_logname, "-bin", buf);  
if (!opt\_bin\_logname && !opt\_binlog\_index\_name)  
{  
  /\*  
    User didn't give us info to name the binlog index file.  
    Picking \`hostname\`-bin.index like did in 4.x, causes replication to  
    fail if the hostname is changed later. So, we would like to instead  
    require a name. But as we don't want to break many existing setups, we  
    only give warning, not error.  
  \*/  
  sql\_print\_warning("No argument was provided to --log-bin, and "  
                    "--log-bin-index was not used; so replication "  
                    "may break when this MySQL server acts as a "  
                    "master and has his hostname changed!! Please "  
                    "use '--log-bin=%s' to avoid this problem.",  
                    ln);  
}  
if (ln == buf)  
{  
  my\_free(opt\_bin\_logname);  
  opt\_bin\_logname = my\_strdup(key\_memory\_opt\_bin\_logname,  
                              buf, MYF(0));  
}

/\*  
  Skip opening the index file if we start with --help. This is necessary  
  to avoid creating the file in an otherwise empty datadir, which will  
  cause a succeeding 'mysqld --initialize' to fail.  
\*/  
if (!opt\_help && mysql\_bin\_log.open\_index\_file(opt\_binlog\_index\_name, ln, TRUE))  
{  
  unireg\_abort(MYSQLD\_ABORT\_EXIT);  
}  

}

if (opt_bin_log)
{
/*
opt_bin_logname[0] needs to be checked to make sure opt binlog name is
not an empty string, incase it is an empty string default file
extension will be passed
log_bin basename 和 log_bin index 检查
*/
log_bin_basename =
rpl_make_log_name(key_memory_MYSQL_BIN_LOG_basename,
opt_bin_logname, default_logfile_name,
(opt_bin_logname && opt_bin_logname[0]) ? "" : "-bin");
log_bin_index =
rpl_make_log_name(key_memory_MYSQL_BIN_LOG_index,
opt_binlog_index_name, log_bin_basename, ".index");
if (log_bin_basename == NULL || log_bin_index == NULL)
{
sql_print_error("Unable to create replication path names:"
" out of memory or path names too long"
" (path name exceeds " STRINGIFY_ARG(FN_REFLEN) " or file name exceeds " STRINGIFY_ARG(FN_LEN) ").");
unireg_abort(MYSQLD_ABORT_EXIT);
}
}

#ifndef EMBEDDED_LIBRARY
// reply_log basename & index
DBUG_PRINT("debug",
("opt_bin_logname: %s, opt_relay_logname: %s, pidfile_name: %s",
opt_bin_logname, opt_relay_logname, pidfile_name));
/*
opt_relay_logname[0] needs to be checked to make sure opt relaylog name is
not an empty string, incase it is an empty string default file
extension will be passed
*/
relay_log_basename =
rpl_make_log_name(key_memory_MYSQL_RELAY_LOG_basename,
opt_relay_logname, default_logfile_name,
(opt_relay_logname && opt_relay_logname[0]) ? "" : "-relay-bin");

if (relay_log_basename != NULL)
relay_log_index =
rpl_make_log_name(key_memory_MYSQL_RELAY_LOG_index,
opt_relaylog_index_name, relay_log_basename, ".index");

if (relay_log_basename == NULL || relay_log_index == NULL)
{
sql_print_error("Unable to create replication path names:"
" out of memory or path names too long"
" (path name exceeds " STRINGIFY_ARG(FN_REFLEN) " or file name exceeds " STRINGIFY_ARG(FN_LEN) ").");
unireg_abort(MYSQLD_ABORT_EXIT);
}
#endif /* !EMBEDDED_LIBRARY */

/* call ha_init_key_cache() on all key caches to init them
key cache handle, 仅适用于ISAM 表
*/
process_key_caches(&ha_init_key_cache);

/* Allow storage engine to give real error messages */
if (ha_init_errors())
DBUG_RETURN(1);

if (opt_ignore_builtin_innodb)
sql_print_warning("ignore-builtin-innodb is ignored "
"and will be removed in future releases.");
// 初始化 GTID server
if (gtid_server_init())
{
sql_print_error("Failed to initialize GTID structures.");
unireg_abort(MYSQLD_ABORT_EXIT);
}

/*
Set tc_log to point to TC_LOG_DUMMY early in order to allow plugin_init()
to commit attachable transaction after reading from mysql.plugin table.
If necessary tc_log will be adjusted to point to correct TC_LOG instance
later.
tc_log 尽早指向 TC_LOG_DUMMY, 以便允许 plugin_init() 在读取 mysql.plugin 表之后提交附加的事务。
*/
tc_log = &tc_log_dummy;

/*Load early plugins */
if (plugin_register_early_plugins(&remaining_argc, remaining_argv,
opt_help ? PLUGIN_INIT_SKIP_INITIALIZATION : 0))
{
sql_print_error("Failed to initialize early plugins.");
unireg_abort(MYSQLD_ABORT_EXIT);
}
/* Load builtin plugins, initialize MyISAM, CSV and InnoDB
注册内置插件, 初始化 MyYSAM, CSV, InnoDB
核心部分。
*/
if (plugin_register_builtin_and_init_core_se(&remaining_argc, remaining_argv))
{
sql_print_error("Failed to initialize builtin plugins.");
unireg_abort(MYSQLD_ABORT_EXIT);
}
/*
Skip reading the plugin table when starting with --help in order
to also skip initializing InnoDB. This provides a simpler and more
uniform handling of various startup use cases, e.g. when the data
directory does not exist, exists but is empty, exists with InnoDB
system tablespaces present etc.
*/
// 注册并初始化动态插件。还要初始化尚未初始化的内置插件[MyISAM CSV INNODB 外的其他内置插件]。
if (plugin_register_dynamic_and_init_all(&remaining_argc, remaining_argv,
(opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) |
(opt_help ? (PLUGIN_INIT_SKIP_INITIALIZATION |
PLUGIN_INIT_SKIP_PLUGIN_TABLE)
: 0)))
{
sql_print_error("Failed to initialize dynamic plugins.");
unireg_abort(MYSQLD_ABORT_EXIT);
}
plugins_are_initialized = TRUE; /* Don't separate from init function */
// session_track_system_variables变量检查: 控制server是否跟踪分配给会话系统变量的任务
Session_tracker session_track_system_variables_check;
LEX_STRING var_list;
char *tmp_str;
size_t len = strlen(global_system_variables.track_sysvars_ptr);
tmp_str = (char *)my_malloc(PSI_NOT_INSTRUMENTED, len * sizeof(char) + 2,
MYF(MY_WME));
strcpy(tmp_str, global_system_variables.track_sysvars_ptr);
var_list.length = len;
var_list.str = tmp_str;
if (session_track_system_variables_check.server_boot_verify(system_charset_info,
var_list))
{
sql_print_error("The variable session_track_system_variables either has "
"duplicate values or invalid values.");
if (tmp_str)
my_free(tmp_str);
unireg_abort(MYSQLD_ABORT_EXIT);
}
if (tmp_str)
my_free(tmp_str);
/* we do want to exit if there are any other unknown options */
if (remaining_argc > 1)
{
int ho_error;
struct my_option no_opts[] =
{
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}};
/*
We need to eat any 'loose' arguments first before we conclude
that there are unprocessed options.
*/
my_getopt_skip_unknown = 0;
// 处理命令行选项
if ((ho_error = handle_options(&remaining_argc, &remaining_argv, no_opts,
mysqld_get_one_option)))
unireg_abort(MYSQLD_ABORT_EXIT);
/* Add back the program name handle_options removes */
remaining_argc++;
remaining_argv--;
my_getopt_skip_unknown = TRUE;

if (remaining\_argc > 1)  
{  
  sql\_print\_error("Too many arguments (first extra is '%s').",  
                  remaining\_argv\[1\]);  
  sql\_print\_information("Use --verbose --help to get a list "  
                        "of available options!");  
  unireg\_abort(MYSQLD\_ABORT\_EXIT);  
}  

}

if (opt_help)
unireg_abort(MYSQLD_SUCCESS_EXIT);

/* if the errmsg.sys is not loaded, terminate to maintain behaviour
如果 errmsg.sys 未加载, 则中止
*/
if (!my_default_lc_messages->errmsgs->is_loaded())
{
sql_print_error("Unable to read errmsg.sys file");
unireg_abort(MYSQLD_ABORT_EXIT);
}

/* We have to initialize the storage engines before CSV logging
在 CSV logging之前, 我们必须初始化存储引擎
*/
// 初始化 system database name cache【当前system database 只有 mysql】
if (ha_init())
{
sql_print_error("Can't init databases");
unireg_abort(MYSQLD_ABORT_EXIT);
}

if (opt_bootstrap)
log_output_options = LOG_FILE;

/*
Issue a warning if there were specified additional options to the
log-output along with NONE. Probably this wasn't what user wanted.
*/
if ((log_output_options & LOG_NONE) && (log_output_options & ~LOG_NONE))
sql_print_warning("There were other values specified to "
"log-output besides NONE. Disabling slow "
"and general logs anyway.");

if (log_output_options & LOG_TABLE)
{
/* Fall back to log files if the csv engine is not loaded. */
LEX_CSTRING csv_name = {C_STRING_WITH_LEN("csv")};
if (!plugin_is_ready(csv_name, MYSQL_STORAGE_ENGINE_PLUGIN))
{
sql_print_error("CSV engine is not present, falling back to the "
"log files");
log_output_options = (log_output_options & ~LOG_TABLE) | LOG_FILE;
}
}

query_logger.set_handlers(log_output_options);

// Open slow log file if enabled. 打开 slow log 文件
if (opt_slow_log && query_logger.reopen_log_file(QUERY_LOG_SLOW))
opt_slow_log = false;

// Open general log file if enabled. 打开 general log 文件
if (opt_general_log && query_logger.reopen_log_file(QUERY_LOG_GENERAL))
opt_general_log = false;

/*
Set the default storage engines; 设置默认存储引擎
*/
// 检查Innodb存储引擎是否初始化
if (initialize_storage_engine(default_storage_engine, "",
&global_system_variables.table_plugin))
unireg_abort(MYSQLD_ABORT_EXIT);
// 检查 Innodb 存储引擎
if (initialize_storage_engine(default_tmp_storage_engine, " temp",
&global_system_variables.temp_table_plugin))
unireg_abort(MYSQLD_ABORT_EXIT);

if (!opt_bootstrap && !opt_noacl)
{
std::string disabled_se_str(opt_disabled_storage_engines);
ha_set_normalized_disabled_se_str(disabled_se_str);

// Log warning if default\_storage\_engine is a disabled storage engine.  
handlerton \*default\_se\_handle =  
    plugin\_data<handlerton \*>(global\_system\_variables.table\_plugin);  
if (ha\_is\_storage\_engine\_disabled(default\_se\_handle))  
  sql\_print\_warning("default\_storage\_engine is set to a "  
                    "disabled storage engine %s.",  
                    default\_storage\_engine);

// Log warning if default\_tmp\_storage\_engine is a disabled storage engine.  
handlerton \*default\_tmp\_se\_handle =  
    plugin\_data<handlerton \*>(global\_system\_variables.temp\_table\_plugin);  
if (ha\_is\_storage\_engine\_disabled(default\_tmp\_se\_handle))  
  sql\_print\_warning("default\_tmp\_storage\_engine is set to a "  
                    "disabled storage engine %s.",  
                    default\_tmp\_storage\_engine);  

}
// 两阶段提交 tc_log
if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log))
{
if (opt_bin_log)
tc_log = &mysql_bin_log;
else
tc_log = &tc_log_mmap;
}
// init tc_log
if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file))
{
sql_print_error("Can't init tc log");
unireg_abort(MYSQLD_ABORT_EXIT);
}
// before recovery
(void)RUN_HOOK(server_state, before_recovery, (NULL));
// xa recovery 操作
if (ha_recover(0))
{
unireg_abort(MYSQLD_ABORT_EXIT);
}

/// @todo: this looks suspicious, revisit this /sven
// gtid_mode
enum_gtid_mode gtid_mode = get_gtid_mode(GTID_MODE_LOCK_NONE);
// ENFORCE_GTID_CONSISTENCY
if (gtid_mode == GTID_MODE_ON &&
_gtid_consistency_mode != GTID_CONSISTENCY_MODE_ON)
{
sql_print_error("GTID_MODE = ON requires ENFORCE_GTID_CONSISTENCY = ON.");
unireg_abort(MYSQLD_ABORT_EXIT);
}

if (opt_bin_log)
{
/*
Configures what object is used by the current log to store processed
gtid(s). This is necessary in the MYSQL_BIN_LOG::MYSQL_BIN_LOG to
corretly compute the set of previous gtids.

\*/  
assert(!mysql\_bin\_log.is\_relay\_log);  
mysql\_mutex\_t \*log\_lock = mysql\_bin\_log.get\_log\_lock();  
mysql\_mutex\_lock(log\_lock);  
// 打开 binlog 文件  
if (mysql\_bin\_log.open\_binlog(opt\_bin\_logname, 0,  
                              max\_binlog\_size, false,  
                              true /\*need\_lock\_index=true\*/,  
                              true /\*need\_sid\_lock=true\*/,  
                              NULL))  
{  
  mysql\_mutex\_unlock(log\_lock);  
  unireg\_abort(MYSQLD\_ABORT\_EXIT);  
}  
mysql\_mutex\_unlock(log\_lock);  

}

if (opt_myisam_log)
(void)mi_log(1);

#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) && !defined(EMBEDDED_LIBRARY)
if (locked_in_memory && !getuid())
{
if (setreuid((uid_t)-1, 0) == -1)
{ // this should never happen
sql_print_error("setreuid: %s", strerror(errno));
unireg_abort(MYSQLD_ABORT_EXIT);
}
if (mlockall(MCL_CURRENT))
{
sql_print_warning("Failed to lock memory. Errno: %d\n", errno); /* purecov: inspected */
locked_in_memory = 0;
}
#ifndef _WIN32
if (user_info)
set_user(mysqld_user, user_info);
#endif
}
else
#endif
locked_in_memory = 0;

/* Initialize the optimizer cost module
初始化优化器成本模块
*/
init_optimizer_cost_module(true);
ft_init_stopwords();
// 初始化 max_user_conns
init_max_user_conn();
// 初始化 sql_command_flags 和 server_command_flags 数组。
init_update_queries();
DBUG_RETURN(0);
}

注册内置插件,初始化 MyISAM、CSV、INNODB插件。 

bool plugin_register_builtin_and_init_core_se(int *argc, char **argv)
{
bool mandatory= true;
DBUG_ENTER("plugin_register_builtin_and_init_core_se");

/* Don't allow initializing twice */
assert(!initialized);

/* Allocate the temporary mem root, will be freed before returning */
MEM_ROOT tmp_root;
init_alloc_root(key_memory_plugin_init_tmp, &tmp_root, 4096, 4096);

mysql_mutex_lock(&LOCK_plugin);
initialized= true;

/* First we register the builtin mandatory and optional plugins
首先, 我们注册内置强制插件和可选插件。包括:binlog, sha256_password,CSV 引擎, MEMORY 引擎,InnoDB 引擎, MyISAM, MGR_MYISAM, Performance_schema等等
*/
for (struct st_mysql_plugin **builtins= mysql_mandatory_plugins;
*builtins || mandatory; builtins++)
{
/* Switch to optional plugins when done with the mandatory ones */
if (!*builtins)
{
builtins= mysql_optional_plugins;
mandatory= false;
if (!*builtins)
break;
}
for (struct st_mysql_plugin *plugin= *builtins; plugin->info; plugin++)
{
struct st_plugin_int tmp;
memset(&tmp, 0, sizeof(tmp));
tmp.plugin= plugin;
tmp.name.str= (char *)plugin->name;
tmp.name.length= strlen(plugin->name);
tmp.state= 0;
tmp.load_option= mandatory ? PLUGIN_FORCE : PLUGIN_ON;

  /\*  
    If the performance schema is compiled in,  
    treat the storage engine plugin as 'mandatory',  
    to suppress any plugin-level options such as '--performance-schema'.  
    This is specific to the performance schema, and is done on purpose:  
    the server-level option '--performance-schema' controls the overall  
    performance schema initialization, which consists of much more that  
    the underlying storage engine initialization.  
    See mysqld.cc, set\_vars.cc.  
    Suppressing ways to interfere directly with the storage engine alone  
    prevents awkward situations where:  
    - the user wants the performance schema functionality, by using  
      '--enable-performance-schema' (the server option),  
    - yet disable explicitly a component needed for the functionality  
      to work, by using '--skip-performance-schema' (the plugin)  
  \*/  
  if (!my\_strcasecmp(&my\_charset\_latin1, plugin->name, "PERFORMANCE\_SCHEMA"))  
  {  
    tmp.load\_option= PLUGIN\_FORCE;  
  }

  free\_root(&tmp\_root, MYF(MY\_MARK\_BLOCKS\_FREE));  
  if (test\_plugin\_options(&tmp\_root, &tmp, argc, argv))  
    tmp.state= PLUGIN\_IS\_DISABLED;  
  else  
    tmp.state= PLUGIN\_IS\_UNINITIALIZED;

  struct st\_plugin\_int \*plugin\_ptr;        // Pointer to registered plugin  
  // 注册插件  
  if (register\_builtin(plugin, &tmp, &plugin\_ptr))  
    goto err\_unlock;

  /\*  
    Only initialize MyISAM, InnoDB and CSV at this stage.  
    Note that when the --help option is supplied, InnoDB is not  
    initialized because the plugin table will not be read anyway,  
    as indicated by the flag set when the plugin\_init() function  
    is called.  
    在此阶段只 初始化 MyISAM  InnoDB  CSV。  
  \*/  
  bool is\_myisam= !my\_strcasecmp(&my\_charset\_latin1, plugin->name, "MyISAM");  
  bool is\_innodb= !my\_strcasecmp(&my\_charset\_latin1, plugin->name, "InnoDB");  
  if (!is\_myisam &&  
      (!is\_innodb || opt\_help) &&  
      my\_strcasecmp(&my\_charset\_latin1, plugin->name, "CSV"))  
    continue;  
  // 初始化存储引擎  
  if (plugin\_ptr->state != PLUGIN\_IS\_UNINITIALIZED ||  
      plugin\_initialize(plugin\_ptr))  
    goto err\_unlock;

  /\*  
    Initialize the global default storage engine so that it may  
    not be null in any child thread.  
  \*/  
  if (is\_myisam)  
  {  
    assert(!global\_system\_variables.table\_plugin);  
    assert(!global\_system\_variables.temp\_table\_plugin);  
    global\_system\_variables.table\_plugin=  
      my\_intern\_plugin\_lock(NULL, plugin\_int\_to\_ref(plugin\_ptr));  
    global\_system\_variables.temp\_table\_plugin=  
      my\_intern\_plugin\_lock(NULL, plugin\_int\_to\_ref(plugin\_ptr));  
    assert(plugin\_ptr->ref\_count == 2);  
  }  
}  

}

/* Should now be set to MyISAM storage engine */
assert(global_system_variables.table_plugin);
assert(global_system_variables.temp_table_plugin);

mysql_mutex_unlock(&LOCK_plugin);

free_root(&tmp_root, MYF(0));
DBUG_RETURN(false);

err_unlock:
mysql_mutex_unlock(&LOCK_plugin);
free_root(&tmp_root, MYF(0));
DBUG_RETURN(true);
}

static int plugin_initialize(st_plugin_int *plugin)
{
int ret = 1;
DBUG_ENTER("plugin_initialize");

mysql_mutex_assert_owner(&LOCK_plugin);
uint state = plugin->state;
assert(state == PLUGIN_IS_UNINITIALIZED);

mysql_mutex_unlock(&LOCK_plugin);
if (plugin_type_initialize[plugin->plugin->type])
{
if ((*_plugin_type_initialize[plugin->plugin->type])(plugin)_)
{
sql_print_error("Plugin '%s' registration as a %s failed.",
plugin->name.str, plugin_type_names[plugin->plugin->type].str);
goto err;
}

/\* FIXME: Need better solution to transfer the callback function  
array to memcached \*/  
if (strcmp(plugin->name.str, "InnoDB") == 0)  
{  
  innodb\_callback\_data = ((handlerton \*)plugin->data)->data;  
}  

}
else if (plugin->plugin->init)
{
if (strcmp(plugin->name.str, "daemon_memcached") == 0)
{
plugin->data = innodb_callback_data;
}

if (plugin->plugin->init(plugin))  
{  
  sql\_print\_error("Plugin '%s' init function returned error.",  
                  plugin->name.str);  
  goto err;  
}  

}
state = PLUGIN_IS_READY; // plugin->init() succeeded

if (plugin->plugin->status_vars)
{
if (add_status_vars(plugin->plugin->status_vars))
goto err;
}

/*
set the plugin attribute of plugin's sys vars so they are pointing
to the active plugin
*/
if (plugin->system_vars)
{
sys_var_pluginvar *var = plugin->system_vars->cast_pluginvar();
for (;;)
{
var->plugin = plugin;
if (!var->next)
break;
var = var->next->cast_pluginvar();
}
}

ret = 0;

err:
mysql_mutex_lock(&LOCK_plugin);
plugin->state = state;

DBUG_RETURN(ret);
}

int ha_initialize_handlerton(st_plugin_int *plugin)
{
handlerton *hton;
DBUG_ENTER("ha_initialize_handlerton");
DBUG_PRINT("plugin", ("initialize plugin: '%s'", plugin->name.str));

hton= (handlerton *)my_malloc(key_memory_handlerton,
sizeof(handlerton),
MYF(MY_WME | MY_ZEROFILL));

if (hton == NULL)
{
sql_print_error("Unable to allocate memory for plugin '%s' handlerton.",
plugin->name.str);
goto err_no_hton_memory;
}

hton->slot= HA_SLOT_UNDEF;
/* Historical Requirement */
plugin->data= hton; // shortcut for the future
if (plugin->plugin->init && plugin->plugin->init(hton))
{
sql_print_error("Plugin '%s' init function returned error.",
plugin->name.str);
goto err;
}

/*
the switch below and hton->state should be removed when
command-line options for plugins will be implemented
*/
DBUG_PRINT("info", ("hton->state=%d", hton->state));
switch (hton->state) {
case SHOW_OPTION_NO:
break;
case SHOW_OPTION_YES:
{
uint tmp;
ulong fslot;
/* now check the db_type for conflict */
if (hton->db_type <= DB_TYPE_UNKNOWN || hton->db_type >= DB_TYPE_DEFAULT ||
installed_htons[hton->db_type])
{
int idx= (int) DB_TYPE_FIRST_DYNAMIC;

    while (idx < (int) DB\_TYPE\_DEFAULT && installed\_htons\[idx\])  
      idx++;

    if (idx == (int) DB\_TYPE\_DEFAULT)  
    {  
      sql\_print\_warning("Too many storage engines!");  
      goto err\_deinit;  
    }  
    if (hton->db\_type != DB\_TYPE\_UNKNOWN)  
      sql\_print\_warning("Storage engine '%s' has conflicting typecode. "  
                        "Assigning value %d.", plugin->plugin->name, idx);  
    hton->db\_type= (enum legacy\_db\_type) idx;  
  }

  /\*  
    In case a plugin is uninstalled and re-installed later, it should  
    reuse an array slot. Otherwise the number of uninstall/install  
    cycles would be limited. So look for a free slot.  
  \*/  
  DBUG\_PRINT("plugin", ("total\_ha: %lu", total\_ha));  
  for (fslot= 0; fslot < total\_ha; fslot++)  
  {  
    if (!hton2plugin\[fslot\])  
      break;  
  }  
  if (fslot < total\_ha)  
    hton->slot= fslot;  
  else  
  {  
    if (total\_ha >= MAX\_HA)  
    {  
      sql\_print\_error("Too many plugins loaded. Limit is %lu. "  
                      "Failed on '%s'", (ulong) MAX\_HA, plugin->name.str);  
      goto err\_deinit;  
    }  
    hton->slot= total\_ha++;  
  }  
  installed\_htons\[hton->db\_type\]= hton;  
  tmp= hton->savepoint\_offset;  
  hton->savepoint\_offset= savepoint\_alloc\_size;  
  savepoint\_alloc\_size+= tmp;  
  hton2plugin\[hton->slot\]=plugin;  
  builtin\_htons\[hton->slot\]= (plugin->plugin\_dl == NULL);  
  if (hton->prepare)  
    total\_ha\_2pc++;  
  break;  
}  
/\* fall through \*/  

default:
hton->state= SHOW_OPTION_DISABLED;
break;
}

/*
This is entirely for legacy. We will create a new "disk based" hton and a
"memory" hton which will be configurable longterm. We should be able to
remove partition and myisammrg.
*/
switch (hton->db_type) {
case DB_TYPE_HEAP:
heap_hton= hton;
break;
case DB_TYPE_MYISAM:
myisam_hton= hton;
break;
case DB_TYPE_INNODB:
innodb_hton= hton;
break;
default:
break;
};

接下来我们会重点关注Innodb存储引擎的启动流程。