连接的建立
主要是在handle_connection函数中实现。
// 对应的源码文件的位置
// ./sql/conn_handler/connection_handler_per_thread.cc
/**
Thread handler for a connection
@param arg Connection object (Channel_info)
This function (normally) does the following:
- Initialize thread
- Initialize THD to be used with this thread
- Authenticate user
- Execute all queries sent on the connection
- Take connection down
- End thread / Handle next connection using thread from thread cache
*/
extern "C" {
static void *handle_connection(void *arg) {
Global_THD_manager *thd_manager = Global_THD_manager::get_instance();
Connection_handler_manager *handler_manager =
Connection_handler_manager::get_instance();
Channel_info *channel_info = static_cast<Channel_info *>(arg);
bool pthread_reused [[maybe_unused]] = false;
初始化连接
for (;;) {
THD *thd = init_new_thd(channel_info);
创建一个线程
// ./sql/conn_handler/connection_handler_per_thread.cc
/**
Construct and initialize a THD object for a new connection.
@param channel_info Channel_info object representing the new connection.
Will be destroyed by this function.
@retval NULL Initialization failed.
@retval !NULL Pointer to new THD object for the new connection.
*/
static THD *init_new_thd(Channel_info *channel_info) {
THD *thd = channel_info->create_thd();
if (thd == nullptr) {
channel_info->send_error_and_close_channel(ER_OUT_OF_RESOURCES, 0, false);
delete channel_info;
return nullptr;
}
函数的具体实现
// ./sql/conn_handler/socket_connection.cc
///////////////////////////////////////////////////////////////////////////
// Channel_info_tcpip_socket implementation
///////////////////////////////////////////////////////////////////////////
/**
This class abstracts the info. about TCP/IP socket mode of communication with
the server.
*/
class Channel_info_tcpip_socket : public Channel_info {
// connect socket object
MYSQL_SOCKET m_connect_sock;
/*
Flag specifying whether a connection is admin connection or
ordinary connection.
*/
bool m_is_admin_conn;
#ifdef HAVE_SETNS
/*
Network namespace associated with the socket.
*/
std::string m_network_namespace;
#endif
protected:
Vio *create_and_init_vio() const override {
Vio *vio = mysql_socket_vio_new(m_connect_sock, VIO_TYPE_TCPIP, 0);
#ifdef USE_PPOLL_IN_VIO
if (vio != nullptr) {
vio->thread_id.reset();
vio->signal_mask = mysqld_signal_mask;
}
#endif
#ifdef HAVE_SETNS
strncpy(vio->network_namespace, m_network_namespace.c_str(),
sizeof(vio->network_namespace) - 1);
vio->network_namespace[sizeof(vio->network_namespace) - 1] = '\0';
#endif
return vio;
}
public:
/**
Constructor that sets the connect socket.
@param connect_socket set connect socket descriptor.
@param is_admin_conn flag specifying whether a connection is admin
connection.
*/
Channel_info_tcpip_socket(MYSQL_SOCKET connect_socket, bool is_admin_conn)
: m_connect_sock(connect_socket), m_is_admin_conn(is_admin_conn) {}
THD *create_thd() override {
THD *thd = Channel_info::create_thd();
if (thd != nullptr) {
thd->set_admin_connection(m_is_admin_conn);
init_net_server_extension(thd);
}
return thd;
}
接下来是有vio的创建和初始化。
VIO(Virtual I/O) 是一个对网络通信底层进行封装的抽象层。它主要用于处理MySQL客户端与服务器之间,以及服务器内部的各种网络I/O操作。
// ./sql/conn_handler/channel_info.cc
THD *Channel_info::create_thd() {
DBUG_EXECUTE_IF("simulate_resource_failure", return nullptr;);
Vio *vio_tmp = create_and_init_vio();
if (vio_tmp == nullptr) return nullptr;
THD *thd = new (std::nothrow) THD;
if (thd == nullptr) {
vio_delete(vio_tmp);
return nullptr;
}
thd->get_protocol_classic()->init_net(vio_tmp);
return thd;
}
创建socket
MYSQL_SOCKET socket = thd->get_protocol_classic()->get_vio()->mysql_socket;
mysql_socket_set_thread_owner(socket);
thd_manager->add_thd(thd);
如果连接活跃,进入循环,执行对应的DB命令
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);
在accept_connection函数上打断点
[root@ecs-test-node2 mysql-8.0.42]# gdb -p 287948
GNU gdb (GDB) Rocky Linux 8.2-20.el8.0.1
(gdb) break accept_connection
Breakpoint 1 at 0x3847962: file /data/mysql-8.0.42/sql/conn_handler/socket_connection.cc, line 914.
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000003847962 in accept_connection(MYSQL_SOCKET, MYSQL_SOCKET*)
at /data/mysql-8.0.42/sql/conn_handler/socket_connection.cc:914
在另外一个终端中,开启一个连接会话
[root@ecs-test-node2 ~]# mysql -h 192.168.0.100 -P 3315 -u lily -p -A
Enter password:
查看栈的执行路径
(gdb) continue
Continuing.
Thread 1 "mysqld" hit Breakpoint 1, accept_connection (listen_sock=..., connect_sock=0x7ffde9ae2960)
at /data/mysql-8.0.42/sql/conn_handler/socket_connection.cc:914
914 for (uint retry = 0; retry < MAX_ACCEPT_RETRY; retry++) {
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000003847962 in accept_connection(MYSQL_SOCKET, MYSQL_SOCKET*)
at /data/mysql-8.0.42/sql/conn_handler/socket_connection.cc:914
breakpoint already hit 1 time
(gdb) backtrace
#0 accept_connection (listen_sock=..., connect_sock=0x7ffde9ae2960)
at /data/mysql-8.0.42/sql/conn_handler/socket_connection.cc:914
#1 0x0000000003848c75 in Mysqld_socket_listener::listen_for_connection_event (this=0x7f4aa2dff6d0)
at /data/mysql-8.0.42/sql/conn_handler/socket_connection.cc:1398
#2 0x000000000345a6a8 in Connection_acceptor<Mysqld_socket_listener>::connection_event_loop (this=0xcdadd60)
at /data/mysql-8.0.42/sql/conn_handler/connection_acceptor.h:65
#3 0x000000000344bd24 in mysqld_main (argc=72, argv=0xa9e5f10) at /data/mysql-8.0.42/sql/mysqld.cc:8287
#4 0x0000000003437c86 in main (argc=11, argv=0x7ffde9ae3328) at /data/mysql-8.0.42/sql/main.cc:26
首先,会执行主函数
// sql/main.cc
int main(int argc, char **argv) { return mysqld_main(argc, argv); }
主函数会返回mysqld_admin函数
进入连接事件循环
// sql/mysqld.cc
#ifdef _WIN32
int win_main(int argc, char **argv)
#else
int mysqld_main(int argc, char **argv)
#endif
{
...
mysqld_socket_acceptor->connection_event_loop();
连接事件循环函数的具体定义
// sql/conn_handler/connection_acceptor.h
/**
This class presents a generic interface to initialize and run
a connection event loop for different types of listeners and
a callback functor to call on the connection event from the
listener that listens for connection. Listener type should
be a class providing methods setup_listener, listen_for_
connection_event and close_listener. The Connection event
callback functor object would on receiving connection event
from the client to process the connection.
*/
template <typename Listener>
class Connection_acceptor {
Listener *m_listener;
public:
Connection_acceptor(Listener *listener) : m_listener(listener) {}
~Connection_acceptor() { delete m_listener; }
/**
Initialize a connection acceptor.
@retval return true if initialization failed, else false.
*/
bool init_connection_acceptor() { return m_listener->setup_listener(); }
/**
Connection acceptor loop to accept connections from clients.
*/
void connection_event_loop() {
Connection_handler_manager *mgr =
Connection_handler_manager::get_instance();
while (!connection_events_loop_aborted()) {
Channel_info *channel_info = m_listener->listen_for_connection_event();
if (channel_info != nullptr) mgr->process_new_connection(channel_info);
}
}
监听连接事件
// sql/conn_handler/socket_connection.cc
Channel_info *Mysqld_socket_listener::listen_for_connection_event() {
#ifdef HAVE_POLL
int retval = poll(&m_poll_info.m_fds[0], m_socket_vector.size(), -1);
#else
m_select_info.m_read_fds = m_select_info.m_client_fds;
int retval = select((int)m_select_info.m_max_used_connection,
&m_select_info.m_read_fds, 0, 0, 0);
#endif
if (accept_connection(listen_socket->m_socket, &connect_sock)) {
#ifdef HAVE_SETNS
if (!network_namespace_for_listening_socket.empty())
(void)restore_original_network_namespace();
#endif
return nullptr;
}
接受连接
// sql/conn_handler/socket_connection.cc
/**
Accept a new connection on a ready listening socket.
@param listen_sock Listening socket ready to accept a new connection
@param [out] connect_sock Socket corresponding to a new accepted connection
@return operation result
@retval true on error
@retval false on success
*/
static bool accept_connection(MYSQL_SOCKET listen_sock,
MYSQL_SOCKET *connect_sock) {
struct sockaddr_storage c_addr;
for (uint retry = 0; retry < MAX_ACCEPT_RETRY; retry++) {
socket_len_t length = sizeof(struct sockaddr_storage);
*connect_sock =
mysql_socket_accept(key_socket_client_connection, listen_sock,
(struct sockaddr *)(&c_addr), &length);
if (mysql_socket_getfd(*connect_sock) != INVALID_SOCKET ||
(socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
break;
}
1022

被折叠的 条评论
为什么被折叠?



