摘要:
简要分析mysql接收新连接的处理
流程处理:
主线程接收请求, 创建新线程:
(gdb) bt
#0 Per_thread_connection_handler::add_connection (this=0x5e7a4d0, channel_info=0x7dc3430) at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/sql/conn_handler/connection_handler_per_:417
#1 0x0000000001db0f66 in Connection_handler_manager::process_new_connection (this=0x5e277a0, channel_info=0x7dc3430)
at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/sql/conn_handler/connection_handler_:275
#2 0x0000000001d73037 in Connection_acceptor<Mysqld_socket_listener>::connection_event_loop (this=0x7dc18a0)
at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/sql/conn_handler/connection_acceptor.h:75
#3 0x0000000001d6a47e in mysqld_main (argc=115, argv=0x5e27c48) at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/sql/:5199
#4 0x0000000001d61097 in main (argc=8, argv=0x7ffdd55cdb28) at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/sql/:32
子线程开始处理请求:
(gdb) c
Continuing.
[New Thread 0x7fdc80137700 (LWP 11313)]
[Switching to Thread 0x7fdc80137700 (LWP 11313)]
Breakpoint 2, pfs_spawn_thread (arg=0x99ee3f0) at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/storage/perfschema/:2197
2197 (*user_start_routine)(user_arg);
(gdb) bt
#0 pfs_spawn_thread (arg=0x99ee3f0) at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/storage/perfschema/:2197
#1 0x00007fdcd505eea5 in start_thread (arg=0x7fdc80137700) at pthread_create.c:307
#2 0x00007fdcd3495b0d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
(gdb) p user_start_routine
$1 = (void *(*)(void *)) 0x2597a15 <handle_connection(void*)>
(gdb) s
handle_connection (arg=0x7dc3430) at /data/jenkins/workspace/stonedb5.7-zsl-centos7.9/sql/conn_handler/connection_handler_per_:251
251 Global_THD_manager *thd_manager= Global_THD_manager::get_instance();
时序流程:
注意:
- clone系统调用返回两次, 一次父线程, 一次为新创建的子线程
- 冷色调为主线程的处理,暖色调为新的子线程的处理
关键函数:
Per_thread_connection_handler::add_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);
#ifndef NDEBUG
handle_error:
#endif // !NDEBUG
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
#ifdef HAVE_PSI_THREAD_INTERFACE
#define mysql_thread_create(K, P1, P2, P3, P4) \
inline_mysql_thread_create(K, P1, P2, P3, P4)
#else
#define mysql_thread_create(K, P1, P2, P3, P4) \
my_thread_create(P1, P2, P3, P4)
#endif
int my_thread_create(my_thread_handle *thread, const my_thread_attr_t *attr,
my_start_routine func, void *arg)
{
#ifndef _WIN32
return pthread_create(&thread->thread, attr, func, arg);
#else
struct thread_start_parameter *par;
unsigned int stack_size;
par= (struct thread_start_parameter *)malloc(sizeof(*par));
if (!par)
goto error_return;
par->func= func;
par->arg= arg;
stack_size= attr ? attr->dwStackSize : 0;
thread->handle= (HANDLE)_beginthreadex(NULL, stack_size, win_thread_start,
par, 0, &thread->thread);
if (thread->handle)
{
/* Note that JOINABLE is default, so attr == NULL => JOINABLE. */
if (attr && attr->detachstate == MY_THREAD_CREATE_DETACHED)
{
/*
Close handles for detached threads right away to avoid leaking
handles. For joinable threads we need the handle during
my_thread_join. It will be closed there.
*/
CloseHandle(thread->handle);
thread->handle= NULL;
}
return 0;
}
my_osmaperr(GetLastError());
free(par);
error_return:
thread->thread= 0;
thread->handle= NULL;
return 1;
#endif
}
pfs_spawn_thread
extern "C" void* pfs_spawn_thread(void *arg)
{
PFS_spawn_thread_arg *typed_arg= (PFS_spawn_thread_arg*) arg;
void *user_arg;
void *(*user_start_routine)(void*);
PFS_thread *pfs;
/* First, attach instrumentation to this newly created pthread. */
PFS_thread_class *klass= find_thread_class(typed_arg->m_child_key);
if (likely(klass != NULL))
{
pfs= create_thread(klass, typed_arg->m_child_identity, 0);
if (likely(pfs != NULL))
{
pfs->m_thread_os_id= my_thread_os_id();
clear_thread_account(pfs);
pfs->m_parent_thread_internal_id= typed_arg->m_thread_internal_id;
memcpy(pfs->m_username, typed_arg->m_username, sizeof(pfs->m_username));
pfs->m_username_length= typed_arg->m_username_length;
memcpy(pfs->m_hostname, typed_arg->m_hostname, sizeof(pfs->m_hostname));
pfs->m_hostname_length= typed_arg->m_hostname_length;
set_thread_account(pfs);
}
}
else
{
pfs= NULL;
}
my_thread_set_THR_PFS(pfs);
/*
Secondly, free the memory allocated in spawn_thread_v1().
It is preferable to do this before invoking the user
routine, to avoid memory leaks at shutdown, in case
the server exits without waiting for this thread.
*/
user_start_routine= typed_arg->m_user_start_routine;
user_arg= typed_arg->m_user_arg;
my_free(typed_arg);
/* Then, execute the user code for this thread. */
(*user_start_routine)(user_arg);
return NULL;
}