比特币源码初级阅读一(2)

在bitcoind.cpp文件中还剩下noui_connect()与AppInit(argc, argv)两个函数,其中noui_connect()用来链接bitcoind的信号处理,与区块链架构相关不大,所以接下来本篇主要分析AppInit(argc, argv)函数,由于该代码比较长,本文分以下几个部分来分析:

一、

    ParseParameters(argc, argv);   //将参数(argc, argv)传入解析,并作初步处理

    // Process help and version before taking care about datadir 在处理文件目录之前先处理“help”与“version”请求
    if (mapArgs.count("-?") || mapArgs.count("-h") ||  mapArgs.count("-help") || mapArgs.count("-version"))
    { //判断是否请求“help”文档或者“vesion”信息
      //FormatFullVersion()返回格式化的全版本信息(版本信息与许可证信息请分开)
        std::string strUsage = _("Bitcoin Core Daemon") + " " + _("version") + " " + FormatFullVersion() + "\n";

        if (mapArgs.count("-version"))
        {
            strUsage += LicenseInfo();  //返回许可证信息
        }
        else
        {
            strUsage += "\n" + _("Usage:") + "\n" +
                  "  bitcoind [options]                     " + _("Start Bitcoin Core Daemon") + "\n";

            strUsage += "\n" + HelpMessage(HMM_BITCOIND);  //返回帮助文档
        }

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

ParseParameters(argc, argv)函数是对(argc, argv)进行参数解析,稍后会对其详解,之后判断在命令行窗口接入的参数是否为"-?"、"-h"、"-help"、"-version",(mapArgs是map类型,由ParseParameters(argc, argv)函数返回),接下来就比较简单了,程序会直接判断用户请求的是“help”文档,还是“version”信息,然后进行相应的响应,返回对应的内容。下面请移步到ParseParameters(argc, argv)中,我将和大家细细品味该函数,该函数的容貌如下 (位置:src/util.cpp):

 void ParseParameters(int argc, const char* const argv[])
    {   // 清空映射
        mapArgs.clear();//单键值映射
        mapMultiArgs.clear(); //多键值映射
for (int i = 1; i < argc; i++)
{
    std::string str(argv[i]); //将参数转为字符串
    std::string strValue;
    size_t is_index = str.find('=');  //在参数字符串中寻找“=”
    if (is_index != std::string::npos) //如果找到了“=”(npos是一个不存在的值,用于判断是否能够寻找到特征值)
    {
        strValue = str.substr(is_index+1); //参数中“=”右侧的值赋给strValue
        str = str.substr(0, is_index);  //参数左侧的值赋给str
    }
        #ifdef WIN32 //对于win32,如果str中以‘/’打头,将‘/’更换为‘-’
            boost::to_lower(str);
            if (boost::algorithm::starts_with(str, "/"))
                str = "-" + str.substr(1);
    #endif


    if (str[0] != '-') //判断str(无参命令行)是否以‘-’打头
        break;

    // Interpret --foo as -foo.
    // If both --foo and -foo are set, the last takes effect.
    if (str.length() > 1 && str[1] == '-') //将‘--’打头的str换为‘-’打头
        str = str.substr(1);

    mapArgs[str] = strValue;  //将str与对应的strValue加入单值映射mapArgs
    mapMultiArgs[str].push_back(strValue);//将str与对应的strValue们加入多值映射mapMultiArgs
}

首先程序从bitcoind上接收的参数是字符数组const char* const argv[],传入的另一个参数是其长度,本函数是对bitcoind接收的命令进行解析,具体做法为: 1,将接受的字符数组转为字符串str 2,以‘=’为标志,将str截断,作为键值对存入map数据类型的 mapArgs中,之所以这么做是因为,命令行一般格式为“变量=值”,将变量与值隔开存入映射之中,便于后面的执行。

二、

try
    {
        if (!boost::filesystem::is_directory(GetDataDir(false)))//判断数据目录的正确性
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
            return false;
        }
        try
        {
            ReadConfigFile(mapArgs, mapMultiArgs);  //读取配置文件
        } catch (const std::exception& e) {
            fprintf(stderr,"Error reading configuration file: %s\n", e.what());
            return false;
        }

这部分代码的功能是读取配置文件,核心函数为GetDataDir(false)与ReadConfigFile(mapArgs, mapMultiArgs),稍后会对这两个函数进行详解。本段代码首先判断由GetDataDir(false)返回的数据目录是否正确,若正确调用ReadConfigFile(mapArgs, mapMultiArgs)读取配置文件。下面进行核心函数讲解:

1,GetDataDir(false)

const boost::filesystem::path &GetDataDir(bool fNetSpecific)
{
    namespace fs = boost::filesystem;
LOCK(csPathCached);

fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;  //判断是否指定网络路径目录,否则选择本地路径

// This can be called during exceptions by LogPrintf(), so we cache the
// value so we don't have to do memory allocations after that.
if (!path.empty())    //如果路径不空直接返回
    return path;

if (mapArgs.count("-datadir")) {   //判断是否从命令行中键入datair,如果有直接取其值作为path,否则path取默认值
    path = fs::system_complete(mapArgs["-datadir"]);
    if (!fs::is_directory(path)) {
        path = "";
        return path;
    }
} else {
    path = GetDefaultDataDir();
}
if (fNetSpecific)                //判断是否指定了网络,根据指定网络路径或者本地路径构造完整路径
    path /= BaseParams().DataDir();

fs::create_directories(path);

return path;}

输入的参数fNetSpecific为bool型,用于判断数据目录是指定的网络目录还是本地目录(这里设置为false),下面进入函数体,首先判断fNetSpecific是否为true,这里为false故将本地目录pathCached(pathCachedNetSpecific 与pathCached事先定义好了,后者定义一般为空)赋值给path,如果path不空,就直接返回,如果为空转为下一步,首先判断之前解析的参数中是否"-datadir",如果有将其对应的值赋给path,否则path取默认值,接下来还是判断fNetSpecific是否为true,如果为true需要根据指定的网络构造完整数据路径,最后构造数据路径path

2,ReadConfigFile(mapArgs, mapMultiArgs)

void ReadConfigFile(map<string, string>& mapSettingsRet,
                    map<string, vector<string> >& mapMultiSettingsRet)
{
    boost::filesystem::ifstream streamConfig(GetConfigFile());  //定义流文件,将配置文件内容导入内存
    if (!streamConfig.good())
        return; // No bitcoin.conf file is OK

set<string> setOptions; //构造迭代器,消除文件中为“*”的行
setOptions.insert("*");

for (boost::program_options::detail::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it) //将mapArgs中没有的值加入其中
{
    // Don't overwrite existing settings so command line settings override bitcoin.conf
    string strKey = string("-") + it->string_key;   //取配置文件中的参数
    if (mapSettingsRet.count(strKey) == 0)          //查看配置文件的参数是否存在于mapArgs中,若不存在加入
    {
        mapSettingsRet[strKey] = it->value[0];      //取参数string_key对应的值
        // interpret nofoo=1 as foo=0 (and nofoo=0 as foo=1) as long as foo not set)
        InterpretNegativeSetting(strKey, mapSettingsRet); //向mapArgs中加入该键值对(参数与其对应的值)
    }
    mapMultiSettingsRet[strKey].push_back(it->value[0]);
}
// If datadir is changed in .conf file:
ClearDatadirCache();}

该函数传入的参数是由函数ParseParameters(argc, argv)返回的map数据(对用户键入命令行的参数解析),下面看函数体,第一行定义了流文件将配置文件内容导入内存(GetConfigFile()函数返回的是配置文件的路径),下面使用set<string>命令构造集合并插入"",用以跳过文件中只有""的行,之后请看for循环,这是本函数的核心,传入参数是迭代器类型it(内容为消除全是"*"的行的配置文件的每一行)与it的最后一个元素的索引end,下面看循环体,取出该行的参数(配置文件的中位于"="左侧的参数)赋值给strkey,下面判断mapSettingsRet(对命令行解析之后的参数对)中是否包含该命令,如果不包含,就将该参数与其对应的值加入到mapSettingsRet中。

AppInit(argc, argv)函数中剩余的命令执行与网络初始化等部分在后续章节中讲解,感谢阅读。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容