从零到壹学习比特币源码解析第六讲:比特币源码解析-003

黎跃春

孔壹学院、ChainDesk创始人兼CEO

        从零到壹学习比特币源码解析为一个系列,一共11讲,包括准备知识、源码解析等。今天我们将为大家介绍从零到壹学习比特币源码解析第六讲:比特币源码解析-003。话不多说,马上开启我们的比特币源码解析学习之旅。

孔壹学院
AppInit
bool AppInit(int argc, char* argv[]){    bool fRet = false;    //    // Parameters    //    // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()    gArgs.ParseParameters(argc, argv);
ParseParameters 参数解析
gArgs.ParseParameters(argc, argv);

作用是解析bitcoind命令行传入的参数,其中gArgs的定义在src/util.h中,类型是ArgsManager,ParseParameters()是该类中的一个主要成员函数,功能是将传入的参数进行解析并存入到两个map当中。

# src/util.cppvoid ArgsManager::ParseParameters(int argc, const char* const argv[]){    LOCK(cs_args);    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)        {            strValue = str.substr(is_index+1);            str = str.substr(0, is_index);        }#ifdef WIN32        boost::to_lower(str);        if (boost::algorithm::starts_with(str, "/"))            str = "-" + str.substr(1);#endif        if (str[0] != '-')            break;        // Interpret --foo as -foo.        // If both --foo and -foo are set, the last takes effect.        if (str.length() > 1 && str[1] == '-')            str = str.substr(1);        InterpretNegativeSetting(str, strValue);        mapArgs[str] = strValue;        mapMultiArgs[str].push_back(strValue);    }}

代码具体解析:

(1) LOCK(cs_args);

  • cs_args 介绍

定义在 src/util.h,mutable CCriticalSection cs_args;

CCriticalSection 为线程中的访问临界资源,多个线程必须互斥地对它进行访问,即保证在该代码后面的全局变量在程序运行过程中不会被其他线程对其后的变量进行篡改。被定义在 src/sync.h

class CCriticalSection : public AnnotatedMixin<std::recursive_mutex>{public:    ~CCriticalSection() {        DeleteLock((void*)this);    }};
  • LOCK 介绍

// src/sync.h#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)

LOCK并不是一个单独的函数,而是一个宏定义,与前面的CCriticalSection对象结合实现对包含代码在各线程中进行互斥加锁处理,防止后续代码中涉及的全局变量被不同线程抢夺。

(2) 参数解析

在使用这两个变量时,程序对其使用clear方法进行了清空操作。

mapArgs.clear();_mapMultiArgs.clear();

mapArgs实现对单个参数及其对应值的映射存储,mapMultiArgs实现单个参数及其对应多个值的映射存储。

随后通过for循环实现对所有参数进行逐个解析,获取参数及其值,对于存在多个值的参数mapArgs只存储最后的参数值,mapMultiArgs则将参数对应的所有的值通过vector变量进行存储。

bitcoind 命令处理
// 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;    }

处理 bitcoind 输入 –version, -?, -h, -help 显示相应的信息。

版本信息

终端输入 bitcoind –version 可以输出的比特币版本信息及比特币版权信息。

帮助信息

在终端中输入“bitcoind -?”、"bitcoind -h"或" bitcoind -help"将获取相同的帮助内容,均为比特币后台进程包含参数使用方法的帮助信息


数据目录
 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;        }

第一行的if语句,在该语句中判断了GetDataDir(false)函数返回的数据路径是否为目录名称,如果不是,则打印指定目录不存在的错误提示信息,并且因为数据目录不正确,而导致比特币核心程序无法正常运行,所以返回false,程序退出。

GetDataDir的详细代码

// src/util.cppconst fs::path &GetDataDir(bool fNetSpecific){    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 (gArgs.IsArgSet("-datadir")) {        path = fs::system_complete(gArgs.GetArg("-datadir", ""));        if (!fs::is_directory(path)) {            path = "";            return path;        }    } else {        path = GetDefaultDataDir();    }    if (fNetSpecific)        path /= BaseParams().DataDir();    if (fs::create_directories(path)) {        // This is the first run, create wallets subdirectory too        fs::create_directories(path / "wallets");    }    return path;}

该函数的具体实现流程如图所示:

通过 GetDefaultDataDir 可以获取比特币后台进程在Windows、Mac以及unix等操作系统下的默认数据目录

// Windows < VistaC:\Documents andSettings\Username\Application Data\Bitcoin// Windows >= VistaC:\Users\Username\AppData\Roaming\Bitcoin// Mac~/Library/Application Support/Bitcoin// Unix~/.bitcoin

第4步中,程序判断是否为网络目录,如果是在执行第5步,在第5步中我们将获得Path中的BaseParams.DataDir()目录,该目录的定义在chainparamsbase.h中有具体实现。

第6步通过fs::create_directories(path);创建数据目录。

第7步返回创建的数据目录,此时程序通过GetDataDir(false)函数获得了数据目录路径,如果路径信息正确且存在,程序将继续运行,否则前文所述,程序将停止运行,返回false。

读取配置文件

完成数据目录的创建后,程序将进入配置文件读取部分,其实现代码如下:

gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));

BITCOIN_CONF_FILENAME :

// src/util.cppconst char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";

gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)

首先判断是否存在"-conf"参数,如果存在,则使用 mapArgs 中参数解析结果中保存的参数值作为配置文件,否则使用默认的 bitcoin.conf。

// src/util.cppstd::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const{    LOCK(cs_args);    auto it = mapArgs.find(strArg);    if (it != mapArgs.end()) return it->second;    return strDefault;}

ReadConfigFile

在获得配置文件名后,我们可以来分析ReadConfigFile函数实现。在该函数实现了配置文件中参数与参数值的读取操作,并将读取的参数信息存入mapArgs与_mapMultiArgs中。

函数最后是为防止配置文件中设置了数据目录参数datadir,通过ClearDatadirCache()函数将数据文件路径参数设置为空目录,这样下次进入GetDataDir()时,我们将会根据新的datadir创建数据目录。

// src/util.cppvoid 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();    if (!fs::is_directory(GetDataDir(false))) {        throw std::runtime_error(strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str()));    }}
本文来源: 区块链部落 文章作者: 黎跃春 我要纠错
声明:本文由入驻金色财经的作者撰写,观点仅代表作者本人,绝不代表金色财经赞同其观点或证实其描述。
提示:投资有风险,入市须谨慎。本资讯不作为投资理财建议。

金色财经 > 区块链 > 从零到壹学习比特币源码解析第六讲:比特币源码解析-003