(十一)繼續(xù)看bitcoind.cpp中的142-146行
if (!AppInitSanityChecks())
{
//InitError將被調(diào)用搓侄,并有詳細(xì)的錯(cuò)誤话速,最終將在控制臺(tái)結(jié)束
exit(EXIT_FAILURE);
}
這個(gè)部分最重要的是AppInitSanityChecks()
函數(shù)泊交。對(duì)這個(gè)函數(shù)名的定義中我找出了幾個(gè)關(guān)鍵詞:“appinit”,“sanity”和“checks”筹麸,我猜測(cè)這個(gè)函數(shù)是關(guān)于初始化健全性檢查的。那么就來(lái)分析這個(gè)函數(shù)留晚。
AppInitSanityChecks()
函數(shù)的聲明在init.h的第45行:
/**
*初始化健全性檢查:ecc初始化告嘲、健全性檢查橄唬、目錄鎖檢查。
*注意:這可以在daemonization之前完成隆判。如果此函數(shù)失敗侨嘀,請(qǐng)不要調(diào)用Shutdown()捂襟。
*前提:參數(shù)解析和配置文件應(yīng)該已經(jīng)被讀過(guò)葬荷,AppInitParameterInteraction()函數(shù)應(yīng)該已經(jīng)被調(diào)用過(guò)宠漩。
*/
bool AppInitSanityChecks();
該函數(shù)的實(shí)現(xiàn)在init.cpp中1190-1209,其中的有效代碼只有8行:
bool AppInitSanityChecks()
{
// *************第四步:健全性檢查
// 初始化橢圓曲線密碼(ECC)
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
// 健全性檢查
if (!InitSanityCheck())
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
// 如果可能照筑,探測(cè)數(shù)據(jù)目錄鎖以提供早期錯(cuò)誤消息凝危。
// 我們不能將數(shù)據(jù)目錄鎖定在這里蛾默,因?yàn)閐eamon()尚未調(diào)用forking指令捉貌,
// 并且fork指令將會(huì)對(duì)它產(chǎn)生一個(gè)奇怪的行為。
return LockDataDirectory(true);
}
由此可以看出牧挣,這個(gè)函數(shù)完成的是初始化的第四步瀑构,在該函數(shù)中主要完成了三個(gè)內(nèi)容:
- 初始化橢圓曲線密碼(ECC)寺晌;
- 健全性檢查;
- 目錄鎖檢查耘婚。
下面對(duì)這三個(gè)內(nèi)容分別說(shuō)明:
1. 初始化橢圓曲線密碼(ECC)
對(duì)于橢圓曲線密碼的概念想必對(duì)比特幣的加密知識(shí)了解的都很清楚:比特幣使用橢圓曲線算法生成公鑰和私鑰沐祷,選擇的是secp256k1曲線攒岛。所以在此也再?gòu)?qiáng)調(diào)一點(diǎn):比特幣中的橢圓曲線算法只在生成公鑰和私鑰的時(shí)候才用到阵子,其他大部分的加密都是用SHA256算法挠进。橢圓曲線算法牽涉到一些密碼學(xué)知識(shí)和許多數(shù)學(xué)原理,感興趣的可以參考:
那么比特幣中應(yīng)用的是現(xiàn)有的SHA256算法和橢圓曲線加密算法暖璧,那么我們對(duì)這兩種算法在源碼中的定義和實(shí)現(xiàn)邏輯不詳細(xì)分析澎办,感興趣的可以在網(wǎng)絡(luò)上自行更深入的了解金砍,我們只是對(duì)應(yīng)用它們之后的效果進(jìn)行分析恕稠。
回到AppInitSanityChecks()
函數(shù)的第一部分:
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
這個(gè)部分調(diào)用了5個(gè)函數(shù):
(1)SHA256AutoDetect()
:該函數(shù)是SHA256算法相關(guān)的函數(shù)鹅巍,函數(shù)聲明在sha256.h中32行料祠,函數(shù)實(shí)現(xiàn)在sha256.cpp的179-192行髓绽。由聲明可以知道該函數(shù)的功能是:
//自動(dòng)選擇最佳有效的SHA256實(shí)現(xiàn)顺呕。
//返回實(shí)現(xiàn)的名稱摆碉。
即這個(gè)函數(shù)選擇了最合適的實(shí)現(xiàn)SHA256的方式巷帝,并在日志中打印該消息楞泼。
(2)RandomInit()
:該函數(shù)聲明在random.h的第144行笤闯,實(shí)現(xiàn)在random.cpp中的464-467行颗味。由聲明知道該函數(shù)是:
初始化RNG
那么RNG是什么呢浦马?通過(guò)查找資料知道RNG(The Random Number Generator)是隨機(jī)數(shù)發(fā)生器,它是被嵌入到計(jì)算機(jī)硬件中的隨機(jī)數(shù)生成器谨娜,生成的是偽隨機(jī)數(shù)趴梢。對(duì)于普通用戶使用的筆記本和臺(tái)式機(jī)來(lái)說(shuō)是本身就含有的一部分币他,和有沒有比特幣客戶端無(wú)關(guān)蝴悉,這里可以理解成比特幣客戶端需要用到這個(gè)隨機(jī)數(shù)生成器辫封,給他在客戶端初始化了廉丽,方便軟件的使用正压。我們讀過(guò)《精通比特幣》這本書都知道:在生成私鑰的時(shí)候焦履,我們是需要有一個(gè)256位的隨機(jī)數(shù)的雏逾,而比特幣軟件使用操作系統(tǒng)底層的隨機(jī)數(shù)生成器來(lái)產(chǎn)生256位的熵(隨機(jī)性)栖博。那么就明白這個(gè)函數(shù)對(duì)私鑰生成的重要作用了仇让。
(3)ECC_Start()
:該函數(shù)的聲明在key.h的183行丧叽,實(shí)現(xiàn)在key.cpp中291-306行。由它的聲明知道該函數(shù)的功能:
初始化橢圓曲線支持假瞬。如果在調(diào)用了第一次之后不首先調(diào)用ECC_Stop笨触,就不能第二次調(diào)用這個(gè)函數(shù)雹舀。
由該函數(shù)的所在位置(key.cpp)可以知道該函數(shù)和私鑰與公鑰的操作有關(guān)说榆。而且它的聲明文件說(shuō)的很清楚签财,是初始化橢圓曲線支持唱蒸,該函數(shù)定義中也多次出現(xiàn)了secp256k1,說(shuō)明這個(gè)函數(shù)是開啟橢圓曲線算法功能庆捺。
(4)ECCVerifyHandle()
:該函數(shù)是ECCVerifyHandle類的一個(gè)方法滔以,聲明在pubkey.h中的241-248行你画,實(shí)現(xiàn)在pubkey.cpp中282-290行。由ECCVerifyHandle類的聲明:
此模塊的用戶必須持有ECCVerifyHandle拟逮。不過(guò)敦迄,它們的構(gòu)造函數(shù)和析構(gòu)函數(shù)不允許并行運(yùn)行颅崩。
說(shuō)明該函數(shù)是實(shí)例化ECC的核實(shí)處理蕊苗,這個(gè)函數(shù)在后面用來(lái)驗(yàn)證基于橢圓曲線創(chuàng)建的密鑰朽砰。
(5)globalVerifyHandle.reset()
:這個(gè)主要是reset()
函數(shù)瞧柔,這個(gè)是重置函數(shù)造锅,主要清除上面的核實(shí)處理內(nèi)存廉邑。
總之蛛蒙,這個(gè)部分是對(duì)橢圓曲線密碼(ECC)的初始化牵祟。選擇SHA256準(zhǔn)備诺苹、隨機(jī)數(shù)生成器準(zhǔn)備雹拄、橢圓曲線功能開啟办桨、驗(yàn)證橢圓曲線開啟和重置內(nèi)存呢撞,都是為ECC的正常運(yùn)行提供的條件殊霞。
2. 健全性檢查
這個(gè)部分只有兩行代碼:
if (!InitSanityCheck())
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
主要是判斷InitSanityCheck()
函數(shù)绷蹲。
該函數(shù)實(shí)現(xiàn)位于init.cpp的707-723行:
/** 健全性檢查
* 確保比特幣在一個(gè)可用的環(huán)境中運(yùn)行顾孽,并提供所有必要的庫(kù)支持若厚。
*/
bool InitSanityCheck(void)
{
if(!ECC_InitSanityCheck()) {
InitError("Elliptic curve cryptography sanity check failure. Aborting.");
return false;
}
if (!glibc_sanity_test() || !glibcxx_sanity_test())
return false;
if (!Random_SanityCheck()) {
InitError("OS cryptographic RNG sanity check failure. Aborting.");
return false;
}
return true;
}
該部分又有三個(gè)判斷函數(shù)测秸,ECC_InitSanityCheck()
為橢圓曲線加密結(jié)果的完整性驗(yàn)證霎冯,glibc_sanity_test()
與glibcxx_sanity_test()
驗(yàn)證當(dāng)前運(yùn)行環(huán)境是否支持C/C++運(yùn)行環(huán)境沈撞,Random_SanityCheck()
驗(yàn)證系統(tǒng)的隨機(jī)數(shù)生成器是否可用缠俺。
(1)ECC_InitSanityCheck()
:橢圓曲線加密結(jié)果驗(yàn)證晋修。
這個(gè)函數(shù)聲明在key.h中189行:
/** 驗(yàn)證橢圓曲線算法的計(jì)算功能是否實(shí)時(shí)有效 */
bool ECC_InitSanityCheck(void);
它的實(shí)現(xiàn)在key.cpp中的284-289行:
bool ECC_InitSanityCheck() {
CKey key;
key.MakeNewKey(true);
CPubKey pubkey = key.GetPubKey();
return key.VerifyPubKey(pubkey);
}
現(xiàn)在我們對(duì)這段代碼進(jìn)行分析:
①CKey
類
這個(gè)類在key.h中第35行定義墓卦,對(duì)這個(gè)類解釋為:一個(gè)封裝的私鑰。則可以知道key
為私鑰對(duì)象睁本,它是一個(gè)無(wú)符號(hào)字符串類型的數(shù)組呢堰,在CKey的構(gòu)造函數(shù)中對(duì)該參數(shù)進(jìn)行了初始化枉疼,定義其字節(jié)大小為32字節(jié)骂维,并且是必須為32字節(jié)。
這個(gè)類中還有兩個(gè)變量:fValid
和fCompressed
fValid
:參數(shù)用于表示私鑰是否有效褪测,該參數(shù)是在私鑰值發(fā)生變化時(shí)進(jìn)行相應(yīng)修改侮措,即私鑰值有效時(shí)分扎,其為true笆包,反之則為false。
fCompressed
:參數(shù)代表的是公鑰是否為壓縮公鑰汛兜,true為壓縮公鑰粥谬,false為非壓縮公鑰漏策。
②MakeNewKey()
函數(shù):
該函數(shù)在key.h中98行聲明:
//使用加密PRNG(偽隨機(jī)數(shù))來(lái)生成私鑰
void MakeNewKey(bool fCompressed);
該函數(shù)的實(shí)現(xiàn)在key.cpp的126-132行:
void CKey::MakeNewKey(bool fCompressedIn) {
do {
GetStrongRandBytes(keydata.data(), keydata.size());
} while (!Check(keydata.data()));
fValid = true;
fCompressed = fCompressedIn;
}
可以看出掺喻,在MakeNewKey()
函數(shù)中是通過(guò)GetStrongRandBytes()
函數(shù)循環(huán)獲取私鑰感耙,直到獲取的私鑰滿足Check()
函數(shù)驗(yàn)證條件時(shí)才停止即硼。
其實(shí)MakeNewKey()
函數(shù)的作用可以這樣理解:我們?cè)谇懊嬷懒怂借€是機(jī)器內(nèi)部的隨機(jī)數(shù)生成器生成的只酥,但是也不是任意生成的隨機(jī)數(shù)就能滿足成為私鑰的條件,還是要規(guī)定能成為私鑰的條件的性锭,MakeNewKey()
函數(shù)就是找到符合要求的私鑰的草冈。其中GetStrongRandBytes()
函數(shù)是生成一個(gè)未驗(yàn)證的私鑰的怎棱,而這個(gè)過(guò)程也不簡(jiǎn)單:
a. 首先會(huì)通過(guò)Open SSL的RNG獲取隨機(jī)數(shù)拳恋;
b. 然后通過(guò)操作系統(tǒng)的RNG獲取隨機(jī)數(shù)谬运;
c. 再然后獲得私鑰的哈希值梆暖;
d. 最后清除隨機(jī)數(shù)的內(nèi)存轰驳。
Check()
函數(shù)就是檢查該隨機(jī)數(shù)是否滿足要求的级解。其實(shí)在Check()
函數(shù)的有個(gè)函數(shù)secp256k1_ec_seckey_verify()
就是通過(guò)調(diào)用libsecp256k1庫(kù)實(shí)現(xiàn)隨機(jī)數(shù)的驗(yàn)證的:如果返回值如果為1勤哗,密鑰有效芒划;如果為0則無(wú)效豁延。傳入的兩個(gè)參數(shù)ctx與seckey均不能為NULL,都需要有值腊状。
獲得私鑰并檢查符合作為私鑰的條件后诱咏,將私鑰有效性標(biāo)志fValid設(shè)置為true,同時(shí)將傳入的fCompressedIn值賦值給fCompressed缴挖,用于標(biāo)識(shí)是否使用壓縮公鑰袋狞。
③CPubKey
類:
和CKey
類相似,這個(gè)就是公鑰類映屋,pubkey
就是公鑰對(duì)象。chHeader值為2或3棚点,為壓縮公鑰早处,長(zhǎng)度為33;chHeader值為4或6或7瘫析,為非壓縮公鑰砌梆,長(zhǎng)度為65;都不是時(shí)其長(zhǎng)度為空贬循,也可說(shuō)明該公鑰值無(wú)效咸包。
④key.GetPubKey()
函數(shù):
那么由私鑰怎么怎么到公鑰呢?其實(shí)就是用這個(gè)函數(shù)來(lái)實(shí)現(xiàn)杖虾。這個(gè)函數(shù)在key.cpp的147-158行烂瘫,其實(shí)包括很多加密相關(guān)函數(shù)的調(diào)用,主要調(diào)用了secp256k1_ec_pubkey_create()
函數(shù)創(chuàng)建公鑰值奇适,然后調(diào)用secp256k1_ec_pubkey_serialize()
函數(shù)實(shí)現(xiàn)壓縮或非壓縮公鑰序列值的計(jì)算坟比。
注意
我們都知道橢圓曲線這種非對(duì)稱加密方式,使比特幣中的私鑰得到公鑰是很簡(jiǎn)單的嚷往,但是要想從公鑰反推出私鑰基本是不可能的葛账,在這里就是用這兩個(gè)橢圓曲線算法函數(shù)來(lái)實(shí)現(xiàn)這個(gè)特點(diǎn)。
⑤VerifyPubKey()
函數(shù):
這個(gè)是驗(yàn)證公鑰的函數(shù)间影,這個(gè)函數(shù)在key.h的134行聲明注竿,在key.cpp中的175-187行實(shí)現(xiàn)茄茁。由它的聲明中的注釋可以知道魂贬,這個(gè)函數(shù)是用來(lái)徹底檢查私鑰和公鑰的匹配,這個(gè)過(guò)程是用另一個(gè)完全不同的機(jī)制來(lái)完成的裙顽。我們知道橢圓曲線加密方式的不可逆性付燥,我們得到公鑰的機(jī)制就用了這個(gè)加密算法,那么驗(yàn)證時(shí)就不能再根據(jù)這個(gè)公鑰反推出私鑰和私鑰比較來(lái)驗(yàn)證愈犹,那么在比特幣中是用什么機(jī)制來(lái)驗(yàn)證的呢键科?其實(shí)可以把它的驗(yàn)證機(jī)制看作是模擬交易的簽名檢驗(yàn)機(jī)制:
a. 通過(guò)OpenSSL的
GetRandBytes()
函數(shù)實(shí)現(xiàn)隨機(jī)數(shù)的生成闻丑;
b. 根據(jù)"Bitcoin key verification\n"字符串與剛生成的隨機(jī)數(shù)共同作用計(jì)算隨機(jī)哈希值;
c. 在Sign函數(shù)通過(guò)該隨機(jī)哈希值基于ECDSA算法實(shí)現(xiàn)簽名值的計(jì)算勋颖;
d. 利用該簽名信息驗(yàn)證獲取的公鑰的有效性嗦嗡,驗(yàn)證函數(shù)位于CPubKey的Verify()
函數(shù)中。
(2)glibc_sanity_test()
與glibcxx_sanity_test()
:C與C++運(yùn)行環(huán)境驗(yàn)證饭玲。
這兩個(gè)函數(shù)主要是為了驗(yàn)證運(yùn)行環(huán)境中的C/C++運(yùn)行庫(kù)的有效性侥祭,即比特幣核心軟件能否在當(dāng)前環(huán)境中正常運(yùn)行,其所需的運(yùn)行庫(kù)是否能夠支撐比特幣核心的正常運(yùn)行茄厘。
(3)Random_SanityCheck()
函數(shù):驗(yàn)證系統(tǒng)的隨機(jī)數(shù)生成器矮冬。
該函數(shù)的聲明在random.h的第141行:
/**檢查操作系統(tǒng)的隨機(jī)性是否可用,
* 并返回所請(qǐng)求的字節(jié)數(shù)次哈。
*/
bool Random_SanityCheck();
該函數(shù)的實(shí)現(xiàn)在random.cpp的411-453行胎署,由實(shí)現(xiàn)函數(shù)代碼中的注釋我們可以知道:
這并不能度量隨機(jī)性的質(zhì)量,但它確實(shí)測(cè)試了OSRandom()在給定最大嘗試次數(shù)的情況下覆蓋了輸出的所有32個(gè)字節(jié)窑滞。
琼牧??哀卫?障陶??聊训?抱究??带斑?鼓寺??勋磕?妈候??挂滓?苦银??赶站?幔虏??贝椿?想括??烙博?
對(duì)于這部分的源碼我有了點(diǎn)困惑:
首先我知道了RNG是隨機(jī)數(shù)生成器瑟蜈,猜測(cè)這個(gè)函數(shù)是檢測(cè)生成的隨機(jī)數(shù)是否可用的烟逊。那么如果是這樣的話為什么不把它放在生成私鑰之前,即ECC_InitSanityCheck()
函數(shù)的前面铺根?像源碼中那樣把該驗(yàn)證放在公鑰都已經(jīng)生成了之后宪躯,那么如果檢測(cè)到的結(jié)果不符合條件,那么按理說(shuō)私鑰和公鑰都有問題位迂,那樣私鑰和公鑰生成和檢測(cè)的工作算是白做了眷唉,何不把它放在私鑰和公鑰生成之前呢?
然后我就懷疑我對(duì)這個(gè)函數(shù)的理解不對(duì)囤官,但注釋中對(duì)它的解釋不夠全面冬阳,代碼有些又看不太明白,就先把這個(gè)問題留著党饮,等詢問大牛和在網(wǎng)上查找相關(guān)問題后再補(bǔ)上肝陪!
?刑顺?氯窍??蹲堂?狼讨??柒竞?朽基?霎俩????具垫?起宽?擒滑?库车??垫蛆?虑乖?仅叫?帜篇??诫咱?
3. 目錄鎖檢查
這個(gè)是AppInitSanityChecks()
函數(shù)的最后一步笙隙,通過(guò)調(diào)用LockDataDirectory()
函數(shù)來(lái)完成,其中LockDataDirectory()
函數(shù)實(shí)現(xiàn)位于init.cpp的1167-1188行坎缭。該函數(shù)主要是確保只有一個(gè)比特幣進(jìn)程正在使用數(shù)據(jù)目錄竟痰,因?yàn)橐C數(shù)據(jù)目錄在同一臺(tái)機(jī)器中僅被一個(gè)比特幣核心核心程序所使用,否則如果多個(gè)比特幣核心程序同時(shí)使用同一數(shù)據(jù)目錄掏呼,將會(huì)造成該程序數(shù)據(jù)內(nèi)容產(chǎn)生不一致的情況坏快。
在LockDataDirectory中,首先獲取數(shù)據(jù)目錄值憎夷,然后打開數(shù)據(jù)目錄下的.lock文件莽鸿,判斷其是否存在。該文件在ubuntu中的$HOME/.bitcoin/文件夾下是存在的岭接,其內(nèi)容為空富拗。.lock文件的作用是:該文件將通過(guò)lock.try_lock()被鎖定,但是如果已被其它先啟動(dòng)的比特幣程序鎖定了的話鸣戴,本次鎖定將失效啃沪,同時(shí)提示錯(cuò)誤信息,并返回false窄锅,整個(gè)程序?qū)⑼顺觥?/p>
********************************************
第四步總結(jié):
這一步在函數(shù)AppInitSanityChecks()
中實(shí)現(xiàn)创千,主要分為三個(gè)部分:首先是初始化橢圓曲線密碼(ECC),為一系列的準(zhǔn)備工作——選擇SHA256準(zhǔn)備入偷、隨機(jī)數(shù)生成器準(zhǔn)備追驴、橢圓曲線功能開啟、驗(yàn)證橢圓曲線開啟和重置內(nèi)存疏之,都是為了下一步做準(zhǔn)備工作殿雪;然后是InitSanityCheck()
函數(shù)進(jìn)行的健全性檢查——包括橢圓曲線加密結(jié)果的完整性驗(yàn)證、驗(yàn)證當(dāng)前運(yùn)行環(huán)境是否支持C/C++運(yùn)行環(huán)境和驗(yàn)證系統(tǒng)的隨機(jī)數(shù)生成器是否可用锋爪,是此步驟的核心丙曙;最后是AppInitSanityChecks()
函數(shù)控制的目錄鎖檢查——確保只有一個(gè)比特幣進(jìn)程正在使用數(shù)據(jù)目錄,也是確保只有一個(gè)bitcoind運(yùn)行其骄。
********************************************
(十二)繼續(xù)看bitcoind.cpp中的147-161行
if (gArgs.GetBoolArg("-daemon", false))
{
#if HAVE_DECL_DAEMON
fprintf(stdout, "Bitcoin server starting\n");
// 后臺(tái)運(yùn)行
if (daemon(1, 0)) { // don't chdir (1), do close FDs (0)
fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno));
return false;
}
#else
fprintf(stderr, "Error: -daemon is not supported on this operating system\n");
return false;
#endif // HAVE_DECL_DAEMON
}
大概可以知道這一部分主要是-deamon
參數(shù)的解析亏镰,該參數(shù)應(yīng)用于比特幣核心的bitcond.exe控制臺(tái)程序,該程序?yàn)楸忍貛藕诵牡暮笈_(tái)服務(wù)程序拯爽。
這段代碼的實(shí)現(xiàn)流程是:
①首先if條件語(yǔ)句判斷是否在啟動(dòng)時(shí)設(shè)置了守護(hù)進(jìn)程
-daemon
參數(shù)索抓,如果設(shè)置了則該參數(shù)使用設(shè)置的代碼,并且繼續(xù)下面的代碼;否則返回false逼肯。
②用條件編譯判斷是否包含HAVE_DECL_DAEMON
宏定義耸黑,包含則繼續(xù)下面的代碼;不包含則將在控制臺(tái)中輸出當(dāng)前系統(tǒng)不支持守護(hù)進(jìn)程的錯(cuò)誤提示汉矿。
HAVE_DECL_DAEMON
宏定義在未經(jīng)編譯的源碼中是不包含的崎坊,需經(jīng)過(guò)./configure配置后才會(huì)出現(xiàn)备禀,該定義位于config/bitcoin-config.h的106行洲拇,默認(rèn)為1,表示定義了daemon曲尸;如果為0則為不定義daemon赋续。③如果包含
HAVE_DECL_DAEMON
宏定義,且該值為1另患,則在控制臺(tái)中輸出"Bitcoin server starting"信息纽乱,表明比特幣后臺(tái)守護(hù)進(jìn)程在運(yùn)行。
④然后再判斷daemon(1, 0)
函數(shù)的返回值昆箕,如果返回值為非零鸦列,則輸出errorno對(duì)應(yīng)的錯(cuò)誤提示,并返回false鹏倘,程序退出薯嗤;如果daemon()函數(shù)返回為0時(shí)則正常運(yùn)行。
daemon()
函數(shù)是成功時(shí)返回0纤泵,否則返回-1并設(shè)置errno骆姐。daemon()
函數(shù)的用法可以參考:http://www.cppblog.com/cjz/archive/2011/12/29/163123.html由
daemon(1, 0)
可知:當(dāng)前設(shè)置的參數(shù)為1和0,根據(jù)其注釋我們可以得知捏题,該守護(hù)進(jìn)程將當(dāng)前目錄設(shè)為根目錄玻褪,即程序中的相對(duì)目錄是從該目錄開始的,第二個(gè)參數(shù)設(shè)置為0則表示不輸出任何信息公荧。
********************************************
-deamon
參數(shù)設(shè)置總結(jié):
該參數(shù)應(yīng)用于比特幣核心的bitcond.exe控制臺(tái)程序带射,該程序?yàn)楸忍貛藕诵牡暮笈_(tái)服務(wù)程序。一般在啟動(dòng)時(shí)會(huì)設(shè)置此參數(shù)循狰,表示啟動(dòng)bitcoin的后臺(tái)守護(hù)進(jìn)程窟社,但是如果判斷沒有在啟動(dòng)時(shí)設(shè)置此參數(shù)會(huì)報(bào)錯(cuò),提示當(dāng)前系統(tǒng)不支持守護(hù)進(jìn)程的錯(cuò)誤消息晤揣。并且就算是啟動(dòng)了該參數(shù)桥爽,如果設(shè)置的參數(shù)不對(duì),也會(huì)報(bào)錯(cuò)昧识∧扑模總之就是檢測(cè)在程序啟動(dòng)時(shí)是否有-deamon
參數(shù)并是否是正確設(shè)置的值,保證程序正常啟動(dòng)后臺(tái)服務(wù)。
********************************************
(十三)繼續(xù)看bitcoind.cpp中的163-167行
// 在守護(hù)進(jìn)程后鎖定數(shù)據(jù)目錄缀去。
if (!AppInitLockDataDirectory())
{
// 如果鎖定數(shù)據(jù)目錄失敗侣灶,立即退出
exit(EXIT_FAILURE);
}
對(duì)于這部分我們可以在注釋中了解它主要是完成鎖定數(shù)據(jù)目錄的。這部分主要調(diào)用了AppInitLockDataDirectory()
函數(shù)缕碎,該函數(shù)的聲明在init.h的51行:
/**
* 鎖定比特幣核心數(shù)據(jù)目錄褥影。
* 注意: 這只能在守護(hù)進(jìn)程之后完成。 如果此功能失敗咏雌,請(qǐng)勿調(diào)用Shutdown()凡怎。
* 前提: 應(yīng)該解析參數(shù)并讀取配置文件,應(yīng)該調(diào)用過(guò)AppInitSanityChecks赊抖。
*/
bool AppInitLockDataDirectory();
從該注釋中我們知道這個(gè)步驟只能在守護(hù)進(jìn)程完成之后统倒,而且要是調(diào)用過(guò)AppInitSanityChecks()
函數(shù)之后。AppInitSanityChecks()
函數(shù)我們前面已經(jīng)學(xué)習(xí)過(guò)了氛雪,這個(gè)是目標(biāo)鎖檢查函數(shù)房匆,其中它主要是調(diào)用LockDataDirectory()
函數(shù)來(lái)完成的。我們?cè)倏纯此膶?shí)現(xiàn)文件报亩,在init.cpp中的1211-1221行:
bool AppInitLockDataDirectory()
{
// 在守護(hù)進(jìn)程之后浴鸿,再次獲取數(shù)據(jù)目錄鎖定并保持它直到退出;
// 這為競(jìng)爭(zhēng)條件創(chuàng)造了一個(gè)小小的窗口弦追,但是這個(gè)條件是無(wú)害的:它最多會(huì)讓我們退出而不打印消息給控制臺(tái)岳链。
if (!LockDataDirectory(false)) {
// 在LockDataDirectory內(nèi)部打印詳細(xì)的錯(cuò)誤
return false;
}
return true;
}
不出所料,該函數(shù)的實(shí)現(xiàn)真的調(diào)用了LockDataDirectory()
函數(shù)骗卜,該函數(shù)實(shí)現(xiàn)位于init.cpp的1167-1188行宠页,它是目錄鎖檢查的主要實(shí)現(xiàn)函數(shù),主要是確保只有一個(gè)bitcoind運(yùn)行寇仓。這里是再次獲取數(shù)據(jù)目錄鎖定并一直保持它目錄鎖鎖定狀態(tài)举户,直到程序的退出。