上一篇看到了函数mysqld_main调用逻辑,这一篇主要研究mysqld_main函数主要做了哪些操作,因为服务器大部分都是在unix/linux下运行,看源代码时也着重看unix流程的代码,对于win32的代码暂时跳过。
感兴趣可以查看上一篇文章《Mysql源码阅读mysqld启动(一)》
从sql/mysqld.cc的4364行的主函数mysqld_main开始看起。跳过一些win32平台的初始化函数。
第一步,可以看到在mysqld_main里面会先加载一些默认配置参数
if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv)) { flush_error_log_messages(); return 1; }
接着有逻辑判断是否开启了性能评估配置
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE /* Initialize the array of performance schema instrument configurations. */ init_pfs_instrument_array(); #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
之后是一些配置检查,比如是直接启动还是daemonize后台启动,然后检测错误日志等配置是不是跟这两种方式吻合。接着是一些sql主要变量名及系统变量的初始化。
init_sql_statement_names(); sys_var_init(); ulong requested_open_files; adjust_related_options(&requested_open_files);
之后跳过性能检查的操作,接着初始化错误日志及加载权限验证插件
init_error_log(); /* Initialize audit interface globals. Audit plugins are inited later. */ mysql_audit_initialize();
接着中间有一大段函数,貌似也没看懂,不属于主要逻辑,包括一些慢查询开启等。这里简要跳过,进入核心代码的开始network_init初始化。
if (init_ssl()) unireg_abort(MYSQLD_ABORT_EXIT); if (network_init()) unireg_abort(MYSQLD_ABORT_EXIT);
接着我们看network_init函数内部代码
static bool network_init(void) { // more... if (!opt_disable_networking || unix_sock_name != "") { std::string const bind_addr_str(my_bind_addr_str ? my_bind_addr_str : ""); Mysqld_socket_listener *mysqld_socket_listener= new (std::nothrow) Mysqld_socket_listener(bind_addr_str, mysqld_port, back_log, mysqld_port_timeout, unix_sock_name); if (mysqld_socket_listener == NULL) return true; mysqld_socket_acceptor= new (std::nothrow) Connection_acceptor<Mysqld_socket_listener>(mysqld_socket_listener); if (mysqld_socket_acceptor == NULL) { delete mysqld_socket_listener; mysqld_socket_listener= NULL; return true; } if (mysqld_socket_acceptor->init_connection_acceptor()) return true; // mysqld_socket_acceptor would be freed in unireg_abort. if (report_port == 0) report_port= mysqld_port; if (!opt_disable_networking) DBUG_ASSERT(report_port != 0); } // more... }
network_init里面使用Mysqld_socket_listener进行了实例化,目的是后面进行socket的侦听。同时对接收处理类Connection_acceptor的实例化,并初始化了这个连接处理类。
接着我们深入到连接处理类中sql/conn_handler/connection_handler.h文件中查看函数init_connection_acceptor
/** Initialize a connection acceptor. @retval return true if initialization failed, else false. */ bool init_connection_acceptor() { return m_listener->setup_listener(); }
再接着查看setup_listener函数内部。在文件sql/conn_handler/socket_connection.cc 发现函数内部进行了socket的绑定。
bool Mysqld_socket_listener::setup_listener() { // Setup tcp socket listener if (m_tcp_port) { TCP_socket tcp_socket(m_bind_addr_str, m_tcp_port, m_backlog, m_port_timeout); MYSQL_SOCKET mysql_socket= tcp_socket.get_listener_socket(); if (mysql_socket.fd == INVALID_SOCKET) return true; m_socket_map.insert(std::pair<MYSQL_SOCKET,bool>(mysql_socket, false)); } // more... }
接着我们返回到sql/mysqld.cc中的mysqld_main函数中继续研究,主要看代码5045行connection_event_loop函数循环。connection_event_loop连接事件循环,我们可以看出这里应该是开启了线程池进行侦听处理了。
if (opt_daemonize) mysqld::runtime::signal_parent(pipe_write_fd,1); mysqld_socket_acceptor->connection_event_loop(); // line no 5465
接着我们去看看connection_event_loop函数内部定义。在文件sql/conn_handler/connection_acceptor.h中。
/** Connection acceptor loop to accept connections from clients. */ void connection_event_loop() { Connection_handler_manager *mgr= Connection_handler_manager::get_instance(); while (!abort_loop) { Channel_info *channel_info= m_listener->listen_for_connection_event(); if (channel_info != NULL) mgr->process_new_connection(channel_info); } }
我们可以看到代码里在循环侦听连接事件,然后使用process_new_connection去建立新的连接。
接着我们去看process_new_connection函数的定义。在文件sql/conn_handler/connection_handler_manager.cc中。
void Connection_handler_manager::process_new_connection(Channel_info* channel_info) { if (abort_loop || !check_and_incr_conn_count()) { channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true); delete channel_info; return; } if (m_connection_handler->add_connection(channel_info)) { inc_aborted_connects(); delete channel_info; } }
我们看到这里有个连接处理在使用add_connection添加连接。
继续查看add_connection函数内部定义。我们选取每个线程一个连接的方式查看文件sql/conn_handler/connection_handler_per_thread.cc里的定义。
bool Per_thread_connection_handler::add_connection(Channel_info* channel_info) { // more... /* There are no idle threads avaliable 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); // more... }
这里我们看到了mysql_thread_create函数进行了线程的创建。此处有个参数handle_connection是在线程创建时注册要处理的函数handle_connection。在当前文件中。
extern "C" void *handle_connection(void *arg) { // more... thd_manager->add_thd(thd); if (thd_prepare_connection(thd)) handler_manager->inc_aborted_connects(); else { while (thd_connection_alive(thd)) { if (do_command(thd)) break; } end_connection(thd); } close_connection(thd, 0, false, false); // more... }
我们看到了这里调用了do_command用来处理客户端发送过来的的sql语句。do_command函数会在下一篇文章中处理。
回到一开始的mysqld_main函数中,经过socket初始化及连接池初始化及注册事件处理函数。mysqld_main的主要工作也就完成。接下来的其他代码就暂不研究了。
One thought on “Mysql源码阅读mysqld启动(二)”