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
主要是用來管理後臺任務,主要的兩個函數是scheduleFromNow
和scheduleEvery
,分別表示從現在開始是過一段時間執行某函數一次,和從現在開始每隔幾秒執行某函數一次。也可創建一個新的線程去執行任務,而不影響主線程的執行。
定義完這兩個變量之後,下面一行是gArgs.ParseParameters(argc, argv);
,作用是解析bitcoind命令行傳入的參數,其中gArgs
的定義在src/util.h
中,類型是ArgsManager
,ParseParameters()
是該類中的一個主要成員函數,功能是將傳入的參數進行解析並存入到兩個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()
根據不同的網絡創建不同的共識參數,實現的方式是使用三個繼承類CMainParams
,CTestNetParams
,CRegTestParams
繼承基類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