三、初始化和啟動模塊(2)

Ⅲ: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)站:

http://blog.csdn.net/hanshuobest/article/details/53984660

那么threadGroup對象作用就是可以統(tǒng)一管理多個(gè)線程。
②CScheduler為比特幣源碼的線程調(diào)度類:它是用于對后臺任務(wù)管理的類,它管理的是在后臺中定期的或者一段時(shí)間運(yùn)行的任務(wù)。它的定義在src/Scheduler.h中的第37行,如圖所示:

它包含下面的7個(gè)方法:
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)行了初始化,如圖所示:
③最后為一個(gè)bool類型的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行。

這個(gè)函數(shù)又是一個(gè)較復(fù)雜的函數(shù),讓我們分析它的實(shí)現(xiàn)邏輯。
(1)LOCK(cs_args);互斥鎖函數(shù)
把這個(gè)函數(shù)中的LOCK和cs_args分別解析:
①cs_args變量參數(shù)的定義在util.h的第198行,對它的定義為:
CCriticalSection cs_args;
所以有必要了解它的類CCriticalSection。
CCriticalSection類定義在sync.h的第91行,如圖所示:
由對這個(gè)類的解釋文字可以知道:

//封裝的boost互斥鎖:支持遞歸鎖定,但不支持等待;
//TODO:我們應(yīng)該避免在默認(rèn)情況下使用遞歸鎖。

由定義代碼可以知道這個(gè)類繼承AnnotatedMixin類,而且有boost::recursive_mutex的類模板??梢灾繡CriticalSection類定義的對象都是boost::recursive_mutex的類,而這是個(gè)互斥的類。所以cs_args為一個(gè)互斥類對象。
②LOCK()函數(shù)的定義在sync.h的第175行,如圖所示:

由代碼可以知道,這是個(gè)宏定義,它和CCriticalSection類的對象cs_args結(jié)合,實(shí)現(xiàn)的是對包含的代碼在各線程中進(jìn)行互斥加鎖處理,防止后續(xù)代碼中涉及的全局變量被不同的線程搶奪。
(2)清空映射存儲

    mapArgs.clear();
    mapMultiArgs.clear();

其中mapArgsmapMultiArgs的定義在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ù)
這個(gè)for循環(huán)主要的方法邏輯是:

對所有輸入?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ù)之后,它主要是幫助和版本信息。具體代碼如下圖所示:

正如這段代碼的注釋所示:在處理數(shù)據(jù)目錄操作前,先完成版本與幫助命令的處理。所以,通過這段代碼,比特幣后臺進(jìn)程可以根據(jù)用戶輸入的相應(yīng)參數(shù)來輸出對應(yīng)的版本與幫助信息。

3-1.版本信息

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

會很容易看到客戶端的版本為v0.15.1.0。
(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行:

注意:bitcoin-config.h需要編譯才能出現(xiàn),詳細(xì)編譯過程可以參考:
《在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行:
b.而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行:

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

這個(gè)函數(shù)在util.cpp中的第886行實(shí)現(xiàn):
它的作用主要是補(bǔ)全許可信息。
所以在命令行輸入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行,部分代碼如圖:

而它的參數(shù)HMM_BITCOIND為比特幣的后臺進(jìn)程幫助信息,它在init.h的61行定義,屬于HelpMessageMode的其中一個(gè)成員。
總之幫助信息的目的就是在忘記或者不會的命令時(shí),可以通過在命令行中輸入bitcoind -?bitcoind -hbitcoind -help得到幫助信息,幫助信息中主要是為后臺進(jìn)程涉及參數(shù)的使用方法說明。

3-3.輸出版本和幫助信息

回到bitcoind.cpp的代碼,繼續(xù)看代碼93行:
fprintf(stdout, "%s", strUsage.c_str());
這個(gè)命令是對版本和幫助信息進(jìn)行輸出,其中stdout為控制臺對象,linux中對應(yīng)的是終端。
到此就完成了版本和幫助信息的處理流程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容