mysql_init调用卡住原因分析

mysql_init调用卡住原因分析.pdf

有同学做类似如下的操作:

class X
{
public:
X() // 类X的构造函数ctor
{
_mysql_handler = mysql_init(NULL);
}
};
// 定义类X的全局变量
X g_x;
// 程序入口main函数
int main()
{
。。。 。。。
}

看似简单的代码,但非常不幸,程序运行时,卡在了mysql_init处。语法上看不出任何破绽,原因会是什么了?

他提供了另一个线索:不在构造函数中调用mysql_init则正常,不会卡住。结合起来分析,推断是因为mysql_init中也使用到了全局变量(另一种原因是有越界),而全局变量的初始化顺序程序是无法约定的,很有可能是因为g_x的初始化,发生在mysql_init依赖的全局变量之前。若推论成立,则mysql_init使用了未初始化的值,这是导致它卡住的根本原因。可以使用valgrind验证一下。当然,使用下列的方法应当也能奏效:全局变量相互依赖和初始化顺序的解决办法(http://blog.chinaunix.net/uid-20682147-id-3245149.html),即改成:

#define g_x x_ref()
X& x_ref()
{
static X x; // 技巧就在这里
return x;
}

当然,良好的习惯是尽量避免使用全局变量,实在无法避免时(如考虑到结构的复杂性),则可以考虑用上述方法规避全局变量互依赖产生的问题。

附1:mysql_init源码

/****************************************************************************
Init MySQL structure or allocate one
****************************************************************************/
MYSQL * STDCALL
mysql_init(MYSQL *mysql)
{
if (mysql_server_init(0, NULL, NULL))
return 0;
if (!mysql)
{
if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
{
set_mysql_error(NULL, CR_OUT_OF_MEMORY, unknown_sqlstate);
return 0;
}
mysql->free_me=1;
}
else
memset(mysql, 0, sizeof(*(mysql)));
mysql->charset=default_client_charset_info;
strmov(mysql->net.sqlstate, not_error_sqlstate);
/*
Only enable LOAD DATA INFILE by default if configured with
--enable-local-infile
*/
#if defined(ENABLED_LOCAL_INFILE) && !defined(MYSQL_SERVER)
mysql->options.client_flag|= CLIENT_LOCAL_FILES;
#endif
#ifdef HAVE_SMEM
mysql->options.shared_memory_base_name= (char*) def_shared_memory_base_name;
#endif
mysql->options.methods_to_use= MYSQL_OPT_GUESS_CONNECTION;
mysql->options.report_data_truncation= TRUE;  /* default */
/*
By default we don't reconnect because it could silently corrupt data (after
reconnection you potentially lose table locks, user variables, session
variables (transactions but they are specifically dealt with in
mysql_reconnect()).
This is a change: < 5.0.3 mysql->reconnect was set to 1 by default.
How this change impacts existing apps:
- existing apps which relyed on the default will see a behaviour change;
they will have to set reconnect=1 after mysql_real_connect().
- existing apps which explicitely asked for reconnection (the only way they
could do it was by setting mysql.reconnect to 1 after mysql_real_connect())
will not see a behaviour change.
- existing apps which explicitely asked for no reconnection
(mysql.reconnect=0) will not see a behaviour change.
*/
mysql->reconnect= 0;
mysql->options.secure_auth= TRUE;
return mysql;
}

附2:mysql_server_init源码

/*
Initialize the MySQL client library
SYNOPSIS
mysql_server_init()
NOTES
Should be called before doing any other calls to the MySQL
client library to initialize thread specific variables etc.
It's called by mysql_init() to ensure that things will work for
old not threaded applications that doesn't call mysql_server_init()
directly.
RETURN
0  ok
1  could not initialize environment (out of memory or thread keys)
*/
int STDCALL mysql_server_init(int argc __attribute__((unused)),
char **argv __attribute__((unused)),
char **groups __attribute__((unused)))
{
int result= 0;
if (!mysql_client_init)
{
mysql_client_init=1;
org_my_init_done=my_init_done;
if (my_init())    /* Will init threads */
return 1;
init_client_errs();
if (mysql_client_plugin_init())
return 1;
if (!mysql_port)
{
char *env;
struct servent *serv_ptr __attribute__((unused));
mysql_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
if ((serv_ptr= getservbyname("mysql", "tcp")))
mysql_port= (uint) ntohs((ushort) serv_ptr->s_port);
#endif
if ((env= getenv("MYSQL_TCP_PORT")))
mysql_port=(uint) atoi(env);
}
if (!mysql_unix_port)
{
char *env;
#ifdef __WIN__
mysql_unix_port = (char*) MYSQL_NAMEDPIPE;
#else
mysql_unix_port = (char*) MYSQL_UNIX_ADDR;
#endif
if ((env = getenv("MYSQL_UNIX_PORT")))
mysql_unix_port = env;
}
mysql_debug(NullS);
#if defined(SIGPIPE) && !defined(__WIN__)
(void) signal(SIGPIPE, SIG_IGN);
#endif
#ifdef EMBEDDED_LIBRARY
if (argc > -1)
result= init_embedded_server(argc, argv, groups);
#endif
}
else
result= (int)my_thread_init();         /* Init if new thread */
return result;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐沙弥的世界

收缩临时表空间

        当排序操作、重建索引等大型操作无法在内存中完成时,临时表空间将为排序提供便利。一般情况下临时表空间为多个用户,多个会话所共 享。不能为会话分...

1103
来自专栏杨建荣的学习笔记

dataguard switchover的自动化脚本实现 (r5笔记第48天)

data guard的主要功能就是作为备库来同步主库的数据变化,一般使用中物理standby使用的比较多。data guard显示威力的一个场景就是switho...

3525
来自专栏xingoo, 一个梦想做发明家的程序员

获取本机IP MAC地址

1 #include <windows.h> 2 #include <stdio.h> 3 #include "Iphlpapi.h" //包含对IP帮助...

2766
来自专栏乐沙弥的世界

ERROR 1160 (08S01) : Got an error writing communication packets

    应用程序端碰到错误提示,MySQLNonTransientConnectionException:Got an error writing commun...

1300
来自专栏陈仁松博客

暴力遍历还没注册的双拼域名

最近突然发现双拼域名越来越少,价格也在不断上涨。想注册一个有趣的双拼域名玩玩,于是动手写了一个暴力查询双拼域名的工具。 思路比较简单,首先找到域名查询的接口,这...

5046
来自专栏zhisheng

Elasticsearch自定义分词器

下载地址:https://github.com/medcl/elasticsearch-analysis-mmseg/releases 根据对应的版本进行下载

6023
来自专栏技术点滴

黑客常用WinAPI函数整理

黑客常用WinAPI函数整理 之前的博客写了很多关于Windows编程的内容,在Windows环境下的黑客必须熟练掌握底层API编程。为了使读者对黑客常用的Wi...

1816
来自专栏沃趣科技

复制状态与变量记录表 | performance_schema全方位介绍

不知不觉中,performance_schema系列快要接近尾声了,今天将带领大家一起踏上系列第六篇的征程(全系共7个篇章),在这一期里,我们将为大家全面讲解p...

1673
来自专栏岑玉海

sqoop 兼容性问题

--direct 只支持mysql 5.0 + 和postgresql 8.3+(只是import) jdbc的jar包需要放在$SQOOP_HOME/lib目...

3996
来自专栏乐沙弥的世界

SQL server 数据导入导出BCP工具使用详解

    数据的导入导出是数据库管理员常见的工作任务之一,尤其是平面文件的导入导出。BCP 工具则为这些任务提供了强有力的支持,它是基于DB-Library,尤其...

1622

扫码关注云+社区