Ⅲ:AppInit()函數(shù)解讀
這個(gè)函數(shù)定義在bitcoind.cpp中的63行,這是個(gè)重要的函數(shù),我們來一行行解析這個(gè)函數(shù)。
如下圖所示為這個(gè)函數(shù)的注釋和定義:

由這個(gè)注釋(start)可以知道,這個(gè)函數(shù)標(biāo)志著比特幣程序真正的開始。函數(shù)的返回值類型為bool類型,輸入的參數(shù)為一個(gè)整數(shù)型參數(shù)和一個(gè)元素是字符指針的數(shù)組類型參數(shù)。
(一)bitcoind.cpp中的65-68行:
這是函數(shù)最開始定義的三個(gè)變量:
boost::thread_group threadGroup;
CScheduler scheduler;
bool fRet = false;
①boost::thread_group是一個(gè)管理一組線程的類,它由boost庫提供,可以實(shí)現(xiàn)對多個(gè)線程統(tǒng)一管理。詳細(xì)解釋可以參考下面網(wǎng)站:
那么threadGroup對象作用就是可以統(tǒng)一管理多個(gè)線程。
②CScheduler為比特幣源碼的線程調(diào)度類:它是用于對后臺任務(wù)管理的類,它管理的是在后臺中定期的或者一段時(shí)間運(yùn)行的任務(wù)。它的定義在src/Scheduler.h中的第37行,如圖所示:

a.
void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now());//在時(shí)間t時(shí)刻或者之后調(diào)用f函數(shù);
b.
void scheduleFromNow(Function f, int64_t deltaMilliSeconds);//從現(xiàn)在開始deltaMilliSeconds時(shí)間段內(nèi)執(zhí)行f函數(shù);
c.
void scheduleEvery(Function f, int64_t deltaMilliSeconds);//每隔deltaMilliSeconds時(shí)間就周期性的執(zhí)行函數(shù)f;
d.
void serviceQueue();//如果沒有出錯(cuò)就一直運(yùn)行;
e.
void stop(bool drain=false);//當(dāng)他們完成了當(dāng)前服務(wù)的任何任務(wù)或者沒有剩下的工作要做了就立刻停止運(yùn)行serviceQueue的所有線程;
f.
size_t getQueueInfo(boost::chrono::system_clock::time_point &first, boost::chrono::system_clock::time_point &last) const;//返回等待服務(wù)的任務(wù)數(shù),以及第一個(gè)和最后一個(gè)任務(wù)的時(shí)間;
g.
bool AreThreadsServicingQueue() const;//如果在serviceQueue()中運(yùn)行了線程,則返回true;
它的實(shí)現(xiàn)是在src/Scheduler.cpp中的14行,并且對三個(gè)變量進(jìn)行了初始化,如圖所示:

fRet變量,并且賦值為false。
(二)bitcoind.cpp中的第74行:
gArgs.ParseParameters(argc, argv);
這里調(diào)用了一個(gè)ParseParameters()函數(shù)
這是AppInit()函數(shù)調(diào)用的第一個(gè)函數(shù)。這個(gè)函數(shù)主要作用是解析命令行參數(shù)。
它的定義代碼放在util.cpp中的第386行。

(1)
LOCK(cs_args);互斥鎖函數(shù)把這個(gè)函數(shù)中的LOCK和cs_args分別解析:
①cs_args變量參數(shù)的定義在util.h的第198行,對它的定義為:
CCriticalSection cs_args;所以有必要了解它的類
CCriticalSection。CCriticalSection類定義在sync.h的第91行,如圖所示:

//封裝的boost互斥鎖:支持遞歸鎖定,但不支持等待;
//TODO:我們應(yīng)該避免在默認(rèn)情況下使用遞歸鎖。
由定義代碼可以知道這個(gè)類繼承AnnotatedMixin類,而且有boost::recursive_mutex的類模板??梢灾繡CriticalSection類定義的對象都是boost::recursive_mutex的類,而這是個(gè)互斥的類。所以cs_args為一個(gè)互斥類對象。
②LOCK()函數(shù)的定義在sync.h的第175行,如圖所示:

(2)清空映射存儲
mapArgs.clear();
mapMultiArgs.clear();
其中mapArgs和mapMultiArgs的定義在util.h中的199和200行,如圖所示:

mapArgs是單個(gè)輸入?yún)?shù)及其對應(yīng)值的映射存儲;mapMultiArgs是單個(gè)輸入?yún)?shù)及其多個(gè)對應(yīng)值的存儲。程序使用這兩個(gè)變量,并且使用clear的方法進(jìn)行了清空映射存儲的操作。
(3)for循環(huán)解析輸入?yún)?shù)

對所有輸入?yún)?shù)進(jìn)行逐個(gè)的解析,來獲取其參數(shù)及其值。對于輸入的參數(shù)及其值放入到mapArgs中存儲,對于參數(shù)對應(yīng)的所有值通過vector的變量放入到mapMultiArgs變量中存儲。
到此,AppInit()函數(shù)調(diào)用的第一個(gè)函數(shù)ParseParameters()就介紹完了。
總結(jié)ParseParameters()流程:
①在程序一開始就給全局變量加上一個(gè)互斥鎖;
②清空內(nèi)存中的映射存儲;
③開始解析輸入的參數(shù),把對應(yīng)參數(shù)映射到mapArgs和mapMultiArgs變量中。
(三)bitcoind.cpp代碼中的第77-95行
這個(gè)部分在ParseParameters()函數(shù)之后,它主要是幫助和版本信息。具體代碼如下圖所示:

3-1.版本信息
如下圖所示,當(dāng)在命令行輸入bitcoind -version時(shí),會出來如下結(jié)果:

(1)代碼內(nèi)容中的if判斷語句是判斷bitcoind的后臺進(jìn)程參數(shù)中是否含有“-?”、“-h”、“-help”或者“-version”命令,如果包含則執(zhí)行If語句中的代碼,執(zhí)行完成后返回true,程序運(yùn)行結(jié)束。否則不執(zhí)行其包含的內(nèi)容,跳出if語句,繼續(xù)執(zhí)行其后語句。
此處判斷是否包含這幾個(gè)參數(shù)的方法為IsArgSet,這個(gè)函數(shù)的聲明在util.h中的第212行:

ArgsManager類中的一個(gè)方法,注釋上對它的描述為:
如果給定的參數(shù)已經(jīng)手動設(shè)置,返回true。
這個(gè)函數(shù)的定義在util.cpp的第430行,如圖所示:

LOCK(cs_args);是互斥鎖函數(shù),前面已經(jīng)詳細(xì)解釋過這個(gè)函數(shù);②
return mapArgs.count(strArg);其中mapArgs該變量中存儲了用戶輸入的所有參數(shù)及其值。所以此處通過mapArgs查找是否包含“-?”、“-h”、“-help”或者“-version”,如果查找到了則返回true,反之為false。還需要說明的是程序通過map類型的變量實(shí)現(xiàn)對參數(shù)的存儲,由于其采用的是鍵值對存儲方式,對于參數(shù)信息的快速查找相比于使用數(shù)組或隊(duì)列方式優(yōu)勢很明顯。(2)當(dāng)符合if判斷條件后,if內(nèi)容的第一行代碼為:
std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";
它是通過strUsage字符串變量存儲包含比特幣后臺進(jìn)程名稱與版本信息內(nèi)容。
①strprintf為字符串格式化命令,主要功能是把格式化的數(shù)據(jù)寫入某個(gè)字符串中。此處是將PACKAGE_NAME寫入_("%s Daemon")中,生成PACKAGE_NAME Daemon形式的字符串內(nèi)容。PACKAGE_NAME的定義位于config/bitcoin-config.h中,其定義在代碼的353行:

《在ubuntu系統(tǒng)下編譯bitcoin源碼過程》。
②
FormatFullVersion()函數(shù)的功能是輸出比特幣核心的完整版本信息。該函數(shù)的實(shí)現(xiàn)位于clientversion.cpp中的第81行,如圖所示:
CLIENT_BUILD函數(shù)的定義在clientversion.cpp的71行,它的定義為:const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);a.其中
CLIENT_VERSION_SUFFIX的定義在clientversion.cpp的第21行:
BUILD_DESC的定義在clientversion.cpp的第61行:
#ifndef BUILD_DESC,我們可以判斷BUILD_DESC將在BUILD_DESC_FROM_UNKNOWN中執(zhí)行,該函數(shù)采用的是預(yù)編譯實(shí)現(xiàn)方式,這樣的好處是對于小型、通用性函數(shù)采用預(yù)編譯方式可以提高程序的執(zhí)行效率。BUILD_DESC_FROM_UNKNOWN的宏定義在當(dāng)前文件的第58行,代碼內(nèi)容如下:
#define BUILD_DESC_FROM_UNKNOWN(maj, min, rev, build) \
"v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-unk"
4次調(diào)用了DO_STRINGIZE()函數(shù),而這個(gè)函數(shù)在clientversion.h的第21-22行實(shí)現(xiàn):

在執(zhí)行宏替換之后,將參數(shù)X轉(zhuǎn)換為字符串。
不要把它們合并成一個(gè)宏!
那么自然maj min rev build就是 宏定義變量,這4個(gè)宏定義變量分別是在clientversion.cpp的第67行中的調(diào)用位置傳入的,分別是:
CLIENT_VERSION_MAJOR
CLIENT_VERSION_MINOR
CLIENT_VERSION_REVISION
CLIENT_VERSION_BUILD
而上面4個(gè)宏定義變量是位于/config/bitcoin-config.h中的第12-24行:
像這樣就會在命令行中輸出版本號:
v0.15.1.0
③版本的許可信息
再回到bitcoind.cpp的77-95行的if語句部分,如果輸入?yún)?shù)中包含"-version",則會執(zhí)行第81行到84行的語句,其中代碼內(nèi)容為:
if (gArgs.IsArgSet("-version"))
{
strUsage += FormatParagraph(LicenseInfo());
}
strUsage變量將FormatParagraph(LicenseInfo());的內(nèi)容拼接,組成完整的輸出信息,而這個(gè)環(huán)節(jié)中主要看FormatParagraph()函數(shù),這個(gè)函數(shù)的聲明在utilstrencodings.h中的第128行:

將一段文本格式化為固定寬度,為其添加空格
縮進(jìn)到任何添加的行。
它的實(shí)現(xiàn)在utilstrencodings.cpp中的第543行:

那么,可以知道這個(gè)函數(shù)在這里的作用就是對比特幣的版本信息進(jìn)行格式化處理。
版本的許可信息內(nèi)容在
LicenseInfo()函數(shù)中,這個(gè)函數(shù)在init.cpp中的第528行實(shí)現(xiàn):
CopyrightHolders()函數(shù),這個(gè)函數(shù)在util.h的320行定義:
這個(gè)函數(shù)在util.cpp中的第886行實(shí)現(xiàn):

所以在命令行輸入
bitcoind -version命令后會輸出如下所示內(nèi)容:
3-2.幫助信息
幫助信息的輸出位于在版本信息的后面,當(dāng)if語句發(fā)現(xiàn)輸入的參數(shù)有-? -h -help的時(shí)候,將會輸出幫助信息。其中調(diào)用了HelpMessage()函數(shù),該函數(shù)的聲明在init.h中的66行:

幫助UI和守護(hù)進(jìn)程共享選項(xiàng)(用于-help)
它的實(shí)現(xiàn)在init.cpp中的第332行,部分代碼如圖:

HMM_BITCOIND為比特幣的后臺進(jìn)程幫助信息,它在init.h的61行定義,屬于HelpMessageMode的其中一個(gè)成員。總之幫助信息的目的就是在忘記或者不會的命令時(shí),可以通過在命令行中輸入
bitcoind -?或bitcoind -h或bitcoind -help得到幫助信息,幫助信息中主要是為后臺進(jìn)程涉及參數(shù)的使用方法說明。
3-3.輸出版本和幫助信息
回到bitcoind.cpp的代碼,繼續(xù)看代碼93行:
fprintf(stdout, "%s", strUsage.c_str());
這個(gè)命令是對版本和幫助信息進(jìn)行輸出,其中stdout為控制臺對象,linux中對應(yīng)的是終端。
到此就完成了版本和幫助信息的處理流程。