zoukankan      html  css  js  c++  java
  • mysql_init调用卡住原因分析

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

    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;

    }

  • 相关阅读:
    共享文件时提示“将安全性信息应用到以下对象时发生错误”
    ROS 5.x自动定时备份并发送到邮箱(实用)
    Android检测网络是否正常代码!
    win7提示“User Profile Service服务未能登录”
    Android-修改TabWidget字体大小颜色及对齐
    Android 实现分页(使用TabWidget/TabHost)
    解决在ScrollView中套用ListView显示不正常
    Android中finish掉其它的Activity
    Android中如何控制元素的显示隐藏?
    <jQuery> 十一. 基本动画(显示, 隐藏, 滑入, 滑出, 淡入淡出)
  • 原文地址:https://www.cnblogs.com/aquester/p/9891630.html
Copyright © 2011-2022 走看看