四、初始化和启动模块(3)

(四)我们接着看bitcoind.cpp文件代码的99-110行

这几行主要作用是:判断数据目录是否存在并读取配置文件参数指定的配置文件。代码的内容是:

四、初始化和启动模块(3)_第1张图片
需要分析4个部分内容:

  • GetDataDir(false)函数;
  • BITCOIN_CONF_FILENAME 宏定义;
  • GetArg()函数;
  • ReadConfigFile()函数。
    (1)GetDataDir(false)函数
    这个函数在util.t中的第172行声明:
    在util.cpp的第555行实现:
    四、初始化和启动模块(3)_第2张图片
    其中注释文字内容为:

这可以通过LogPrintf()来调用,因此我们缓存了值,这样我们就不必在之后执行内存分配了.

更详细的讲解可以参考下面的链接:

https://www.jianshu.com/p/bd1903a31b4b

(2)BITCOIN_CONF_FILENAME 宏定义
该定义在util.cpp中的第90行:


(3) GetArg()函数
此处的函数使用的代码为:
gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)
其中 gArgsArgsManager类定义的变量, GetArg()声明在util.h中第221行,属于ArgsManager类中的方法:
std::string GetArg(const std::string& strArg, const std::string& strDefault);
它的实现在util.cpp中436-442行:

std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault)
{
    LOCK(cs_args);
    if (mapArgs.count(strArg))
        return mapArgs[strArg];
    return strDefault;
}

它在这里实现的功能为:

判断是否有参数"-conf",如果有,则使用前面ParseParmeters()函数使用后保存的参数值作为配置文件;如果没有,则使用默认的“bitcoin.conf”。

(4)ReadConfigFile()函数
这个函数在util.h的第203行声明,在util.cpp的第601实现,其中它的实现代码如下图所示:

四、初始化和启动模块(3)_第3张图片
这个函数主要是先读取配置文件的参数和参数值,并存储这些信息;最后用 ClearDatadirCache()函数将数据文件路径参数设置成空目录。其中 ClearDatadirCache()函数在util.cpp的第584行实现:
四、初始化和启动模块(3)_第4张图片

总之,对于这部分实现的功能逻辑如下所示:

  1. 判断函数GetDataDir()得到的路径是否为规范的目录名称。如果不是,打印指定目录不存在的错误信息,并返回false值,程序退出;如果是,则继续下面的程序。
  2. 使用ReadConfigFile()函数来读取配置文件中的参数名与参数值,并把得到的参数信息存入mapArgs和_mapMultiArgs中。

(五)继续看bitcoind.cpp中代码的112-117行

四、初始化和启动模块(3)_第5张图片

对此段代码的注释为:

检查- testnet- regtest参数 (Params()调用仅在此子句之后有效)

其中- testnet指代比特币的测试网络Testnet,而- regtest指代比特币的私有网络Regression test。那么可以知道这个部分是选择比特币网络的。
其中主要的是两个函数:

  • ChainNameFromCommandLine()函数;
  • SelectParams()函数。
    其中SelectParams()是以ChainNameFromCommandLine()函数的返回值当作参数的。下面对这两个函数具体分析:
    (1)ChainNameFromCommandLine()函数
    这个函数的声明在chainparamsbase.h中的第60行:
    对它的注释内容为:

寻找- regtest- testnet并返回适当的BIP70链名。
如果给定无效组合,则返回CBaseChainParams::MAX_NETWORK_TYPES;默认情况下返回CBaseChainParams::MAIN

它的实现在chainparamsbase.cpp中的第90行,具体代码如图所示:
四、初始化和启动模块(3)_第6张图片

对于它的具体实现过程可以参考下面链接:

https://www.jianshu.com/p/6fcfc5969733

总之:这是一个获取网络名称的函数。对于具体的实现逻辑如下:

检测是否含有- regtest- testnet参数,如果只含有- regtest参数,则获取网络名称为"regtest",即私有网络;如果只含有- testnet参数,则获取的网络名称为"test",即测试网络;当两个参数同时含有,则会报错;当两个参数都没有则获取的网络名称为"main",即主网络。

(2)SelectParams()函数
这个函数的声明在chainparams.h中的第116行:

对这个函数的注释为:

根据params()返回的值设置成指定的BIP70的链名称的参数。
当链不被支持时,抛出错误信息std::runtime_error

它的实现在chainparams.cpp中的第353行:

这个函数调用了两个函数:

  • SelectBaseParams()函数;
  • CreateChainParams()函数。
    SelectBaseParams()函数的声明在chainparamsbase.h的第54行注释上对它的解释是:

根据params()返回的值设置成指定网络的参数。

函数的实现在chainparamsbase.cpp中的第85行:

可以知道它调用了 CreateBaseChainParams()函数,这个和下面说要说的 CreateChainParams()函数名称相似,其实这两个函数的功能也相似,都是对不同默认网络的参数设置,只是 CreateChainParams()函数设置的更具体,而 CreateBaseChainParams()函数只是基本设置。
CreateBaseChainParams()函数在chainparamsbase.h中的第39行声明,在chainparamsbase.cpp的第73行实现:
四、初始化和启动模块(3)_第7张图片
可以知道根据不同的链名称选择对应的 CBaseMainParams()CBaseTestNetParams()CBaseRegTestParams() ,而则三个函数是分别对私有网络、测试网络和主网络的RPC端口参数和数据目录。在chainparamsbase.cpp的30-63行:
四、初始化和启动模块(3)_第8张图片
从这些数据可以知道比特币主网络的RPC端口为8332,测试网络的RPC端口为18332,私有网络的RPC端口为18332,以及他们各自的目录名 。
CreateChainParams()函数
这个函数的声明在chainparams.h中的第104行,其中注释对它解释为:

创建并返回一个std::unique_ptr 的选择链。
返回所选链的CChainParams *指针。
如果这个链没有被支持则抛出一个错误信息:std::runtime_error

这个就是根据上面得到的网络名称设置成对应的网络参数,并把参数赋值给globalChainParams
它的实现在chainparams.cpp的第342行:

四、初始化和启动模块(3)_第9张图片
CreateBaseChainParams()函数类似,它在实现的时候同时也实例化了对应网络类型的参数设置函数: CMainParams()CTestNetParams()CRegTestParams()。对于这三个函数在chainparams.cpp的73-333行的大量代码中有详细的代码实现,这里基本上包含了不同网络的网络名称、挖矿难度、区块信息等等几乎所有需要的参数。

所以,如果想要创建自己需要的数字货币,只需要单独修改这里面的参数就行了,比如修改难度,奖励金额等。这个部分是开发时经常要修改的地方。

总结:
SelectParams()函数实现的功能就是把ChainNameFromCommandLine()函数获取得到的网络名称设置成对应的基本的网络基本参数。


(六)继续看bitcoind.cpp中代码的120-125行

四、初始化和启动模块(3)_第10张图片

这段代码的注释为:

当命令行中有不准确的无参数符号时出现错误提示。

这部分有个判断函数IsSwitchChar(),它的作用是判断是否有'-'或'/',含有的将会是正确的。它的实现在util.h中的第186行:

四、初始化和启动模块(3)_第11张图片
那么如果符号不正确,则在终端输出:
Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.
而且程序会因为异常而退出。


(七)继续看bitcoind.cpp中代码的第128行

这部分的代码就只有一行,注释对它的说明为:

对于bitcoind来说-server默认是开启的,但对于GUI(图形界面)-server默认则是关闭的,所以在此添加代码。

这里面有个主要的函数:SoftSetBoolArg(),这个函数的声明在util.h的第257行:

四、初始化和启动模块(3)_第12张图片
它的注释主要的意思是:

如果它还没有值,就设置一个布尔参数

它的实现在util.cpp中的第469行:

四、初始化和启动模块(3)_第13张图片
这个函数中调用了一个函数 SoftSetArg(),这个函数在util.h中声明,主要是:

如果它还没有值,就设置一个参数

它的实现在util.cpp中的第460行:

四、初始化和启动模块(3)_第14张图片

这个函数就有很多熟悉面孔,它也是ArgsManager(即参数管理类)的一个方法;也有个互斥锁: LOCK(cs_args);;也有 mapArgs.count(strArg)函数,这个是关于 mapArgs参数的验证。还有个我们不是很熟悉的函数: ForceSetArg(),这个函数在当前文件中的第477行实现:
主要还是对 mapArgsmapMultiArgs这两个参数的操作。

总的来看:
gArgs.SoftSetBoolArg("-server", true);函数的实现逻辑是:

解析mapArgs参数,判断其中是否有-server这个参数,如果存在就无需设置了;如果没有这个参数,就根据SoftSetBoolArg()传入的fValue值进行设置。


(八)继续看bitcoind.cpp中代码的130-131行

这个部分只有两行,分别有两个函数,其中对它的注释为:

提前设置这部分,使参数交互内容进入控制台中。

InitLogging()函数
这个函数的声明在init.h的第25行:

对它的注释为:

初始化日志基础设施

它的实现在init.cpp的821行:

四、初始化和启动模块(3)_第15张图片

可以了解这是一个 初始化日志的函数,而且通过查询也知道这个函数作用的是把数据存储在debug.log文件中,这个文件在ubuntu系统是在$HOME/.bitcoin/文件夹中,接下来对它初始化实现的6个设置内容较详细说明:
a.函数的第一行代码主要是检测参数命令中是否含有 -printtoconsole命令,如果有则让日志信息发送跟踪/调试信息到控制台中,但默认是false,即默认只是记录在日志文件debug.log中,而不是在控制台中显示;
///////////////////////////////////////////////////////////////////
补充一点:
DEFAULT_LOGTIMESTAMPS DEFAULT_LOGTIMEMICROS
DEFAULT_LOGIPS这三个静态常量在util.h中有定义:
///////////////////////////////////////////////////////////////////
b.函数的第二行代码主要是检测参数命令中是否含有 -logtimestamps,该参数的含义是在日志中打印时间戳,由上面的补充内容知道默认是在日志文件中打印时间戳的;
c.函数的第三行代码主要是检测参数命令中是否含有 -logtimemicros,该参数的含义是打印日志单位精确到微妙(μs),由上面的补充内容知道默认是false,即默认初始化日志文件精确到秒(s);
d. 函数的第四行代码主要是检测参数命令中是否含有 -logips,该参数的含义是打印IP地址,由上面的补充内容知道默认是false,即默认在日志文件中是不打印IP地址的;
e.函数第五行是在日志文件中空19行;
f.函数最后一行是打印“Bitcoin version”,并紧跟上比特币客户端的版本信息。

由上面可以知道,这个函数只是一个基本的初始化日志文件函数。而接下来的InitParameterInteraction函数是日志文件中存储参数交互信息。

如下图为初始化日志的基本格式:
四、初始化和启动模块(3)_第16张图片

InitParameterInteraction()函数
开始还是解析这个函数代码:
这个函数的声明在init.h第27行:

对它的注释为:

参数交互:根据不同的规则改变当前参数。

它的实现在init.cpp中的744-814行,它主要包括了8个规则:

四、初始化和启动模块(3)_第17张图片
四、初始化和启动模块(3)_第18张图片
下面简单的对这8部分解释:
a.绑定并监听地址
注释为:当显示指定了绑定地址后,即使指定了-connect和-proxy参数信息,程序将会接受来自外部的连接,并监听该地址。
通过参数 -bind-whitebind这两个参数设置,并通过 SoftSetBoolArg()函数实现了 -listen参数的设置,把它设置成true,代表要监听设置的外部连接IP地址。
b.连接可信的节点
首先判断参数命令中是否含有 -connect参数,如果有将 -dnsseed(使用DNS查找节点)和 -listen(即接受来自外部的连接,并对其进行监听)设置为true。并进行日志打印。
注意:此处代码的有效执行是在为设置 -bind-whitebind参数的情况下进行的。
c.代理模式
设置代理参数的目的是为了保护隐私,则此处将 -listen-upnp以及 -discover均设置为false,也就是说比特币后台进程只使用代理提供的监听地址与端口,并且不去查找默认的监听地址。这里的upnp代表的意思是使用全局即插即用(UPNP)映射监听端口,默认不使用。
注意:此处代码的有效执行也是在为设置 -bind-whitebind参数的情况下进行的。
d.监听设置处理
当不监听时,不要映射端口或尝试检索公共IP。就是如果监听参数设置为false,则upnp(端口)、discover(自动发现默认地址)以及listenonion(匿名地址监听)均设置为false。
/////////////////////////////////////////////////////////////////////
补充:这里的 listenonion(匿名地址监听)实际上设计一个通信机制的一个概念: 第二代洋葱路由(onion routing),对它的理解可以借鉴网络的解释:

Tor(The Onion Router)是第二代洋葱路由(onion routing)的一种实现,用户通过Tor可以在因特网上进行匿名交流。Tor专门防范流量过滤、嗅探分析,让用户免受其害。最初该项目由美国海军研究实验室赞助。2004年后期,Tor成为电子前哨基金会的一个项目。2005年后期,EFF不再赞助Tor项目,但他们继续维持Tor的官方网站。

而在bitcoind源码src文件夹下的torcontrol.h和torcontrol.cpp实现了Tor的控制。
/////////////////////////////////////////////////////////////////////
e.外部IP参数处理
如果显示指定了公共IP地址,那么bitcoind就不需要查找其他监听地址了。
f.区块模式参数设置
DEFAULT_BLOCKSONLY在net.h中定义,默认值为false。表示在blocksonly模式(区块模式)下禁用whitelistrelay。即在区块模式下白名单列表失效。
g.强制白名单节点连接参数处理
强制白名单节点连接参数处理意味着比特币网络中的信息将优先在白名单节点间传递。
g.区块大小设置
这个区块大小是设置默认的矿工产生区块的大小。

总结:

InitParameterInteraction()函数主要是根据参数命令,改变当前的参数,并把相应的信息打印到日志文件中。其中还涉及到一些网络中IP地址的监听设置方法、白名单的禁用和启用情况等。其实就是日志文件中初始化日志后(即版本信息打印后)的大量的日志内容,如图:

四、初始化和启动模块(3)_第19张图片
而这两个函数基本决定了日志文件debug.log中的所有预输出日志的打印内容,但是我们知道日志不仅包括初始化准备的预输出日志,还包括启动程序时和启动之后的一系列操作的日志,那么日志文件中的启动后的输出日志内容是什么呢?我们将在后面的 AppInitMain()函数中进一步讲解程序初始化启动时的调试日志内容。


你可能感兴趣的:(四、初始化和启动模块(3))