您的位置:首页 > 其它

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

2017-09-08 15:48 471 查看

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.
//


主要是用来管理后台任务,主要的两个函数是
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()
获取命令行中设置的当前程序运行的网络,包括以下三种:

Main:表示主网,也就是当前比特币所有用户交易的网络,bitcoind中的默认值。

Testnet:测试网,测试网中专门有一条测试链,所有的交易都只是用于测试,并且测试网中的币可以方便的获取,主要目的就是模拟真实交易环境测试新的功能。

Regtest:回归测试,又称为私有网,用于个人开发测试,挖矿难度较低,并且参数都可以自行设置。

所以一般在本地环境开始时使用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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: