環境信息
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...
}
總結 :
大體瞭解了此部分的代碼,後續繼續學習