环境信息
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...
}
总结 :
大体了解了此部分的代码,后续继续学习