比特币源码分析(10) - 可执行程序 - Bitcoind

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012183589/article/details/77895680

0x01 AppInit

接下来分析main函数中的最后一个函数AppInit,首先看前面一部分代码,

   // src/bitcoind.cpp line 65-95
boost::thread_group threadGroup;
CScheduler scheduler;

bool fRet = false;
// Parameters
//
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
gArgs.ParseParameters(argc, argv);

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

  if (gArgs.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;
}

程序首先定义了一个线程组threadGroup,线程组的功能就是分组管理线程,功能和http://blog.csdn.net/pure_lady/article/details/77675915#t3 中介绍的Thread功能几乎一样。接下来定义了一个scheduler,这个类的声明在src/scheduler.h中,根据代码中的介绍,

//
// Simple class for background tasks that should be run
// periodically or once "after a while"
//
// Usage:
//
// CScheduler* s = new CScheduler();
// s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { }
// s->scheduleFromNow(std::bind(Class::func, this, argument), 3);
// boost::thread* t = new boost::thread(boost::bind(CScheduler::serviceQueue, s));
//
// ... then at program shutdown, clean up the thread running serviceQueue:
// t->interrupt();
// t->join();
// delete t;
// delete s; // Must be done after thread is interrupted/joined.
//

主要是用来管理后台任务,主要的两个函数是scheduleFromNowscheduleEvery,分别表示从现在开始是过一段时间执行某函数一次,和从现在开始每隔几秒执行某函数一次。也可创建一个新的线程去执行任务,而不影响主线程的执行。

定义完这两个变量之后,下面一行是gArgs.ParseParameters(argc, argv);,作用是解析bitcoind命令行传入的参数,其中gArgs的定义在src/util.h中,类型是ArgsManagerParseParameters()是该类中的一个主要成员函数,功能是将传入的参数进行解析并存入到两个map当中。

解析完参数之后,下面就开始进行一系列参数设置,这部分分析的最后一部分代码,也就是上面的那个if语句,功能是判断参数中是否有显示help或者version信息,如果有,就直接显示对应的信息,然后退出程序,忽略其他所有的参数。

再来看接下来的一段代码,

   // src/bitcoind.cpp line 99-118
if (!fs::is_directory(GetDataDir(false)))  // 检查数据目录
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
            return false;
        }
        try
        {
          // 读取配置文件
            gArgs.ReadConfigFile(gArgs.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;
        }

这段代码首先检查数据目录是否合法,数据目录在Ubuntu下默认的路径是~/.bitcoin/,当然也能通过-datadir参数进行设置,该目录下主要保存同步的区块信息,钱包信息,配置信息等等几乎所有的区块链运行信息都保存在这里。然后开始读取配置文件,配置文件的默认名称是~/.bitcoin/bitcoinf.conf也是在数据目录下,不过默认是没有这个文件的,进入ReadConfigFile可以看到文件不存在也是可以的。

// src/util.cpp line 599-623
void ArgsManager::ReadConfigFile(const std::string& confPath)
{
    fs::ifstream streamConfig(GetConfigFile(confPath));
    if (!streamConfig.good())
        return; // No bitcoin.conf file is OK

    {
        LOCK(cs_args);
        std::set<std::string> setOptions;
        setOptions.insert("*");

        for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
        {
            // Don't overwrite existing settings so command line settings override bitcoin.conf
            std::string strKey = std::string("-") + it->string_key;
            std::string strValue = it->value[0];
            InterpretNegativeSetting(strKey, strValue);
            if (mapArgs.count(strKey) == 0)
                mapArgs[strKey] = strValue;
            mapMultiArgs[strKey].push_back(strValue);
        }
    }
    // If datadir is changed in .conf file:
    ClearDatadirCache();
}

接下来是这句SelectParams(ChainNameFromCommandLine());,首先通过ChainNameFromCommandLine()获取命令行中设置的当前程序运行的网络,包括以下三种:

  • Main:表示主网,也就是当前比特币所有用户交易的网络,bitcoind中的默认值。
  • Testnet:测试网,测试网中专门有一条测试链,所有的交易都只是用于测试,并且测试网中的币可以方便的获取,主要目的就是模拟真实交易环境测试新的功能。
  • Regtest:回归测试,又称为私有网,用于个人开发测试,挖矿难度较低,并且参数都可以自行设置。

所以一般在本地环境开始时使用Regtest,本地开发完成后,进入Testnet进行大规模实际环境测试,运行正常后再进入主网,这也是目前众多区块链(ICO)项目的主流开发路线。

回到代码中,获取到当前的网络之后通过SelectParams()根据不同的网络创建不同的共识参数,实现的方式是使用三个继承类CMainParamsCTestNetParamsCRegTestParams继承基类CChainParams,然后根据选择的不同的网络返回不同的继承类,返回值由一个CChainParams类型的智能指针(unique_ptr)globalChainParams来接收,最后使用时就用这个智能指针来访问相应的共识参数。所谓智能指针就是当指针离开作用域时自动的删除(使用delete)所指向的对象。

设置好网络后,下面一部分代码是用来判断命令行中是否存在错误的参数,判断方法是看每一个参数的第一个字母是否为-或者在windows环境中- or /,如果不是就报错然后退出程序。

// src/bitcoind.cpp line 119-125        
// Error out when loose non-argument tokens are encountered on command line
        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);
            }
        }

猜你喜欢

转载自blog.csdn.net/u012183589/article/details/77895680