区块链研习社

比特币源码阅读(三):初始化和启动(2)

2017-11-12  本文已影响0人  沙漠中的猴

3. AppInit

用于AppInit函数有点长,咱们把函数拆开一点一点的看,我将该函数分成了5个部分,并通过数字来标识当前函数代码块。

// 1.
// 函数AppInit()  位置:src/bitcoind.cpp

boost::thread_group threadGroup;  //定义线程组,用来管理线程
CScheduler scheduler;
   
bool fRet = false;

定义了一个线程组用来管理线程。CScheduler这个类声明在/src/scheduler.h。我们来看下CScheduler中的两个函数

//类:CScheduler  文件位置:/src/scheduler.h

// Convenience method: call f once deltaSeconds from now
void scheduleFromNow(Function f, int64_t deltaSeconds);

// Another convenience method: call f approximately
// every deltaSeconds forever, starting deltaSeconds from now.
// To be more precise: every time f is finished, it
// is rescheduled to run deltaSeconds later. If you
// need more accurate scheduling, don't use this method.
void scheduleEvery(Function f, int64_t deltaSeconds);

scheduleFromNow()作用是过一段时间执行Function fscheduleEvery()作用是每隔一段时间来执行Function f。可以单独新建线程来处理业务逻辑,不会干扰主线程。
CScheduler的作用主要是用来管理后台任务

// 2.
//函数:AppInit() 位置:/src/bitcoind.cpp 
 
    gArgs.ParseParameters(argc, argv);

    // Process help and version before taking care about datadir
    if (IsArgSet("-?") || IsArgSet("-h") ||  IsArgSet("-help") || IsArgSet("-version"))
    {
        std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";

        if (IsArgSet("-version"))
        {
            strUsage += FormatParagraph(LicenseInfo());
        }
        else
        {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoind [options]                     " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";

            strUsage += "\n" + HelpMessage(HMM_BITCOIND);
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return true;
    }

gArgs声明在/src/util.h类型是ArgsManager。函数ParseParameters(argc, argv)解析bitcoind命令行输入的参数,并存入map中。函数定义在/src/util.cpp

上面的代码会将命令行参数为help或者version做显示处理。其他的命令忽略。

// 3. 
   try
    {
        // 检查文件目录的有效性
        if (!boost::filesystem::is_directory(GetDataDir(false)))
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
            return false;
        }
        try
        {
            // 读取配置文件
            ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME));
        } catch (const std::exception& e) {
            fprintf(stderr,"Error reading configuration file: %s\n", e.what());
            return false;
        }
        // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
        try {
            SelectParams(ChainNameFromCommandLine());
        } catch (const std::exception& e) {
            fprintf(stderr, "Error: %s\n", e.what());
            return false;
        }

检查数据目录的正确性,数据目录的位置可以通过-datadir来指定。文件目录主要存储了保存同步的区块信息,钱包信息,配置信息等等重要信息。

默认的数据目录位置:
    Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
    Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
    Mac: ~/Library/Application Support/Bitcoin
    Unix: ~/.bitcoin

然后读取配置文件,配置文件默认的名称是bitcoin.conf存储在数据目录文件夹下 ~/.bitcoin/bitcoin.conf 。也可以通过-conf命令来指定配置文件的位置。当然这个文件也可以不设置。

// src/util.cpp   
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";

比特币网络主要有以下三种:

接下来通过ChainNameFromCommandLine函数来获取当前要连接到哪个网络,之后通过SelectParams函数来对该网络进行参数配置。

// src/chainparams.cpp
void SelectParams(const std::string& network)
{
    SelectBaseParams(network);
    globalChainParams = CreateChainParams(network);
}

SelectBaseParams函数根据当前设置的网络来配置相应的RPC端口号。

// 4. 
        // Error out when loose non-argument tokens are encountered on command line
        //判断命令行参数是否正确,取第一个参数,如果是WIN32系统可以使用'-'或者'/',其他系统则是'-'
        for (int i = 1; i < argc; i++) {
            if (!IsSwitchChar(argv[i][0])) {
                fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
                exit(EXIT_FAILURE);
            }
        }
// 5.
 // -server defaults to true for bitcoind but not for the GUI so do this here
        gArgs.SoftSetBoolArg("-server", true);
        // Set this early so that parameter interactions go to console
        InitLogging();
        InitParameterInteraction();
        if (!AppInitBasicSetup())
        {
            // InitError will have been called with detailed error, which ends up on console
            exit(EXIT_FAILURE);
        }
        if (!AppInitParameterInteraction())
        {
            // InitError will have been called with detailed error, which ends up on console
            exit(EXIT_FAILURE);
        }
        if (!AppInitSanityChecks())
        {
            // InitError will have been called with detailed error, which ends up on console
            exit(EXIT_FAILURE);
        }
        if (gArgs.GetBoolArg("-daemon", false))
        {
#if HAVE_DECL_DAEMON
            fprintf(stdout, "Bitcoin server starting\n");

            // Daemonize
            if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
                fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
                return false;
            }
#else
            fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
            return false;
#endif // HAVE_DECL_DAEMON
        }
        // Lock data directory after daemonization
        if (!AppInitLockDataDirectory())
        {
            // If locking the data directory failed, exit immediately
            exit(EXIT_FAILURE);
        }
        fRet = AppInitMain(threadGroup, scheduler);
    }
    catch (const std::exception& e) {
        PrintExceptionContinue(&e, "AppInit()");
    } catch (...) {
        PrintExceptionContinue(nullptr, "AppInit()");
    }

    if (!fRet)
    {
        Interrupt(threadGroup);
        threadGroup.join_all();
    } else {
        WaitForShutdown(&threadGroup);
    }
    Shutdown();

    return fRet;
}

用于这段代码内容较多,所以在这里做一个简单的讲解。后续内容会做一个详细讲解。
gArgs.SoftSetBoolArg("-server", true);-server参数设置为1。先判断该参数是否存在,如果存在返回false,不存在返回true。

// src/util.cpp 

// 如果-server存在,则返回false。不存在则返回true。
bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
{
    LOCK(cs_args);
    if (mapArgs.count(strArg))
        return false;
    ForceSetArg(strArg, strValue);
    return true;
}
//当fValue设置为true时,将-server设置成1。否则设置为0。
bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
{
    if (fValue)
        return SoftSetArg(strArg, std::string("1"));
    else
        return SoftSetArg(strArg, std::string("0"));
}

下面的函数大概作用为:

  1. InitLogging() 初始化日志系统
  2. InitParameterInteraction() 初始化网络参数规则
  3. AppInitBasicSetup() 设置消息格式及处理回调逻辑。
  4. AppInitParameterInteraction() 设置区块链运行参数
  5. AppInitSanityChecks() 对依赖库进行检查
  6. gArgs.GetBoolArg("-daemon", false)
  7. AppInitMain() 主程序初始化
  8. ShutDown() 关闭进程
上一篇下一篇

猜你喜欢

热点阅读