接下来分析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.
//
主要是用来管理后台任务,主要的两个函数是scheduleFromNow
和scheduleEvery
,分别表示从现在开始是过一段时间执行某函数一次,和从现在开始每隔几秒执行某函数一次。也可创建一个新的线程去执行任务,而不影响主线程的执行。
定义完这两个变量之后,下面一行是gArgs.ParseParameters(argc, argv);
,作用是解析bitcoind命令行传入的参数,其中gArgs
的定义在src/util.h
中,类型是ArgsManager
,ParseParameters()
是该类中的一个主要成员函数,功能是将传入的参数进行解析并存入到两个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()
获取命令行中设置的当前程序运行的网络,包括以下三种:
所以一般在本地环境开始时使用Regtest,本地开发完成后,进入Testnet进行大规模实际环境测试,运行正常后再进入主网,这也是目前众多区块链(ICO)项目的主流开发路线。
回到代码中,获取到当前的网络之后通过SelectParams()
根据不同的网络创建不同的共识参数,实现的方式是使用三个继承类CMainParams
,CTestNetParams
,CRegTestParams
继承基类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);
}
}