mysqld源码阅读(启动:网络模块初始化)

环境信息

mysql5.7.27
OS版本: linux版本

阅读分析

网络模块的初始化
mysqld_main --> network_init()

代码目录信息:

  if (init_ssl())
    unireg_abort(MYSQLD_ABORT_EXIT);
  if (network_init())
    unireg_abort(MYSQLD_ABORT_EXIT);

分析network_init()

network_init主要做了以下几件事情:
set_ports() 进行相应的初始化端口
创建监听对象Mysqld_socket_listener,并对其进行相应的初始化操作。然后对listener进行相应的侦听动作

static bool network_init(void)
{
  if (opt_bootstrap)
    return false;

  set_ports(); //端口设置
  //others to do 
 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);
}//监听对象
// others to do 
mysqld_socket_acceptor=
      new (std::nothrow) Connection_acceptor<Mysqld_socket_listener>(mysqld_socket_listener);//
//others to do 
    if (mysqld_socket_acceptor->init_connection_acceptor())//实例化并监听
      return true; // mysqld_socket_acceptor would be freed in unireg_abort.

set_ports端口设置分析:

static void set_ports()
{
  char  *env;
  if (!mysqld_port && !opt_disable_networking)
  {         // Get port if not from commandline
    mysqld_port= MYSQL_PORT;

    /*
      if builder specifically requested a default port, use that
      (even if it coincides with our factory default).
      only if they didn't do we check /etc/services (and, failing
      on that, fall back to the factory default of 3306).
      either default can be overridden by the environment variable
      MYSQL_TCP_PORT, which in turn can be overridden with command
      line options.
    */

#if MYSQL_PORT_DEFAULT == 0 //这个参数是在make编译生成之后,在include/mysql_version.h生成,其值为 #define MYSQL_PORT_DEFAULT          @MYSQL_TCP_PORT_DEFAULT@
	//MYSQL_TCP_PORT_DEFAULT这个值是在cmake/mysql_version.cmake的文件中配置的
	/*
	[root@pcserver mysql-5.7.27]# grep -rn "MYSQL_TCP_PORT_DEFAULT" *
	cmake/mysql_version.cmake:88:SET(MYSQL_TCP_PORT_DEFAULT "3306")
	cmake/mysql_version.cmake:91:  SET(MYSQL_TCP_PORT ${MYSQL_TCP_PORT_DEFAULT})
	cmake/mysql_version.cmake:92:  SET(MYSQL_TCP_PORT_DEFAULT "0")
	cmake/mysql_version.cmake:93:ELSEIF(MYSQL_TCP_PORT EQUAL MYSQL_TCP_PORT_DEFAULT)
	cmake/mysql_version.cmake:94:  SET(MYSQL_TCP_PORT_DEFAULT "0")
	include/mysql_version.h.in:18:#define MYSQL_PORT_DEFAULT          @MYSQL_TCP_PORT_DEFAULT@
	scripts/mysql_config.pl.in:161:if ( '@MYSQL_TCP_PORT_DEFAULT@' == 0 ) {
	scripts/mysql_config.sh:106:if [ @MYSQL_TCP_PORT_DEFAULT@ -eq 0 ]; then

	*/
    struct  servent *serv_ptr;
    if ((serv_ptr= getservbyname("mysql", "tcp")))
      mysqld_port= ntohs((u_short) serv_ptr->s_port); /* purecov: inspected */
#endif
    if ((env = getenv("MYSQL_TCP_PORT"))) 
      mysqld_port= (uint) atoi(env);    /* purecov: inspected 通过getenv获取启动配置命令行的端口*/
  }
  if (!mysqld_unix_port)
  {
#ifdef _WIN32
    mysqld_unix_port= (char*) MYSQL_NAMEDPIPE;
#else
    mysqld_unix_port= (char*) MYSQL_UNIX_ADDR;
#endif
    if ((env = getenv("MYSQL_UNIX_PORT")))
      mysqld_unix_port= env;      /* purecov: inspected */
  }
}

关于创建监听的操作分析:
创建 Mysqld_socket_listener 类型对象

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);

通过Connection_acceptor模板进行Mysqld_socket_listener的初始化,通过循环监控不同类型的client connection event的,也就是通过循环监听客户端来的连接

创建mysql_socket_acceptor对象:

mysqld_socket_acceptor=
      new (std::nothrow) Connection_acceptor<Mysqld_socket_listener>(mysqld_socket_listener);

初始化listener:

    if (mysqld_socket_acceptor->init_connection_acceptor())
      return true; // mysqld_socket_acceptor would be freed in unireg_abort.

实现:

template <typename Listener> class Connection_acceptor
{
//some operations
  bool init_connection_acceptor()
  {
    return m_listener->setup_listener();
  }
//some operations
  }

调用了setup_listener()的方法实现公仆干嘛

而此时我们查看Mysqld_socket_listener中的setup_listener()具体的实现:
通过以下关键变量来记录listener的主要信息
m_tcp_port:记录tcp socket连接
m_unix_sockname: 使用的是std::string类型记录如果使用socket文件,则文件名(路径+文件)
通过使用m_socket_map来区分所使用的连接是tcp_socket还是unix socket的连接方式

通过两个if判断,分别判断是否tcp还是socket文件连接,并记录于m_socket_map中
然后通过迭代器循环实现监听事件的记录,代码通过pool_info_t实现监听相应的事件

for (socket_map_iterator_t sock_map_iter=  m_socket_map.begin();
       sock_map_iter != m_socket_map.end(); ++sock_map_iter)  //创建迭代器,并通过循环的方式监听事件
  {
    MYSQL_SOCKET listen_socket= sock_map_iter->first;
    mysql_socket_set_thread_owner(listen_socket);
#ifdef HAVE_POLL
    m_poll_info.m_fds[count].fd= mysql_socket_getfd(listen_socket); //轮询记录监听事件
    m_poll_info.m_fds[count].events= POLLIN;
    m_poll_info.m_pfs_fds[count]= listen_socket;
    count++;
#else  // HAVE_POLL
    FD_SET(mysql_socket_getfd(listen_socket), &m_select_info.m_client_fds);
    if ((uint) mysql_socket_getfd(listen_socket) > m_select_info.m_max_used_connection)
      m_select_info.m_max_used_connection= mysql_socket_getfd(listen_socket);
      //如果记录HAVE_POLL事件,则直接使用m_select_info记录socket_listener的相应信息
#endif // HAVE_POLL
  }

然后我们回到mysqld_main.cc中的mysqld_main函数,继续往后阅读,找到如下部分

#if defined(_WIN32)
  setup_conn_event_handler_threads();
#else
  mysql_mutex_lock(&LOCK_socket_listener_active);
  // Make it possible for the signal handler to kill the listener.
  socket_listener_active= true;
  mysql_mutex_unlock(&LOCK_socket_listener_active);

  if (opt_daemonize)
    mysqld::runtime::signal_parent(pipe_write_fd,1);

  mysqld_socket_acceptor->connection_event_loop();
#endif /* _WIN32 */

通过nysqld_socket_acceptor变量引用connect_event_loop()方法,循环监听客户端来的连接。通过模板调用如下 :

/**
    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);
    }
  }

通过查看Mysqld_socket_listener类中listen_for_connection_event()来得到Channel_info信息,然后通过process_new_connection创建新的连接来。再进一步查看Connection_handler_manager的process_new_connection的方法,发现有m_connection_handler->add_connection(channel_info)在connection_handler中增加一个connection,并记录数量增加

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)) //增加一个connection
  {
    inc_aborted_connects(); //记录数量增加
    delete channel_info;
  }
}

对于m_connection_handler的add_connection增加connection,看具体的实现:


bool Per_thread_connection_handler::add_connection(Channel_info* channel_info)
{
  int error= 0;
  my_thread_handle id;

  DBUG_ENTER("Per_thread_connection_handler::add_connection");

  // 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))
    DBUG_RETURN(false);

  /*
    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);//给定信息,创建thread进程
                             
#ifndef DBUG_OFF
handle_error:
#endif // !DBUG_OFF

  if (error)
  {
    connection_errors_internal++;
    if (!create_thd_err_log_throttle.log())
      sql_print_error("Can't create thread to handle new connection(errno= %d)",
                      error);
    channel_info->send_error_and_close_channel(ER_CANT_CREATE_THREAD,
                                               error, true);
    Connection_handler_manager::dec_connection_count();
    DBUG_RETURN(true);
  }

  Global_THD_manager::get_instance()->inc_thread_created();
  DBUG_PRINT("info",("Thread created"));
  DBUG_RETURN(false);
}


创建mysql_thread_create创建thread,并接收一个方法参数 handle_connection,这个是一个函数

extern "C" void *handle_connection(void *arg)
{
//sometings ...
 THD *thd= init_new_thd(channel_info); //创建线程对象
 //somethings...
mysql_thread_set_psi_id(thd->thread_id()); //初始化线程信息等
    mysql_thread_set_psi_THD(thd);
    mysql_socket_set_thread_owner(
      thd->get_protocol_classic()->get_vio()->mysql_socket);

    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);

    thd->get_stmt_da()->reset_diagnostics_area();
    thd->release_resources();

//somethings...
}

总结 :
大体了解了此部分的代码,后续继续学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值