比特幣原始碼解析(9)
0x00 摘要
bitcoin-cli
:是Bitcoind的一個功能完備的RPC客戶端,包括查詢區塊,交易資訊等等,具體將在相應章節介紹。bitcoind
:是比特幣執行的核心程式俗稱bitcoin core,也是我們分析的重點。bitcoin-qt
:比特幣錢包。bitcoin-tx
:比特幣交易處理模組,支援交易的查詢和建立。test_bitcoin
:執行各個模組的測試程式碼。test_bitcoin-qt
:執行錢包的模組測試程式碼。
我們首先從最核心的bitcoind開始分析,然後再看其他的,因為其他部分的程式碼使用的很多類、很多函式都是bitcoind中使用過的,所以分析完bitcoind,其他部分也就輕而易舉。
另外提及一下程式碼的檢視軟體,我用的是Sublime Text,能快速的找到函式的定義和實現的位置,並且還支援在專案內查詢,一個好的編輯器對於程式碼的分析也是很有幫助的。
0x01 Main
對於c++程式碼,整個程式都是從main函式開始執行的,所以我們首先尋找bitcoind的main函式。而一般編譯出來的可執行程式都是有對應檔名的cpp檔案,所以我們找到了src/bitcoind.cpp,程式碼拉到最後就找到了我們的main函式。
// src/bitcoind.cpp line 188
int main(int argc, char* argv[])
{
SetupEnvironment() ;
// Connect bitcoind signal handlers
noui_connect();
return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
0x02 SetupEnvironment
找到SetupEnvironment的實現位置,位於src/util.cpp中,sublime中直接右鍵Goto Definition即可。
// src/util.cpp line 834
void SetupEnvironment()
{
#ifdef HAVE_MALLOPT_ARENA_MAX
if (sizeof(void*) == 4) { //判斷是否為32位系統
mallopt(M_ARENA_MAX, 1);
}
#endif
#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
try {
std::locale(""); // Raises a runtime error if current locale is invalid
} catch (const std::runtime_error&) {
setenv("LC_ALL", "C", 1);
}
#endif
std::locale loc = fs::path::imbue(std::locale::classic());
fs::path::imbue(loc);
}
函式首先通過sizeof(void*) == 4
來判斷當前系統是否是32位,如果是64位的話那麼sizeof(void*)
值就為8。mallopt
函式是用來控制malloc
記憶體分配時的行為的(具體請參考http://man7.org/linux/man-pages/man3/mallopt.3.html),而M_ARENA_MAX
引數是值最多能建立的arena數,一個arena是指malloc
在分內記憶體時的一個記憶體池,而這個arena是執行緒安全的,也就是說多執行緒訪問時是互斥訪問的,既然是互斥訪問的,那麼很明顯,當arena數量越多時,執行緒的競爭就越小,但是需要的記憶體也就越多(因為arena就相當於一次性申請大量記憶體,然後在malloc時慢慢分配出去)。通過程式碼中的註釋,我們發現glibc
庫會為每個核建立2個arena,而這會對32為系統造成虛擬地址空間不足的問題,所以這裡設為1.
下面是locale()
是設定系統區域,這將決定程式所使用的當前語言編碼、日期格式、數字格式及其它與區域有關的設定。最後兩行是檔案路徑的本地化設定,主要設計寬字元(Wide char)和多位元組(Multi bytes)之間的轉換問題。
0x03 noui_connect
// src/noui.cpp line 52
void noui_connect()
{
// Connect bitcoind signal handlers
uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox);
uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion);
uiInterface.InitMessage.connect(noui_InitMessage);
}
// src/ui_interface.h
// line 74-81
/** Show message box. */
boost::signals2::signal<bool (const std::string& message,
const std::string& caption,
unsigned int style),
boost::signals2::last_value<bool>> ThreadSafeMessageBox;
/** If possible, ask the user a question.
* If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */
boost::signals2::signal<bool (const std::string& message,
const std::string& noninteractive_message,
const std::string& caption, unsigned int style), boost::signals2::last_value<bool> > ThreadSafeQuestion;
/** Progress message during initialization. */
boost::signals2::signal<void (const std::string &message)> InitMessage;
extern CClientUIInterface uiInterface; // line 123
這裡在CClientUIInterface
類中定義了一些訊號,其中三個分別是ThreadSafeMessageBox
,ThreadSafeQuestion
和InitMessage
。再看前面的noui_connect
中的變數,我們發現通過connect
連線的插槽函式定義和訊號中的定義完全一致,所以當訊號觸發的時候,這些連線的函式都會被呼叫。