Mysql源码阅读mysqld启动(二)

上一篇看到了函数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启动(二)”

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>