比特币源码初级阅读一(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)函数中剩余的命令执行与网络初始化等部分在后续章节中讲解,感谢阅读。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容