比特幣源碼分析(10) - 可執行程序 - Bitcoind

0x01 AppInit

接下來分析main函數中的最後一個函數AppInit,首先看前面一部分代碼,

   // src/bitcoind.cpp line 65-95
boost::thread_group threadGroup;
CScheduler scheduler;

bool fRet = false;
// Parameters
//
// If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
gArgs.ParseParameters(argc, argv);

// 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

程序首先定義了一個線程組threadGroup,線程組的功能就是分組管理線程,功能和http://blog.csdn.net/pure_lady/article/details/77675915#t3 中介紹的Thread功能幾乎一樣。接下來定義了一個scheduler,這個類的聲明在src/scheduler.h中,根據代碼中的介紹,

//
// Simple class for background tasks that should be run
// periodically or once "after a while"
//
// Usage:
//
// CScheduler* s = new CScheduler();
// s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { }
// s->scheduleFromNow(std::bind(Class::func, this, argument), 3);
// boost::thread* t = new boost::thread(boost::bind(CScheduler::serviceQueue, s));
//
// ... then at program shutdown, clean up the thread running serviceQueue:
// t->interrupt();
// t->join();
// delete t;
// delete s; // Must be done after thread is interrupted/joined.
//
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

主要是用來管理後臺任務,主要的兩個函數是scheduleFromNowscheduleEvery,分別表示從現在開始是過一段時間執行某函數一次,和從現在開始每隔幾秒執行某函數一次。也可創建一個新的線程去執行任務,而不影響主線程的執行。

定義完這兩個變量之後,下面一行是gArgs.ParseParameters(argc, argv);,作用是解析bitcoind命令行傳入的參數,其中gArgs的定義在src/util.h中,類型是ArgsManagerParseParameters()是該類中的一個主要成員函數,功能是將傳入的參數進行解析並存入到兩個map當中。

解析完參數之後,下面就開始進行一系列參數設置,這部分分析的最後一部分代碼,也就是上面的那個if語句,功能是判斷參數中是否有顯示help或者version信息,如果有,就直接顯示對應的信息,然後退出程序,忽略其他所有的參數。

再來看接下來的一段代碼,

   // src/bitcoind.cpp line 99-118
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;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

這段代碼首先檢查數據目錄是否合法,數據目錄在Ubuntu下默認的路徑是~/.bitcoin/,當然也能通過-datadir參數進行設置,該目錄下主要保存同步的區塊信息,錢包信息,配置信息等等幾乎所有的區塊鏈運行信息都保存在這裏。然後開始讀取配置文件,配置文件的默認名稱是~/.bitcoin/bitcoinf.conf也是在數據目錄下,不過默認是沒有這個文件的,進入ReadConfigFile可以看到文件不存在也是可以的。

// src/util.cpp line 599-623
void 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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

接下來是這句SelectParams(ChainNameFromCommandLine());,首先通過ChainNameFromCommandLine()獲取命令行中設置的當前程序運行的網絡,包括以下三種:

  • Main:表示主網,也就是當前比特幣所有用戶交易的網絡,bitcoind中的默認值。
  • Testnet:測試網,測試網中專門有一條測試鏈,所有的交易都只是用於測試,並且測試網中的幣可以方便的獲取,主要目的就是模擬真實交易環境測試新的功能。
  • Regtest:迴歸測試,又稱爲私有網,用於個人開發測試,挖礦難度較低,並且參數都可以自行設置。

所以一般在本地環境開始時使用Regtest,本地開發完成後,進入Testnet進行大規模實際環境測試,運行正常後再進入主網,這也是目前衆多區塊鏈(ICO)項目的主流開發路線。

回到代碼中,獲取到當前的網絡之後通過SelectParams()根據不同的網絡創建不同的共識參數,實現的方式是使用三個繼承類CMainParamsCTestNetParamsCRegTestParams繼承基類CChainParams,然後根據選擇的不同的網絡返回不同的繼承類,返回值由一個CChainParams類型的智能指針(unique_ptr)globalChainParams來接收,最後使用時就用這個智能指針來訪問相應的共識參數。所謂智能指針就是當指針離開作用域時自動的刪除(使用delete)所指向的對象。

設置好網絡後,下面一部分代碼是用來判斷命令行中是否存在錯誤的參數,判斷方法是看每一個參數的第一個字母是否爲-或者在windows環境中- or /,如果不是就報錯然後退出程序。

// src/bitcoind.cpp line 119-125        
// Error out when loose non-argument tokens are encountered on command line
        for (int i = 1; i < argc; i++) {
            if (!IsSwitchChar(argv[i][0])) {
                fprintf(stderr, "Error: Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]);
                exit(EXIT_FAILURE);
            }

} 轉自:http://blog.csdn.net/pure_lady/article/details/77895680

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章