开始研读比特币2

2018-02-28  本文已影响0人  NanoLeak

上次读到src/bitcoind.cpp 中AppInit,大概的流程是

1,解析-?,-h,-help,-version,输出帮助和版本信息
2,解析-datadir参数,GetDataDir函数判定所指定数据目录是否合法
3,通过-conf参数,ReadConfigFile函数读取配置文件
4,通过-testnet和-regtest参数,ChainNameFromCommandLine函数设置的当前程序运行的网络
5,通过-printtoconsole等参数,InitLogging函数初始化日志记录以及打印方式
6,InitParameterInteraction函数初始化网络参数
7,AppInitBasicSetup函数注册相应的消息以及处理方式
8,AppInitParameterInteraction函数设置区块链运行参数
9,AppInitSanityChecks函数检查比特币运行时所需要的所有的库是否都运行正常
10,通过-daemon参数设置是否后台运行
11,AppInitMain函数运行主应用程序
12,启动失败中断操作和清理工作

AppInit函数代码如下:
代码如下:

    54  //////////////////////////////////////////////////////////////////////////////
    55  //
    56  // Start
    57  //
    58  bool AppInit(int argc, char* argv[])
    59  {
    60      bool fRet = false;
    61  
    62      //
    63      // Parameters
    64      //
    65      // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
    66      gArgs.ParseParameters(argc, argv);
    67       //如果命令行参数是-?,-h,-help,-version的话首先构造版本和usage信息
    68      // Process help and version before taking care about datadir
    69      if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") ||  gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
    70      {
                //构造版本字符串,例如:Bitcoin Core Daemon version v0.16.0rc2
    71          std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";
    72  
                  //如果是-version,构造追加license信息
    73          if (gArgs.IsArgSet("-version"))
    74          {
    75              strUsage += FormatParagraph(LicenseInfo());
    76          }
                 //如果是-?,-h,-help,构造追加usage信息
    77          else
    78          {
    79              strUsage += "\n" + _("Usage:") + "\n" +
    80                    "  bitcoind [options]                     " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";
    81  
    82              strUsage += "\n" + HelpMessage(HMM_BITCOIND);
    83          }
    84  
    85          fprintf(stdout, "%s", strUsage.c_str());
    86          return true;
    87      }
    88  
             //检测数据目录
    89      try
    90      {
            //解析-datadir参数,通过GetDataDir检测和处理-datadir参数指定的目录
    91          if (!fs::is_directory(GetDataDir(false)))
    92          {
    93              fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
    94              return false;
    95          }
              //通过-conf读取配置文件
    96          try
    97          {
    98              gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
    99          } catch (const std::exception& e) {
   100              fprintf(stderr,"Error reading configuration file: %s\n", e.what());
   101              return false;
   102          }
               //检测-testnet和-regtest设置的当前程序运行的网络
   103          // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
   104          try {
   105              SelectParams(ChainNameFromCommandLine());
   106          } catch (const std::exception& e) {
   107              fprintf(stderr, "Error: %s\n", e.what());
   108              return false;
   109          }
   110  
              //不合法的参数字符检测
   111          // Error out when loose non-argument tokens are encountered on command line
   112          for (int i = 1; i < argc; i++) {
   113              if (!IsSwitchChar(argv[i][0])) {
   114                  fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
   115                  return false;
   116              }
   117          }
   118

                 //设置-server参数为true,而-server参数表示是否接收RPC命令,
                 //这里因为是bitcoind,默认作为核心服务器接收bitcoin-cli以及bitcoin-tx传送的命令    
   119          // -server defaults to true for bitcoind but not for the GUI so do this here
   120          gArgs.SoftSetBoolArg("-server", true);
   121          // Set this early so that parameter interactions go to console
                  //初始化日志记录以及打印方式
   122          InitLogging();
                    //初始化网络参数
   123          InitParameterInteraction();
                   //注册相应的消息以及处理方式
   124          if (!AppInitBasicSetup())
   125          {
   126              // InitError will have been called with detailed error, which ends up on console
   127              return false;
   128          }
                 //设置区块链运行参数
   129          if (!AppInitParameterInteraction())
   130          {
   131              // InitError will have been called with detailed error, which ends up on console
   132              return false;
   133          }
               //Sanity Check是用来检查比特币运行时所需要的所有的库是否都运行正常
   134          if (!AppInitSanityChecks())
   135          {
   136              // InitError will have been called with detailed error, which ends up on console
   137              return false;
   138          }
             //通过-daemon参数设置是否后台运行
   139          if (gArgs.GetBoolArg("-daemon", false))
   140          {
   141  #if HAVE_DECL_DAEMON
   142              fprintf(stdout, "Bitcoin server starting\n");
   143  
   144              // Daemonize
   145              if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
   146                  fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
   147                  return false;
   148              }
   149  #else
   150              fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
   151              return false;
   152  #endif // HAVE_DECL_DAEMON
   153          }
                  //后台运行后锁定数据目录
   154          // Lock data directory after daemonization
   155          if (!AppInitLockDataDirectory())
   156          {
   157              // If locking the data directory failed, exit immediately
   158              return false;
   159          }
                   //运行主应用程序
   160          fRet = AppInitMain();
   161      }
   162      catch (const std::exception& e) {
   163          PrintExceptionContinue(&e, "AppInit()");
   164      } catch (...) {
   165          PrintExceptionContinue(nullptr, "AppInit()");
   166      }
   167
              //如果返回值fRet为false,那么强制结束所有线程;否则就等待所有线程运行结束
   168      if (!fRet)
   169      {
   170          Interrupt();
   171      } else {
   172          WaitForShutdown();
   173      }
            //最后通过ShutDown()完成清理工作
   174      Shutdown();
   175  
   176      return fRet;
   177  }
   178  

大概浏览了以下AppInit函数的实现,发现大部分都是在解析bitcoind的命令行参数,首先解析-?,-h,-help帮助信息和-version版本信息,91行执行GetDataDir函数,检查数据目录是否合法,通过-datadir参数进行设置,该目录下主要保存同步的区块信息,钱包信息,配置信息等等几乎所有的区块链运行信息都保存在这里,这个函数在src/util.cpp中实现,代码如下:

581 static fs::path pathCached;
   582  static fs::path pathCachedNetSpecific;
   583  static CCriticalSection csPathCached;
   584  
   585  const fs::path &GetDataDir(bool fNetSpecific)
   586  {
   587  
   588      LOCK(csPathCached);
   589  
             //判断fNetSpecific是否为true,true使用pathCachedNetSpecific(网络路径),否则使用
             //pathCached(本地路径)
   590      fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
   591  
              //如果path不为空,返回path
   592      // This can be called during exceptions by LogPrintf(), so we cache the
   593      // value so we don't have to do memory allocations after that.
   594      if (!path.empty())
   595          return path;
   596  
              //如果有通过-datadir参数指定目录
   597      if (gArgs.IsArgSet("-datadir")) {
   598          path = fs::system_complete(gArgs.GetArg("-datadir", ""));
                        //检测参数传入的路径是否为目录,不为目录的话,置空path返回
   599          if (!fs::is_directory(path)) {
   600              path = "";
   601              return path;
   602          }
              //如果没有通过-datadir参数指定目录,使用GetDefaultDataDir获取默认目录
   603      } else {
   604          path = GetDefaultDataDir();
   605      }
              //如果事网络路径,修改path为BaseParams().DataDir();
   606      if (fNetSpecific)
   607          path /= BaseParams().DataDir();
   608  
                     //创建wallets目录
   609      if (fs::create_directories(path)) {
   610          // This is the first run, create wallets subdirectory too
   611          fs::create_directories(path / "wallets");
   612      }
   613  
   614      return path;
   615  }

AppInit函数105行: SelectParams(ChainNameFromCommandLine());设置的当前程序运行的网络,有三种:Main,Testnet,Regtest
ChainNameFromCommandLine函数,在src/chainparamsbase.cpp中实现,代码如下

    90  std::string ChainNameFromCommandLine()
    91  {
            //获取命令行-regtest参数,是否为私有网
    92      bool fRegTest = gArgs.GetBoolArg("-regtest", false);
            //获取命令行-testnet参数,是否为测试网
    93      bool fTestNet = gArgs.GetBoolArg("-testnet", false);
    94     //不能同时配置两个参数
    95      if (fTestNet && fRegTest)
    96          throw std::runtime_error("Invalid combination of -regtest and -testnet.");
           //如果为私网,返回私网
    97      if (fRegTest)
    98          return CBaseChainParams::REGTEST;
           //如果为测试网,返回测试网
    99      if (fTestNet)
   100          return CBaseChainParams::TESTNET;
           //不是以上两种的返回主网
   101      return CBaseChainParams::MAIN;
   102  }

ChainNameFromCommandLine
AppInit函数122行:InitLogging函数,初始化日志记录以及打印方式,在src/init.cpp中实现,代码如下

void InitLogging()
{
   //通过-printtoconsole参数设置是否打印日志到终端
    fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
    //通过-logtimestamps参数设置每一条输出信息附带时间戳,默认值为附带
    fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
   //通过-logtimemicros设置时间戳精确到微秒精度,默认不附加
    fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
   //通过-logips设置输出信息中附加ip地址,默认不附加
    fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);

    LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
    std::string version_string = FormatFullVersion();
#ifdef DEBUG
    version_string += " (debug build)";
#else
    version_string += " (release build)";
#endif
    LogPrintf(PACKAGE_NAME " version %s\n", version_string);
}

AppInit函数123行: InitParameterInteraction();初始化网络参数函数后的其他函数,下篇继续

上一篇下一篇

猜你喜欢

热点阅读